V0.1
Liker
这是ati的一个开源demo,展示了一些d3d8.1的特性。这篇文章主要是我读代码时候作的笔记,由于我还是个初学者,所以如果有什么理解错误的地方还要请各位老大帮忙提出来。Demo和源码可以在http://www.ati.com/developer/Treasurechest.html找到。
流程框架
是我们都很熟悉的d3d81Framework。
While(1)
{
Render3DEnvironment()
{
FrameMove()
{
RenderSceneIntoCubeMap()
{
For(6)
{
RenderScene(ReflectionCubeMap)
RenderScene(DiffuseCubeMap)
}
}
}
Render()
{
RenderScene(NORMAL)
}
}
}
场景组织
Object主要分为两大类,一类是静止的相当于环境,包括墙地板门窗和箱子;另一类是活动的装在箱子里的(财宝^_^)。其中箱子由好几部分组成,金属部分可以反光,木头部分不行。需要说一下ButtonMode(ObjectManagement.cpp_15)这个结构:
typedef struct _ButtonMode
{
int x;
int y;
DWORD object;
DWORD objectEffect;
char* objectTechnique;
DWORD WoodEffect;
char* WoodTechnique;
DWORD MetalEffect;
char* MetalTechnique;
DWORD KeepEffect;
char* KeepTechnique;
} ButtonMode;
前两个是button坐标,之后分别是活动物体,木箱上的木头,金属,以及周围环境所用的effect file和technique name。有些演示当中没有活动物体。这四项中每一项都可能有多个effect file,每个effect file中有多个technique。
关于Cubemap将环境render到cubemap时需要把camera放在需要做环境影射物体的重心上。代码如下,当场景中只有chest的时候,它的重心是0, 2.5f, 0;其他情况时需要找活动物体的重心,这个时候的cubemap是围绕着活动物体的,所以在chest上会看到错误的反射。
ShaderDemo.cpp_323
if (m_pDisplayObject[0])
cameraPos = m_pDisplayObject[0]->GetWorldCenterOfGravity();
else
cameraPos = D3DXVECTOR3(0,2.5f,0);
matViewDir._41 = -cameraPos.x; matViewDir._42 = -cameraPos.y; matViewDir._43 =-cameraPos.z;
Pofmesh.cpp_407
m_centroid = (m_maxPos+m_minPos)*.5;
Normalizer
Normalizer是一个cubemap,把需要normalize的向量用vs以TexCoord的形式传到ps(或者是普通的pixel管道),sample出来的颜色值就是Normalize的结果,而且是Clamp到0~1的,可以直接做dot3了。当然事先要做恐怖的precompute。
节省带宽 or 节省指令
TreasureChest里边使用了COMPACT_BUFFER顶点格式。为了节省带宽,把法线存成4个byte的压缩格式(注意倒装……),同时仅仅保存了切线空间3条basis的两条。然后在vs中解压缩,同时cross另一条basis。两者到底应该节省哪一个取决于不同的硬件。
PerVertex & PerPixel Cubemap我们很熟悉PerVertex的cubemap,也很熟悉Perpixel的光照。跟光照很类似,perpixel的cubemap也是用很少的顶点实现凹凸不平的效果,这次是反射。
(很光滑的金属)
(有花纹的金属)
其实已经有很多地方介绍过perpixel cubemap,不像perpixel lighting,这次好像只能在ps里做。
Ps1.1~1.3中用texm3x3vspec这个凶猛的指令来计算反射光线。说它凶猛是因为它实在做了太多的计算。在1.4中又回到了dp3,mul和add的套路。大体上,我们需要在vs中把世界空间的basis变换到切线坐标[1],所以他们组成了从切线空间变换到世界空间的矩阵。之后我们把世界空间的vertex to eye向量保存在3个basis的w分量中(texm3x3vspec必需),然后在ps中,把从normal map取出来的法线变换到世界坐标,再与视线向量一起计算反射向量。这样就会有从凹凸不平的表面上反射的效果了。
关于Perpixel cubemap,推荐参考NVEffectsBrowser中的Bump refraction。
更精确的cube寻址Cube是靠一个世界空间的方向向量来寻址的,但是我们计算出来的反射向量都是从vertex出发,指向我们所希望采样的地方。采样的时候它被平移到cube的中心,很可能会指向不同的采样点。
所以我们让反射向量加上一个从cube中心指向vertex方向的向量来计算精确的方向。这个方向向量要scale到某个特定的长度才能达到要求。
Perpixel的时候把顶点的世界坐标传到ps,插值以后就可以跟vs一样了。
ShaderX里边有详细介绍。这本书的电子版网上到处都是,真后悔买了纸版,555555555
Diffuse Cube MapCubeMap上保存的是多个光的叠加,它的好处是无论有多少个光源,只要一个pass,而且可以动态修改光源的个数。叠加的时候需要这样:
ZEnable = False;
AlphaBlendEnable = True;
SrcBlend = One;
DestBlend = One;
Bump Specularbase*(ambient+N.L)) + (Gloss*Highlight)
gloss保存在base texture的alpha中,与N.H的64次方相乘得到真正的specular。
待续…
[1] 我们经常使用的将顶点从object space变换到world space的矩阵,其实就是世界坐标系的3条basis在那个object space中的值,竖着排列为[x,y,z]
本文地址:http://com.8s8s.com/it/it24535.htm