Introduction to Advanced Shader Language HLSL
1. Introduction to Data Types
Unlike CPUs, in a graphics card chip, the smallest data throughput unit is a quadruple composed of 32-bit floating-point numbers. This makes a lot of sense. Just think about all the data involved in your rendering process, the most complex ones are four-dimensional coordinates (x, y, z, w) or colors (r, g, b, a), so that the GPU can process a quadruple at once. And integers are used in a component of a quadruple in graphics cards, while many graphics cards do not directly support integers and Boolean values, but instead convert them to floating-point numbers. As for the matrix, it is usually represented by 4 quadruples as a 4x4 matrix (by default, one quadruple stores one row, or column storage can be specified, which belongs to detail problems, goto: detail problems), and so on for other sizes
Reflected in the program, a four-dimensional vector is declared as float4, a four-dimensional square matrix is declared as float4x4, and so on. Of course, you can also use any vector or matrix with dimensions not exceeding 4, such as int3, float3x3, double1. This double1 is actually a scalar, 1 can be omitted and not written.
2. Texture& Introduction to Sampler
These two things can be regarded as special type variables. Texture is the texture resource used in Shader, and I don't think there's much to say about it. Let me explain the sampler: Actually, each texture needs to use a sampler when in use. The sampler is equivalent to a structure that not only stores the data of the texture itself, but also includes sampling information such as filtering parameters. Usually, instructions such as reading textures receive parameters of the sampler type rather than directly receiving texture maps. Declare and use textures or samplers just like using regular variables. Here are some methods for initializing the sampler, let's wait for the following examples to explain.
3. Complete List of Data Types
The data types include value type, vector, matrix, sampler, and structure
1. Value type
Bool Boolean variable
Half 16 is plastic surgery
Int 32-bit integer
Float single precision floating-point number
Double Double precision floating-point number
Declaration method: float f
Assignment method: f= 1
2. Vector
Declaration method: float4 f
Assignment method: f= {1,2,3,4}
Value method: float3 ff= f. RGB
Explanation: The specified field in the vector can be accessed through xyzw or rgba, where x or r represents field number 0. Not only can one field be operated on independently, but multiple fields can also be operated on simultaneously, for example, 3 f.xyz, which means multiplying all xyz in f by 3
3. Matrix
Declaration method: float2x4 f; First, second, second
Assignment method: f= {1,1,2,2,3,4,4}
Value taking method: float ff= F [0] [0]
Explanation: If you want to perform multiplication on a matrix, please use the mul function, such as mul (ff, f)
4. Sampler Declaration method:
Texture// Texture variables
Sampler TextureSampler= Sampler_ State//Texture sampler
{
Texture=// Texture objects used by texture samplers
MinFilter= Linear// Reduce graphics using linear filtering
MagFilter= Linear// Enlarge the graph using linear filtering
MipFilter= Linear// Mipmap uses linear filtering
AddressU; Wrap// The texture addressing mode in the U direction adopts the Wrap method
AddressV; Wrap// The texture addressing mode in the V direction adopts the Wrap method
}
Assignment method: Assign a value to Texture in C #, effect Parameters ["Texture"] SetValue (Game. Content. Load (" "))
Value taking method: tex2D (TextureSampler, TEXCOORD0)
Explanation: MinFilter, MagFilter, MipFilter, AddressU, and AddressV are optional options. If not written, the default value, which is the value assigned above, will be used
5. Structure Declaration method:
Struct VertexShaderInput {
Float4 Position: Position
Float2 TextureCoordinates: TEXCOORD0
Float3 Normal: NORMAL
}
VertexShaderInput input
There is a slight difference in syntax between this and C #. It is written directly like this, without the need to write something like new
Assignment method: Consistent with C # syntax
Value taking method: Consistent with C # syntax.
4. Control flow
Control flow is like if... else, for, while, and so on. In the CPU, these control flows actually cause instruction jumps. But instruction redirection is not widely supported in GPUs, and most graphics cards in the past only knew how to execute instructions in order, sentence by sentence. Therefore, HLSL compilers may do reckless things such as unrolling loops, traversing branches, and so on to adapt to graphics cards. So be especially careful when using it, and not all control flow statements are supported in all situations. Many specific rules are still in the details.
5. Functions
HLSL provides many functions to call, which can be found in the Direct3D documentation; DirectX Graphics -> Reference -> HLSL Shader Reference -> There is a detailed list of these functions in HLSL Intrinsic Functions. You can also write functions for yourself, but in earlier Shader versions, just like inline functions, when compiling, the function needs to be expanded and inserted into the function call. Another thing I think you will definitely think of is what the main function will be. Vertex Shader and Pixel Shader each require a main function, to be specified by the programmer! That's right, programmers specify it outside of the Shader
The function definitions in HLSL are exactly the same as those in other programming languages:
ReturnValue FunctionName (parameterName: semantic)
{
//Function code goes here
}.
the return value of a function can be any HLSL types defined in, including combination types and void empty type. when defining a parameter list for a shader function, it is necessary to fully specify the semantic identifier following the variable. when defining function parameters, there are still some things to note, because HLSL there is no specific way to return a reference to a value in the parameter list, which requires defining a small number of keywords to achieve the same result.
Before declaring the parameter, using the keyword out can let the compiler know that the variable can be used for output. Additionally, the keyword inout allows variables to serve as both input and output:
Void GetColor (out float3 color)
{
Color; Float3 (0.0f, 1.0f, 1.0f)
}.
6. Vertex Shaders
When objects need to be drawn when transmitted through pipelines, their vertices will be sent to
Processing in your vertex shader. If you don't want to do any processing on the incoming vertices, you can directly pass them to the pixel shader for drawing. In most cases, you need to use at least one world or projection transformation to apply these vertices so that they are rendered in the correct spatial position
With vertex shaders, you can have a lot of control over vertices, not just simple transformations. You can translate vertices to any coordinate axis, change their color, or control any other properties.
PS_ Input VS_ Main (VS-Input vertex)
{
PS_ Input vsOut= (PS-Input) 0
VsOut. pos= Vertex. pos
VsOut.tex0= Vertex.tex0
Return vsOut
}.
This vertex shader looks like a function in C language. HLSL uses C syntax similar to C/C++ After understanding, learning HLSL is easier. We can see that the vertex renderer, named VS, takes float4 as a parameter and returns a value of float4. In HLSL, float4 is composed of four floating-point numbers. The colon defines the semantics of the parameter, and the same applies to the return value. As mentioned earlier, semantics in HLSL describes the properties of data. In the renderer above, we chose POSTION as the semantic input parameter for Pos, as this parameter contains vertex coordinate information. The returned semantics are SV_ Position (SV stands for System Value). SV_ Position is a predefined semantics, and the vertex semantics of pixel shaders are not Position, but SV_ The difference between these two semantics is that SV represents hardware interpolation. SV_ The result of using POSITION in PS is the same as the result of performing perspective division in the pixel shader yourself. See the input structure of vertex shaders and pixel shaders:
Struct VS_ Input
{
Float4 pos: Position
Float2 tex0: TEXCOORD0
}
Struct PS_ Input
{
Float4 pos: SV_ Position
Float2 tex0: TEXCOORD0
};
7. Pixel Shaders
Modern computer monitors typically display at high speeds, with the smallest unit of screen being pixels. Each pixel has a color, and each pixel is independent of each other. When we want to render a triangle on the screen, we don't draw the entire triangle as a solid. Actually, we draw the pixels of the triangle area
The operation of drawing a series of pixels covered by the three vertices of a triangle is called rasterization. The GPU first needs to determine which pixels are covered by triangular regions. Then the GPU calls the activated pixel renderer to render these pixels. The main goal of a pixel renderer is to calculate the color of each pixel. The renderer calculates vertex colors based on input, or if a geometric renderer is not used, as in this tutorial, the input of the pixel renderer will come directly from the output of the vertex renderer
Pixel shaders allow you to access any pixel before being output through pipelines. Before pixels are drawn onto the screen, you have the opportunity to change the color of each pixel. In some cases, you only need to simply return the pixel color passed in by the vertex or geometric shader, but in most cases, you need to handle the impact of lighting or mapping on the pixel color.
Float4 PS_ Main (PS-Input frag): SV_ TARGET
{
Return colorMap_ Sample (colorSampler_, frag. tex0)
}.
in the above function, SV_TARGET it returns the value semantics, which represents an output semantics used to specify the output of pixel shaders for rendering the target.
8. Semantic
A semantic name is a string that describes the purpose of an element. For example, if an element serves as the position of a vertex, its semantics are "POSTION". We can use this element for vertex colors through semantic "COLOR", for normal vectors through "NORMAL", and so on. Semantics bind an element to an HLSL shader as its input or output variable
In addition, we must update the input structure of vertex shaders and pixel shaders to allow for the use of map coordinates. The vertex shader will obtain map coordinates from the vertex cache block and directly pass them to the pixel shader, allowing them to access them.
Struct VS_ Input
{
Float4 pos: Position
Float2 tex0: TEXCOORD0
};
some common semantics include :
SV_ Position - The float4 value of a specific transformation position
NORMAL0- Define a normal vector
COLOR0- Define a color value
There are also some other semantics that can be found in the complete list of HLSL chapters in the DirectX SDK documentation. A large number of semantics end with a number, as multiple such semantic types may be defined.
9. register
Texture2D colorMap_: Register (t0)
SamplerState colorSampler_: Register (s0);
object colorMap_ yes Texture2D type, as it is used for 2 D textures, and colorSampler_ yes HLSL a type of advanced coloring language SamplerState . in order to bind these objects in the shader input of the rendering function we provide, we must use HLSL registration keywords register . to bind the first input map, we use t 0, here he represents the texture type, 0 represents using the first index texture. for using state objects s 0 for the same reason. because we use functions PSSetSamplers and PSSetShaderResource to pass an array element to our shader, we must bind the data index we use to each one HLSL variables. because we only have one texture and one sampling state, we only need to use t 0 and s 0 is sufficient. it should be noted that these registered caches must comply with our rendering function Render only as specified in can the cache be correctly registered to HLSL in the object.
10. Details
You may feel that what was mentioned earlier was too rough, and there are still many issues that have not been discussed, but relatively speaking, these are all considered minor details. For example, what are the reserved keywords in HLSL; The scope of variables; Detailed information on data types; The rules for using quadruple components, and so on, are documented in the Direct3D document -> DirectX Graphics -> Programming Guide -> The Programmable Pipeline -> Programmable HLSL Shaders -> The HLSL Language Basics is explained more clearly than I am, and I no longer need to translate it.