文档章节

D3D中的光照(1)

rise-worlds
 rise-worlds
发布于 2016/06/20 13:39
字数 2207
阅读 1
收藏 0

为了提高场景的真实性,我们可以为其加入灯光。灯光也能帮助表现物体的立体感以及物体的实体形状。当使用灯光时,我们不再自己指定顶点的颜色;Direct3D中每个顶点都通过灯光引擎来计算顶点颜色,该计算是基于定义的灯光资源,材质以及灯光资源关心的表面方向。通过灯光模型计算顶点颜色会得到更真实的场景。

5.1灯光的组成

       在Direct3D灯光模型中,灯光是通过灯光资源的三个成员之一来照射的,即有三种灯光。

环境光(Ambient Light)——这种类型的灯光将被其他所有表面反射且被用在照亮整个场景。例如,物体的各部分都被照亮,对于一个角度,甚至穿过不在光源直接照射的地方他们都能被照亮。环境光的使用是粗略的,便宜的,它模仿反射光。

漫反射(Diffuse Reflection)——这种灯光按照特殊方向传播。当它照射到一个表面,它将在所有方向上均匀的反射。因为漫射光在所有方向上都均匀的反射,被反射的光线将到达眼睛而与观察点无关,因此我们不必为观察者考虑。因而,漫射光仅仅需要考虑灯光方向和表面的朝向。这种灯光将成为你的资源中照射的普通灯光。

镜面反射(Specular Reflection)——这种灯光按照特殊方向传播。当它照射到一个表面时,它严格地按照一个方向反射。这将产生一个明亮的光泽,它能在某角度被看见。因为这种灯光在一个方向反射。明显的观察点,必须考虑灯光的方向和表面朝向,且必须按照镜面灯光等式来考虑。镜面灯光被用在物体上产生高光的地方,这种光泽只有在灯光照射在磨光的表面上才会产生。

镜面光比其他灯光类型要求更多的计算;因此,Direct3D提供了一个开关选择。实际上,它默认是被关闭的;要使用镜面光你必须设置D3DRS_SPECULARENABLE渲染状态。

Device->SetRenderState(D3DRS_SPECULARENABLE, true);

每一种灯光都是通过D3DCOLORVALUE结构或者描述灯光颜色的D3DXCOLOR来描绘的。这里有几个灯光颜色的例子:

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

注意:在D3DXCOLOR类中的alpha值用在描述灯光颜色时是被忽略的。

5.2材质

在现实世界中我们看到的物体颜色将由物体反射回来的灯光颜色来决定。比如,一个红色的球是红色的,因为它吸收所有的灯光颜色除了红色光。红色光是被球反射回来进入我们眼睛的,因此我们看到的球是红色的。Direct3D通过我们定义的物体材质来模拟这些所有的现象。材质允许我们定义表面反射灯光的百分比。在代码中通过D3DMATERIAL9结构描述一个材质。

typedef struct _D3DMATERIAL9 {

       D3DCOLORVALUE Diffuse, Ambient, Specular, 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

red.Ambient = D3DXCOLOR(1.0f, 0.0f, 0.0f, 1.0f); // red

red.Specular = D3DXCOLOR(1.0f, 0.0f, 0.0f, 1.0f); // red

red.Emissive = D3DXCOLOR(0.0f, 0.0f, 0.0f, 1.0f); // no emission

red.Power = 5.0f;

这里我们设置绿色和蓝色的值为0,这表明材质反射0%此颜色的光。我们设置红色为1,表示材质反射100%的红光。注意,我们能够控制每种灯光反射的颜色(环境、漫射和镜面光)。

同样假如我们定义一个只发出蓝色光的光源,对球的光照将失败因为蓝色光将被全部吸收而没有红光被反射。当物体吸收了所有光以后,物体看起来就为黑色。同样的,当物体反射100%的红、绿和蓝光,物体就将呈现为白色。

因为手工填充一个材质结构将是乏味的工作,我们添加下列有用的函数和全局材质常数到d3dUtility.h/cpp文件中:

// lights
D3DLIGHT9 init_directional_light(D3DXVECTOR3* direction, D3DXCOLOR* color);
D3DLIGHT9 init_point_light(D3DXVECTOR3* position, D3DXCOLOR* color);
D3DLIGHT9 init_spot_light(D3DXVECTOR3* position, D3DXVECTOR3* direction, D3DXCOLOR* color);
// materials
D3DMATERIAL9 init_material(D3DXCOLOR ambient, D3DXCOLOR diffuse, D3DXCOLOR specular,
                           D3DXCOLOR emissive, float power);
const D3DMATERIAL9 WHITE_MATERIAL  = init_material(WHITE,  WHITE,  WHITE,  BLACK, 2.0f);
const D3DMATERIAL9 RED_MATERIAL       = init_material(RED,       RED,       RED,    BLACK, 2.0f);
const D3DMATERIAL9 GREEN_MATERIAL  = init_material(GREEN,  GREEN,  GREEN,  BLACK, 2.0f);
const D3DMATERIAL9 BLUE_MATERIAL   = init_material(BLUE,   BLUE,   BLUE,   BLACK, 2.0f);
const D3DMATERIAL9 YELLOW_MATERIAL = init_material(YELLOW, YELLOW, YELLOW, BLACK, 2.0f);
D3DLIGHT9 init_directional_light(D3DXVECTOR3* direction, D3DXCOLOR* color)
{
    D3DLIGHT9 light;
    ZeroMemory(&light, sizeof(light));
    light.Type        = D3DLIGHT_DIRECTIONAL;
    light.Ambient    = *color * 0.6f;
    light.Diffuse    = *color;
    light.Specular    = *color * 0.6f;
    light.Direction = *direction;
return light;
}
D3DLIGHT9 init_point_light(D3DXVECTOR3* position, D3DXCOLOR* color)
{
    D3DLIGHT9 light;
    ZeroMemory(&light, sizeof(light));
    light.Type            = D3DLIGHT_POINT;
    light.Ambient        = *color * 0.6f;
    light.Diffuse        = *color;
    light.Specular        = *color * 0.6f;
    light.Position        = *position;
    light.Range            = 1000.0f;
    light.Falloff        = 1.0f;
    light.Attenuation0    = 1.0f;
    light.Attenuation1    = 0.0f;
    light.Attenuation2    = 0.0f;
return light;
}
D3DLIGHT9 init_spot_light(D3DXVECTOR3* position, D3DXVECTOR3* direction, D3DXCOLOR* color)
{
    D3DLIGHT9 light;
    ZeroMemory(&light, sizeof(light));
    light.Type            = D3DLIGHT_SPOT;
    light.Ambient        = *color * 0.6f;
    light.Diffuse        = *color;
    light.Specular        = *color * 0.6f;
    light.Position        = *position;
    light.Direction        = *direction;
    light.Range            = 1000.0f;
    light.Falloff        = 1.0f;
    light.Attenuation0    = 1.0f;
    light.Attenuation1    = 0.0f;
    light.Attenuation2    = 0.0f;
    light.Theta            = 0.4f;
    light.Phi            = 0.9f;
return light;
}
D3DMATERIAL9 init_material(D3DXCOLOR ambient, D3DXCOLOR diffuse, D3DXCOLOR specular,
                           D3DXCOLOR emissive, float power)
{
    D3DMATERIAL9 material;
    material.Ambient  = ambient;
    material.Diffuse  = diffuse;
    material.Specular = specular;
    material.Emissive = emissive;
    material.Power      = power;
return material;
}

顶点结构没有材质属性;一个通用的材质必须被设置。设置它我们使用IDirect3DDevice9::SetMaterial(CONST D3DMATERIAL9*pMaterial)方法。

假设我们想渲染几个不同材质的物体;我们将按照如下的写法去做:

D3DMATERIAL9 blueMaterial, redMaterial;

// set up material structures

Device->SetMaterial(&blueMaterial);

drawSphere(); // blue sphere

Device->SetMaterial(&redMaterial);

drawSphere(); // red sphere

5.3顶点法线

       面法线(face normal)是描述多边形表面方向的一个向量(如图5.1)。

顶点法线(Vertex normals)也是基于同样的概念,但是我们与其指定每个多边形的法线,还不如为每个顶点指定(如图5.2)。

Direct3D需要知道顶点法线以便它能够确定灯光照射到物体表面的角度,并且一旦计算了每个顶点的灯光,Direct3D需要知道每个顶点的表面方向。注意顶点法线不一定和面法线相同。球体/环形物就是很好的实物例子,它们的顶点法线和三角形法线是不相同的(如图5.3)。

为了描述顶点的顶点法线,我们必须更新原来的顶点结构::

class cLightVertex
{
public:
float m_x, m_y, m_z;
float m_nx, m_ny, m_nz;
    cLightVertex() {}
    cLightVertex(float x, float y, float z, float nx, float ny, float nz)
    {
        m_x  = x;    m_y  = y;    m_z  = z;
        m_nx = nx;    m_ny = ny;    m_nz = nz;
    }
};
const DWORD LIGHT_VERTEX_FVF = D3DFVF_XYZ | D3DFVF_NORMAL;

作为一个简单的物体比如立方体和球体,我们能够通过观察看见顶点法线。对于更多复杂的网格,我们需要一个更多的机械方法。假设一个由p0,p1,p2构成的三角形,我们需要计算每个顶点的法线n0,n1,n2。

简单的步骤,我们列举它是为了找到由三个点构成的三角形的面法线,同时使用面法线作为顶点法线。首先计算三角形上的两个向量:

  • p1p0 = u

  • p2p0 = v

Then the face normal is:

  • n = u × v

Since each vertex normal is the same as the face normal:

  • n0 = n1 = n2 = n

下面是一个C函数,它通过三角形的三个顶点计算三角形的面法线。注意这个函数的三个顶点是按照顺时针方向指定的。假如不是这样,那么法线方向将是相反的。

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的顶点法线vn,我们找到网格模型中所有三角形的面法线记为顶点v。vn是通过计算他们的平均面法线得到的。这里有一个例子,假设有3个三角形它们的面法线分别是n0,n1,n2,指定为顶点v。那么vn的平均法线就是:

vn = (1/3) . (n0 + n1 + n2)

变换过程中把顶点法线变为non-normal,这是有可能的。因此最好通过D3DRS_NORMALIZENORMALS设置渲染状态,Direct3D重新单位化所有法线。

Device->SetRenderState(D3DRS_NORMALIZENORMALS, true);

本文转载自:http://www.cnblogs.com/flying_bat/archive/2008/03/20/1115372.html

共有 人打赏支持
上一篇: 内存对齐指令
下一篇: 内存对齐指令
rise-worlds

rise-worlds

粉丝 2
博文 1755
码字总数 0
作品 0
深圳
程序员
私信 提问
如何使用DXUT框架

DXUT是什么? DXUT即DirectX Utility Library,它是微软为DirectX Samples写的一个框架,有了这个框架,Sample的构建就方便多了,这个框架实际上抽取了构建Sample的公共代码,比如处理窗口消...

吞吞吐吐的
2017/10/12
0
0
D3D9 Device类型

D3D9主要有两种类型的Device,一种是HAL Device(Hardware Abstraction Layer,硬件抽象层)。另一种是REF Device(References Device)。 HAL Device 这是主要的设备类型,该类型支持硬件加...

吞吞吐吐的
2017/09/11
0
0
SDL2源代码分析6:复制到渲染器(SDL_RenderCopy())

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/leixiaohua1020/article/details/40895593 ===================================================== SDL源代码......

雷霄骅
2014/11/08
0
0
如何学好游戏3D引擎编程

注:本文是网上看到的一篇文章,感觉写的很好,因此收藏了下来 《如何学好游戏3D引擎编程》 此篇文章献给那些为了游戏编程不怕困难的热血青年,它的神秘要我永远不间断的去挑战自我,超越自我...

loving_forever_
2016/07/28
0
0
D3D中的X文件格式、模板、约束、类型介绍

在D3D中,我知道X文件是模板驱动(Template-Driven)的,也就是说它存储数据的格式是基于模板的. 这种文件格式具有结构自由,内容丰富,易应用,可移植性高等优点. 关于模板、约束、类型还有哪些详...

hhcczz
2015/04/13
64
1

没有更多内容

加载失败,请刷新页面

加载更多

指针数组和数组指针的区别

这两个名字不同当然所代表的意思也就不同。我刚开始看到这就吓到了,主要是中文太博大精深了,整这样的简称太专业了,把人都绕晕了。从英文解释或中文全称看就比较容易理解。 指针数组:arr...

天王盖地虎626
14分钟前
0
0
Qt那些事0.0.18

今天要记一下Qt中的Resource。自我感觉理解的不错,但是还会难免有谬误,在日后有可能会更新,也有可能不会。 小声的念叨一句,女人心,海底针。 今天就直接跳过了关于QML在qrc文件中的介绍,...

Ev4n
22分钟前
0
0
深入解析js的作用域、预解析机制

虽然,ES6在我们工作中应用得越来越广泛,但是还是很多项目保留着ES5的写法,所以,今天,带着大家重新巩固下ES5下的作用域及预解析机制。 概念: 作用域:域,指的是一个空间、范围、区域,...

前端攻城老湿
25分钟前
0
0
Spring Cloud Feign - 声明式 REST Client

1、Feign是什么 声明式REST client,来自NetFlix。 允许你编写无实现代码调用REST services 替换RestTemplate(甚至更简单) Spring Cloud 为使用Feign提供了包装器 2、怎样使用Feign 对比:...

Benz001
30分钟前
0
0
前端、后端和全栈到底不该学什么

1、前言 在职业规划咨询过程中经常会被问到这样的问题: 老师,我是该深入钻研专精一门,走技术大牛路线,还是所有都要精通,做一个全栈工程师? 类似问题的变种还有,老师我是不是该30岁最迟...

前端攻城小牛
32分钟前
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部