光照的组成
环境光:这种类型的光经其他表面反射到达物体表面,并照亮整个场景,要想以较低代价粗略模拟这类反射光,环境光是一个很好的选择
漫射光:这种类型光沿着特定的方向传播。当它到达某一表面时,将沿着各个方向均匀反射,无论从哪个方位观察,表面亮度均相同,所以采用该模型时无须考虑观察者的位置,这样,漫射光方程中仅需考虑光传播的方向以及表面朝向,从一个光源发出的光一般都是这种类型的。
镜面光:这种类型的光沿特定方向传播,当此类光到达一个表面时,将严格地沿着另一个方向反射,从而形成只能在一定角度范围内才能观察到的高亮度照射,所以在镜面光照方程中不仅需要考虑光线的入射方向和图元的表面朝向,还需要考虑观察点的位置。镜面光可用于模拟物体上的高光点,例如当光线照射到一个抛光的表面所形成的高亮照射。
镜面光比其他类型光计算量大,因此Direct3D提供了开关选项,默认状态下Direct不进行镜面反射计算,如果想启用镜面光,必须调用接口进行设置
Device->SetRenderState(D3DRS_SPECULARENABLE, true);
每种类型的光都可用结构D3DCOLORVALUE或者D3DXCOLOR来表示,描述光线的颜色时,D3DXCOLOR类中的Alpha值都将被忽略
D3DXCOLOR redAmbient(1.0f, 0.0f, 0.0f, 1.0f);
D3DXCOLOR blueDiffuse(0.0f, 0.0f, 1.0f, 1.0f);
D3DXCOLOR whiteSpecular(1.0f, 1.0f, 1.0f, 1.0f);
材质
现实世界中,我们所观察到的物体的颜色是由该物体所反射的光的颜色决定的,例如一个纯红色的球体反射了全部的红色入射光,并吸收了所有非红色的光,所以呈现为红色。当一个物体吸收了所有的光时,便呈现为黑色,如果一个物体能够100%地反射红色光、绿色光、蓝色光,它将呈现为白色,Direct3D通过定义物体的材质来模拟同样的现象,材质允许我们定义物体表面对各种颜色光的反射比例,材质用结构D3DMATERIAL9来表示。
typedef struct D3DMATERIAL9 {D3DCOLORVALUE Diffuse; D3DCOLORVALUE Ambient; D3DCOLORVALUE Specular; D3DCOLORVALUE Emissive; float Power;
} D3DMATERIAL9;
Diffuse:指定材质对漫射光的反着率
Ambient:指定材质对环境光的反射率
Specular:指定材质对镜面光的反射率
Emissive:该分量用于增强物体的亮度,使之看起来好像可以自己发光
Power:指定镜面高光点的锐度,该值越大,高光点的锐度越大
//只反射红色光
D3DMATERIAL9 red;
::ZeroMemory(&red, sizeof(red));
red.Diffuse = D3DXCOLOR(1.0f, 0.0f, 0.0f, 1.0f);
red.Ambient= D3DXCOLOR(1.0f, 0.0f, 0.0f, 1.0f);
red.Specular = D3DXCOLOR(1.0f, 0.0f, 0.0f, 1.0f);
red.Emissive = D3DXCOLOR(0.0f, 0.0f, 0.0f, 1.0f);
red.Power = 5.0f;
设置材质接口SetMaterial
D3DMATERIAL9 blueMaterial;
Device->SetMaterial(&blueMaterial);
//draw...
顶点法线
顶点法线描述的是构成多边形的各个顶点的法线,Direct3D需要知道顶点的法线方向,以确定光线到达表面时的入射角,由于光照计算是对每个顶点进行的,所以Direct3D需要知道表面在每个顶点处的局部朝向(法线方向),描述一个顶点法线需要修改顶点结构
struct Vertex
{float _x, _y, _z;float _nx, _ny, _nz;static const DWORD FVF;
};
const DWORD Vertex::FVF = D3DFVF_XYZ | D3DFVF_NORMAL;
计算三角形三个顶点的法线,可由计算该面的法向量得到,首先计算位于三角形平面内的俩个向量
void ComputeNormal(D3DXVECTOR3* p0, D3DXVECTOR3* p1, D3DXVECTOR3* p2, D3DXVECTOR3* out)
{D3DXVECTOR3 u = *p1 - *p0;D3DXVECTOR3 v = *p2 - *p0;D3DXVec3Cross(out, &u, &v);D3DXVec3Normalize(out, out);
}
当用三角形单元逼近表示曲面时,将面片法向量作为构成该面片的顶点法向量不可能产生很平滑的效果,一种更好的求取顶点法向量的方法是计算法向量均值,我们需要求出共享点v的所有三角形的面法向量,然后将这些法向量相加后除以个数进行平均。
在变换过程中,顶点法线有可能不再是规范的所以最好的方法是在变换完成后,通过设置绘制状态来重新规范化
Device->SetRenderState(D3DRS_NORMALIZENORMALS, true);
光源
Direct3D支持3种类型的光源,分别是点光源、方向光、聚光灯
点光源:该光源在世界坐标系中有固定的位置,并向所有的方向发射光线(D3DLIGHT_POINT)
方向光:该光源没有位置信息,所发射的光线相互平行地沿某一特定方向传播(D3DLIGHT_DIRECTIONAL)
聚光灯:这种类型的光源与手电筒类似,该光源有位置信息,其发射的光线呈锥形沿着特定方向传播(D3DLIGHT_SPOT)
typedef struct D3DLIGHT9 {D3DLIGHTTYPE Type; D3DCOLORVALUE Diffuse; D3DCOLORVALUE Specular; D3DCOLORVALUE Ambient; D3DVECTOR Position; D3DVECTOR Direction; float Range; float Falloff; float Attenuation0;float Attenuation1;float Attenuation2;float Theta; float Phi;
} D3DLIGHT9;
Type:定义创建的光源类型,可取以下3种枚举值,D3DLIGHT_POINT、D3DLIGHT_SPOT、D3DLIGHT_DIRECTIONAL
Diffuse:该光源所发出的漫射光的颜色
Specular:该光源所发出的镜面光的颜色
Ambient:该光源所发出的环境光的颜色
Position:用于描述光源在世界坐标系中位置的向量,对于方向光该参数无意义
Direction:一个描述光在世界坐标系中传播方向的向量,对于点光源该参数无意思
Range:光线"消亡"前,所能到达的最大光程,该值的最大取值为,对于方向光该参数无意义
Falloff:该值用于聚光灯,该参数定义了光强从内锥形到外锥形的衰减方式,该参数一版取为1.0f
Attenuation0、Attenuation1、Attenuation2这些衰减变量定义了光强随距离衰减的方式,这些变量仅用于点光源和聚光灯,分别表示光的常量、线性、2次距离衰减系数,衰减公式为,其中D为光源到顶点的距离。
Theta:仅用于聚光灯,指定了内部锥形的圆锥角,单位为弧度
Phi:仅用于聚光灯,指定了外部锥形的圆锥角,单位为弧度
与D3DMATERIAL9结构初始化类似,当需要一个简单的光源时,D3DLIGHT9结构的初始化也很繁琐
D3DLIGHT9 d3d::InitDirectionalLight(D3DXVECTOR3* direction, D3DXCOLOR* color)
{D3DLIGHT9 light;::ZeroMemory(&light, sizeof(light));light.Type = D3DLIGHT_DIRECTIONAL;light.Ambient = *color * 0.4f;light.Diffuse = *color;light.Specular = *color * 0.6f;light.Direction = *direction;return light;
}D3DXVECTOR3 dir(1.0f, 0.0f, 0.0f);
D3DXCOLOR c = d3d::WHITE;
D3DLIGHT9 dirLight = d3d::InitDirectionalLight(&dir, &c);
D3DLIGHT9实例初始化完毕之后,我们需要在Direct3D所维护的一个光源内部列表中对所要使用的光源进行注册,注册成功后就可以对其开关状态进行控制
Device->SetLight(0, &light);
Device->LightEnable(0, true);
光照例程
bool SetUpPyramid()
{//启用光照(默认是启用的)Device->SetRenderState(D3DRS_LIGHTING, true);Device->CreateVertexBuffer(12 * sizeof(PyramidVertex), D3DUSAGE_WRITEONLY, PyramidVertex::FVF, D3DPOOL_MANAGED, &Pyramid, 0);PyramidVertex* v;Pyramid->Lock(0, 0, (void**)&v, 0);v[0] = PyramidVertex(-1.0f, 0.0f, -1.0f, 0.0f, 0.707f, -0.707f);v[1] = PyramidVertex(0.0f, 1.0f, 0.0f, 0.0f, 0.707f, -0.707f);v[2] = PyramidVertex(1.0f, 0.0f, -1.0f, 0.0f, 0.707f, -0.707f);v[3] = PyramidVertex(-1.0f, 0.0f, 1.0f, -0.707f, 0.707f, 0.0f);v[4] = PyramidVertex(0.0f, 1.0f, 0.0f, -0.707f, 0.707f, 0.0f);v[5] = PyramidVertex(-1.0f, 0.0f, -1.0f, -0.707f, 0.707f, 0.0f);v[6] = PyramidVertex(1.0f, 0.0f, -1.0f, 0.707f, 0.707f, 0.0f);v[7] = PyramidVertex(0.0f, 1.0f, 0.0f, 0.707f, 0.707f, 0.0f);v[8] = PyramidVertex(1.0f, 0.0f, 1.0f, 0.707f, 0.707f, 0.0f);v[9] = PyramidVertex(1.0f, 0.0f, 1.0f, 0.0f, 0.707f, 0.707f);v[10] = PyramidVertex(0.0f, 1.0f, 0.0f, 0.0f, 0.707f, 0.707f);v[11] = PyramidVertex(-1.0f, 0.0f, 1.0f, 0.0f, 0.707f, 0.707f);Pyramid->Unlock();D3DMATERIAL9 mtrl;mtrl.Ambient = d3d::WHITE;mtrl.Diffuse = d3d::WHITE;mtrl.Specular= d3d::WHITE;mtrl.Emissive = d3d::BLACK;mtrl.Power = 5.0f;Device->SetMaterial(&mtrl);D3DLIGHT9 dir;::ZeroMemory(&dir, sizeof(dir));dir.Type = D3DLIGHT_DIRECTIONAL;dir.Diffuse = d3d::WHITE;dir.Specular = d3d::WHITE * 0.3f;dir.Ambient = d3d::WHITE * 0.6f;dir.Direction = D3DXVECTOR3(1.0f, 0.0f, 0.0f);Device->SetLight(0, &dir);Device->LightEnable(0, true);//规范化法向量Device->SetRenderState(D3DRS_NORMALIZENORMALS, true);//启用镜面高光Device->SetRenderState(D3DRS_SPECULARENABLE, true);//取景变换(观察者坐标系)D3DXVECTOR3 position(3.0f, 2.0f, -3.0f);D3DXVECTOR3 target(0.0f, 0.0f, 0.0f);D3DXVECTOR3 up(0.0f, 1.0f, 0.0f);D3DXMATRIX V;D3DXMatrixLookAtLH(&V, &position, &target, &up);Device->SetTransform(D3DTS_VIEW, &V);//投影变换D3DXMATRIX proj;D3DXMatrixPerspectiveFovLH(&proj, D3DX_PI*0.5f, (float)Width / (float)Height, 1.0f, 1000.0f);Device->SetTransform(D3DTS_PROJECTION, &proj);return true;
}