VertexShader入门

编程入门 行业动态 更新时间:2024-10-26 02:34:48

VertexShader<a href=https://www.elefans.com/category/jswz/34/1770026.html style=入门"/>

VertexShader入门

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 出处: / 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.

更多推荐

VertexShader入门

本文发布于:2024-02-27 02:39:09,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1704890.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:入门   VertexShader

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!