上帝说要有光,于是就有了光
3D世界中的光是按照物理世界的光模拟的,从而可以做出非常逼真的效果
使用光照时,顶点的颜色是由光照计算引擎依据 光源类型,材质,物体对于光源的朝向(决定反射类型)计算每个顶点的颜色值
D3D光照模型里,光源发出的光由3种光组成
环境光
漫反射
镜面反射
环境光是物体不是被光源直接照亮,而是由其他物体反射光而被照亮
漫反射用来模拟从每个角度看表面亮度都相同的情况,使用漫反射时,无需考虑观察者的位置,只需考虑光的传播方向和表面的朝向
镜面反射 这种光沿着特定方向传播,当光达到一个表面时,将严格的按照一个特定的角度传播,并且观察者需要在一个特定的角度范围才可以观察到反射光
镜面反射计算量比其他两种大的多
D3D中默认是不使用的,可以用SetRenderState开启
光可以用颜色结构体表示(光其实就是颜色值)
D3DCOLORVALUE
D3DXCOLOR
描述光线时,透明度值被忽略
材质
有光只是提供了看到物体的途径,物体是什么颜色是由它反射什么颜色的光所决定的
红色的物体反射红色的光而吸收所有其他颜色的光,因此它是红色
材质就是用来模拟这种现象的
材质用结构 D3DMATERIAL9 表示
typedef struct D3DMATERIAL9 {
D3DCOLORVALUE Diffuse;
D3DCOLORVALUE Ambient;
D3DCOLORVALUE Specular;
D3DCOLORVALUE Emissive;
float Power;
} D3DMATERIAL9, *LPD3DMATERIAL9;
前三个不用怎么说,分别是漫反射光反射率,环境光反射率,镜面反射反射率
Emissive 这个分量用于增强光的亮度,可以让物体看起来是它自己在发光
Power 光的锐度
锐度,有时也叫“清晰度”,它是反映图像平面清晰度和图像边缘锐利程度的一个指标。如果将锐度调高,图像平面上的细节对比度也更高,看起来更清楚。(百度百科)
在摄影里这个用的更多
前4个量都是颜色值,最后一个是浮点数
使用这个结构,可以控制在不同反射模型中各种光的反射率
再使用反射时,需要注意当无法反射时会呈现黑色。
设置好材质后,使用设备中的SetMaterial来设置当前材质
面法线描述多边形的朝向,顶点发现基于这个理论,描述各个顶点的法线,用这个来确定光线达到表面时的入射角
光照计算是对每个顶点而言的,因此需要每个顶点的法线向量
为了描述顶点法线,需要修改顶点结构,和灵活顶点格式,加入对顶点法线的描述
struct Vertex
{
Vertex(){}
Vertex(float x, float y, float z, float nx, float ny, float nz)
{
_x = x; _y = y; _z = z;
_nx = nx; _ny = ny; _nz = nz;
}
float _x, _y, _z;
float _nx, _ny, _nz;
static const DWORD FVF;
};
const DWORD Vertex::FVF = D3DFVF_XYZ | D3DFVF_NORMAL;
位置信息和顶点法线
这里不需要颜色信息了,因为顶点颜色是由光照来计算的
在一般的多边形中,顶点法向量就是面法向量,面法向量由多边形任意两个不平行的边X乘就得到了
但是在用多边形逼近曲面的时候,这样得到的顶点法向量就不够平滑了,这种情况使用均值,取所有共享顶点的面的法向量的均值
光源
设置好一切之后,需要光源的作用产生反射光,再有摄像机捕捉到从而————有了光。。。。。。。
D3D支持三种光源
点(point)从一点向所有方向发射光线
方向光(Directional)没有位置信息,所有光线沿同方向平行发射
聚光灯(Spot)类似CS里的手电筒就是用这个模拟的,有位置,有方向
光源结构
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, *LPD3DLIGHT;
光源结构,参数分别为
类型,上面说的三种,用定义好的枚举值
漫反射光的颜色
镜面反射光的颜色
发出的环境光颜色
光源在世界坐标中的位置(对于方向光无意义)
传播方向(对于点光源无意义)
最大光程,光的范围,在消亡前能达到的最大距离(对方向光无意义)
衰减,仅用于聚光灯,定义光强从内锥形到外锥形的衰减方式(一般为1.0f)
下来的三个 定义了光强随距离衰减的方式,只用于点光源和聚光灯
attenuation = 1/(a0 + a1*D + a2^2 * D) D为距离
得出的值是衰减系数
最后两个仅用于聚光灯,分别是弧度表示的 内部锥形圆锥角和外部锥形圆锥角(顶角)
将结构初始化后再进行注册(SetLight)和打开(LightEnable)就可以使用了
最后附上一段例程
例程中光照的初始化操作再另外的文件中,和D3D初始化在一起
//
//
// File: litPyramid.cpp
//
// Author: Frank Luna (C) All Rights Reserved
//
// System: AMD Athlon 1800+ XP, 512 DDR, Geforce 3, Windows XP, MSVC++ 7.0
//
// Desc: Renders a lit pyramid. Demonstrates how to specify the vertex
// normals, how to create and set a material, and how to create
// and set a directional light.
//
//
#include "d3dUtility.h"
//
// Globals
//
IDirect3DDevice9* Device = 0;
const int Width = 640;
const int Height = 480;
IDirect3DVertexBuffer9* Pyramid = 0;
//
// Classes and Structures
//
struct Vertex
{
Vertex(){}
Vertex(float x, float y, float z, float nx, float ny, float nz)
{
_x = x; _y = y; _z = z;
_nx = nx; _ny = ny; _nz = nz;
}
float _x, _y, _z;
float _nx, _ny, _nz;
static const DWORD FVF;
};
const DWORD Vertex::FVF = D3DFVF_XYZ | D3DFVF_NORMAL;
//
// Framework Functions
//
bool Setup()
{
//
// Turn on lighting.
//
Device->SetRenderState(D3DRS_LIGHTING, true);
//
// Create the vertex buffer for the pyramid.
//
Device->CreateVertexBuffer(
12 * sizeof(Vertex),
D3DUSAGE_WRITEONLY,
Vertex::FVF,
D3DPOOL_MANAGED,
&Pyramid,
0);
//
// Fill the vertex buffer with pyramid data.
//
Vertex* v;
Pyramid->Lock(0, 0, (void**)&v, 0);
// front face
v[0] = Vertex(-1.0f, 0.0f, -1.0f, 0.0f, 0.707f, -0.707f);
v[1] = Vertex( 0.0f, 1.0f, 0.0f, 0.0f, 0.707f, -0.707f);
v[2] = Vertex( 1.0f, 0.0f, -1.0f, 0.0f, 0.707f, -0.707f);
// left face
v[3] = Vertex(-1.0f, 0.0f, 1.0f, -0.707f, 0.707f, 0.0f);
v[4] = Vertex( 0.0f, 1.0f, 0.0f, -0.707f, 0.707f, 0.0f);
v[5] = Vertex(-1.0f, 0.0f, -1.0f, -0.707f, 0.707f, 0.0f);
// right face
v[6] = Vertex( 1.0f, 0.0f, -1.0f, 0.707f, 0.707f, 0.0f);
v[7] = Vertex( 0.0f, 1.0f, 0.0f, 0.707f, 0.707f, 0.0f);
v[8] = Vertex( 1.0f, 0.0f, 1.0f, 0.707f, 0.707f, 0.0f);
// back face
v[9] = Vertex( 1.0f, 0.0f, 1.0f, 0.0f, 0.707f, 0.707f);
v[10] = Vertex( 0.0f, 1.0f, 0.0f, 0.0f, 0.707f, 0.707f);
v[11] = Vertex(-1.0f, 0.0f, 1.0f, 0.0f, 0.707f, 0.707f);
Pyramid->Unlock();
//
// Create and set the material.
//
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);
//
// Setup a directional light.
//
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);
//
// Set and Enable the light.
//
Device->SetLight(0, &dir);
Device->LightEnable(0, true);
//
// Turn on specular lighting and instruct Direct3D
// to renormalize normals.
//
Device->SetRenderState(D3DRS_NORMALIZENORMALS, true);
Device->SetRenderState(D3DRS_SPECULARENABLE, true);
//
// Position and aim the camera.
//
D3DXVECTOR3 pos(0.0f, 1.0f, -3.0f);
D3DXVECTOR3 target(0.0f, 0.0f, 0.0f);
D3DXVECTOR3 up(0.0f, 1.0f, 0.0f);
D3DXMATRIX V;
D3DXMatrixLookAtLH(&V, &pos, &target, &up);
Device->SetTransform(D3DTS_VIEW, &V);
//
// Set the projection matrix.
//
D3DXMATRIX proj;
D3DXMatrixPerspectiveFovLH(
&proj,
D3DX_PI * 0.5f, // 90 - degree
(float)Width / (float)Height,
1.0f,
1000.0f);
Device->SetTransform(D3DTS_PROJECTION, &proj);
return true;
}
void Cleanup()
{
d3d::Release<IDirect3DVertexBuffer9*>(Pyramid);
}
bool Display(float timeDelta)
{
if( Device )
{
//
// Update the scene: Rotate the pyramid.
//
D3DXMATRIX yRot;
static float y = 0.0f;
D3DXMatrixRotationY(&yRot, y);
y += timeDelta;
if( y >= 6.28f )
y = 0.0f;
Device->SetTransform(D3DTS_WORLD, &yRot);
//
// Draw the scene:
//
Device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0x00000000, 1.0f, 0);
Device->BeginScene();
Device->SetStreamSource(0, Pyramid, 0, sizeof(Vertex));
Device->SetFVF(Vertex::FVF);
Device->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 4);
Device->EndScene();
Device->Present(0, 0, 0, 0);
}
return true;
}
//
// WndProc
//
LRESULT CALLBACK d3d::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch( msg )
{
case WM_DESTROY:
::PostQuitMessage(0);
break;
case WM_KEYDOWN:
if( wParam == VK_ESCAPE )
::DestroyWindow(hwnd);
break;
}
return ::DefWindowProc(hwnd, msg, wParam, lParam);
}
//
// WinMain
//
int WINAPI WinMain(HINSTANCE hinstance,
HINSTANCE prevInstance,
PSTR cmdLine,
int showCmd)
{
if(!d3d::InitD3D(hinstance,
Width, Height, true, D3DDEVTYPE_HAL, &Device))
{
::MessageBox(0, "InitD3D() - FAILED", 0, 0);
return 0;
}
if(!Setup())
{
::MessageBox(0, "Setup() - FAILED", 0, 0);
return 0;
}
d3d::EnterMsgLoop( Display );
Cleanup();
Device->Release();
return 0;
}