VertexShader入门
Shader与固定管线
顶点着色(Vertex Shader)是一段执行在GPU上的程序(一般用HLSL来编写),用来取代fixed pipeline中的transformation和lighting,Vertex Shader主要操作顶点。
有图则一目了然。

Vertex Shader做了什么工作
由上图知,Vertex Shader对输入顶点完成了从local space到homogeneous clip space的变换过程,homogeneous clip space即projection space的下一个space。在这其间共有world transformation, view transformation和projection transformation及lighting几个过程。
优点(与fixed pipeline比较)
由于Vertex Shader是用户自定义程序,所以有很大的灵活性,不必再局限于D3D固定的算法,可以应用许多其他算法,比如可以操作顶点位置模拟衣服效果,操作顶点大小模拟例子系统及顶点混合,变形等,此外,顶点的数据结构也更加灵活。
Shader代码文件
Shader代码可以用纯文本文件来保存,比如记事本文件。我们来看一个最简单的Shader文件,该文件完成的功能是,将顶点由Local Space变换到Homogeneous Space,编辑Shader文件可以用DirectX的Effect Edit,不过新版SDK中没有这个工具了,另外显卡厂商也有自己的编辑器,NVIDIA 有FX Composer,ATI有Render Monkey。
//
World, View and Projection Matrix
uniform
extern
float4x4 gWVP;
//
Output Vertex structure
struct
OutputVS
{
float4 posH : POSITION0;
};
OutputVS Main(float3 posL : POSITION0)
{
//
Zero out our output.
OutputVS outVS
=
(OutputVS)
0
;
//
Transform to homogeneous clip space.
outVS.posH
=
mul(float4(posL,
1.0f
), gWVP);
//
Done--return the output.
return
outVS;
}
简单解释一下上述代码
第一行定义了一个全局变量gWVP,这个变量的命名是有规则的,g表示global,即全局变量,W表示World,V表示View,P表示Projection,也就是说这是World,View和Projection矩阵的乘积。但是在程序中我们并没有用到World矩阵,所以实际上就是View矩阵和Projection矩阵的乘积。uniform表示这是个常量,也就是在Shader执行的过程中这个量是不能改变的,extern表示这是一个外部输入量,全局变量默认就是uniform extern的。
接下来的Struct定义了顶点的输出格式,所谓输出就是有VertexShader操作完以后,顶点以何种方式呈现给下一级处理器(一般是Pixel Shader)。这里的输出格式很简单,只包含一个信息,就是顶点的位置。
最后的Main函数就是主要的处理过程,需要注意的是这个函数的名字要和程序中指定的名字保持一致,否则编译会失败。首先定义一个输出结构并清零,然后就是顶点变换,使用mul函数将顶点从Local Space变换到 Homogeneous Space。注意输入顶点是三维的,而齐次坐标是四维的,所以需要转换一下。
如何使用Shader
有了上面的Shader文件,我们就可以在程序中使用它了,下面将以逐步添加代码的方式讲述如何使用Shader文件,为了简化程序,我们将着重讲述有关Shader的代码,其他代码简单带过。
1.定义一个VertexShader指针,该指针可以用来用来保存编译后的Shader
IDirect3DVertexShader9
*
g_pVertexShader
=
NULL ;
//
vertex shader
2. 定义一个常量表指针,常量表用来保存Shader文件中的变量,这些变量是Shader文件与C++ code通讯的媒介,比如我们要设置某些渲染状态,那么首先要通过程序修改这些变量,然后Shader文件读取这些变量就可以得到修改后的值。
ID3DXConstantTable
*
g_pConstantTable
=
NULL ;
//
shader constant table
3. 定义一个函数PrepareVertexShader,该函数用来编译Shader文件并做一些必要的设置,这个函数主要做两件事,一是从编译Shader文件,二是在编译完成后创建相应的Shader,每个步骤后面都有对应的错误处理,如果编译有错误,则输出错误信息,如果创建Shader失败也通知用户。当创建完Shader以后,就释放codeBuffer和errorBuffer。
bool
PrepareShader()
{
//
Buffer to hold the compiled code
ID3DXBuffer
*
codeBuffer
=
NULL;
//
Buffer to hold the error message if complile failed
ID3DXBuffer
*
errorBuffer
=
NULL;
//
Compile shader from file
HRESULT hr
=
D3DXCompileShaderFromFileA(
"
vertexshader.txt
"
,
0
,
0
,
"
Main
"
,
"
vs_1_1
"
,
D3DXSHADER_ENABLE_BACKWARDS_COMPATIBILITY,
&
codeBuffer,
&
errorBuffer,
&
g_pConstantTable) ;
//
output any error messages
if
( errorBuffer )
{
MessageBoxA(
0
, (
char
*
)errorBuffer
->
GetBufferPointer(),
0
,
0
);
errorBuffer
->
Release() ;
return
false
;
}
if
(FAILED(hr))
{
MessageBox(
0
, L
"
D3DXCompileShaderFromFile() - FAILED
"
,
0
,
0
);
return
false
;
}
//
Create vertex shader
hr
=
g_pd3dDevice
->
CreateVertexShader((DWORD
*
)codeBuffer
->
GetBufferPointer(),
&
g_pVertexShader) ;
//
handling error
if
(FAILED(hr))
{
MessageBox(
0
, L
"
CreateVertexShader - FAILED
"
,
0
,
0
);
return
false
;
}
//
Release code buffer
if
(codeBuffer
!=
NULL)
{
codeBuffer
->
Release() ;
codeBuffer
=
NULL ;
}
//
Release DX buffer
if
(errorBuffer
!=
NULL)
{
errorBuffer
->
Release() ;
errorBuffer
=
NULL ;
}
//
Set handle
ViewProjMatrixHanle
=
g_pConstantTable
->
GetConstantByName(
0
,
"
gWVP
"
) ;
return
true
;
}
4. 设置View Matrix和Projection Matrix。
单独定义一个函数用来设置矩阵,然后对View Matrix和Projection Matrix做乘积,最后通过常量表将乘积矩阵传递给Shader。Shader中通过这个矩阵来变换顶点,这就是本文的核心了。
代码
5 渲染,在Render函数中设置VertexShader,然后就可以渲染了。
VOID Render()
{
//
Clear the back-buffer to a red color
g_pd3dDevice
->
Clear(
0
, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(
0
,
0
,
0
),
1.0f
,
0
);
//
Begin the scene
if
( SUCCEEDED( g_pd3dDevice
->
BeginScene() ) )
{
//
Set Vertex Shader
g_pd3dDevice
->
SetVertexShader(g_pVertexShader) ;
//
Render teapot
g_pMesh
->
DrawSubset(
0
) ;
//
End the scene
g_pd3dDevice
->
EndScene();
}
//
Present the back-buffer contents to the display
g_pd3dDevice
->
Present( NULL, NULL, NULL, NULL );
}
下面是完整的代码
/*
This is a simple vertex shader program which illustrate how to use vertex shader instead of fixed pipeline
to perform world, view and projection transform
*/
#include
<
d3dx9.h
>
#include
<
MMSystem.h
>
LPDIRECT3D9 g_pD3D
=
NULL ;
//
Used to create the D3DDevice
LPDIRECT3DDEVICE9 g_pd3dDevice
=
NULL ;
//
Our rendering device
ID3DXMesh
*
g_pMesh
=
NULL ;
//
Hold the sphere
IDirect3DVertexShader9
*
g_pVertexShader
=
NULL ;
//
vertex shader
ID3DXConstantTable
*
g_pConstantTable
=
NULL ;
//
shader constant table
//
Handle for world, view and projection matrix
//
We use this variable to communicate between the effect file Shader.txt and the C++ code
D3DXHANDLE ViewProjMatrixHanle
=
0
;
void
SetupMatrix() ;
bool
PrepareShader() ;
HRESULT InitD3D( HWND hWnd )
{
//
Create the D3D object, which is needed to create the D3DDevice.
if
( NULL
==
( g_pD3D
=
Direct3DCreate9( D3D_SDK_VERSION ) ) )
return
E_FAIL;
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(
&
d3dpp,
sizeof
(d3dpp) );
d3dpp.Windowed
=
TRUE;
d3dpp.SwapEffect
=
D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat
=
D3DFMT_UNKNOWN;
//
Create device
if
( FAILED( g_pD3D
->
CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&
d3dpp,
&
g_pd3dDevice ) ) )
{
return
E_FAIL;
}
//
Turn off culling, so we see the front and back of the triangle
g_pd3dDevice
->
SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE );
//
g_pd3dDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME);
g_pd3dDevice
->
SetRenderState( D3DRS_LIGHTING , FALSE );
//
Create a teapot
D3DXCreateTeapot(g_pd3dDevice,
&
g_pMesh, NULL) ;
//
Prepare Shader
PrepareShader() ;
//
Setup matrix
SetupMatrix() ;
return
S_OK;
}
VOID Cleanup()
{
if
( g_pd3dDevice
!=
NULL)
g_pd3dDevice
->
Release();
if
( g_pD3D
!=
NULL)
g_pD3D
->
Release();
if
(g_pMesh
!=
NULL)
g_pMesh
->
Release() ;
}
bool
PrepareShader()
{
//
Buffer to hold the compiled code
ID3DXBuffer
*
codeBuffer
=
NULL;
//
Buffer to hold the error message if complile failed
ID3DXBuffer
*
errorBuffer
=
NULL;
//
Compile shader from file
HRESULT hr
=
D3DXCompileShaderFromFileA(
"
vertexshader.txt
"
,
0
,
0
,
"
Main
"
,
"
vs_1_1
"
,
D3DXSHADER_ENABLE_BACKWARDS_COMPATIBILITY,
&
codeBuffer,
&
errorBuffer,
&
g_pConstantTable) ;
//
output any error messages
if
( errorBuffer )
{
MessageBoxA(
0
, (
char
*
)errorBuffer
->
GetBufferPointer(),
0
,
0
);
errorBuffer
->
Release() ;
return
false
;
}
if
(FAILED(hr))
{
MessageBox(
0
, L
"
D3DXCompileShaderFromFile() - FAILED
"
,
0
,
0
);
return
false
;
}
//
Create vertex shader
hr
=
g_pd3dDevice
->
CreateVertexShader((DWORD
*
)codeBuffer
->
GetBufferPointer(),
&
g_pVertexShader) ;
//
handling error
if
(FAILED(hr))
{
MessageBox(
0
, L
"
CreateVertexShader - FAILED
"
,
0
,
0
);
return
false
;
}
//
Release code buffer
if
(codeBuffer
!=
NULL)
{
codeBuffer
->
Release() ;
codeBuffer
=
NULL ;
}
//
Release DX buffer
if
(errorBuffer
!=
NULL)
{
errorBuffer
->
Release() ;
errorBuffer
=
NULL ;
}
//
Set handle
ViewProjMatrixHanle
=
g_pConstantTable
->
GetConstantByName(
0
,
"
gWVP
"
) ;
return
true
;
}
void
SetupMatrix()
{
D3DXVECTOR3 eyePt(
0.0f
,
0.0f
,
-
10.0f
) ;
D3DXVECTOR3 upVec(
0.0f
,
1.0f
,
0.0f
) ;
D3DXVECTOR3 lookCenter(
0.0f
,
0.0f
,
0.0f
) ;
//
Set view matrix
D3DXMATRIX view ;
D3DXMatrixLookAtLH(
&
view,
&
eyePt,
&
lookCenter,
&
upVec) ;
//
Set projection matrix
D3DXMATRIX proj ;
D3DXMatrixPerspectiveFovLH(
&
proj, D3DX_PI
/
4
,
1.0f
,
1.0f
,
1000.0f
) ;
D3DXMATRIX viewproj
=
view
*
proj ;
g_pConstantTable
->
SetMatrix(g_pd3dDevice, ViewProjMatrixHanle,
&
viewproj) ;
//
this line is mandatory if you have used Constant table in your code
g_pConstantTable
->
SetDefaults(g_pd3dDevice);
}
VOID Render()
{
//
Clear the back-buffer to a red color
g_pd3dDevice
->
Clear(
0
, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(
0
,
0
,
0
),
1.0f
,
0
);
//
Begin the scene
if
( SUCCEEDED( g_pd3dDevice
->
BeginScene() ) )
{
//
Set Vertex Shader
g_pd3dDevice
->
SetVertexShader(g_pVertexShader) ;
//
Render teapot
g_pMesh
->
DrawSubset(
0
) ;
//
End the scene
g_pd3dDevice
->
EndScene();
}
//
Present the back-buffer contents to the display
g_pd3dDevice
->
Present( NULL, NULL, NULL, NULL );
}
LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
switch
( msg )
{
case
WM_KEYDOWN:
{
switch
( wParam )
{
case
VK_ESCAPE:
SendMessage( hWnd, WM_CLOSE,
0
,
0
);
break
;
default
:
break
;
}
}
break
;
case
WM_DESTROY:
Cleanup();
PostQuitMessage(
0
);
return
0
;
}
return
DefWindowProc( hWnd, msg, wParam, lParam );
}
INT WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR szCmdLine,
int
iCmdShow)
{
WNDCLASSEX winClass ;
winClass.lpszClassName
=
L
"
Teapot
"
;
winClass.cbSize
=
sizeof
(WNDCLASSEX);
winClass.style
=
CS_HREDRAW
|
CS_VREDRAW;
winClass.lpfnWndProc
=
MsgProc;
winClass.hInstance
=
hInstance;
winClass.hIcon
=
NULL ;
winClass.hIconSm
=
NULL ;
winClass.hCursor
=
NULL ;
winClass.hbrBackground
=
NULL ;
winClass.lpszMenuName
=
NULL ;
winClass.cbClsExtra
=
0
;
winClass.cbWndExtra
=
0
;
RegisterClassEx (
&
winClass) ;
HWND hWnd
=
CreateWindowEx(NULL,
winClass.lpszClassName,
//
window class name
L
"
Teapot
"
,
//
window caption
WS_OVERLAPPEDWINDOW,
//
window style
32
,
//
initial x position
32
,
//
initial y position
600
,
//
initial window width
600
,
//
initial window height
NULL,
//
parent window handle
NULL,
//
window menu handle
hInstance,
//
program instance handle
NULL) ;
//
creation parameters
//
Create window failed
if
(hWnd
==
NULL)
{
MessageBoxA(hWnd,
"
Create Window failed!
"
,
"
Error
"
,
0
) ;
return
-
1
;
}
//
Initialize Direct3D
if
( SUCCEEDED(InitD3D(hWnd)))
{
//
Show the window
ShowWindow( hWnd, SW_SHOWDEFAULT );
UpdateWindow( hWnd );
//
Enter the message loop
MSG msg ;
ZeroMemory(
&
msg,
sizeof
(msg) );
PeekMessage(
&
msg, NULL,
0U
,
0U
, PM_NOREMOVE );
//
Get last time
static
DWORD lastTime
=
timeGetTime();
while
(msg.message
!=
WM_QUIT)
{
if
( PeekMessage(
&
msg, NULL,
0U
,
0U
, PM_REMOVE)
!=
0
)
{
TranslateMessage (
&
msg) ;
DispatchMessage (
&
msg) ;
}
else
//
Render the game if there is no message to process
{
//
Get current time
DWORD currTime
=
timeGetTime();
//
Calculate time elapsed
float
timeDelta
=
(currTime
-
lastTime)
*
0.001f
;
//
Render
Render() ;
//
Update last time to current time for next loop
lastTime
=
currTime;
}
}
}
UnregisterClass(winClass.lpszClassName, hInstance) ;
return
0
;
}
如何查看显卡所支持的Vertex Shader版本
1 使用DirectX Caps Viewer查看
2 使用下面的代码查看
1
//
Check shader version
2
bool
CheckShaderVersion(LPDIRECT3DDEVICE9 g_pd3dDevice)
3
{
4
//
Get device capabilities
5
D3DCAPS9 caps ;
6
g_pd3dDevice
->
GetDeviceCaps(
&
caps);
7
8
//
Make sure vertex shader version greater than 2.0
9
if
(caps.VertexShaderVersion
<
D3DVS_VERSION(
2
,
0
))
10
{
11
return
false
;
12
}
13
14
//
Make sure pixel shader version greater than 2.0
15
if
(caps.PixelShaderVersion
<
D3DPS_VERSION(
2
,
0
))
16
{
17
return
false
;
18
}
19
20
return
true
;
21
};


4721

被折叠的 条评论
为什么被折叠?



