HLSL-高级着色语言简介

HLSL-High Level Shader Language

优点

用来书写Vertex Shader和Pixel Shader程序的代码,语法类似于C/C++,在DirectX 8.x的时代,Shader程序都是用低级Shader汇编语言编写的,姑且称之为LLSL吧,HLSL与之相比具有以下优点:

  • 更高的生产力,使用HLSL编程更快更容易,使我们有更多的时间关注与算法而不是编码
  • 更好的可读性,使用HLSH编写的程序更易读,易调试及维护
  • 编译器将生成更加高效的汇编代码
  • 可以将同一份代码编译成任何可用的Shader版本,但是用LLSL则必须按不同版本编写代码

如果你的显卡不支持Shader的话,那么可以使用REF Device模拟,这样可以得到正确的结果,但是速度非常慢

编写Shader代码

你可以将Shader代码写入到你的程序中,但是为了更方便,更模块化,通常将Shader程序放在单独的文件里,比如记事本,然后使用D3DXCompileShaderFromFile函数来编译它,使用记事本写Shader程序可能不太方便,以前微软提供编写Shader的工具,在DirectX SDK当中,但是现在不支持了,大家可以用AMD的RenderMonkey来编写。

一般的Shader程序包含以下几个部分

  • 全局变量-viewprojmatrix, color等
  • 输入结构和输出结构-VS_INPUT, VS_OUTPUT
  • 入口函数-Main, note the name is not mandatory, you can used any valid function name

常量表

每个Shader程序都有一个常量表来存储它的变量。可以用ID3DXConstantTable接口访问长量表,可以设置Shader程序中的全局变量值

实战-一个简单的Shader程序

下面我们就来看看如何编写一个Shader程序,我们还以茶壶为例,看看如何用Shader绘制一个蓝色的茶壶,学习如何用Shader处理颜色,这个例子简单的不能再简单,但是,它确实能反映如何使用Shader。首先用VS建立一个Win32工程,添加必要的框架代码,主要是创建窗口,简单的消息处理以及初始化D3D等,如下

Code
  1 #include <d3dx9.h>
  2 
  3 LPDIRECT3D9             g_pD3D            = NULL ; // Used to create the D3DDevice
  4 LPDIRECT3DDEVICE9       g_pd3dDevice    = NULL ; // Our rendering device
  5 
  6 HRESULT InitD3D( HWND hWnd )
  7 {
  8     // Create the D3D object, which is needed to create the D3DDevice.
  9     if( NULL == ( g_pD3D = Direct3DCreate9( D3D_SDK_VERSION ) ) )
 10         return E_FAIL;
 11 
 12     D3DPRESENT_PARAMETERS d3dpp; 
 13     ZeroMemory( &d3dpp, sizeof(d3dpp) );
 14     d3dpp.Windowed = TRUE;
 15     d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
 16     d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
 17 
 18     // Create device
 19     if( FAILED( g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
 20         D3DCREATE_SOFTWARE_VERTEXPROCESSING,
 21         &d3dpp, &g_pd3dDevice ) ) )
 22     {
 23         return E_FAIL;
 24     }
 25 
 26     return S_OK;
 27 }
 28 
 29 VOID Cleanup()
 30 {
 31     if( g_pd3dDevice != NULL) 
 32         g_pd3dDevice->Release();
 33 
 34     if( g_pD3D != NULL)
 35         g_pD3D->Release();
 36 }
 37 
 38 VOID Render()
 39 {
 40     if( NULL == g_pd3dDevice )
 41         return;
 42 
 43     // Clear the back-buffer to a RED color
 44     g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,0), 1.0f0 );
 45 
 46     // Begin the scene
 47     if( SUCCEEDED( g_pd3dDevice->BeginScene() ) )
 48     {
 49         // Rendering of scene objects can happen here
 50 
 51         // End the scene
 52         g_pd3dDevice->EndScene();
 53     }
 54 
 55     // Present the back-buffer contents to the display
 56     g_pd3dDevice->Present( NULL, NULL, NULL, NULL );
 57 }
 58 
 59 LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
 60 {
 61     switch( msg )
 62     {
 63     case WM_KEYDOWN:
 64         {
 65             switch( wParam )
 66             {
 67             case VK_ESCAPE:
 68                 SendMessage( hWnd, WM_CLOSE, 00 );
 69                 break ;
 70             default:
 71                 break ;
 72             }
 73         }
 74         break ;
 75 
 76     case WM_DESTROY:
 77         Cleanup();
 78         PostQuitMessage( 0 );
 79         return 0;
 80     }
 81 
 82     return DefWindowProc( hWnd, msg, wParam, lParam );
 83 }
 84 
 85 INT WINAPI wWinMain( HINSTANCE hInst, HINSTANCE, LPWSTR, INT )
 86 {
 87     // Register the window class
 88     WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L0L
 89         GetModuleHandle(NULL), NULL, NULL, NULL, NULL,
 90         L"D3D Tutorial", NULL };
 91     RegisterClassEx( &wc );
 92 
 93     // Create the application's window
 94     HWND hWnd = CreateWindow( L"D3D Tutorial", L"D3D Tutorial 01: CreateDevice"
 95         WS_OVERLAPPEDWINDOW , 00800600,
 96         NULL, NULL, wc.hInstance, NULL );
 97 
 98     // Initialize Direct3D
 99     if( SUCCEEDED( InitD3D( hWnd ) ) )
100     { 
101         // Show the window
102         ShowWindow( hWnd, SW_SHOWDEFAULT );
103         UpdateWindow( hWnd );
104 
105         // Enter the message loop
106         MSG    msg ; 
107         ZeroMemory( &msg, sizeof(msg) );
108         PeekMessage( &msg, NULL, 0U0U, PM_NOREMOVE );
109 
110         while (msg.message != WM_QUIT)  
111         {
112             if( PeekMessage(&msg, NULL, 0U0U, PM_REMOVE) != 0)
113             {
114                 TranslateMessage (&msg) ;
115                 DispatchMessage (&msg) ;
116             }
117             else // Render the game if no message to process
118             {
119                 Render() ;
120             }
121         }
122     }
123 
124     UnregisterClass( L"D3D Tutorial", wc.hInstance );
125     return 0;
126 }

然后按照下面的步骤绘制一个茶壶

1.在程序头部添加茶壶对应的全局变量

1  ID3DXMesh *                 g_pMesh                 =  NULL ;

2.在InitD3D函数尾部(返回语句之前)创建绘制茶壶的代码

1  D3DXCreateTeapot(g_pd3dDevice,  & g_pMesh, NULL) ;
2 

3. 在Render函数中添加绘制茶壶的代码,位于BeginScene和EndScene之间

1  g_pMesh -> DrawSubset( 0 ) ;

4.在Cleanup函数中清除茶壶对应的mesh

1  if (g_pMesh  !=  NULL)
2      g_pMesh -> Release() ;
3 

下面编写Shader程序,打开记事本,输入如下代码,代码很简单-略加解释,ViewProjMatrix对应是view matrix和projection matrix的乘积,Blue是顶点颜色,input只包含一个信息,顶点位置,output包含两个信息,顶点的位置和颜色,在main函数中,将顶点的位置由local坐标系变换到投影空间,将颜色设置为蓝色,就这么简单。

Code
 1 
 2 matrix ViewProjMatrix;
 3 vector Blue = {0.0f0.0f1.0f0.0f} ;
 4 
 5 struct VS_INPUT
 6 {
 7     vector position : POSITION;
 8 };
 9 
10 struct VS_OUTPUT
11 {
12     vector position : POSITION;
13     vector diffuse  : COLOR;
14 };
15 
16 
17 VS_OUTPUT Main(VS_INPUT input)
18 {
19     VS_OUTPUT output = (VS_OUTPUT)0;
20 
21     output.position = mul(input.position, ViewProjMatrix);
22 
23     output.diffuse = Blue ;
24     
25     return output;
26 }
27 
28 

回到主程序中,添加全局变量g_pVertexShader用来保存创建的shader

1  IDirect3DVertexShader9 *     g_pVertexShader         =  NULL ;

添加全局变量g_pConstantTable用来保存Shader对应的常量表,有了常量表就可以存取和修改shader中的变量

1  ID3DXConstantTable *         g_pConstantTable     =  NULL ;

添加一个变量句柄来存取shader中对应的ViewProjMatrix

1  D3DXHANDLE ViewProjMatrixHanle  =   0  ;

添加函数PrepareShader,这个函数负责从文件编译shader,创建shader,设置shader中的变量等

Code
 1 bool PrepareShader()
 2 {
 3     ID3DXBuffer *shader ;
 4     ID3DXBuffer *errorBuffer ;
 5     
 6     // Compile shader from file
 7     HRESULT hr = D3DXCompileShaderFromFileA("Shader.txt"00"Main""vs_1_1"
 8         D3DXSHADER_ENABLE_BACKWARDS_COMPATIBILITY, &shader, &errorBuffer, &g_pConstantTable) ;
 9 
10     // error handling
11     // output compile error message
12     if( errorBuffer )
13     {
14         MessageBoxA(0, (char*)errorBuffer->GetBufferPointer(), 00);
15         errorBuffer->Release() ;
16     }
17 
18     // compile failed
19     if(FAILED(hr))
20     {
21         MessageBox(0, L"D3DXCompileShaderFromFile() - FAILED"00);
22         return false;
23     }
24 
25     // Create shader
26     hr = g_pd3dDevice->CreateVertexShader((DWORD*)shader->GetBufferPointer(), &g_pVertexShader) ;
27 
28     // create failed
29     if(FAILED(hr))
30     {
31         MessageBox(0, L"CreateVertexShader - FAILED"00);
32         return false;
33     }
34 
35     shader->Release() ;
36 
37     // map handle to variable in shader
38     ViewProjMatrixHanle = g_pConstantTable->GetConstantByName(0"ViewProjMatrix") ;
39 }
40 

最后一行将shader中的变量ViewProjMatrix与ViewProjMatrixHandle相关联,这样接下来就可以用ViewProjMatrixHandle来存取和设置ViewProjMatrix了。注意,GetConstantByName函数的第二个参数是一个字符串,用来表示要获取的变量名字,这个名字必须与Shader文件中指定的名字一样才行,否则的话不起作用,也就是说Shader文件中一定有一个名为ViewProjectMatrix的变量。

添加函数SetupMatrix

这个函数负责设置view matrix, projection matrix, 并将二者的乘积赋值给shader中的ViewProjMatrix以便完成shader中的运算

Code
 1 void SetupMatrix()
 2 {
 3     // set view
 4     D3DXVECTOR3 eyePt(0.0f0.0f-10.0f) ;
 5     D3DXVECTOR3 upVec(0.0f1.0f0.0f) ;
 6     D3DXVECTOR3 lookCenter(0.0f0.0f0.0f) ;
 7 
 8     D3DXMATRIX view ;
 9     D3DXMatrixLookAtLH(&view, &eyePt, &lookCenter, &upVec) ;
10 
11     // set projection
12     D3DXMATRIX proj ;
13     D3DXMatrixPerspectiveFovLH(&proj, D3DX_PI / 41.0f1.0f1000.0f) ;
14 
15     D3DXMATRIX viewproj = view * proj ;
16     g_pConstantTable->SetMatrix(g_pd3dDevice, ViewProjMatrixHanle, &viewproj) ;
17 
18     // this line is mandatory
19     g_pConstantTable->SetDefaults(g_pd3dDevice);
20 }

注意最后一行很重要,他是用来设置常量表中所有变量的默认值的,很多时候如果不加这一句,窗口中将看不到任何东西!还有要在InitD3D函数中禁用光照效果,因为我们是自己设置颜色

1  g_pd3dDevice -> SetRenderState( D3DRS_LIGHTING , FALSE ); 

最后,也是最重要的,在Render函数中设置shader,只有这样,它才能生效

1  g_pd3dDevice -> SetVertexShader(g_pVertexShader) ;

程序结束后,在Cleanup函数中清除常量表和shader

1  if (g_pConstantTable  !=  NULL)
2      g_pConstantTable -> Release() ;
3 
4  if (g_pVertexShader  !=  NULL)
5      g_pVertexShader -> Release() ;
6 

好了,大功告成,现在编译并运行这个程序,你将看到一个蓝色的茶壶!

完整源代码

Code
  1 #include <d3dx9.h>
  2 
  3 LPDIRECT3D9             g_pD3D                = NULL ; // Used to create the D3DDevice
  4 LPDIRECT3DDEVICE9       g_pd3dDevice        = NULL ; // Our rendering device
  5 ID3DXMesh*                g_pMesh                = NULL ; // Hold the teapot
  6 IDirect3DVertexShader9*    g_pVertexShader        = NULL ; // vertext shader
  7 ID3DXConstantTable*        g_pConstantTable    = NULL ; // shader constant table
  8 
  9 // variable handle in shader file
 10 D3DXHANDLE ViewProjMatrixHanle = 0 ;
 11 
 12 HRESULT InitD3D( HWND hWnd )
 13 {
 14     // Create the D3D object, which is needed to create the D3DDevice.
 15     if( NULL == ( g_pD3D = Direct3DCreate9( D3D_SDK_VERSION ) ) )
 16         return E_FAIL;
 17 
 18     D3DPRESENT_PARAMETERS d3dpp; 
 19     ZeroMemory( &d3dpp, sizeof(d3dpp) );
 20     d3dpp.Windowed = TRUE;
 21     d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
 22     d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
 23 
 24     // Create device
 25     if( FAILED( g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
 26         D3DCREATE_SOFTWARE_VERTEXPROCESSING,
 27         &d3dpp, &g_pd3dDevice ) ) )
 28     {
 29         return E_FAIL;
 30     }
 31 
 32     //g_pd3dDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME);   
 33     g_pd3dDevice->SetRenderState( D3DRS_LIGHTING , FALSE );   
 34 
 35     D3DXCreateTeapot(g_pd3dDevice, &g_pMesh, NULL) ;
 36 
 37     return S_OK;
 38 }
 39 
 40 VOID Cleanup()
 41 {
 42     if( g_pd3dDevice != NULL) 
 43         g_pd3dDevice->Release();
 44 
 45     if( g_pD3D != NULL)
 46         g_pD3D->Release();
 47 
 48     if(g_pMesh != NULL)
 49         g_pMesh->Release() ;
 50 
 51     if(g_pConstantTable != NULL)
 52         g_pConstantTable->Release() ;
 53 
 54     if(g_pVertexShader != NULL)
 55         g_pVertexShader->Release() ;
 56 }
 57 
 58 bool PrepareShader()
 59 {
 60     ID3DXBuffer *shader ;
 61     ID3DXBuffer *errorBuffer ;
 62     
 63     // Compile shader from file
 64     HRESULT hr = D3DXCompileShaderFromFileA("Shader.txt"00"Main""vs_1_1"
 65         D3DXSHADER_ENABLE_BACKWARDS_COMPATIBILITY, &shader, &errorBuffer, &g_pConstantTable) ;
 66 
 67     // error handling
 68     // output compile error message
 69     if( errorBuffer )
 70     {
 71         MessageBoxA(0, (char*)errorBuffer->GetBufferPointer(), 00);
 72         errorBuffer->Release() ;
 73     }
 74 
 75     // compile failed
 76     if(FAILED(hr))
 77     {
 78         MessageBox(0, L"D3DXCompileShaderFromFile() - FAILED"00);
 79         return false;
 80     }
 81 
 82     // Create shader
 83     hr = g_pd3dDevice->CreateVertexShader((DWORD*)shader->GetBufferPointer(), &g_pVertexShader) ;
 84 
 85     // create failed
 86     if(FAILED(hr))
 87     {
 88         MessageBox(0, L"CreateVertexShader - FAILED"00);
 89         return false;
 90     }
 91 
 92     shader->Release() ;
 93 
 94     // map handle to variable in shader
 95     ViewProjMatrixHanle = g_pConstantTable->GetConstantByName(0"ViewProjMatrix") ;
 96 }
 97 
 98 void SetupMatrix()
 99 {
100     // set view
101     D3DXVECTOR3 eyePt(0.0f0.0f-10.0f) ;
102     D3DXVECTOR3 upVec(0.0f1.0f0.0f) ;
103     D3DXVECTOR3 lookCenter(0.0f0.0f0.0f) ;
104 
105     D3DXMATRIX view ;
106     D3DXMatrixLookAtLH(&view, &eyePt, &lookCenter, &upVec) ;
107 
108     // set projection
109     D3DXMATRIX proj ;
110     D3DXMatrixPerspectiveFovLH(&proj, D3DX_PI / 41.0f1.0f1000.0f) ;
111 
112     D3DXMATRIX viewproj = view * proj ;
113     g_pConstantTable->SetMatrix(g_pd3dDevice, ViewProjMatrixHanle, &viewproj) ;
114 
115     // this line is mandatory
116     g_pConstantTable->SetDefaults(g_pd3dDevice);
117 }
118 
119 VOID Render()
120 {
121     if(!PrepareShader())
122         return ;
123 
124     SetupMatrix() ;
125 
126     // Clear the back-buffer to a RED color
127     g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,0), 1.0f0 );
128 
129     // Begin the scene
130     if( SUCCEEDED( g_pd3dDevice->BeginScene() ) )
131     {
132         // Rendering of scene objects can happen here
133         g_pd3dDevice->SetVertexShader(g_pVertexShader) ;
134 
135         g_pMesh->DrawSubset(0) ;
136         // End the scene
137         g_pd3dDevice->EndScene();
138     }
139 
140     // Present the back-buffer contents to the display
141     g_pd3dDevice->Present( NULL, NULL, NULL, NULL );
142 }
143 
144 LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
145 {
146     switch( msg )
147     {
148     case WM_KEYDOWN:
149         {
150             switch( wParam )
151             {
152             case VK_ESCAPE:
153                 SendMessage( hWnd, WM_CLOSE, 00 );
154                 break ;
155             default:
156                 break ;
157             }
158         }
159         break ;
160 
161     case WM_DESTROY:
162         Cleanup();
163         PostQuitMessage( 0 );
164         return 0;
165     }
166 
167     return DefWindowProc( hWnd, msg, wParam, lParam );
168 }
169 
170 INT WINAPI wWinMain( HINSTANCE hInst, HINSTANCE, LPWSTR, INT )
171 {
172     // Register the window class
173     WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L0L
174         GetModuleHandle(NULL), NULL, NULL, NULL, NULL,
175         L"D3D Tutorial", NULL };
176     RegisterClassEx( &wc );
177 
178     // Create the application's window
179     HWND hWnd = CreateWindow( L"D3D Tutorial", L"Simple shader"
180         WS_OVERLAPPEDWINDOW , 00600600,
181         NULL, NULL, wc.hInstance, NULL );
182 
183     // Initialize Direct3D
184     if( SUCCEEDED( InitD3D( hWnd ) ) )
185     { 
186         // Show the window
187         ShowWindow( hWnd, SW_SHOWDEFAULT );
188         UpdateWindow( hWnd );
189 
190         // Enter the message loop
191         MSG    msg ; 
192         ZeroMemory( &msg, sizeof(msg) );
193         PeekMessage( &msg, NULL, 0U0U, PM_NOREMOVE );
194 
195         while (msg.message != WM_QUIT)  
196         {
197             if( PeekMessage(&msg, NULL, 0U0U, PM_REMOVE) != 0)
198             {
199                 TranslateMessage (&msg) ;
200                 DispatchMessage (&msg) ;
201             }
202             else // Render the game if no message to process
203             {
204                 Render() ;
205             }
206         }
207     }
208 
209     UnregisterClass( L"D3D Tutorial", wc.hInstance );
210     return 0;
211 }
212 
213 
214 
215 

happy coding!

你可能感兴趣的:(HLSL-高级着色语言简介)