Microsoft DirectX 8.1 (version 1.0, 1.1) |
This example creates a vertex shader that applies a constant color to an object. The example will show the contents of the shader file as well as the code required in the application to set up the Microsoft® Direct3D® pipeline for the shader data.
To create a vertex shader
If you already know how to build and run Direct3D samples, you can cut and paste code from this example into your existing application.
This example uses a quadrilateral that is made up of two triangles. The vertex data will contain (x,y,z) position and a diffuse color. The D3DFVF_CUSTOMVERTEX macro is defined to match the vertex data. The vertex data is declared in a global array of vertices (g_Vertices). The four vertices are centered about the origin, and each vertex is given a different diffuse color.
// Declare vertex data structure. struct CUSTOMVERTEX { FLOAT x, y, z; DWORD diffuseColor; }; // Declare custom FVF macro. #define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE) // Declare the vertex position and diffuse color data. CUSTOMVERTEX g_Vertices[]= { // x y z diffuse color { -1.0f, -1.0f, 0.0f, 0xffff0000 }, // red - bottom right { +1.0f, -1.0f, 0.0f, 0xff00ff00 }, // green - bottom left { +1.0f, +1.0f, 0.0f, 0xff0000ff }, // blue - top left { -1.0f, +1.0f, 0.0f, 0xffffff00 }, // red and green = yellow - top right };
This shader applies a constant color to each vertex. The shader file VertexShader.vsh follows:
vs.1.0 // version instruction m4x4 oPos, v0, c0 // transform vertices by view/projection matrix mov oD0, c4 // load constant color
This file contains three instructions.
The first instruction in a shader file must be the shader version declaration. This instruction (vs) declares the vertex shader version, which is 1.0 in this case.
The second instruction (m4x4) transforms the object vertices using the view/projection matrix. The matrix is loaded into four constant registers c0, c1, c2, c3 (as shown below).
The third instruction (mov) copies the constant color in register c4 to the output diffuse color register oD0. This results in coloring the output vertices.
The device capability can be queried for vertex shader support before using a vertex shader.
D3DCAPS8 caps; m_pd3dDevice->GetDeviceCaps(&caps); // initialize m_pd3dDevice before using if( D3DSHADER_VERSION_MAJOR( caps.VertexShaderVersion ) < 1 ) return E_FAIL;
The caps structure returns the functional capabilities of the hardware after GetDeviceCaps is called. Use the D3DSHADER_VERSION_MAJOR macro to test the supported version number. If the version number is less than 1.0, this call will fail. The result of this method should be used to control whether or not vertex shaders are invoked by an application.
The shader is created by declaring the shader registers and compiling the shader file. Once created, Direct3D returns a shader handle, which is an integer number that is used to identify the shader.
// Create the shader declaration. DWORD dwDecl[] = { D3DVSD_STREAM(0), D3DVSD_REG(D3DVSDE_POSITION, D3DVSDT_FLOAT3), D3DVSD_REG( D3DVSDE_DIFFUSE, D3DVSDT_D3DCOLOR ), D3DVSD_END() };
The vertex declaration declares the mapping between input data streams and vertex buffers. Multiple streams can be declared in the shader declaration, up to the number specified in the MaxStreams cap. Vertex buffers are associated with input streams using SetStreamSource as illustrated in step 5.
The shader is assembled and created next.
// Create the vertex shader. TCHAR strPath[512]; // location of the shader file LPD3DXBUFFER pCode; // assembled shader code DXUtil_FindMediaFile( strPath, _T("VertexShader.vsh") ); D3DXAssembleShaderFromFile( strPath, 0, NULL, &pCode, NULL ); // assemble shader code m_pd3dDevice->CreateVertexShader( dwDecl, (DWORD*)pCode->GetBufferPointer(), &m_hVertexShader, 0 ))) pCode->Release();
Once the shader file is located, D3DXAssembleShaderFromFile reads and validates the shader instructions. CreateVertexShader takes the shader declaration and the assembled instructions and creates the shader. It returns the shader handle, which is used to render the output.
CreateVertexShader can be used to create programmable or fixed function shaders. Programmable shaders are generated if a pointer to a shader declaration is passed as the second parameter. Otherwise, a fixed function vertex shader is generated if NULL is passed as the second parameter.
Here is an example of the code that could be used in the render loop to render the object, using the vertex shader. The render loop updates the vertex shader constants as a result of changes in the 3-D scene and draws the output vertices with a call to DrawPrimitive.
// Turn lighting off. This is included for clarity but is not required. m_pd3dDevice->SetRenderState( D3DRS_LIGHTING, FALSE ); // Update vertex shader constants from view projection matrix data. D3DXMATRIX mat, matView, matProj; D3DXMatrixMultiply( &mat, &matView, &matProj ); D3DXMatrixTranspose( &mat, &mat ); m_pd3dDevice->SetVertexShaderConstant( 0, &mat, 4 ); // Declare and define the constant vertex color. float color[4] = {0,1,0,0}; m_pd3dDevice->SetVertexShaderConstant( 4, &color, 1 ); // Render the output. m_pd3dDevice->SetStreamSource( 0, m_pQuadVB, sizeof(CUSTOMVERTEX) ); m_pd3dDevice->SetVertexShader( m_hVertexShader ); m_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLEFAN, 0, 2 );
Lighting is turned off just to make it clear that the vertex color is from the shader only. This statement is optional in this example.
The view and projection matrixes contain camera position and orientation data. Getting updated data and updating the shader constant registers is included in the render loop because the scene might change between rendered frames.
As usual, DrawPrimitive renders the output data using the vertex data provided from SetStreamSource. SetVertexShader is called to tell Direct3D to use the vertex shader. The result of the vertex shader is shown in the following image. It shows the constant color on the plane object.