今天说下颜色和光照...
一、颜色
1.颜色表示: RGB数据可用2种不同的结构表示:
(1) D3DCOLOR //eg:
D3DCOLOR brightRed = D3DCOLOR_ARGB(255, 255, 0, 0); //四个参数的顺序是(Alpha,r,g,b)
(2)D3DCOLORVALUE/D3DXCOLOR,后者更为方便(是对象,前者是结构),因为他提供了很多操作符,而且由于和前者的类型都一致,所以可以直接类型转换。
//eg:
D3DXCOLOR WHITE( D3DCOLOR_XRGB(255,255,255) ); //三个参数的顺序是(r,g,b)
D3DXCOLOR redAmbient(1.0f, 0.0f, 0.0f, 1.0f); //四个参数的顺序是(r,g,b,Alpha)
上面这两种结构仅仅指出了声明形式,对他们的具体设定还要用D3DCOLOR_ARGB和D3DCOLOR_XRGB,或者直接D3DXCOLOR(,,,)也行,还有0xffffffff这种形式也是描述颜色的
2.顶点颜色:
图元的颜色由构成该图元的顶点的颜色决定,所以还要为顶点数据结构添加一个表示颜色的数据成员 struct ColorVertex
{
ColorVertex(){}
ColorVertex(float x, float y, float z, D3DCOLOR c)
{
_x = x; _y = y; _z = z; _color = c;
}
float _x, _y, _z;
D3DCOLOR _color;
static const DWORD FVF;
};
const DWORD ColorVertex::FVF = D3DFVF_XYZ | D3DFVF_DIFFUSE;
3.着色:
在光栅化的过程中,需要对多边形进行着色,着色规定了利用顶点的颜色来计算构成图元的像素的颜色2种着色方式: 平面着色和Gouraud着色(也称为平滑着色),
平面着色: 每个图元的每个像素都被一致地赋予该图元的 第一个 顶点指定的颜色,也就是说其他顶点即使给了颜色,也要被忽略,这种着色容易出现“块状”
Gouraud着色: 图元中各像素的颜色值由 各顶点的颜色 经 线性插值 得到
在Direct3D中,着色模式由SetRenderState函数控制:
device->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_FLAT);//平面着色
device->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD);//Gouraud着色
4.着色实例:
在开始绘制之前先设置清楚着色模式 D3DXMatrixTranslation(&WorldMatrix, -1.25f, 0.0f, 0.0f);
Device->SetTransform(D3DTS_WORLD, &WorldMatrix);
Device->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_FLAT);
Device->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 1);
//其余部分略过...
//注:显示颜色其实很简单,就是调用一个着色的状态而已,但是还需要事先为每个顶点设置好颜色
请注意,下面两行代码并没有出现,
Device->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME); //设置线框模式的时候就不是对这个面着色了,是只对边着色。
Device->SetRenderState(D3DRS_LIGHTING, false); //由于我们自己设置了颜色,所以不需要光照给予颜色。
二、光照
使用光照时,无需自行指定顶点的颜色值,因为光本来就是有颜色的
1.光照的组成:
环境光(ambient):这种光经其他表面反射到物体表面,并照亮整个场景漫射光(diffuse):无论从哪个方位观察,表面的亮度均相同
镜面光(specular):模拟物体上的高光点
镜面光的计算量更大,默认状态下是关闭的,若想启用,可借助绘制状态:
SetRenderState(D3DRS_SPECULARENABLE, true);
描述光的颜色:用D3DCOLORVAlUE或D3DXCOLOR,
//eg:
D3DXCOLOR redAmbient(1.0f, 0.0f, 0.0f, 1.0f);//描述光线的颜色时,Alpha中的值都将被忽略
D3DXCOLOR blueDiffuse(0.0f, 0.0f, 1.0f, 1.0f);
2.材质:定义物体表面对各种颜色光的反射比例
Direct3D提供了D3DMATERIAL9 来创建材质。(可以对他的具体属性填充值) //eg:
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;//提高镜面高光点的锐度
创建材质完了以后就要使用他了,顶点的结构中是不含材质属性的,但我们又必须对材质进行设定,所以有这样的解决办法:
IDirect3DDevice9::SetMaterial(const D3DMATERIAL9 * pMaterial);
//eg:
D3DMATERIAL9 blueM;
Device->SetMaterial(&blueM);
Device->DrawPrimitive(...);
设置顶点法线的目的:确定光线到达平面的入射角。
为了求出顶点v的法向量vn,需要求出共享该点的所有三角形面的法向量,vn可由这些法向量取均值得到。
还要注意,变换后的向量通常不是标准化的,所以还需要:
SetRenderState(D3DRS_NORMALIZENORMALS,true);
4.光源
Direct3D支持3种光:点光源(point)
方向光(Directional)
聚光灯(Spot)
Direct中提供了创建光源的类型:D3DLIGHT9
与D3DMATERIAL9类似,创建好后,还需要对具体的属性值填充
//eg:
D3DLIGHT9 light;
light.Type = D3DLIGHT_DIRECTIONAL;
light.Ambient = WHITE * 0.4f;//WHITE为已经建立好的D3DXCOLOR全局变量
light.Diffuse = HWITE;
light.Specular = WHITE * 0.6f;
light.Direction = dir;//D3DXVECTOR3 dir(1.0f, 0.0f, 0.0f);
当D3DLIGHT9实例初始化完毕之后,要对所使用的光源注册:Device->SetLight(0, &light);
注册成功后,便可以对开关进行控制:Device->LightEnable(0, true);
Display里面重要的部分:
for(int i = 0; i < 4; i++)//书上的例子里面绘制了4个模型
{
Device->SetMaterial(&Mtrls[i]);
Device->SetTransform(D3DTS_WORLD, &Worlds[i]);
Objects[i]->DrawSubset(0);
}
//材质和世界位置对于不同的模型是不一样的(纹理也是,后面会说到),所以要分开设定,而灯光是“全局的”,所以在Setup里面先设置好了
Setup的主要改变如下:
Device->SetRenderState(D3DRS_LIGHTING, true);//开启灯光
//创建顶点缓存并设置,省略...
//创建D3DMATERIAL9对象
D3DMATERIAL9 mtrl;
//设置D3DMATERIAL9对象
mtrl.Ambient = d3d::WHITE;
mtrl.Diffuse = d3d::WHITE;
mtrl.Specular = d3d::WHITE;
mtrl.Emissive = d3d::WHITE;
mtrl.Power = 5.0f;
//启用刚才设置好的材质
Device->SetMaterial(&mtrl);
//创建D3DLIGHT9对象
D3DLIGHT9 dir;
//设置D3DLIGHT9对象
::ZeroMemory(&dir, sizeof(dir));
dir.Type = D3DLIGHT_DIRECTIONAL;
dir.Diffuse = d3d::WHITE;
dir.Specular = d3d::WHITE;
dor.Ambient = d3d::WHITE;
dir.Direction = D3DXVECTOR3(1.0f, 0.0f, 0.0f);
//注册Light对象并打开灯的开关
Device->SetLight(0,&dir);
Device->LightEnable(0, true);
Device->SetRenderState(D3DRS_NORMALIZENORMALS, true);//规范化所有顶点法向量
Device->SetRenderState(D3DRS_SPECULARENABLE, true);//启用镜面光
//设置观察矩阵和投影矩阵,省略...
:注1:如果D3DRS_LIGHTING为true,却没有设定某种有颜色的灯光,就会出现全部黑色的情况哦...
注2:这个例子只使用了D3DLIGHT_DIRECTIONAL类型的光,也就是方向光,书中还有point光和spot光的例子...