在Direct X初始化后,我们当然要在上面画东西。这个过程事实上是极其复杂的,在直接进入API的解释和使用前,先简单说下3D世界转换到2D画面(我们在屏幕上看到的)的一些基本理论。按惯例说明下,本文使用了Introduction to 3D Game Programming with DirectX 10中的理论,代码和图片,在很多部分,笔者只是做了翻译整理工作。
1.3D视觉效果
首先,从感性来说,一个平面图形怎么让人眼产生立体感,最简单的其实就是"近大远小"。
如上图,两条平行的线,由进及远距离原来越小,知道称为同一点(vanishing point),就形成了立体的效果。
上图描绘了另外一种情况(其实就是之前depth buffer的例子),四种图形相互掩盖,也能让人形成前后的视觉。
那单个物体,怎么现实立体感呢,看下图:
左边是个圆,正常人都会这么认为。。。不会把它想成球,右边则相反,怎么看都是个球体。两边差了啥呢?光照(lighting)和着色(shading,注意这个词不是阴影的意思)。
再看上图,一个飞机,嗯,为什么我们觉得它在空中,因为地上有影子(shadow),从影子我们可以知道两件事情,1.飞机的高度;2. 光照的方向。
OK,到这里,还没讨论啥高深的东西,你可能觉得这些东西你小学就知道,但后面我们要把这些简单的东西通过计算机图形学表现出来。
2. 建模
一个3D模型,或者说一个3D对象,在计算机图形学中,事实上(通常情况下)是一个三角形网格组成的近似模拟。
如上图,可以看出,越复杂的模型需要越多的三角形来组成(好吧,我承认是废话。。。)。当然了,用代码写出这个么模型,基本是丝路一条,一般我们都用建模工具来完成这些工作,如3ds Max (http://www.autodesk.com/3dsmax), LightWave 3D (http://www.newtek.com/lightwave/), Maya (http://www.autodesk.com/maya), and Softimage | XSI (www.softimage.com)。
3. 计算机颜色
计算机一般通过RGB三原色以及每种颜色的灰度来存储一种具体的颜色,随便打开个windows下的选色器就看出。
一般来说,我们用一个有三个数的结构来表现一个颜色,如(0.25, 0.67, 1.0),表示25%的红,67%的绿以及100%的蓝。由于这个结构很像向量,我们在D3D中还是用向量也表示它,称为颜色向量。
显然,向量的加减对颜色向量来说是有意义的:
加法相当于两种颜色重叠产生新颜色。
减法类似加法,代表我从一种颜色转换为另一种颜色(通过减掉某些颜色的比重)。
常数乘以一个颜色也同样有意义:
相当于整个颜色的浓度产生一定程度的变化。
接下来是点乘和×乘,后者是没有实际意义的,前者的意义是:
RGB三种颜色分别做了一定程度的变化,事实上,这是很有用的,主要用于模拟光照现象。比如我们有一束光,从远处照过一个平面,这个平面能吸收50%的红光,75%的绿光以及25%的蓝光,那通过一下算式,能得到光通过那个平面的颜色:
下面说下在D3D中具体的颜色的使用,D3D中,颜色被封装在结构D3DXCOLOR中(博客园的代码插入功能呢。。。):
typedef struct D3DXCOLOR
{
#ifdef __cplusplus
public:
D3DXCOLOR() {};
D3DXCOLOR(UINT argb);
D3DXCOLOR(CONST FLOAT *);
D3DXCOLOR(CONST D3DXFLOAT16 *);
D3DXCOLOR(FLOAT r, FLOAT g, FLOAT b, FLOAT a);
// casting
operator UINT () const;
operator FLOAT* ();
operator CONST FLOAT* () const;
// assignment operators
D3DXCOLOR& operator += (CONST D3DXCOLOR&);
D3DXCOLOR& operator -= (CONST D3DXCOLOR&);
D3DXCOLOR& operator *= (FLOAT);
D3DXCOLOR& operator /= (FLOAT);
// unary operators
D3DXCOLOR operator + () const;
D3DXCOLOR operator - () const;
// binary operators
D3DXCOLOR operator + (CONST D3DXCOLOR&) const;
D3DXCOLOR operator - (CONST D3DXCOLOR&) const;
D3DXCOLOR operator * (FLOAT) const;
D3DXCOLOR operator / (FLOAT) const;
friend D3DXCOLOR operator * (FLOAT, CONST D3DXCOLOR&);
BOOL operator == (CONST D3DXCOLOR&) const;
BOOL operator != (CONST D3DXCOLOR&) const;
#endif //__cplusplus
FLOAT r, g, b, a;
} D3DXCOLOR, *LPD3DXCOLOR;
可以看到上面的结构没有提供点乘的方法,因此,补充下面这个函数来完成此类操作。
D3DXCOLOR* D3DXColorModulate(
D3DXCOLOR* pOut, // Returns (cr, cg, cb, ca) (kr, kg, kb, ka)
CONST D3DXCOLOR* pC1, // (cr, cg, cb, ca)
CONST D3DXCOLOR* pC2 // (kr, kg, kb, ka)
);
从构造函数中,可以发现,有4个float,怎么多了一个呢?不是三原色么?多的那个就是传说中的alpha通道,用于阿尔法混合(blending),这里暂时不展开。
如果我们通过 D3DXCOLOR(FLOAT r, FLOAT g, FLOAT b, FLOAT a)这个构造函数构造的颜色向量是128bit的一个数据结构,事实上,咋某些情况下,我们认为太大了,有些浪费,但由于颜色总是需要经过复杂的运算,float的高精度可以保证错误不会一直积累导致最后偏差过大,所以,在计算过程中,我们使用浮点数表示颜色(每个颜色元素是0到1)。
当我们使用 D3DXCOLOR(UINT argb)来构造颜色向量的时候(传入一个32位的无符号整形),我们得到一个32位的颜色,它是这样对应4个子元素的:
由于32位色基本能满足视觉的要求(windows的桌面设置最高就是32位色),因此,将计算完的结构用32位色表示是最合适的,这里涉及到一个转化的问题,32位色和128位色怎么转化呢,看下面两个例子:
如上,32位色转化成了128位色。
最后,用一张图表示图像渲染的过程,具体的解释请期待后文^-^