VertexShader入门
VertexShader入门Shader与固定管线顶点着色(Vertex Shader)是一段执行在GPU上的程序(一般用HLSL来编写),用来取代fixed pipeline中的transformation和lighting,Vertex Shader主要操作顶点。有图则一目了然。Vertex Shader做了什么工作由上图知,Vertex Shader对输入
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 };
昇腾计算产业是基于昇腾系列(HUAWEI Ascend)处理器和基础软件构建的全栈 AI计算基础设施、行业应用及服务,https://devpress.csdn.net/organization/setting/general/146749包括昇腾系列处理器、系列硬件、CANN、AI计算框架、应用使能、开发工具链、管理运维工具、行业应用及服务等全产业链
更多推荐



所有评论(0)