一.简介
顶点着色器(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;
}