VC++游戏开发随记之二十九.docx
《VC++游戏开发随记之二十九.docx》由会员分享,可在线阅读,更多相关《VC++游戏开发随记之二十九.docx(39页珍藏版)》请在冰豆网上搜索。
VC++游戏开发随记之二十九
【VisualC++】游戏开发笔记二十九一步一步教你用优雅的Direct3D11代码画一个三角形
这个demo演示的效果是用Direct3D11在屏幕上渲染一个三角形,当然是通过这个demo进一步巩固和学习Direct3D11,而不是单单为了画一个三角形这么简单。
正如之前所说,这个demo是建立在笔记二十八中讲解的D3DBlankWindowsDemo之上的。
那么,我们就开门见山,直入正题吧。
有个别童鞋表示编译源代码时出现了“无法解析的外部符号”的一系列错误,这都是lib库文件没添加造成的,这属于我们的DirectX11的开发环境没有配置好~
具体配置方法可以参考《VisualC++游戏开发笔记》系列第25篇文章:
【VisualC++】游戏开发笔记二十五最简化的DirectX11开发环境的配置
针对单个的程序的话,也可以在代码开头#include语句附近使用pragma语句来添加库文件的~ 针对这个DirectX11demo的话,在代码开头添加以下几句就好了:
#pragma comment(lib,"dxerr.lib")
#pragma comment(lib,"d3d11.lib")
#pragma comment(lib,"d3dx11.lib")
#pragma comment(lib,"d3dcompiler.lib")
————————浅墨于2012年11月06日
一、载入几何体
我们知道,为了渲染几何图形,我们需要一个顶点缓存,一个描述顶点布局的输入层,以及一系列的着色器,自DirectX10以来,着色器开始作为图形渲染的基础组成部分,在这个demo之中我们会指定顶点着色器与像素着色器,渲染一种简单的纯色表面。
后面我们将延伸的讲解如何拓展使用这种效果来在表面映射图形纹理。
下面就开始进行这个demo的书写:
这个demo的核心内容当然是一个叫做TriangleDemo的类,我们为这个类定义几个成员变量,他们分别是ID3D11VertexShader类型的取名为solidColorVS_的变量,一个ID3D11PixelShader类型的唤作solidColorPS的变量。
一个ID3D11InputLayout类型的唤作inputLayout_的变量,以及一个ID3D11Buffer类型的叫做vertexBuffer_的变量。
下面就是TriangleDemo.h头文件的源代码,简单的勾勒出了本文主角TriangleDemo类的轮廓:
代码段一 TriangleDemo.h头文件
[cpp] viewplaincopyprint?
1.#include"Dx11DemoBase.h"
2.
3.class TriangleDemo :
public Dx11DemoBase
4.
5.{
6.
7.public:
8.
9. TriangleDemo( );
10.
11. virtual ~TriangleDemo( );
12.
13. bool LoadContent( );
14.
15. void UnloadContent( );
16.
17. void Update( float dt );
18.
19. void Render( );
20.
21.private:
22.
23. ID3D11VertexShader* solidColorVS_;
24.
25. ID3D11PixelShader* solidColorPS_;
26.
27. ID3D11InputLayout* inputLayout_;
28.
29. ID3D11Buffer* vertexBuffer_;
30.
31.};
顶点我们采用一个简单的三分量式的浮点型结构体,在XNAMathlibrary中一个叫做XMFLOAT3的结构体可以胜任这项殊荣。
接下来,开始丰富我们的TriangleDemo类,我们在代码段二中书写顶点结构体VertexPos和TriangleDemo的类的构造函数以及析构函数
代码段二TriangleDemo顶点结构体,构造函数和析构函数.
[cpp] viewplaincopyprint?
1.#include"TriangleDemo.h"
2.
3.#include
4.
5.struct VertexPos
6.
7.{
8.
9. XMFLOAT3 pos;
10.
11.};
12.
13.TriangleDemo:
:
TriangleDemo( ) :
solidColorVS_( 0 ), solidColorPS_( 0 ),
14.
15.inputLayout_( 0 ), vertexBuffer_( 0 )
16.
17.{
18.
19.}
20.
21.TriangleDemo:
:
~TriangleDemo( )
22.
23.{
24.
25.}
下面继续丰富我们的TriangleDemo类,在代码段三中我们进行UnloadContent函数的书写,顾名思义,UnloadContent是进行unloadcontent工作的,与后面将书写的LoadContent函数相对应。
代码段三TriangleDemo类的UnloadContent函数的书写
[cpp] viewplaincopyprint?
1.void TriangleDemo:
:
UnloadContent( )
2.
3.{
4.
5. if( solidColorVS_ ) solidColorVS_->Release( );
6.
7. if( solidColorPS_ ) solidColorPS_->Release( );
8.
9. if( inputLayout_ ) inputLayout_->Release( );
10.
11. if( vertexBuffer_ ) vertexBuffer_->Release( );
12.
13. solidColorVS_ = 0;
14.
15. solidColorPS_ = 0;
16.
17. inputLayout_ = 0;
18.
19. vertexBuffer_ = 0;
20.
21.}
顺理成章的,下一步便是LoadContent函数的书写。
这个函数由顶点着色器载入,在文件SolidGreenColor.fx中可以查看。
一旦顶点着色器的源代码编译完成,着色器便创建一个CreateVertexShader函数的调用,我们接着创建顶点格式。
由于顶点着色器与顶点格式相关联,所以我们还需要将顶点着色器加载到内存中。
创建完顶点着色器和输入格式后,下一步我们创建像素着色器。
下面这段代码实现了LoadContent方法的一半的功能:
代码段四 LoadContent函数着色器载入代码
[cpp] viewplaincopyprint?
1.bool TriangleDemo:
:
LoadContent( )
2.
3.{
4.
5. ID3DBlob* vsBuffer = 0;
6.
7. bool compileResult = CompileD3DShader( "SolidGreenColor.fx",
8.
9. "VS_Main", "vs_4_0", &vsBuffer );
10.
11. if( compileResult == false )
12.
13. {
14.
15. MessageBox( 0, "载入顶点着色器错误!
", "编译错误", MB_OK );
16.
17. return false;
18.
19. }
20.
21. HRESULT d3dResult;
22.
23. d3dResult = d3dDevice_->CreateVertexShader( vsBuffer->GetBufferPointer(
24.
25.),
26.
27.vsBuffer->GetBufferSize( ), 0, &solidColorVS_ );
28.
29.if( FAILED( d3dResult ) )
30.
31.{
32.
33. if( vsBuffer )
34.
35. vsBuffer->Release( );
36.
37. return false;
38.
39.}
40.
41.D3D11_INPUT_ELEMENT_DESC solidColorLayout[] =
42.
43.{
44.
45.{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT,
46.
47. 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }
48.
49.};
50.
51.unsigned int totalLayoutElements = ARRAYSIZE( solidColorLayout );
52.
53.d3dResult = d3dDevice_->CreateInputLayout( solidColorLayout,
54.
55.totalLayoutElements, vsBuffer->GetBufferPointer( ),
56.
57.vsBuffer->GetBufferSize( ), &inputLayout_ );
58.
59.vsBuffer->Release( );
60.
61.if( FAILED( d3dResult ) )
62.
63.{
64.
65. return false;
66.
67.}
68.
69.ID3DBlob* psBuffer = 0;
70.
71.compileResult = CompileD3DShader( "SolidGreenColor.fx",
72.
73."PS_Main", "ps_4_0", &psBuffer );
74.
75.if( compileResult == false )
76.
77.{
78.
79. MessageBox( 0, "载入像素着色器错误!
", "编译错误", MB_OK );
80.
81. return false;
82.
83.}
84.
85.d3dResult = d3dDevice_->CreatePixelShader( psBuffer->GetBufferPointer( ),
86.
87.psBuffer->GetBufferSize( ), 0, &solidColorPS_ );
88.
89.psBuffer->Release( );
90.
91.if( FAILED( d3dResult ) )
92.
93.{
94.
95. return false;
96.
97.}
98.
99....
100.
101.//后接函数的下半段
102.
103.}
CompileD3DShader相关的代码在代码段五中进行了演绎,这段代码巧妙地被分离于LoadContent之外,这样在加载多个不同的着色效果的时候便可以避免大段大段的冗余代码:
代码段五 CompileShader函数的实现方法
[cpp] viewplaincopyprint?
1.bool Dx11DemoBase:
:
CompileD3DShader( char* filePath, char* entry, char*
2.
3.shaderModel, ID3DBlob** buffer )
4.
5.{
6.
7. DWORD shaderFlags = D3DCOMPILE_ENABLE_STRICTNESS;
8.
9. #if defined( DEBUG ) || defined( _DEBUG )
10.
11. shaderFlags |= D3DCOMPILE_DEBUG;
12.
13. #endif
14.
15. ID3DBlob* errorBuffer = 0;
16.
17. HRESULT result;
18.
19. result = D3DX11CompileFromFile( filePath, 0, 0, entry, shaderModel,
20.
21. shaderFlags, 0, 0, buffer, &errorBuffer, 0 );
22.
23. if( FAILED( result ) )
24.
25. {
26.
27. if( errorBuffer !
= 0 )
28.
29. {
30.
31. OutputDebugStringA( ( char* )errorBuffer->GetBufferPointer( ) );
32.
33. errorBuffer->Release( );
34.
35. }
36.
37. return false;
38.
39. }
40.
41. if( errorBuffer !
= 0 )
42.
43. errorBuffer->Release( );
44.
45. return true;
46.
47.}
上面我们介绍了上半段LoadContent函数的构成,而下半段LoadContent函数主要实现了顶点缓存的创建。
这段代码行文思路很明朗,首先定义一个简单的三角形,沿X轴与Y轴都是0.5f(半个单位的长度)。
Z轴依然设为为0.5f,来使此三角形可见。
因为若镜头隔表面太近或者太远,表面都不会成功的渲染。
顶点列表存储于一个叫做vertices的数组中,它提供了一个子资源数据,在CreateBuffer函数开始调用进行实际顶点缓存的创建的时候,这些数据可以派上用场。
下面就是上面这段叙述的代码实现,LoadContent函数的下半部分书写风格如下:
代码段六LoadContent函数的几何图形载入代码
[cpp] viewplaincopyprint?
1.bool TriangleDemo:
:
LoadContent( )
2.
3.{
4.
5.//前接函数的上半段
6.
7.
8.
9....
10.
11. VertexPos vertices[] =
12.
13. {
14.
15. XMFLOAT3( 0.5f, 0.5f, 0.5f ),
16.
17. XMFLOAT3( 0.5f, -0.5f, 0.5f ),
18.
19. XMFLOAT3( -0.5f, -0.5f, 0.5f )
20.
21. };
22.
23. D3D11_BUFFER_DESC vertexDesc;
24.
25. ZeroMemory( &vertexDesc, sizeof( vertexDesc ) );
26.
27. vertexDesc.Usage = D3D11_USAGE_DEFAULT;
28.
29. vertexDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
30.
31. vertexDesc.ByteWidth = sizeof( VertexPos ) * 3;
32.
33. D3D11_SUBRESOURCE_DATA resourceData;
34.
35. ZeroMemory( &resourceData, sizeof( resourceData ) );
36.
37. resourceData.pSysMem = vertices;
38.
39. d3dResult = d3dDevice_->CreateBuffer( &vertexDesc,
40.
41. &resourceData, &vertexBuffer_ );
42.
43. if( FAILED( d3dResult ) )
44.
45. {
46.
47. return false;
48.
49. }
50.
51. return true;
52.
53.}
二、渲染几何体
Direct11三角形Demo代码的最后两部分由实现几何渲染功能的代码和着色器本身构成。
渲染几何图形的构成代码在TriangleDemo类中的Render函数中进行。
函数中有
有一个条件语句,这样可以确保在Direct3D的上下文是有效的。
接下来,我们清除渲染目标,并设定输出程序集(inputassembler)。
而实际上,因为在这个demo之中的三角形是静态的,我们并不一定非要清除渲染目标,这里只是为了规范我们的代码书写,以免养成不良的开发习惯。
在输出程序集阶段的设置由我们已经创建的输出结构(inputlayout)进行绑定,并提供顶点缓存,设置拓扑三角形的列表。
下面贴出Render函数的书写思路:
代码段七TriangleDemo类的render函数书写
[cpp] viewplaincopyprint?
1.void TriangleDemo:
:
Render( )
2.
3.{
4.
5. if( d3dContext_ == 0 )
6.
7. return;
8.
9. float clearColor[4] = { 0.5, 0.5f, 0.5f, 1.0f }; //设定背景颜色
10.
11. d3dContext_->ClearRenderTargetView( backBufferTarget_, clearColor );
12.
13. unsigned int stride = sizeof( VertexPos );
14.
15. unsigned int offset = 0;
16.
17. d3dContext_->IASetInputLayout( inputLayout_ );
18.
19. d3dContext_->IASetVertexBuffers( 0, 1, &vertexBuffer_, &stride, &offset );
20.
21. d3dContext_->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_
22.
23. TRIANGLELIST );
24.
25. d3dContext_->VSSetShader( solidColorVS_, 0, 0 );
26.
27. d3dContext_->PSSetShader( solidColorPS_, 0, 0 );
28.
29. d3dContext_->Draw( 3, 0 );
30.
31. swapChain_->Present( 0, 0 );
32.
33.}
最后一部分要介绍的代码是着色器。
笼统的来说,顶点着色器基于它得到的内容。
详细的来说,顶点着色器的作用是将内部得到的顶点位置传递到输出处,之后,我们须处理这些数据,正确绘制出我们的图形。
但对于这个非常基础的demo,仅仅进行顶点位置内容的传递就够了。
如果没有几何图形着色器绑定到输出程序集之上,顶点