DirectX11编程6 红龙书第六章练习

环境:VS2017  语言:C++

 

在经过红龙书第六章大量的Demo之后,我们来到了第六章的练习题,基本上没有什么难度,但我们还是来看一下,了解更多的知识。

 

先附上工程的链接:https://github.com/anguangzhihen/Dx11

1.这边的程序都是以win64运行的;

2.如果没有找到Common脚本,请到工程/属性/VC++目录中添加包含目录“../Common”;

3.如果没有找到libs,请到工程/属性/链接器添加附加库目录“../Common/libs”

4.所有的练习都在工程中,全局搜索“练习6”关键字就能找到,想要运行打开注释即可。

 

有任何错误,请大佬们指正。

 

1.写出以下的数据结构对应的D3D10_INPUT_ELEMENT_DESC数组:

struct Vertex
{
	XMFLOAT3 Pos;
	XMFLOAT3 Tangent;
	XMFLOAT3 Normal;
	XMFLOAT2 Tex0;
	XMFLOAT2 Tex1;
	XMCOLOR Color;
};
答:
D3D11_INPUT_ELEMENT_DESC vertexLayout[6] = {
	{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
	{ "TANGENT", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
	{ "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 24, D3D11_INPUT_PER_VERTEX_DATA, 0 },
	{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 36, D3D11_INPUT_PER_VERTEX_DATA, 0 },
	{ "TEXCOORD", 1, DXGI_FORMAT_R32G32_FLOAT, 0, 44, D3D11_INPUT_PER_VERTEX_DATA, 0 },
	{ "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 52, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 }
};

 

2.重做Cube Demo,使用两个顶点缓存分别传递位置和颜色数据?

答:

首先是两个顶点缓存的创建:

std::vector<VertexPos> verticesPos =
{
	{ XMFLOAT3(-1.0f, 0.0f, -1.0f) },
	{ XMFLOAT3(-1.0f, 0.0f, +1.0f) },
	{ XMFLOAT3(+1.0f, 0.0f, +1.0f) },
	{ XMFLOAT3(+1.0f, 0.0f, -1.0f) },
	{ XMFLOAT3(0.0f, 1.41f, 0.0f) },
};

// 顶点缓存 Position
D3D11_BUFFER_DESC vbdPos;
ZeroMemory(&vbdPos, sizeof(vbdPos));
vbdPos.Usage = D3D11_USAGE_DEFAULT;
vbdPos.ByteWidth = sizeof(VertexPos) * verticesPos.size();
vbdPos.BindFlags = D3D11_BIND_VERTEX_BUFFER;
vbdPos.CPUAccessFlags = 0;

D3D11_SUBRESOURCE_DATA InitData;
ZeroMemory(&InitData, sizeof(InitData));
InitData.pSysMem = &verticesPos[0];
HR(md3dDevice->CreateBuffer(&vbdPos, &InitData, &mBoxVB));

UINT stride = sizeof(VertexPos);
UINT offset = 0;
md3dImmediateContext->IASetVertexBuffers(0, 2, &mBoxVB, &stride, &offset);

std::vector<VertexColor> verticesColor = 
{
	{ XMFLOAT4((const float*)&Colors::Red) },
	{ XMFLOAT4((const float*)&Colors::Green) },
	{ XMFLOAT4((const float*)&Colors::Blue) },
	{ XMFLOAT4((const float*)&Colors::Yellow) },
	{ XMFLOAT4((const float*)&Colors::Black) },
};

// 顶点缓存 Color
D3D11_BUFFER_DESC vbdColor;
ZeroMemory(&vbdColor, sizeof(vbdColor));
vbdColor.Usage = D3D11_USAGE_DEFAULT;
vbdColor.ByteWidth = sizeof(VertexColor) * verticesColor.size();
vbdColor.BindFlags = D3D11_BIND_VERTEX_BUFFER;
vbdColor.CPUAccessFlags = 0;

ZeroMemory(&InitData, sizeof(InitData));
InitData.pSysMem = &verticesColor[0];
HR(md3dDevice->CreateBuffer(&vbdColor, &InitData, &mBoxVB));

stride = sizeof(VertexColor);
offset = 0;
md3dImmediateContext->IASetVertexBuffers(1, 2, &mBoxVB, &stride, &offset);

 

然后在创建布局时使用以下描述:

D3D11_INPUT_ELEMENT_DESC vertexLayout[2] = {
	{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
	{ "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }
};

 

3.实现第五章的不同图元的效果?

答:

md3dImmediateContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_POINTLIST);
md3dImmediateContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_LINESTRIP);
md3dImmediateContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_LINELIST);
md3dImmediateContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);

 

4.实现一个顶端为红色、低端为绿色的椎体?

答:

误打误撞就是我们坍缩的正方体!那么我们改一下颜色就可以了:

std::vector<Vertex> vertices =
{
	{ XMFLOAT3(-1.0f, 0.0f, -1.0f), XMFLOAT4((const float*)&Colors::Green) },
	{ XMFLOAT3(-1.0f, 0.0f, +1.0f), XMFLOAT4((const float*)&Colors::Green) },
	{ XMFLOAT3(+1.0f, 0.0f, +1.0f), XMFLOAT4((const float*)&Colors::Green) },
	{ XMFLOAT3(+1.0f, 0.0f, -1.0f), XMFLOAT4((const float*)&Colors::Green) },
	{ XMFLOAT3(0.0f, 1.41f, 0.0f), XMFLOAT4((const float*)&Colors::Red) },
};

 

5.运行Box Demo,解释方体上各个像素点为什么呈现出我们看到的颜色?

答:

我们只指定了顶点的颜色,而顶点之间的颜色值便是通过插值的方式来确定的。

 

6.以Cube Demo为基础,实现顶点动画?

答:

在常量缓存中添加float gTime,然后在Update函数中每帧使用mTimer.TotalTime()来赋值该值,传递到shader中。

 

shader在计算齐次坐标前,先计算顶点位置即可:

pIn.PosL.xy += 0.5f*sin(pIn.PosL.x)*sin(3.0f*gTime);
pIn.PosL.z *= 0.6f + 0.4f*sin(2.0f*gTime);

 

7.使用一个顶点缓存分别渲染椎体和方体?

这个和Shapes Demo的思路是一样的,改动比较大,所以新建工程了,请参见Chapter 6_6 Exercise7 BoxAndPryramid。

 

8.实现Cube Demo在线框模式下运行?

答:

线框模式没啥好说的,使用以下代码创建线框模式的描述:

D3D11_RASTERIZER_DESC wireframeDesc;
ZeroMemory(&wireframeDesc, sizeof(D3D11_RASTERIZER_DESC));
wireframeDesc.FillMode = D3D11_FILL_WIREFRAME;
wireframeDesc.CullMode = D3D11_CULL_BACK;
wireframeDesc.FrontCounterClockwise = false;
wireframeDesc.DepthClipEnable = true;

HR(md3dDevice->CreateRasterizerState(&wireframeDesc, &mWireframeRS));

 

再在DrawIndex之前设置线框模式即可:

md3dImmediateContext->RSSetState(mWireframeRS);

 

9.尝试在Cube Demo下设置不同的CullMode?

答:

三种CullMode可以尝试一下效果:

wireframeDesc.CullMode = D3D11_CULL_BACK;
wireframeDesc.CullMode = D3D11_CULL_NONE;
wireframeDesc.CullMode = D3D11_CULL_FRONT;

 

10.使用32bit的Color代替128bit的Color传递到Shader中?

答:

本题shader不用做任何更改,转换应该是在内部完成了。

 

首先是创建缓存:

// 创建32bitColor的顶点数据
std::vector<Vertex32bitColor> vertices =
{
	{ XMFLOAT3(-1.0f, 0.0f, -1.0f), XMCOLOR(Colors::Red) },
	{ XMFLOAT3(-1.0f, 0.0f, +1.0f), XMCOLOR(Colors::Green) },
	{ XMFLOAT3(+1.0f, 0.0f, +1.0f), RgbaToArgb (0x0000ffff) },
	{ XMFLOAT3(+1.0f, 0.0f, -1.0f), XMCOLOR(Colors::Yellow) },
	{ XMFLOAT3(0.0f, 1.41f, 0.0f), XMCOLOR(Colors::Black) },
};


D3D11_BUFFER_DESC vbd;
ZeroMemory(&vbd, sizeof(vbd));
vbd.Usage = D3D11_USAGE_DEFAULT;
vbd.ByteWidth = sizeof(Vertex32bitColor) * vertices.size();
vbd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
vbd.CPUAccessFlags = 0;

D3D11_SUBRESOURCE_DATA InitData;
ZeroMemory(&InitData, sizeof(InitData));
InitData.pSysMem = &vertices[0];
HR(md3dDevice->CreateBuffer(&vbd, &InitData, &mBoxVB));

UINT stride = sizeof(Vertex32bitColor);
UINT offset = 0;
md3dImmediateContext->IASetVertexBuffers(0, 1, &mBoxVB, &stride, &offset);

 

然后是布局设定:

D3D11_INPUT_ELEMENT_DESC vertexLayout[2] = {
	{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
	{ "COLOR", 0, DXGI_FORMAT_B8G8R8A8_UNORM, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 }
};

 

RgbaToAbgr的函数如下:

static UINT RgbaToArgb(UINT rgba)
{
	BYTE R = (rgba >> 24) & 0xff;
	BYTE G = (rgba >> 16) & 0xff;
	BYTE B = (rgba >> 8) & 0xff;
	BYTE A = (rgba >> 0) & 0xff;

	return (A << 24) | (R << 16) | (G << 8) | (B << 0);
}

 

这边使用XMCOLOR进行创建是最好的,它内部会进行转换。但我们需要知道一般情况下传递给shader的是小端储存的,即ABGR,使用UINT作为数据类型时就这么传;而使用UINT创建XMCOLOR时,需要以ARGB的顺序创建。

 

11.在Skull Demo改变viewport,只渲染窗口内的一小块?

答:

参见练习4.6

 

12.以下数据结构、描述,如果Pos和Color互相调换,数据结构之间不一一对应,shader还能获取到正确的数据吗?

struct Vertex
{
	DirectX::XMFLOAT3 Pos;
	DirectX::XMFLOAT4 Color;
};
D3D11_INPUT_ELEMENT_DESC vertexLayout[2] = {
	{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
	{ "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
};
struct VertexIn
{
	float3 PosL : POSITION;
	float4 Color : COLOR;
};

 

答:

可以,C++中的数据结构对应的是DESC中数据offset的数值,而shader中的数据是和DESC中名称相互对应,POSITION对应POSITION、COLOR对应COLOR。

 

但是这让我想到常量,常量没有类似的指定,所以必须一一对应,而且不能有null。

 

13.使用scissor test的技术剪裁像素点,实现类似练习11的效果?

答:

在DrawIndex之前:

D3D11_RECT rects = { 100, 100, 400, 400 };
md3dImmediateContext->RSSetScissorRects(1, &rects);

 

在创建线框模式时启用该技术:

wireframeDesc.ScissorEnable = true;

 

14.使用CreateGeosphere替代CreateSphere,并观察效果?

答:

GeometryGenerator::CreateGeosphere(0.5f, 0, sphere);
GeometryGenerator::CreateGeosphere(0.5f, 1, sphere);
GeometryGenerator::CreateGeosphere(0.5f, 2, sphere);
GeometryGenerator::CreateGeosphere(0.5f, 3, sphere);

 

我们就看一下1阶的Geosphere(Geosphere是以20面体为基础,每增加一阶就细分一次三角形,细分的越细越接近球体):