VertexShader入门

154 篇文章 8 订阅
27 篇文章 1 订阅

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 };
复制代码

 

 

作者: zdd
出处: http://www.cnblogs.com/graphics/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值