Because Direct3D no longer supports the fixed
function pipeline, it falls to you to designate the behavior of how
vertices and pixels are handled. The fixed function pipeline previously
had a set way of processing vertices as they passed through on their way
to being drawn. This restricted the options you had to fully control
the pipeline, limiting you to the functionality the pipeline supported.
There was a single method for handling lighting and a set maximum value
of textures you could work with. This severely limited the effects you
could achieve using Direct3D. With Direct3D 10 that all changed. The
fixed pipeline is no more and how objects are drawn is completely within
your hands.
As each vertex is processed by the system, you get
the opportunity to manipulate it or to allow it to pass through
unchanged. The same can be said for pixels. Any pixel being rendered by
the system also is provided to you to be changed before going to the
screen. The functionality to change vertices and pixels is contained
within Direct3D’s shader mechanism.
Shaders are Direct3D’s way of exposing pieces of
the pipeline to be dynamically reprogrammed by you. Direct3D supports
three types of shaders: vertex, pixel, and geometry.
Vertex shaders operate on just what you’d expect,
vertices. Every vertex going through the pipeline is made available to
the current vertex shader before being outputted. Likewise, any pixel
being rendered must also pass through the pixel shaders. Geometry
shaders are a new special type of shader introduced with Direct3D 10.
Geometry shaders allow for multiple vertices to be manipulated
simultaneously, giving the option of controlling entire pieces of
geometry. Since shaders are required for even the simplest 3D drawing,
I’ll explain how shaders are loaded and applied.
Loading an Effect File
Shaders are bundled together in what’s called an
effect. Most of the time, you’ll be using a combination of vertex and
pixel shaders together to create a certain behavior called a technique.
Because the different types of shaders depend on each other, their
functionality is combined into one file. The effect file contains the
code for vertex and pixel shaders and now with Direct3D10, geometry
shaders as well.
The simplest form of effect contains a technique
with a vertex shader that allows the incoming data from the vertex
structure to just pass through. This means the vertex position and other
properties will not be changed in any way and are passed on to the next
stage in the pipeline.
A simple pixel shader will perform no calculations and return only a single color. Geometry shaders are optional and can be NULL. The contents of a basic effect file are shown next.
// PS_INPUT - input variables to the pixel shader
// This struct is created and filled in by the
// vertex shader
struct PS_INPUT
{
float4 Pos : SV_POSITION;
float4 Color : COLOR0;
};
////////////////////////////////////////////////
// Vertex Shader - Main Function
///////////////////////////////////////////////
PS_INPUT VS (float4 Pos : POSITION)
{
PS_INPUT psInput;
psInput.Pos = Pos;
psInput.Color = float4 (1.0f, 1.0f, 0.0f, 1.0f);
return psInput;
}
///////////////////////////////////////////////
// Pixel Shader
///////////////////////////////////////////////
float4 PS(PS_INPUT psInput) : SV_Target
{
return psInput.Color;
}
// Define the technique
technique10 Render
{
pass P0
{
SetVertexShader( CompileShader( vs_4_0, VS() ) );
SetGeometryShader( NULL );
SetPixelShader( CompileShader( ps_4_0, PS() ) );
}
}
Go ahead and look at the code within the effect
file. You probably won’t understand much of the syntax yet, but it
should give you an idea as to what to expect.
Effects are usually loaded in from a file using the D3DX10CreateEffectFromFile
function. I’ll only go over the use of the function now because effect
files and their components will be explained in more detail later. An
example of how to use the D3DX10CreateEffectFromFile function is shown next.
// The name of the effect file to load
LPCWSTR effectFilename = L"..\\simple.fx";
// The effect object
ID3D10Effect* pEffect = NULL;
// Load the effect file and create the effect object
HRESULT hr = D3DX10CreateEffectFromFile (effectFilename,
NULL,
NULL,
"fx_4_0",
D3D10_SHADER_ENABLE_STRICTNESS,
0,
pD3DDevice,
NULL,
NULL,
&pEffect,
NULL);
if (FAILED(hr))
{
return false;
}
The file name contained in the effectFilename variable is loaded and the resulting effect is created and placed into the pEffect
variable. If the loading of the effect was successful, the effect can
now be used. One important parameter to take note of is the fourth
parameter; this parameter specifies the shader model the shader should
be compiled with upon load. Under Direct3D10, the new shader model 4.0
is used.
The Technique
Effects
files also include a section called the technique. The technique is a
way for the effect file to declare a method of shading behavior.
Each technique has a set of vertex and pixel
shaders that it uses as vertices and pixels are passed through the
pipeline. Effects allow for multiple techniques to be defined but you
must have at least one technique defined. Each technique can also
contain multiple passes. Most techniques you come across will contain
only one pass but just be aware that multiple passes are possible for
more complicated effects. Each pass uses the available shader hardware
to perform different kinds of special effects.
After loading the effect file, you need to gain access to its technique in order to use it. The technique is then stored in an ID3D10EffectTechnique
object for use later when rendering or defining a vertex layout. A
small code sample showing how to create the technique object from an
effect is shown here:
// The name of the technique in the effect file
LPCSTR effectTechniqueName = "Render";
// The technique object
ID3D10EffectTechnique* pTechnique;
// Obtain the technique
pTechnique = pEffect->GetTechniqueByName( effectTechniqueName );
You now have a technique object ready to use when
drawing your objects. Techniques are used by looping through the
available passes and calling your draw functions. Before drawing using
the shader technique in a pass, the technique is applied preparing the
hardware for drawing. The Apply function is used to set the current technique.
// Render an object
D3D10_TECHNIQUE_DESC techniqueDescription;
pTechnique->GetDesc(&techniqueDescription);
// Loop through the technique passes
for(UINT p=0; p < techniqueDescription.Passes; ++p)
{
pTechnique->GetPassByIndex(p)->Apply(0);
// Draw function
}
Note
The Apply function takes only a single parameter that is currently unused for Direct3D. 0 is always passed in for this value.