新建网页 1
17.3使用顶点着色器的步骤
下面的列表概括了创建和使用顶点着色器的必须步骤:
1. 编写并编译顶点着色器
2. 创建一个IDirect3DVertexShader9接口以引用已编译的着色器代码上的顶点着色器。
3. 用IDirect3DDevice9:: SetVertexShader方法使用这个顶点着色器。
当然,在我们做完这些之后,我们还得销毁这个顶点着色器。
17.3.1 编写并编译顶点着色器
首先,我们必须编写一个顶点着色器程序。一旦着色器代码写好之后,我们就使用D3DXCompileShaderFromFile函数编译这个着色器。回忆一下,这个函数返回一个ID3DXBuffer指针,它包含已编译的着色器代码。
17.3.2 创建顶点着色器
一旦我们拥有了编译好的着色器代码,我们就能够获得一个IDirect3DVertexShader9接口的指针,它代表一个顶点着色器——通过使用下面的方法:
HRESULT IDirect3DDevice9::CreateVertexShader(
const DWORD *pFunction,
IDirect3DVertexShader9** ppShader
);
|
pFunction——已编译着色器代码的指针
ppShader——返回一个IDirect3DVertexShader9接口的指针
例如,假设变量shader是一个包含已编译的,着色器代码的ID3DXBuffer指针。然后要获得一个IDirect3DVertexShader9接口,我们可以写:
IDirect3DVertexShader9* ToonShader = 0;
hr = Device->CreateVertexShader(
(DWORD*)shader->GetBufferPointer(),
&ToonShader);
|
注意:重申一遍,D3DXCompileShaderFromFile是一个函数,它将返回已编译着色器的代码(shader)。
17.3.3 建立顶点着色器
在我们获得了一个代表我们的顶点着色器的IDirect3DVertexShader9接口的指针之后,我们就能够使用下面的方法使用它:
HRESULT IDirect3DDevice9::SetVertexShader(
IDirect3DVertexShader9* pShader
);
|
这个方法仅接受一个参数,我们在其中传递一个想要使用的顶点着色器的指针。要使用这个着色器,我们可以写:Device->SetVertexShader(ToonShader);
17.3.4 销毁顶点着色器
和所有的Direc3D接口一样,要清除他们,我们就必须在用完它们之后调用其的Release方法。
17.4应用程序:散射光照
作为创建并使用顶点着色器的热身,我们写一个顶点着色器,它用一个方向(平行)光对每个顶点进行标准的散射光照。简而言之,散射光照根据顶点法线和光线向量(它朝向光源方向)的角度计算顶点接收到的光线的数量。角度越小,则顶点接收到的光线就越多;而角度越大,则顶点接收到的光线就越少。如果角度大于等于90度,顶点就接收不到光线了。
我们以检阅着色器代码作为开始:
/**********************************************************************************
Vertex shader that does diffuse lighting.
**********************************************************************************/
matrix g_view_matrix;
matrix g_view_proj_matrix;
vector g_ambient_material;
vector g_diffuse_material;
vector g_dir_to_light; // the direction to the light source
// Global variables used to hold the ambient light intensity (ambient light the light
// source emits) and the diffuse light intensity (diffuse light the light source emits).
// These variables are initialized here in the shader.
const vector DIFFUSE_LIGHT_INTENSITY = {0.5f, 0.5f, 0.5f, 1.0f};
const vector AMBIENT_LIGHT_INTENSITY = {2.0f, 2.0f, 1.0f, 1.0f};
struct sVertexInput
{
vector position : POSITION;
vector normal : NORMAL;
};
struct sVertexOutput
{
vector position : POSITION;
vector diffuse : COLOR;
};
///////////////////////////////////////////////////////////////////////////////////////////////
sVertexOutput main(sVertexInput vertex_input)
{
sVertexOutput vertex_output = (sVertexOutput) 0;
// transform position to homogeneous clip space
vertex_output.position = mul(vertex_input.position, g_view_proj_matrix);
// Transform lights and normals to view space.
// Set w components to zero since we're transforming vectors here and not points.
g_dir_to_light.w = 0.0f;
vertex_input.normal.w = 0.0f;
g_dir_to_light = mul(g_dir_to_light, g_view_matrix);
vertex_input.normal = mul(vertex_input.normal, g_view_matrix);
// compute cosine of the angle between light and normal
float scalar = dot(g_dir_to_light, vertex_input.normal);
// Recall that if the angle between the surface and light is greater than 90 degrees
// the surface recieves no light. Thus, if the angle is greater than 90 degrees we set
// scalar to zero so that the surface will not be lit.
if(scalar < 0.0f)
scalar = 0.0f;
// Ambient light reflected is computed by performing a component wise multiplication with
// the ambient material vector and the ambient light intensity vector.
//
// Diffuse light reflected is computed by performing a component wise multiplication with
// the diffuse material vector and the diffuse light intensity vector.
// Further we scale each component by the shading scalar s, which shades the color based on
// how much light the vertex received from the light source.
//
// The sum of both the ambient and diffuse components gives us our final vertex color.
vertex_output.diffuse = (g_ambient_material * AMBIENT_LIGHT_INTENSITY) +
(scalar * (g_diffuse_material * DIFFUSE_LIGHT_INTENSITY));
return vertex_output;
}
执行程序:
/**************************************************************************************************
Demonstrates diffuse lighting using a vertex shader. You will have to switch to
the REF device to run this sample if your hardware does not support shaders.
Or you can use software vertex processing: D3DCREATE_SOFTWARE_VERTEXPROCESSING.
**************************************************************************************************/
#include "d3dUtility.h"
#pragma warning(disable : 4100)
const int WIDTH = 640;
const int HEIGHT = 480;
IDirect3DDevice9* g_device;
ID3DXMesh* g_teapot_mesh;
IDirect3DVertexShader9* g_vertex_shader;
ID3DXConstantTable* g_constant_table;
D3DXHANDLE g_view_matrix_handle;
D3DXHANDLE g_view_proj_matrix_handle;
D3DXHANDLE g_ambient_material_handle;
D3DXHANDLE g_diffuse_material_handle;
D3DXHANDLE g_dir_to_light_handle;
D3DXMATRIX g_proj_matrix;
////////////////////////////////////////////////////////////////////////////////////////////////////
bool setup()
{
D3DXCreateTeapot(g_device, &g_teapot_mesh, NULL);
// compile shader
ID3DXBuffer* shader_buffer;
ID3DXBuffer* error_buffer;
HRESULT hr = D3DXCompileShaderFromFile("DiffuseShader.cxx", NULL, NULL, "main", "vs_1_1",
D3DXSHADER_ENABLE_BACKWARDS_COMPATIBILITY,
&shader_buffer, &error_buffer, &g_constant_table);
// output any error messages
if(error_buffer)
{
MessageBox(NULL, (char*)error_buffer->GetBufferPointer(), "ERROR", MB_OK);
safe_release<ID3DXBuffer*>(error_buffer);
}
if(FAILED(hr))
{
MessageBox(NULL, "D3DXCreateEffectFromFile() - FAILED", "ERROR", MB_OK);
return false;
}
hr = g_device->CreateVertexShader((DWORD*) shader_buffer->GetBufferPointer(), &g_vertex_shader);
if(FAILED(hr))
{
MessageBox(NULL, "CreateVertexShader - FAILED", "ERROR", MB_OK);
return false;
}
safe_release<ID3DXBuffer*>(shader_buffer);
// get handles
g_view_matrix_handle = g_constant_table->GetConstantByName(NULL, "g_view_matrix");
g_view_proj_matrix_handle = g_constant_table->GetConstantByName(NULL, "g_view_proj_matrix");
g_ambient_material_handle = g_constant_table->GetConstantByName(NULL, "g_ambient_material");
g_diffuse_material_handle = g_constant_table->GetConstantByName(NULL, "g_diffuse_material");
g_dir_to_light_handle = g_constant_table->GetConstantByName(NULL, "g_light_direction");
//
// set shader constants
//
// light direction
D3DXVECTOR4 dir_to_light(-0.57f, 0.57f, -0.57f, 0.0f);
g_constant_table->SetVector(g_device, g_dir_to_light_handle, &dir_to_light);
// materials
D3DXVECTOR4 ambient_material(1.0f, 1.0f, 0.5f, 1.0f);
D3DXVECTOR4 diffuse_material(1.0f, 1.0f, 0.5f, 1.0f);
g_constant_table->SetVector(g_device, g_ambient_material_handle, &ambient_material);
g_constant_table->SetVector(g_device, g_diffuse_material_handle, &diffuse_material);
g_constant_table->SetDefaults(g_device);
// set the projection matrix
D3DXMatrixPerspectiveFovLH(&g_proj_matrix, D3DX_PI/4.0f, (float)WIDTH/HEIGHT, 1.0f, 1000.0f);
//g_device->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME);
return true;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////
void cleanup()
{
safe_release<ID3DXMesh*>(g_teapot_mesh);
safe_release<IDirect3DVertexShader9*>(g_vertex_shader);
safe_release<ID3DXConstantTable*>(g_constant_table);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////
bool display(float time_delta)
{
static float angle = (3.0f * D3DX_PI) / 2.0f;
static float height = 3.0f;
if(GetAsyncKeyState(VK_LEFT) & 0x8000f)
angle -= 0.5f * time_delta;
if(GetAsyncKeyState(VK_RIGHT) & 0x8000f)
angle += 0.5f * time_delta;
if(GetAsyncKeyState(VK_UP) & 0x8000f)
height += 5.0f * time_delta;
if(GetAsyncKeyState(VK_DOWN) & 0x8000f)
height -= 5.0f * time_delta;
D3DXVECTOR3 position(cosf(angle) * 7.0f, height, sinf(angle) * 7.0f);
D3DXVECTOR3 target(0.0f, 0.0f, 0.0f);
D3DXVECTOR3 up(0.0f, 1.0f, 0.0f);
D3DXMATRIX view_matrix;
D3DXMatrixLookAtLH(&view_matrix, &position, &target, &up);
D3DXMATRIX view_proj_matrix = view_matrix * g_proj_matrix;
g_constant_table->SetMatrix(g_device, g_view_proj_matrix_handle, &view_proj_matrix);
// render now
g_device->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0x00000000, 1.0f, 0);
g_device->BeginScene();
g_device->SetVertexShader(g_vertex_shader);
g_teapot_mesh->DrawSubset(0);
g_device->EndScene();
g_device->Present(NULL, NULL, NULL, NULL);
return true;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////
LRESULT CALLBACK wnd_proc(HWND hwnd, UINT msg, WPARAM word_param, LPARAM long_param)
{
switch(msg)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_KEYDOWN:
if(word_param == VK_ESCAPE)
DestroyWindow(hwnd);
break;
}
return DefWindowProc(hwnd, msg, word_param, long_param);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////
int WINAPI WinMain(HINSTANCE inst, HINSTANCE, PSTR cmd_line, int cmd_show)
{
if(! init_d3d(inst, WIDTH, HEIGHT, true, D3DDEVTYPE_HAL, &g_device))
{
MessageBox(NULL, "init_d3d() - failed.", 0, MB_OK);
return 0;
}
if(! setup())
{
MessageBox(NULL, "Steup() - failed.", 0, MB_OK);
return 0;
}
enter_msg_loop(display);
cleanup();
g_device->Release();
return 0;
}
运行截图:
setup函数执行下列任务:
创建茶壶网格
编译顶点着色器
根据已编译代码创建顶点着色器
通过常量表获取着色器程序中的几个变量的句柄
通过常量表初始化着色器的这几个变量
注意:对于本应用程序,我们的顶点结构不需要任何自由顶点格式没有的额外的分量。
display函数非常简单。它检测用户输入(这里指的是用户输入的传入着色器程序的变量),并相应的更新视图矩阵。但是,因为我们在着色器中执行这个视图矩阵变换,所以我们还必须更新着色器中的视图矩阵变量。我们用常量表完成这件事情。
下载源程序