6 光照

上帝说要有光,于是就有了光

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;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值