ATI的财宝箱

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主要分为两大类,一类是静止的相当于环境,包括墙地板门窗和箱子;另一类是活动的装在箱子里的(财宝^_^)。其中箱子由好几部分组成,金属部分可以反光,木头部分不行。需要说一下ButtonModeObjectManagement.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 filetechnique name。有些演示当中没有活动物体。这四项中每一项都可能有多个effect file,每个effect file中有多个technique

关于Cubemap

       将环境rendercubemap时需要把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的向量用vsTexCoord的形式传到ps(或者是普通的pixel管道),sample出来的颜色值就是Normalize的结果,而且是Clamp0~1的,可以直接做dot3了。当然事先要做恐怖的precompute

 

 

 

节省带宽 or 节省指令

       TreasureChest里边使用了COMPACT_BUFFER顶点格式。为了节省带宽,把法线存成4byte的压缩格式(注意倒装……),同时仅仅保存了切线空间3basis的两条。然后在vs中解压缩,同时cross另一条basis。两者到底应该节省哪一个取决于不同的硬件。

PerVertex & PerPixel Cubemap

       我们很熟悉PerVertexcubemap,也很熟悉Perpixel的光照。跟光照很类似,perpixelcubemap也是用很少的顶点实现凹凸不平的效果,这次是反射。


(很光滑的金属)


(有花纹的金属)

 

其实已经有很多地方介绍过perpixel cubemap,不像perpixel lighting,这次好像只能在ps里做。

       Ps1.1~1.3中用texm3x3vspec这个凶猛的指令来计算反射光线。说它凶猛是因为它实在做了太多的计算。在1.4中又回到了dp3muladd的套路。大体上,我们需要在vs中把世界空间的basis变换到切线坐标[1],所以他们组成了从切线空间变换到世界空间的矩阵。之后我们把世界空间的vertex to eye向量保存在3basisw分量中(texm3x3vspec必需),然后在ps中,把从normal map取出来的法线变换到世界坐标,再与视线向量一起计算反射向量。这样就会有从凹凸不平的表面上反射的效果了。

      关于Perpixel cubemap,推荐参考NVEffectsBrowser中的Bump refraction

更精确的cube寻址

       Cube是靠一个世界空间的方向向量来寻址的,但是我们计算出来的反射向量都是从vertex出发,指向我们所希望采样的地方。采样的时候它被平移到cube的中心,很可能会指向不同的采样点。

       所以我们让反射向量加上一个从cube中心指向vertex方向的向量来计算精确的方向。这个方向向量要scale到某个特定的长度才能达到要求。

       Perpixel的时候把顶点的世界坐标传到ps,插值以后就可以跟vs一样了。

       ShaderX里边有详细介绍。这本书的电子版网上到处都是,真后悔买了纸版,555555555

Diffuse Cube Map

       CubeMap上保存的是多个光的叠加,它的好处是无论有多少个光源,只要一个pass,而且可以动态修改光源的个数。叠加的时候需要这样:

ZEnable = False;

AlphaBlendEnable = True;

SrcBlend = One;

DestBlend = One;

Bump Specular

       base*(ambient+N.L)) + (Gloss*Highlight)

       gloss保存在base texturealpha中,与N.H64次方相乘得到真正的specular

 

待续…



[1] 我们经常使用的将顶点从object space变换到world space的矩阵,其实就是世界坐标系的3basis在那个object space中的值,竖着排列为[x,y,z]