?/TD> |
Microsoft DirectX 9.0 |
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.
If you already know how to build and run Direct3D samples, you can cut and paste code from this example into your existing application.
The following steps are discussed in this article.
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 vertex data is declared in a global array of vertices (g_Vertices). The four vertices are centered about the origin.
// Declare vertex data structure. struct CUSTOMVERTEX { FLOAT x, y, z; }; // Declare the vertex position and diffuse color data. CUSTOMVERTEX g_Vertices[]= { // x y z { -1.0f, -1.0f, 0.0f }, { +1.0f, -1.0f, 0.0f }, { +1.0f, +1.0f, 0.0f }, { -1.0f, +1.0f, 0.0f }, };
This shader applies a constant color to each vertex. The shader file VertexShader.vsh follows:
vs_1_1 // version instruction dcl_position v0 // define position data in register v0 m4x4 oPos, v0, c0 // transform vertices by view/projection matrix mov oD0, c4 // load constant color
This file contains three arithmetic instructions and a register definition. The first instruction in a shader file must be the shader version declaration. The vs instruction declares the vertex shader version, which is 1_1 in this case.
The dcl_position instruction defines register v0 as the source of vertex position data. The m4x4 instruction 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 mov instruction 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.
D3DCAPS9 caps; m_pd3dDevice->GetDeviceCaps(&caps); // initialize m_pd3dDevice before using if( caps.VertexShaderVersion < D3DVS_VERSION(1,1) ) return E_FAIL;
The caps structure returns the functional capabilities of the hardware after IDirect3DDevice9::GetDeviceCaps is called. Use the D3DVS_VERSION macro to test the supported version number. If the version number is less than 1_1, this call will fail. The result of this method should be used to control whether vertex shaders are invoked by an application.
The vertex declaration describes the vertex data.
// Create the shader declaration. D3DVERTEXELEMENT9 decl[] = { { 0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 }, D3DDECL_END() };
This declaration specifies the following: The data comes from stream 0, starts at an offset of 0 from the start of the stream, declares the position data as 3 floating-point numbers (D3DDECLTYPE_FLOAT3), tells the tessellator to copy the vertex data (D3DDECLMETHOD_DEFAULT), defines the usage for the data as position data (D3DDECLUSAGE_POSITION), and specifies the usage index of 0.
D3DDECLEND() is a macro that is used to end a vertex declaration.
The shader is assembled and created next.
LPDIRECT3DPIXELSHADER9 m_pVertexShader; TCHAR strShaderPath[512]; LPD3DXBUFFER pCode; // Buffer with the assembled shader code LPD3DXBUFFER pErrorMsgs; // Buffer with error messages DXUtil_FindMediaFileCb( strShaderPath, sizeof(strShaderPath), _T("VertexShader.vsh") ); D3DXAssembleShaderFromFile( strPixelShaderPath, NULL, NULL, 0, &pCode, &pErrorMsgs, NULL ); m_pd3dDevice->CreateVertexShader((DWORD*)pCode->GetBufferPointer(), &m_pVertexShader) pCode->Release(); pErrorMsgs->Release()
When the shader file is located, D3DXAssembleShaderFromFile reads and validates the shader instructions. IDirect3DDevice9::CreateVertexShader then takes the shader declaration and the assembled instructions and creates the shader. It returns the shader object which is used during the draw call.
CreateVertexShader is used to create a programmable shader.
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 IDirect3DDevice9::DrawPrimitive.
// Update vertex shader constants from view projection matrix data. D3DXMATRIX mat, matView, matProj; D3DXMatrixMultiply( &mat, &matView, &matProj ); D3DXMatrixTranspose( &mat, &mat ); m_pd3dDevice->SetVertexShaderConstantF( 0, (float*)&mat, 4 ); // Declare and define the constant vertex color. float color[4] = {0,1.0,0,0}; m_pd3dDevice->SetVertexShaderConstantF( 4, (float*)&color, 1 ); // Turn off specular light contribution because the specular value // is not written in the vertex shader. m_pd3dDevice->SetRenderState( D3DRS_SPECULAR, FALSE ); // Render the output. m_pd3dDevice->SetStreamSource( 0, m_pQuadVB, sizeof(CUSTOMVERTEX) ); m_pd3dDevice->SetVertexShader( m_pVertexShader ); m_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLEFAN, 0, 2 );
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 may change between rendered frames.
As usual, IDirect3DDevice9::DrawPrimitive renders the output data using the vertex data provided from IDirect3DDevice9::SetStreamSource. IDirect3DDevice9::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.