• 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中通过这个矩阵来变换顶点,这就是本文的核心了。

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

    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 };

  • 相关阅读:
    开源一些C#不常用知识(附上DEMO)
    开源:C# 代码自动生成工具,支持站点前后台
    Xposed 集成 Android 6.0.1环境中,总结
    Android 视频通信,低延时解决方案
    Android studio,第一个生成,调用成功的jni(说多了都是泪)
    C#之文件缓存
    JavaScript 基本常识
    排序算法
    LeetCode:字符串转换整数 (atoi)
    LeetCode:判断回文数
  • 原文地址:https://www.cnblogs.com/graphics/p/1799200.html
Copyright © 2020-2023  润新知