今天才发现, 原来金容俊的<3D游戏编程>第一篇的体系和内容是和DX SDK一样的, 这本书上的例子基本就是SDK的例子. 尽管如此, 我还是决定按部就班的实践一番. 这一次, 就来创建一个圆筒, 并加上光源.
第五步: 光源
(一) 材质类型
由多边形组成的三维物体, 称为网格 (Mesh), 网格对于对象是缺乏表现力的, 需要用材质填充其表面, 并以光源使其更加真实.
1. 环境光 (ambient): 物体只具有整体亮度;
2. 漫反射光 (diffuse): 均匀照射在物体表面;
3. 镜面反射光 (specular): 发射到特定方向的光, 光源与摄像机位置不同;
4. 放射光 (emissive): 物体自发光, 不对其他网格产生影响.
(二) 光源类型
1. 环境光源 (ambient light): 与三维空间内网格配置, 位置无关, 没有方向和位置, 只有颜色和强度;
2. 点光源 (point light): 光从一个点发出, 光源位置, 方向不同, 强度也不同;
3. 方向光源 (directional light): 光源的方向是定向的 (如太阳), 和光源位置无关, 方向是最重要的因素;
4. 聚光光源 (spot light): 只对固定位置和方向进行照射, 有方向和强弱, 电影, 舞台的光源就是聚光光源.
这些光源模型只在调用D3D固定函数管道时有效, 如使用HLSL, 顶点着色, 像素着色时就不再有效.
struct
CUSTOMVERTEX
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
...
{
D3DXVECTOR3 position; // 顶点三维坐标
D3DXVECTOR3 normal; // 顶点法线向量
}
;
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
BOOL InitD3D(HWND hWnd)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
...
{
.......
D3Dpp.EnableAutoDepthStencil = TRUE;
D3Dpp.AutoDepthStencilFormat = D3DFMT_D16;
.......
// 卷起, 渲染三角形前面及后面
g_pD3DDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
// 起到Z缓冲的作用
g_pD3DDevice->SetRenderState(D3DRS_ZENABLE, TRUE);
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
return S_OK;
}
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
HRESULT InitGeometry()
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
...
{
// 创建顶点缓冲
if(FAILED(g_pD3DDevice->CreateVertexBuffer(100 * sizeof(CUSTOMVERTEX),
0, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &g_pVB, NULL)))
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
...{
return E_FAIL;
}
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
// 使用算法绘制气缸
CUSTOMVERTEX *pVertices;
if(FAILED(g_pVB->Lock(0, 0, (void **)&pVertices, 0)))
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
...{
return E_FAIL;
}
for(DWORD i=0; i<99; i+=2)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
...{
FLOAT theta = (2 * D3DX_PI * i) / (50 -1);
pVertices[i+0].position = D3DXVECTOR3(sinf(theta), -1.0f, cosf(theta)); // 气缸上方圆
pVertices[i+0].normal = D3DXVECTOR3(sinf(theta), 0.0f, cosf(theta)); // 气缸上方圆法线
pVertices[i+1].position = D3DXVECTOR3(sinf(theta), 1.0f, cosf(theta)); // 气缸下方圆
pVertices[i+1].normal = D3DXVECTOR3(sinf(theta), 0.0f, cosf(theta)); // 气缸下方圆法线
}
g_pVB->Unlock();
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
return S_OK;
}
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
void
SetupMatrices()
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
...
{
// 世界矩阵
D3DXMATRIXA16 matWorld;
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
D3DXMatrixIsIdentity(&matWorld); // 设定世界矩阵为单位矩阵
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
D3DXMatrixRotationX(&matWorld, timeGetTime()/500.0f); // 绕X轴旋转
g_pD3DDevice->SetTransform(D3DTS_WORLD, &matWorld);
.......
}
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
void
SetupLight()
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
...
{
// 创建材质
D3DMATERIAL9 mtrl;
ZeroMemory(&mtrl,sizeof(D3DMATERIAL9));
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
mtrl.Diffuse.a = mtrl.Ambient.a = 1.0f;
mtrl.Diffuse.r = mtrl.Ambient.r = 1.0f;
mtrl.Diffuse.g = mtrl.Ambient.g = 1.0f;
mtrl.Diffuse.b = mtrl.Ambient.b = 0.0f;
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
// 创建光源
D3DXVECTOR3 vecDir; // 方向光源的照射方向
D3DLIGHT9 light; // 光源结构体
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
ZeroMemory(&light, sizeof(D3DLIGHT9));
light.Type = D3DLIGHT_DIRECTIONAL; // 光源类型为方向光源
light.Diffuse.r = 1.0f;
light.Diffuse.g = 1.0f;
light.Diffuse.b = 1.0f;
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
vecDir = D3DXVECTOR3(cosf(timeGetTime()/350.0f), // 光源方向
1.0f,
sinf(timeGetTime()/350.0f));
D3DXVec3Normalize((D3DXVECTOR3 *)&light.Direction, &vecDir); // 将光源方向设为单位向量
light.Range = 1000.0f; // 光源能够照射到的最远距离
g_pD3DDevice->SetLight(0, &light); // 在设备设置0号光源
g_pD3DDevice->LightEnable(0, TRUE); // 打开0号光源
g_pD3DDevice->SetRenderState(D3DRS_LIGHTING, TRUE); // 打开光源设置
g_pD3DDevice->SetRenderState(D3DRS_AMBIENT, 0x00202020); // 设定环境光源的值
}
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
void
Render()
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
...
{
.......
// 开始渲染
if(SUCCEEDED(g_pD3DDevice->BeginScene()))
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
...{
// 创建光源
SetupLight();
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
// 创建矩阵
SetupMatrices();
.......
// 输出
g_pD3DDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 90);
// 结束渲染
g_pD3DDevice->EndScene();
}
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
// 显示后置缓冲的画面
g_pD3DDevice->Present(NULL, NULL, NULL, NULL);
}
我的程序出来的圆筒居然是黑色的, 没有光线.
显然应该是SetLight函数出了问题, 原来, 定义的材质没有使用:-(. 在对mtrl初始化之后加上这样一句:
g_pD3DDevice->SetMaterial(&mtrl);
搞定! :-)