DirectX9:第四部分 顶点着色器

一.简介

顶点着色器(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;
}
原文地址:https://www.cnblogs.com/k5bg/p/11088553.html