A shader code let us to manage our graphics by transforming our vertices from what comes from vertex buffer to one or more float4 that will become our colors on the screen.
Let’s examine better the shader
A shader it’s very similar to a generic C code, but much more simple and without pointers. In the code it’s possible to define functions, structures and also include other files (in this mode we can separate effects in fx files).
At end of file there is the technique that describes what functions will be used into device for vertex,pixel and geometry shader as i’ve said in previous tutorials, the shader it’s executed for each vertex and pixes that are called in the Draw() function. This means that it’s difficult to have a complete control of single pixel or vertex of a primitive. It’s important to remember this the first input it’s vertex buffer that contains vertices of scene. In previous tutorial we have saw how, with vertex layout and semantics, it’s possible to get vertices contained in the buffer. For each vertex of vertexbuffer , the device executes shader code. Then a first mode to insert data it’s the vertex buffer, using his variables. This for data that must be unique for each vertex. But there is some data that are in common for all vertices. These informations are called constans. A const data must be inserted in the shader code but out from functions (like global variables). These are visible in all the shader but they cannot be modified from code (if you modify a const variable you will not get an error, but it’s value will return to initial state in the next cycle). This won’t let you to store data (for example, understand how many times have you done a cycle) the only way to set a const value it’s by passing it by device.
float4 valore:MyValue;
struct VS_INPUT
{
float4 Pos : POSITION;
float4 color:COLOR;
};
struct PS_INPUT
{
float4 Pos : SV_POSITION;
float4 color:COLOR;
};
PS_INPUT VS( VS_INPUT input )
{
PS_INPUT output = (PS_INPUT)0;
output.Pos = input.Pos;
output.color=input.color;
return output;
}
float4 PS( PS_INPUT input) : SV_Target
{
return input.color + valore;
}
technique10 Render
{
pass P0
{
SetVertexShader( CompileShader( vs_4_0, VS() ) );
SetGeometryShader(NULL);
SetPixelShader( CompileShader( ps_4_0, PS() ) );
}
}
In this example, in top i’ve defined a float4 const with “valore” as name, and “MyValue” as Semantic. In pixel shader i’ve used it to increase the input color value. The semantics it’s very useful in DirectX10 to give a meaningful name about the variable use.
In HLSL we have some primitive types:
- bool : true or false
- int : 32 bit
- uint : 32 bit unsigned (only positive values)
- half : 16 bit (It’s a float but with half precision. It’s only a pretended type for retrocompability, infact DirectX10 converts it to a float (32 bit) value automatically)
- float : 32 bit
- double : 64-bit
More precision gives us better results but the code will run more slow and will gain more memory. In HLSL variables can be anticipated with prefixes snorm and unorm
unorm float myValue;
snorm prefix will remember to compiler that that variable value must be limited in the range -1 and 1, while unorm from 0 and 1. It seems foolish, but in the shader are used a lot of values included in this range
It’s possible also initialize variables with a value, as we like. This value will be, obiously, overwritten if we, from device, set a new value
In HLSL we have got also vector types, which are very importants for our scopes. A vector it’s a base type repeated. For example float2 means a vector with 2 floats
I valori vanno da 1 a 4 (non è possibile utilizzare vettori più grandi). Un valore di tipo vettore si utilizza come una struttura ed i suoi sottovalori saranno X,Y, Z e W.
The array values goesfrom 1 to 4 (you can’t use bigger values). A value of a vector it’s used as a structure and it’s subvalues will be X,Y,Z,W
Example:
float3 myValue;
myValue.x=0;
Vector can also be used as a primitive type. This menas that there is an automatic operator overloading in +,-,*,/
The overloading will apply the default operation for each value of vector
Example:
float3 v1; float3 v2;
v1 + v2 <=> (v1.x+ v2.x , v1.y+ v2.y , v1.z+ v2.z)
Vector value can be read and set in a lot of mode. For example
float4 v1;
v1.xy=5;
x and y will be set to 5
You can also write xz, or yz, wx, as you want.
Another important data type it’s the matrix.A matrix it’s a vector of vectors.
Float4x4 myMatrix;
This is a matrix composed by 16 floats values. To read and set a value, you must write
matrix.m11;
You can also create structures:
struct myStruct
{
int val1;
float val2;
};
For arrays you must use const dimensions:
float val[10];
float val[x]; //this is an error
These are main type of DirectX. Now let’s see how these values can be set and read from our C++ code
Each shader variable can be obtained by his effect.
ID3D10EffectVariable* myVariable;
myVariable=effect->GetVariableByIndex(index);
myVariable=effect->GetVariableByName(name);
myVariable=effect->GetVariableBySemantic(semantic);
You can choose if obtain it by the creation order, its name or its semantic.
With this code you will obtain a generic object that you will work with ID3D10EffectVariable methods
ID3D10EffectVectorVariable* var1 = myVariable->AsVector();
ID3D10EffectMatrixVariable* var2 = myVariable->AsMatrix();
ID3D10EffectScalarVariable* var3 = myVariable->AsScalar();
In this way you’ll be able to cast generic value in Vector, Matrix, Scalar and other. The cast type will be under your control (if you want, you can also get an int from a matrix!). To set shader variables you will use Set functions contained in each object. For example, this vector
var1->SetFloatVector(value);
var1->SetIntVector(value);
Where value, in first case, it’sa float array, and in second an int array. You can also use
var1->SetFloatVectorArray(data,offset,count);
This is useful to set entire array. Data it’s your float[],offset it’s the start float array point (for entire array use 0), and count it’s the number of floats.
To set structures you must use effectVariable to get it’s data, with istructions
myVariable->GetMemberByIndex(index);
myVariable->GetMemberByName(name);
myVariable->GetMemberBySemantic(semantic);
To set entire structure you must use
myVariable->SetRawValue(data,offset,count);
where data it’s a void pointer.
Last thing: the GetDesc description will let you toh ave a description about variableTo pass a value in the shader you just must use SetValue before the draw istruction of your mesh. These values will remain in the shader also after, then for variable that you rarely will change, it’s better to not set it in the Render function.
In next tutorial we will realize a complete vertex shader.
A special thanks goes to Vincent who translate this lesson.
I commenti sono disabilitati.