一.简介
顶点着色器(Vertex Shader)是一段运行在图形卡GPU中的程序,它可取代固定流水线(渲染管线)中的变换和光照环节
二.使用顶点着色器的步骤
(1)顶点声明的创建使用
在使用固定流水线(渲染管线)的时候,使用灵活的顶点格式(FVF)来描述顶点结构的分量
在可编程流水线中,顶点结构的描述为一个 D3DVERTEXELEMENT9 类型的结构数组,该结构数组中的每个元素都描述了顶点结构的一个分量
D3DVERTEXELEMENT9 decl[] = { {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0}, {0, 12, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0}, D3DDECL_END() }; // 顶点声明的创建 g_pd3dDevice->CreateVertexDeclaration(decl, &VertexDecl); // 顶点声明的启用 g_pd3dDevice->SetVertexDeclaration(VertexDecl);
(2)编写顶点着色器程序
// Globals matrix ViewProjMatrix; // Structures struct VS_INPUT { vertor position : POSITION; vector color : COLOR; }; struct VS_OUTPUT { vector position : POSITION; vector diffuse : COLOR; }; VS_OUTPUT Main(VS_INPUT input) { VS_OUTPUT output = (VS_OUTPUT)0; output.position = mul(input.position, ViewProjMatrix); output.diffuse = input.color; return output; }
(3)编译顶点着色器程序
HRESULT hr = 0; ID3DXBuffer* shader = 0; ID3DXBuffer* errorBuffer =0; hr = D3DXCompileShaderFromFile( L"shade.txt", 0, 0, "Main", "vs_1_1", D3DXSHADER_ENABLE_BACKWARDS_COMPATIBILITY, &shader, &errorBuffer, &VertexConstantTable);
(4)创建IDirect3DVertexShader9
hr = g_pd3dDevice->CreateVertexShader((DWORD*)shader->GetBufferPointer(), &TriangleShader);
(5)绑定顶点着色器
g_pd3dDevice->SetVertexShader(TriangleShader);
(6)释放顶点着色器
if(TriangleShader != NULL) TriangleShader->Release();
三.例子
1.彩色三角形
#include <Windows.h> #include <mmsystem.h> #include <d3dx9.h> #pragma warning( disable : 4996 ) // disable deprecated warning #include <strsafe.h> #pragma warning( default : 4996 ) #include <d3dx9math.h> LPDIRECT3D9 g_pD3D = NULL; LPDIRECT3DDEVICE9 g_pd3dDevice = NULL; LPDIRECT3DVERTEXBUFFER9 g_pVB = NULL; LPDIRECT3DVERTEXSHADER9 TriangleShader = NULL; LPDIRECT3DVERTEXDECLARATION9 VertexDecl = NULL; LPD3DXCONSTANTTABLE VertexConstantTable = NULL; D3DXHANDLE ViewProjMatrixHandle = NULL; struct CUSTOMVERTEX { FLOAT x, y, z; DWORD color; }; bool VertexShader() { if(TriangleShader) return true; HRESULT hr = 0; ID3DXBuffer* shader = 0; ID3DXBuffer* errorBuffer = 0; hr = D3DXCompileShaderFromFile( L"shade.txt", 0, 0, "Main", "vs_1_1", D3DXSHADER_ENABLE_BACKWARDS_COMPATIBILITY, &shader, &errorBuffer, &VertexConstantTable); hr = g_pd3dDevice->CreateVertexShader( (DWORD*)shader->GetBufferPointer(), &TriangleShader); if(shader) shader->Release(); if(errorBuffer) errorBuffer->Release(); return true; } bool VertexBuffer() { if(g_pVB) return true; g_pd3dDevice->CreateVertexBuffer( 3 * sizeof(CUSTOMVERTEX), 0, 0, D3DPOOL_MANAGED, &g_pVB, 0); CUSTOMVERTEX* v; g_pVB->Lock(0, 0, (void**)&v, 0); v[0].x = -1, v[0].y = -1, v[0].z = 0, v[0].color = D3DCOLOR_XRGB(255, 0, 0); v[1].x = 0, v[1].y = 1, v[1].z = 0, v[1].color = D3DCOLOR_XRGB(0, 255, 0); v[2].x = 1, v[2].y = -1, v[2].z = 0, v[2].color = D3DCOLOR_XRGB(0, 0, 255); g_pVB->Unlock(); return true; } bool VertexDeclaration() { if(VertexDecl) return true; D3DVERTEXELEMENT9 decl[] = { {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0}, {0, 12, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0}, D3DDECL_END() }; g_pd3dDevice->CreateVertexDeclaration(decl, &VertexDecl); g_pd3dDevice->SetVertexDeclaration(VertexDecl); return true; } bool GetHandles() { if(ViewProjMatrixHandle) return true; ViewProjMatrixHandle = VertexConstantTable->GetConstantByName(0, "ViewProjMatrix"); VertexConstantTable->SetDefaults(g_pd3dDevice); return true; } HRESULT InitD3D(HWND hWnd) { if (NULL == (g_pD3D = Direct3DCreate9(D3D_SDK_VERSION))) return E_FAIL; // Set up the structure used to create the D3DDevice D3DPRESENT_PARAMETERS d3dpp; ZeroMemory(&d3dpp, sizeof(d3dpp)); d3dpp.Windowed = TRUE; d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; d3dpp.BackBufferFormat = D3DFMT_UNKNOWN; d3dpp.EnableAutoDepthStencil = TRUE; d3dpp.AutoDepthStencilFormat = D3DFMT_D16; // Create the D3DDevice if (FAILED(g_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &g_pd3dDevice))) { return E_FAIL; } return S_OK; } VOID SetupMatrices() { D3DXVECTOR3 vEyePt(0.0f, 0.0f, -5); D3DXVECTOR3 vLookatPt(0.0f, 0.0f, 0.0f); D3DXVECTOR3 vUpVec(0.0f, 1.0f, 0.0f); D3DXMATRIXA16 matView; D3DXMatrixLookAtLH(&matView, &vEyePt, &vLookatPt, &vUpVec); //D3DXMatrixPerspectiveFovLH()函数中的最远、最近距离为相对于视点的距离(即vEyePt中的距离) D3DXMATRIXA16 matProj; D3DXMatrixPerspectiveFovLH(&matProj, D3DX_PI / 4, 1.0f, 1.0f, 200.0f); D3DXMATRIX ViewProjMatrix = matView * matProj; VertexConstantTable->SetMatrix(g_pd3dDevice, ViewProjMatrixHandle, &ViewProjMatrix); } VOID Cleanup() { if (g_pVB != NULL) g_pVB->Release(); if (g_pd3dDevice != NULL) g_pd3dDevice->Release(); if (g_pD3D != NULL) g_pD3D->Release(); if (TriangleShader != NULL) TriangleShader->Release(); if (VertexDecl != NULL) VertexDecl->Release(); if (VertexConstantTable != NULL) VertexConstantTable->Release(); } int Render() { VertexBuffer(); VertexShader(); VertexDeclaration(); GetHandles(); SetupMatrices(); g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(255, 255, 255), 1.0f, 0); if (SUCCEEDED(g_pd3dDevice->BeginScene())) { g_pd3dDevice->SetStreamSource(0, g_pVB, 0, sizeof(CUSTOMVERTEX)); g_pd3dDevice->SetVertexShader(TriangleShader); g_pd3dDevice->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD); g_pd3dDevice->SetRenderState( D3DRS_LIGHTING, FALSE ); g_pd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); g_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLELIST,0,1); g_pd3dDevice->EndScene(); } g_pd3dDevice->Present(NULL, NULL, NULL, NULL); return 0; } LRESULT WINAPI MsgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_DESTROY: Cleanup(); PostQuitMessage(0); return 0; } return DefWindowProc(hWnd, msg, wParam, lParam); } INT WINAPI wWinMain(HINSTANCE hInst, HINSTANCE, LPWSTR, INT) { UNREFERENCED_PARAMETER(hInst); // Register the window class WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L, GetModuleHandle(NULL), NULL, NULL, NULL, NULL, L"D3D Tutorial", NULL }; RegisterClassEx(&wc); // Create the application's window HWND hWnd = CreateWindow(L"D3D Tutorial", L"D3D: VertexShader", WS_OVERLAPPEDWINDOW, 100, 100, 700, 700, NULL, NULL, wc.hInstance, NULL); if (SUCCEEDED(InitD3D(hWnd))) { ShowWindow(hWnd, SW_SHOWDEFAULT); UpdateWindow(hWnd); MSG msg; ZeroMemory(&msg, sizeof(msg)); while (msg.message != WM_QUIT) { if (PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } else Render(); } } UnregisterClass(L"D3D Tutorial", wc.hInstance); return 0; }
2.蓝色茶壶
//////////////////////////////////////////////////////////////////////////// // // File: transform.txt // // Author: Frank Luna (C) All Rights Reserved // // System: AMD Athlon 1800+ XP, 512 DDR, Geforce 3, Windows XP, MSVC++ 7.0 // // Desc: Vertex shader that transforms a vertex by the view and // projection transformation, and sets the vertex color to blue. // //////////////////////////////////////////////////////////////////////////// // // Globals // // Global variable to store a combined view and projection // transformation matrix. We initialize this variable // from the application. matrix ViewProjMatrix; // Initialize a global blue color vector. vector Blue = {0.0f, 0.0f, 1.0f, 1.0f}; // // Structures // // Input structure describes the vertex that is input // into the shader. Here the input vertex contains // a position component only. struct VS_INPUT { vector position : POSITION; }; // Output structure describes the vertex that is // output from the shader. Here the output // vertex contains a position and color component. struct VS_OUTPUT { vector position : POSITION; vector diffuse : COLOR; }; // // Main Entry Point, observe the main function // receives a copy of the input vertex through // its parameter and returns a copy of the output // vertex it computes. // VS_OUTPUT Main(VS_INPUT input) { // zero out members of output VS_OUTPUT output = (VS_OUTPUT)0; // transform to view space and project output.position = mul(input.position, ViewProjMatrix); // set vertex diffuse color to blue output.diffuse = Blue; return output; }
////////////////////////////////////////////////////////////////////////////////////////////////// // // File: colorTriangle.cpp // // Author: Frank Luna (C) All Rights Reserved // // System: AMD Athlon 1800+ XP, 512 DDR, Geforce 3, Windows XP, MSVC++ 7.0 // // Desc: Renders two colored triangles, one shaded with flat shading and the // other shaded with Gouraud shading. Demontrates vertex colors and, // the shading render states. // ////////////////////////////////////////////////////////////////////////////////////////////////// #include "d3dUtility.h" // // Globals // IDirect3DDevice9* Device = 0; const int Width = 640; const int Height = 480; // 顶点着色器 IDirect3DVertexShader9* TransformShader = 0; // 常量表 ID3DXConstantTable* TransformConstantTable = 0; // 茶壶模型 ID3DXMesh* Teapot = 0; // D3DXHANDLE TransformViewProjHandle = 0; // 投影矩阵 D3DXMATRIX ProjMatrix; // // Framework Functions // bool Setup() { HRESULT hr = 0; // // 创建茶壶模型 // D3DXCreateTeapot(Device, &Teapot, 0); // // 缓存 // ID3DXBuffer* shader = 0; ID3DXBuffer* errorBuffer = 0; // 加载shader文件,设立缓存区 hr = D3DXCompileShaderFromFile( "transform.txt", 0, 0, "Main", "vs_1_1", D3DXSHADER_DEBUG, &shader, &errorBuffer, &TransformConstantTable ); // 如果shader文件错误 if (errorBuffer) { ::MessageBox(0, (char*)errorBuffer->GetBufferPointer(), 0, 0); d3d::Release<ID3DXBuffer*>(errorBuffer); } // 如果加载出错 if (FAILED(hr)) { ::MessageBoxA(0, (char*)errorBuffer->GetBufferPointer(), 0, 0); d3d::Release<ID3DXBuffer*>(errorBuffer); } // 根据缓存区创建顶点着色器 hr = Device->CreateVertexShader( (DWORD*)shader->GetBufferPointer(), &TransformShader); if (FAILED(hr)) { ::MessageBox(0, "CreateVertexShader-FAILED", 0, 0); return false; } // 释放缓存区 d3d::Release<ID3DXBuffer*>(shader); // // Get Handles // TransformViewProjHandle = TransformConstantTable->GetConstantByName(0, "ViewProjMatrix"); // // Set shader contants // TransformConstantTable->SetDefaults(Device); // // 设置投影矩阵,设置一个圆锥体投影 // D3DXMatrixPerspectiveFovLH(&ProjMatrix, D3DX_PI * 0.25f, (float)Width / (float)Height, 1.0f, 1000.0f); // // 设置线框模式 // //Device->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME); return true; } void Cleanup() { d3d::Release<ID3DXMesh*>(Teapot); d3d::Release<IDirect3DVertexShader9*>(TransformShader); d3d::Release<ID3DXConstantTable*>(TransformConstantTable); } bool Display(float timeDelta) { if (Device) { // // Update the scene: Allow user to rotate around scene // static float angle = (3.0f * D3DX_PI) / 2.0f; static float height = 5.0f; if (::GetAsyncKeyState(VK_LEFT) & 0x8000f) angle -= 0.5f * timeDelta; if (::GetAsyncKeyState(VK_RIGHT) & 0x8000f) angle += 0.5f * timeDelta; if (::GetAsyncKeyState(VK_UP) & 0x8000f) height += 5.0f * timeDelta; if (::GetAsyncKeyState(VK_DOWN) & 0x8000f) height -= 5.0f * timeDelta; D3DXVECTOR3 position( cosf(angle) * 10.0f, height, sinf(angle) * 10.0f); D3DXVECTOR3 target(0.0f, 0.0f, 0.0f); D3DXVECTOR3 up(0.0f, 1.0f, 0.0f); D3DXMATRIX V; D3DXMatrixLookAtLH(&V, &position, &target, &up); D3DXMATRIX ViewProj = V * ProjMatrix; TransformConstantTable->SetMatrix(Device, TransformViewProjHandle, &ViewProj); // // Render // Device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xffffffff, 1.0f, 0); Device->BeginScene(); Device->SetVertexShader(TransformShader); Teapot->DrawSubset(0); Device->EndScene(); Device->Present(0, 0, 0, 0); } return true; } // // WndProc // LRESULT CALLBACK d3d::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch( msg ) { case WM_DESTROY: ::PostQuitMessage(0); break; case WM_KEYDOWN: if( wParam == VK_ESCAPE ) ::DestroyWindow(hwnd); break; } return ::DefWindowProc(hwnd, msg, wParam, lParam); } // // WinMain // int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE prevInstance, PSTR cmdLine, int showCmd) { if(!d3d::InitD3D(hinstance, Width, Height, true, D3DDEVTYPE_HAL, &Device)) { ::MessageBox(0, "InitD3D() - FAILED", 0, 0); return 0; } if(!Setup()) { ::MessageBox(0, "Setup() - FAILED", 0, 0); return 0; } d3d::EnterMsgLoop( Display ); Cleanup(); Device->Release(); return 0; }