绘制更复杂的图形
这里绘制了一个圆柱体,不过由于出了点问题,变成了一个莫比乌斯环。
除了InitVB函数中算法的改变以外,还有以下细节改变:
- CUSTOMVERTEX结构体中FLOAT x, y, z;改成了等价的D3DXVECTOR3 position;
- SetupMatrices函数中D3DXMatrixRotationY改为了D3DXMatrixRotationX
- Render函数中绘制数据流的指令由D3DPT_TRIANGLELIST改为D3DPT_TRIANGLESTRIP,同时最后一个参数指定2*50 - 2个项
///////////////////////////////////////
LPDIRECT3D9 pD3D = NULL;
LPDIRECT3DDEVICE9 pD3DDevice = NULL;
LPDIRECT3DVERTEXBUFFER9 pVB = NULL; // 顶点缓冲区实例
#include <d3dx9.h>
#pragma comment(lib, "d3dx9.lib")
#pragma comment(lib, "winmm.lib") // timeGetTime
// 自定义顶点结构体
struct CUSTOMVERTEX
{
D3DXVECTOR3 position; // 未变换的x,y,z
DWORD color; // 颜色
};
// 自定义顶点格式
// 未变换顶点+面反射色
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE)
///////////////////////////////////////
// 初始化顶点缓冲区(被InitD3D调用)
HRESULT InitVB()
{
HRESULT hr = S_OK;
// 创建顶点缓冲区
hr = pD3DDevice->CreateVertexBuffer(
50 * 2 * sizeof(CUSTOMVERTEX), // 大小
0, // 怎样使用缓存的额外信息
D3DFVF_CUSTOMVERTEX, // 自定义顶点格式
D3DPOOL_DEFAULT, // 顶点缓冲存储方式
&pVB, // 返回顶点缓存实例
NULL); // 保留
if (FAILED(hr)) goto ret;
// 锁定并获取缓冲区指针
CUSTOMVERTEX *pVertices = NULL;
hr = pVB->Lock(0, 0, (void**)&pVertices, 0);
if (FAILED(hr)) goto ret;
// 在缓冲区写入圆柱面的数据
for (DWORD i = 0; i < 50; i++)
{
FLOAT theta = (2 * D3DX_PI * i) / (50 - 1);
pVertices[2 * i].position = D3DXVECTOR3(sinf(theta), -1.0f, cosf(theta));
pVertices[2 * i].color = D3DCOLOR_XRGB(0, 255, 255);
pVertices[2 * i + 1].position = D3DXVECTOR3(sinf(theta), 1.0f, cosf(theta));
pVertices[2 * i + 1].color = D3DCOLOR_XRGB(255, 255, 255);
}
// 释放锁定
hr = pVB->Unlock();
if (FAILED(hr)) goto ret;
ret:
return hr;
}
// 初始化Direct3D
HRESULT InitD3D(HWND hwnd)
{
HRESULT hr = S_OK;
// 创建Direct3D 9根接口
pD3D = Direct3DCreate9(D3D_SDK_VERSION);
if (pD3D == NULL)
{
hr = E_FAIL;
goto ret;
}
// 配置Direct3D 9设备
D3DPRESENT_PARAMETERS d3dpp;
RtlZeroMemory(&d3dpp, sizeof d3dpp);
d3dpp.Windowed = TRUE; // 窗口化
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; // 交换缓冲加载后被删除
d3dpp.BackBufferFormat = D3DFMT_UNKNOWN; // 显示模式
// 创建Direct3D 9设备接口
hr = pD3D->CreateDevice(
D3DADAPTER_DEFAULT, // 默认适配器
D3DDEVTYPE_HAL, // 硬件驱动
hwnd, // 窗体
D3DCREATE_SOFTWARE_VERTEXPROCESSING, // 顶点像素渲染方式
&d3dpp, // 参数
&pD3DDevice); // 返回的设备接口
if (FAILED(hr)) goto ret;
// 设置剔除模式为NONE,这样可以看到三角形背面
pD3DDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
// 关闭光照系统,这样可以显示出本身的颜色
pD3DDevice->SetRenderState(D3DRS_LIGHTING, FALSE);
// 初始化顶点缓冲区
hr = InitVB();
if (FAILED(hr)) goto ret;
ret:
return hr;
}
// 设置世界、观察、投影变换矩阵
void SetupMatrices()
{
HRESULT hr;
// 设定世界变换矩阵为沿Y轴不停旋转
D3DXMATRIXA16 matWorld;
UINT iTime = timeGetTime() % 1000;
FLOAT fAngle = iTime * (2.0f * D3DX_PI) / 1000.0f;
D3DXMatrixRotationX(&matWorld, fAngle);
hr = pD3DDevice->SetTransform(D3DTS_WORLD, &matWorld);
// 设定观察变换矩阵
D3DXMATRIXA16 matView;
D3DXVECTOR3 vEyePt(0.0f, 3.0f, -5.0f); // 眼睛的位置
D3DXVECTOR3 vLookatPt(0.0f, 0.0f, 0.0f); //观察的位置
D3DXVECTOR3 vUpVec(0.0f, 1.0f, 0.0f); //向上的方向
D3DXMatrixLookAtLH(&matView, &vEyePt, &vLookatPt, &vUpVec);
hr = pD3DDevice->SetTransform(D3DTS_VIEW, &matView);
// 设定投影变换矩阵
D3DXMATRIXA16 matProj;
// 距离缩小比例、屏幕纵横比、最近剪切平面、最远剪切平面
D3DXMatrixPerspectiveFovLH(&matProj, D3DX_PI / 4, 1.0f, 1.0f, 100.0f);
hr = pD3DDevice->SetTransform(D3DTS_PROJECTION, &matProj);
}
// 绘制图形
void Render()
{
HRESULT hr = S_OK;
if (pD3DDevice == NULL)
goto ret;
// 清空缓存
hr = pD3DDevice->Clear(0, NULL, D3DCLEAR_TARGET,
D3DCOLOR_XRGB(0, 0, 255), 1.0f, 0);
// 开始绘制场景
hr = pD3DDevice->BeginScene();
if (FAILED(hr)) goto present;
// 设置变换矩阵
SetupMatrices();
// 绘制三角形
// 设置数据流0为pVB
hr = pD3DDevice->SetStreamSource(0, pVB, 0, sizeof(CUSTOMVERTEX));
// 设置顶点格式
hr = pD3DDevice->SetFVF(D3DFVF_CUSTOMVERTEX);
// 按三角形列表绘制数据流0
hr = pD3DDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2 * 50 - 2);
// 结束绘制场景
hr = pD3DDevice->EndScene();
present:
// 显示缓存内容
hr = pD3DDevice->Present(NULL, NULL, NULL, NULL);
ret:
return;
}
// 清理Direct3D
void CleanupD3D()
{
safe_release(&pVB);
safe_release(&pD3DDevice);
safe_release(&pD3D);
}
///////////////////////////////////////
</d3dx9.h>
补充解决办法——
添加深度缓冲区支持即可解决莫比乌斯环问题。
效果:
// 在InitD3D函数中加上这几句
d3dpp.EnableAutoDepthStencil = TRUE; // 开启深度模板
d3dpp.AutoDepthStencilFormat = D3DFMT_D16; // 16位
// ...
// 打开zbuffer
pD3DDevice->SetRenderState(D3DRS_ZENABLE, TRUE);
// 在Render函数中将
// 清空缓存
hr = pD3DDevice->Clear(0, NULL, D3DCLEAR_TARGET,
D3DCOLOR_XRGB(0, 0, 255), 1.0f, 0);
// 改为
// 清空缓存
hr = pD3DDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,
D3DCOLOR_XRGB(0, 0, 255), 1.0f, 0);