Hello, Structs!
In this section, you rewrite your HelloShaders effect to use C-style structs. Data structures provide a way to supply multiple shader inputs and outputs with a bit more organization than as individual parameters.
To start, create a new effect and material in NVIDIA FX Composer. You can do this through the Add Effect Wizard, as you did at the beginning of this chapter, or you can copy HelloShaders.fx to a new file, HelloStructs.fx. I like the second option because you’ll often reuse your shader code, building upon the previous material. With a copied HelloStructs.fx file, you add it to NVIDIA FX Composer by right-clicking the Materials section of the Assets panel and choosing Add Material from File. Find and select your HelloStructs.fx file, and you’ll see newly created HelloStructs and HelloStructs_Material objects in the Assets panel.
Listing 4.6 contains a full listing of the HelloStructs.fx effect.
Listing 4.6 HelloStructs.fx
cbuffer
CBufferPerObject {float4x4
WorldViewProjection : WORLDVIEWPROJECTION; }RasterizerState
DisableCulling { CullMode = NONE; };struct
VS_INPUT {float4
ObjectPosition: POSITION; };struct
VS_OUTPUT {float4
Position: SV_Position; }; VS_OUTPUT vertex_shader(VS_INPUT IN) { VS_OUTPUT OUT = (VS_OUTPUT)0; OUT.Position = mul(IN.ObjectPosition, WorldViewProjection);return
OUT; }float4
pixel_shader(VS_OUTPUT IN) : SV_Target {return
float4
(1, 0, 0, 1); }technique10
main10 {pass
p0 { SetVertexShader(CompileShader
(vs_4_0, vertex_shader())); SetGeometryShader(NULL
); SetPixelShader(CompileShader
(ps_4_0, pixel_shader())); SetRasterizerState(DisableCulling); } }
The differences between HelloShaders.fx and HelloStructs.fx are minor but significant because they establish the conventions we use throughout this text. First, note what has not changed. The CBufferPerObject and DisableCulling objects are the same, as are the main10 technique and its pass. The body of the pixel shader hasn’t changed, either. What’s new are the two structs named VS_INPUT and VS_OUTPUT. These names identify the structures as vertex shader inputs and outputs, respectively. Notice that the VS_INPUT struct has the same ObjectPosition input variable as the HelloShaders vertex shader. The only difference is that the variable is declared as a float4 instead of a float3. This removes the need to append the value 1 to the w component of the vector. Additionally, the vertex shader now returns a VS_OUTPUT instance instead of a float4, and the SV_Position semantic is no longer associated directly to the return value because it’s attached instead to the Position member of the VS_OUTPUT struct. That Position member replaces the previously unnamed return value of the vertex shader from the HelloShaders effect.
Next, examine the body of your updated vertex shader. Notice that you’re declaring and returning a VS_OUTPUT instance, and in C-programming fashion, you access the Position member of the instance through the dot operator. Also notice that the ObjectPosition member of the VS_INPUT parameter IN is used for the mul invocation. In addition, you’re using a C-style cast to initialize the members of the OUT variable to zero. Although this is not strictly necessary, it is a good programming practice.
Finally, observe that the input parameter for the pixel shader is the output data type from the vertex shader. You’re not using any members of the input in this example, but you will do so in future shaders. The point of this reorganization is that now you can add shader inputs and outputs without modifying the signature of your vertex and pixel shaders. The output of HelloStructs should be identical to that of HelloShaders, in Figure 4.3.