1.创建和使用顶点/索引缓冲的步骤如下:
·创建缓冲
顶点:Direct3DDevice9::CreateVertexBuffer
索引:Direct3DDevice9::CreateVertexBuffer && Direct3DDevice9::CreateIndexBuffer
· 锁定缓冲,写入数据再解锁。/**如果不Lock/Unlock 就直接使用会造成使用未定义指针;之所以这样做,而不是事先分配vertex数组是因为这不是在主存中分配空间,而是在显存中分配空间。*/
顶点:IDirect3DVertexBuffer9::Lock
索引: IDirect3DVertexBuffer9::Lock && IDirect3DIndexBuffer9::Lock
·绘制。
Direct3DDevice9::Clear
Direct3DDevice9::BeginScene();
Direct3DDevice9::SetStreamSource
Direct3DDevice9::SetIndices//索引专有
Device->SetFVF(Vertex::FVF);
顶点:Direct3DDevice9::DrawIndexedPrimitive
索引:Direct3DDevice9::DrawPrimitive
Direct3DDevice9::EndScene();
Direct3DDevice9::Present(0, 0, 0, 0);
2. 填充模式
Device->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID); );默认的是
D3DFILL_SOLID。还可以是D3DFILL_POINT和 D3DFILL_WIREFRAME。
3. 使用顶点着色时要关闭灯光,否则会变成黑色的。因为没有指定材质。
顶点着色这里指顶点的FVF格式中带有D3DFVF_DIFFUSE成分。
v[0] = ColorVertex(
4.设置灯光的步骤:
·开启灯光。
Direct3DDevice9::SetRenderState(D3DRS_LIGHTING, true);
·设置材质。顶点结构体不具备材质特性;因此必须设置当前材质。为了设置当前材质,我们使用 IDirect3DDevice9::SetMaterial(CONST D3DMATRIAL9* pMaterial) 方法。
·创建一个光源。
D3DLIGHT9 myLight;
· 设置状态。
顶点结构体不具备材质特性;因此必须设置当前材质。为了设置当前材质,我们使用 IDirect3DDevice9::SetMaterial(CONST D3DMATRIAL9* pMaterial) 方法。
5.纹理
读取纹理:
HRESULT D3DXCreateTextureFromFile(
LPDIRECT3DDEVICE9 pDevice, // 创建纹理的设备指针
LPCSTR pSrcFile, // 加载图像的文件名
LPDIRECT3DTEXTURE9* ppTexture // 接收创建的纹理的指针
);
设置纹理:
HRESULT IDirect3DDevice9::SetTexture(
DWORD Stage, // 该参数取值范围是0-7,用于确定纹理层
IDirect3DBaseTexture9* pTexture // 指向纹理设置的指针
);
纹理过滤:
• 临近点取样(Nearest point sampling)
Device->SetSamplerState(0, D3DSAMP MAGFILTER, D3DTEXF_POINT);
Device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT);
• 线性过滤(Linear filtering) ──线性过滤推荐用于缩小图像。
Device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
Device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
• 双向过滤(Anisotropic filtering)
Device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_ANISOTROPIC);
Device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_ANISOTROPIC);
当使用双向过滤时,我们必须设置 D3DSAMP_MAXANISOTROPY 等级,它决定了双向过滤的质量。该值越高处理的效果越好。检查 D3DCAPS9 结构体,确认你的显卡是否支持此功能。下面的代码设置该值为4:
Device->SetSamplerState(0, D3DSAMP_MAXANISOTROPY, 4);
Mipmaps
MIP 映射过滤器用于控制 Direct3D 如何使用 MIP 映射。你可以像下面所写代码一样设置 MipMap :
Device->SetSamplerState(0, D3DSAMP_MIPFILTER, Filter);
其中 Filter 是以下三个设置选项中的一种:
• D3DTEXF_NONE ── 不使用 MIP 映射。
• D3DTEXF_POINT ── 通过使用该过滤选项,Direct3D 将选择最接近屏幕三角形大小的 MIP 映射等级。一旦选中该等级,Direct3D就将按照指定的过滤器进行缩小和放大过滤。
• D3DTEXF_LINEAR ── 通过使用该过滤选项,Direct3D 将获得两个最接近的 MIP 映射等级,缩小和放大过滤每个等级,并线性组合两个等级计算得到最终的颜色值。
6.混合
默认情况下,混合是被关闭的,你需要通过设置 D3DRS_ALPHABLENDENABLE 渲染状态为 true 来开启混合。
Device->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
混合是一个开销比较大的操作,只能对那些需要使用的几何体进行混合。当你渲染完了几何体,你应该关闭 Alpha 混合。同样,你应当尝试批量对三角形进行混合并立即渲染他们,避免在渲染和混合之间来回切换,导致绘制每一帧的耗时增加。
Device->SetRenderState(D3DRS_SRCBLEND, Source);
Device->SetRenderState(D3DRS_DESTBLEND, Destination);
默认状况下,源混合要素与目标混合要素分别设置为 D3DBLEND_SRCALPHA 和 D3DBLEND_INVSRCALPHA
• D3DBLEND_SRCALPHA ── blendFactor = (as, as, as, as)
• D3DBLEND_INVSRCALPHA ── blendFactor = (1-as, 1-as, 1-as, 1-as)
默认情况下,假如设置一个有 alpha 通道的纹理,alpha 值从在 alpha 通道中获得。假如没有 alpha 通道,那么 alpha 值是通过顶点颜色获得。你可以通过下面的渲染状态来指定使用哪一个资源:
// 在着色期间从漫射色计算 alpha 值
Device->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE);
Device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
// 通过 Alpha 通道获得 alpha 值
Device->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
Device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
7.模板缓冲
使用步骤:
1. 清空模板缓冲
先清空模板缓冲使得整个模板缓冲的画面为一个指定值,例如:
Device->Clear(0, 0,
D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL,
0xff000000,
就指定了清空模板缓冲为0。
2.启用模板缓冲
Device->SetRenderState(D3DRS_STENCILENABLE, true);
这样我们就可以使用模板缓冲了。
3. 在模板缓冲中标记需要绘制的区域
这里要涉及到模板测试,然后更新模板缓冲入口(value)
模板测试:
( ref & mask ) ComparisonOperation ( value & mask )
模板测试是针对每个像素的,不过先假定模板是开启的(enabled),而且它获得两个操作数:
·左边的操作数( LHS = ref & mask ),表明应用程序定义的模板参考值(ref)和应用程序定义的掩码值(mask)的与操作。
·右边的操作数( RHS = value & mask )表明模板缓冲中我们将要测试的特定像素(value)和应用程序定义的掩码值(mask)的与操作。
模板测试将通过指定的比较运算来比较LHS和RHS,这整个表达式的最终结果是一个布尔值(true 或 false)。如果测试结果为true我们将把像素写入后台缓冲,如果是false我们就阻止像素的写入。当然,如果像素没有被写入后台缓冲,它也将不会被写入深度缓冲。
( ref & mask ) ComparisonOperation ( value & mask )
模板参考值Stencil Reference Value
模板参考值 ref默认为0,但是我们可以通过D3DRS_STENCILREF渲染状态来改变它。
例如,以下的代码设置模板参考值为1:
Device->SetRenderState ( D3DRS_STENCILREF,0x1 );
( ref & mask ) ComparisonOperation ( value & mask )
模板掩码值mask用来 遮盖ref和value变量的位。默认的mask为0xffffffff,它不会掩盖任何位。我们可以通过设置D3DRS_STENCILMASK渲染状态来改变mask.以下例子表明掩盖高16位:
Device->SetRenderState(D3DRS_STENCILMASK,0x0000ffff);
( ref & mask ) ComparisonOperation ( value & mask )
typedef enum _D3DCMPFUNC {
D3DCMP_NEVER = 1, 模板测试永远不能成功
D3DCMP_LESS = 2, 如果LHS < RHS的话,模板测试成功
D3DCMP_EQUAL = 3, 如果LHS = RHS,模板测试成功
D3DCMP_LESSEQUAL = 4, 如果LHS <= RHS 模板测试成功
D3DCMP_GREATER = 5, 如果LHS>RHS 模板测试成功
D3DCMP_NOTEQUAL = 6, 如果LHS !=RHS模板测试成功
D3DCMP_GREATEREQUAL = 7, 如果LHS >= RHS模板测试成功
D3DCMP_ALWAYS = 8, 模板测试永远成功
D3DCMP_FORCE_DWORD = 0x7fffffff
} D3DCMPFUNC;
更新模板缓冲
除了决定是否写入或阻止一个特定像素被写入后台缓冲,我们还能通过3种方法来更新定义模板缓冲入口将如何被更新:
1.[i][j]处像素模板缓冲测试失败:
我们可以设置D3DRS_STENCILFAIL渲染状态来定义如何更新模板缓冲中的[i][j]来响应这种情况。
Device->SetRenderState(D3DRS_STENCILFAIL,StencilOperation);
2.[i][j]处像素深度缓冲测试失败:
我们可以设置D3DRS_STENCILZFAIL渲染状态来定义如何更新[i][j]入口来响应这种情况。
Device->SetRenderState(D3DRS_STENCILZFAIL,StencilOperation);
3.[i][j]处像素深度缓冲和模板缓冲测试均成功:
我们可以设置D3DRS_STENCILPASS渲染状态来定义如何更新[i][j]入口来响应这种情况。
Device->SetRenderState( D3DRS_STENCILPASS,StencilOperation);
其中StencilOperation可以是以下预定义常数之一:
· D3DSTENCILOP_KEEP—指定不改变模板缓冲
· D3DSTENCILOP_ZERO—指定设置模板缓冲为0
· D3DSTENCILOP_REPLACE—指定用模板引用值来代替模板缓冲
· D3DSTENCILOP_INCRSAT—指定增加模板缓冲入口. 如果增加后的值大于最大值,我们就把它限定为那个最大值。
· D3DSTENCILOP_DECRSAT—指定减少模板缓冲入口. 如果减少后的值小于0,我们就把它限定为0。
· D3DSTENCILOP_INVERT—指定取模板缓冲入口的逆。
· D3DSTENCILOP_INCR—指定增加模板缓冲入口. 如果增加后的值大于最大值,我们把它限定为0。
· D3DSTENCILOP_DECR—指定减少模板缓冲入口. 如果减少后的值小于0,我们就把它限定为无穷大 (maximum allowed value.)
以下代码使得模板缓冲中镜子区域为0x1,其它地方为0x0
Device->SetRenderState(D3DRS_STENCILENABLE, true);//开启模板缓冲
//设置比较运算符,指定模板缓冲测试将一直成功
Device->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_ALWAYS);
//( ref & mask ) ComparisonOperation ( value & mask )
//设置模板模板参考值为0x1
Device->SetRenderState(D3DRS_STENCILREF, 0x1);
//设置模板掩码值,mask为0xffffffff,它不会掩盖任何位
Device->SetRenderState(D3DRS_STENCILMASK,0xffffffff);
//设置模板掩码写入值,它可以屏蔽任何我们写入模板的值,
//设置为0xffffffff,它不会屏蔽任何位
Device->SetRenderState(D3DRS_STENCILWRITEMASK, 0xffffffff);
//深度缓冲测试失败不改变模板缓冲
Device->SetRenderState(D3DRS_STENCILZFAIL, D3DSTENCILOP_KEEP);
//模板缓冲测试失败不改变模板缓冲
Device->SetRenderState(D3DRS_STENCILFAIL, D3DSTENCILOP_KEEP);
//如果测试通过,用模板引用值0x1来代替模板缓冲入口
Device->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_REPLACE);
//接下来的代码块是渲染镜子,但是仅仅是渲染模板缓冲中的镜子.
//所以可以屏蔽深度缓冲中的渲染
Device->SetRenderState(D3DRS_ZWRITEENABLE, false);//阻止写入深度缓冲
//我们可以用设置混合来阻止更新后台缓冲,
//因为如果测试结果为true我们将把像素写入后台缓冲,
//但此时的pass不是为了写入后台缓冲而是使镜子和墙壁那个面在模板缓冲的值为//0x1
//但后面我们只渲染镜子到模板缓冲,所以只有镜子的值为0x1
Device->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
Device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ZERO);//源混合因子
Device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE);//目标混合因子
//混合结果和目标混合因子相同
// 把镜子画入模板缓冲
Device->SetStreamSource(0, VB, 0, sizeof(Vertex));
Device->SetFVF(Vertex::FVF);
Device->SetMaterial(&MirrorMtrl);
Device->SetTexture(0, MirrorTex);
D3DXMATRIX I;
D3DXMatrixIdentity(&I);
Device->SetTransform(D3DTS_WORLD, &I);
Device->DrawPrimitive(D3DPT_TRIANGLELIST, 18, 2);
// 重新开启深度缓冲
Device->SetRenderState( D3DRS_ZWRITEENABLE, true );
4. 根据模板测试在后台缓冲中绘制场景
//以下表示仅仅在模板测试成功的地方才会绘制茶壶的倒影。
//即只在镜子区域才会绘制场景。
//如果LHS = RHS,模板测试成功,此时ref为0x1,镜子的所在的地方也为0x1
Device->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_EQUAL);
//…
Teapot->DrawSubset(0);
//…
5. 恢复状态设置
Device->SetRenderState(D3DRS_ALPHABLENDENABLE, false);
Device->SetRenderState( D3DRS_STENCILENABLE, false);//关闭模板缓冲
Device->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW);
绘制阴影的时候可以通过设置
Device->SetRenderState(D3DRS_STENCILPASS,D3DSTENCILOP_INCR); // increment to 1
来使得同一个地方只被绘制一次