上一节学习笔记地址
上一节画三角形只有三个顶点,所以直接按照三个顶点顺序画即可。不过那里漏过了一个细节,那就是你的三角形的顶点顺序,这里要引入一个叫“背面消除(backface culling)”的概念。因为三角形有两个面,假如你的正面是背对着摄像机,那摄像机就不会渲染你三角形的正面的图像(被剔除掉),复习一下上节所传入的三角形顶点。
// 设置三角形顶点
VertexPosColor vertices[] =
{
{
XMFLOAT3(0.0f, 0.5f, 0), XMFLOAT4(0.0f, 1.0f, 0.0f, 1.0f) },
{
XMFLOAT3(0.5f, -0.5f, 0), XMFLOAT4(0.0f, 0.0f, 1.0f, 1.0f) },
{
XMFLOAT3(-0.5f, -0.5f, 0), XMFLOAT4(1.0f, 0.0f, 0.0f, 1.0f) }
};
DX使用左手坐标系,并规定顶点顺序按照顺时针排序的为正面,那另一面就是反面。
可以修改这种剔除方法Device->SetRenderState(D3DRS_CULLMODE,Value);
可以传入三种参数
参数 | 描述 |
---|---|
D3DCULL_NONE | 表示不采用背面消除,那么此时,不过正面指向哪里都将不会被剔除掉 |
D3DCULL_CW | 表示跟我们刚才说的相反,按右手顺时针方向指向的的方向指向我们的眼睛的话,那么剔除掉 |
D3DCULL_CCW | 默认值,顺时针为正面 |
所以,如果你把第一个顶点和第三个顶点调换,就显示不出东西了,因为你看到的是背面。
如果你要渲染一个四边形,可以在三角形的基础上再加三个点,,这样就变成了两个三角形,6个顶点,表示一个四边形,但是实际上有两个顶点是重复的,在顶点比较多的时候,会引起巨大的浪费,比如渲染一个立方体,甚至一个顶点会重复更多次,所以为了节省资源,DX引入了索引这个概念。
另外,由于我们现在渲染的是3维空间,必然要引入MVP变换,这个到着色器里再说。
顶点缓冲区
// ******************
// 设置立方体顶点
// 5________ 6
// /| /|
// /_|_____/ |
// 1|4|_ _ 2|_|7
// | / | /
// |/______|/
// 0 3
VertexPosColor vertices[] =
{
{
XMFLOAT3(-1.0f, -1.0f, -1.0f), XMFLOAT4(0.0f, 0.0f, 0.0f, 1.0f) },
{
XMFLOAT3(-1.0f, 1.0f, -1.0f), XMFLOAT4(1.0f, 0.0f, 0.0f, 1.0f) },
{
XMFLOAT3(1.0f, 1.0f, -1.0f), XMFLOAT4(1.0f, 1.0f, 0.0f, 1.0f) },
{
XMFLOAT3(1.0f, -1.0f, -1.0f), XMFLOAT4(0.0f, 1.0f, 0.0f, 1.0f) },
{
XMFLOAT3(-1.0f, -1.0f, 1.0f), XMFLOAT4(0.0f, 0.0f, 1.0f, 1.0f) },
{
XMFLOAT3(-1.0f, 1.0f, 1.0f), XMFLOAT4(1.0f, 0.0f, 1.0f, 1.0f) },
{
XMFLOAT3(1.0f, 1.0f, 1.0f), XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f) },
{
XMFLOAT3(1.0f, -1.0f, 1.0f), XMFLOAT4(0.0f, 1.0f, 1.0f, 1.0f) }
};
// 设置顶点缓冲区描述
D3D11_BUFFER_DESC vbd;
ZeroMemory(&vbd, sizeof(vbd));
vbd.Usage = D3D11_USAGE_IMMUTABLE;
vbd.ByteWidth = sizeof vertices;
vbd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
vbd.CPUAccessFlags = 0;
// 新建顶点缓冲区
D3D11_SUBRESOURCE_DATA InitData;
ZeroMemory(&InitData, sizeof(InitData));
InitData.pSysMem = vertices;
HR(m_pd3dDevice->CreateBuffer(&vbd, &InitData, m_pVertexBuffer.GetAddressOf()));
m_pd3dImmediateContext->IASetVertexBuffers(0, 1, m_pVertexBuffer.GetAddressOf(), &stride, &offset);
方法与上一节的笔记一样,只不过变成了8个顶点,三维空间坐标。
索引缓冲区
索引会引导DX选择渲染次序。
// ******************
// 索引数组
//
WORD indices[] = {
// 正面
0, 1, 2,
2, 3, 0,
// 左面
4, 5, 1,
1, 0, 4,
// 顶面
1, 5, 6,
6, 2, 1,
// 背面
7, 6, 5,
5, 4, 7,
// 右面
3, 2, 6,
6, 7, 3,
// 底面
4, 0, 3,
3, 7, 4
};
// 设置索引缓冲区描述
D3D11_BUFFER_DESC ibd;
ZeroMemory(&ibd, sizeof(ibd));
ibd.Usage = D3D11_USAGE_IMMUTABLE;
ibd.ByteWidth = sizeof indices;
ibd.BindFlags = D3D11_BIND_INDEX_BUFFER;
ibd.CPUAccessFlags = 0;
// 新建索引缓冲区
InitData.pSysMem = indices;
HR(m_pd3dDevice->CreateBuffer(&ibd, &InitData, m_pIndexBuffer.GetAddressOf()));
// 输入装配阶段的索引缓冲区设置
m_pd3dImmediateContext->IASetIndexBuffer(m_pIndexBuffer.Get(), DXGI_FORMAT_R16_UINT, 0);
方法同上文的顶点缓冲区类似,简单比较一下就能看懂了。
void ID3D11DeviceContext::IASetIndexBuffer(
ID3D11Buffer *pIndexBuffer, // [In]索引缓冲区
DXGI_FORMAT Format, // [In]数据格式
UINT Offset); // [In]字节偏移量
在装配的时候你需要指定每个索引所占的字节数:’
Format参数 | 字节数 | 索引范围 |
---|---|---|
DXGI_FORMAT_R8_UINT | 1 | 0-255 |
DXGI_FORMAT_R16_UINT | 2 | 0-65535 |
DXGI_FORMAT_R32_UINT | 4 | 0-2147483647 |
使用16位的索引有利于节省空间,现阶段绝大多数模型的顶点数目都少于65535个。
数学基础
补习一下向量和线代的知识
向量(vector)
向量由模(magnitude)和方向(direction)组成,与标量(scalar)区分。
标量与向量积
向量加法
二维向量加法的几何意义
向量的模
向量单位化
向量点积(dot product)
(ka)b=a(kb)=k(ab)
a(b+c)=ab+ac
vv=|v|^2
向量点积的几何意义
向量叉积(cross product)
axb!=bxa
axb=-(bxa)
(axb)xc!=ax(bxc)
叉积的几何意义: 得到垂直于a和b所在平面的向量。通过将a的头和b的尾相接,并检查从a到b是顺时针还是逆时针,能够确定a叉乘b的方向。左手坐标系中,如果a与b呈顺时针,则叉乘方向指向