`

[转]辐射度算法(一) - GameRes.com

    博客分类:
  • 3D
阅读更多

 



简介:这篇文章是一个经典的辐射度算法的教程,详细的讲述了如何通过辐射度算法为静态场景计算光照贴图。这也是大多数游戏所采用的技术。现在很多的论文和书籍都讨论了如何在实时渲染中应用光照贴图来产生逼真的光照效果,然而他们主要着重于如何组织光照贴图。而光照贴图究竟是怎样计算出来的,也就是全局照明算法,却极少有资料进行详细的解释。我在网上搜索到这篇文章,看了之后受益匪浅,于是决定将它翻译出来,让更多的人了解这方面的知识。如果对这篇文章有不理解的地方,可以联系作者,也可以和我共同讨论(我的网站:http://program.stedu.net)。如果翻译有错漏,也敬请指正和谅解,因为这毕竟是本人第一次翻译文章。如果你的英文水平不错,建议直接看原文:单击这里


    光照和阴影投射算法可以大致地分为两大类:直接照明和全局照明。许多人都会对前者较为熟悉,同时也了解它所带来的问题。这篇文章将首先简要地介绍两种方法,然后将深入地研究一种全局照明算法,这就是辐射度。

直接照明

    直接照明是一个被老式渲染引擎(3D StudioPOV)所采用的主要光照方法。一个场景由两种动态物体组成:普通物件和光源。光源在不被其他物件遮挡的情况下向某些物件投射光线,若光源被其他物体遮挡,则会留下阴影。

    在这种思想之下有许多方法来产生阴影,如Shadow Volume(阴影体), Z缓冲方法,光线追踪等等。但由于它们都采用一个普遍的原则,因此这些方法都有同样的问题,而且都需要捏造一些东西来解决这些问题。

    直接照明的优缺点:

 

优点

缺点

光线追踪:

- 能够同时渲染由参数或多边形描述的物体
-
允许你实现一些酷的体效果(volumetric effects)

- 慢速
-
非常锐利的阴影和反射

阴影体:

- 可以加以修改修改以渲染软阴影 (非常有技巧性)

- 实现起来需要技巧
-
非常锐利的阴影
-
物体只能用多边形描述

Z缓冲
(Shadow Mapping):

- 容易实现
-
快速 (能做到实时)

- 锐利的阴影,锯齿问题。

 

     

    需要考虑的最重要的问题是,由于这些方法会产生超越真实的图像,他们只能处理只有点光源的场景,而且场景中的物体都能做到完美地反射和漫反射。现在,除非你是某种富裕的白痴,你的房子可能并不是装满了完全有光泽的球体和点状的光源。事实上,除非你生活在完全不同的物理背景下的一个宇宙空间,你的房间是不可能出现任何超级锐利的阴影的。

    人们宣称光线追踪器和其他渲染器能够产生照片级的真实效果是一件非常自然的事情。但想象如果有人拿一张普通光线追踪(这种渲染方法类似经典OpenGL光栅和光照渲染方法)的图片给你看,然后声称它是一张照片,你可能会回敬他是一个瞎子或者骗子。

    同时也应该注意到,在真实世界里,我们仍然能看到不被直接照亮的物体。阴影永远都不是全黑的。直接照明的渲染器试图通过加入环境光来解决这样的问题。这样一来所有的物体都接受到一个最小的普遍直接照明值。

全局照明

    全局照明方法试图解决由光线追踪所带来的一些问题。一个光线追踪器往往模拟光线在遇到漫反射表面时只折射一次,而全局照明渲染器模拟光线在场景中的多次反射。在光线追踪算法里,场景中的每个物体都必须被某个光源照亮才可见,而在全局照明中,这个物体可能只是简单的被它周围的物体所照亮。很快就会解释为什么这一点很重要。

全局照明的优缺点

    由全局照明方法产生的图片看起来真正让人信服。这些方法独自成为一个联盟,让那些老式渲染器艰苦地渲染一些悲哀的卡通。但是,而且是一个巨大的但是但是它们更加地慢。正像你可能离开你的光线追踪渲染器一整天,然后回来看着它产生地图像激动地发抖,在这儿也一样。

 

优点

缺点

辐射度算法:

- 非常真实的漫反射表面光照
-
概念简单,容易实现
-
能够容易地使用3D硬件加速计算

-
-
不能很好地处理点光源
-
也不能处理有光泽的表面
-
总是过于复杂而且很少在书本中解释

蒙特卡罗法:

- 非常、非常好的效果
-
能够很好地模拟各种光学效果

-
-
轻度困难
-
需要聪明才智来优化
-
总是过于复杂而且很少在书本中解释

 

用直接照明照亮一个简单的场景

    我用3D Studio 对这个简单的场景进行了建模。我想让这个房间看起来就像被窗外的太阳照亮一样。

    因此,我设置了一个聚光灯照射进来。当我渲染它时,整个房间都几乎是黑色的,除了那一小部分能够被光射到的地方。

    打开环境光只是让场景看起来呈现一种统一的灰色,除了地面被照射到的地方呈现统一的红色。

    在场景中间加入点光源来展现更多细节,但场景并没有你想象中的被太阳照亮的房间那样的亮斑。

     最后,我把背景颜色设为白色,来展现一个明亮的天空。

 

 

用全局照明照亮这个简单的场景

    我用我自己的辐射度渲染器来渲染这个场景。我用Terragen渲染了一个天空盒来作为光源,并把它放置与窗户之外。除此之外没有使用任何其他光源。

    无需任何其他工作,这个房间看起来被真实的照亮了。

    注意以下几点有趣的地方:

·         整个房间都被照亮并且可见,甚至那些背对者太阳的表面。

·         软阴影。

·         墙面上的亮度微妙地过度。

·         原本灰色地墙面,再也不是原始的灰色,在它们上面有了些温意。天花板甚至可以说是呈现了浅粉红色。

辐射度渲染器的工作原理

     清空你脑子里任何你所知道的正常的光照渲染方法。你之前的经验可能会完全地转移你的注意力。

     我想询问一个在阴影方面的专家,他会向你解释所有他们所知道的关于这个学科的东西。我的专家是在我面前的一小片墙上的油漆。

    Hugo: "为什么你在阴影当中,而你身边的那一片跟你很相像的油漆却在光亮之中?"
    
油漆: "你什么意思?"
    Hugo: "
你是怎么知道你什么时候应该在阴影之中,什么时候不在? 你知道哪些阴影投射算法?你只是一些油漆而已啊。"
    
油漆: "听着,伙计。我不知道你在说什么。我的任务很简单:任何击中我的光线,我把它分散开去。"
    Hugo: "
任何光线?"
    
油漆: "是的。任何光线。我没有任何偏好。"

    因此你应该知道了。这就是辐射度算法的基本前提。任何击中一个表面的光都被反射回场景之中。是任何光线。不仅仅是直接从光源来的光线。任何光线。这就是真实世界中的油漆是怎么想的,这就是辐射度算法的工作机制。

    在接下来的文章中,我将详细讲解怎样制作你自己的会说话的油漆。

    这样,辐射度渲染器背后的基本原则就是移除对物体和光源的划分。现在,你可以认为所有的东西都是一个潜在的光源。任何可见的东西不是辐射光线,就是反射光线。总之,它是一个光的来源,一个光源。一切周围你能看到的东西都是光源。这样,当我们考虑场景中的某一部分要接受多少光强时,我们必须注意把所有的可见物体发出的光线加起来。

基本前提:

    1: 光源和普通物体之间没有区别。
    2:
场景中的一个表面被它周围的所有可见的表面所照亮。

    现在你掌握这个总要的思想。我将带你经历一次为场景计算辐射度光照的全过程。


一个简单的场景

    我们以这个简单的场景开始:一个有三扇窗户的房间。这里有一些柱子和凹槽,可以提供有趣的阴影。

    它会被窗外的景物所照亮,我假设窗外的景物只有一个很小、很亮的太阳,除此之外一片漆黑。

 

     现在,我们来任意选择一个表面。然后考察它上面的光照。

 

    由于一些图形学中难以解决的问题,我们将把它分割成许多小片(的油漆),然后试着从他们的角度来观察这个世界。

    从这里开始,我将使用面片来指代“一小片油漆”。

 

 

    选取他们之中的一个面片。然后想象你就是那个面片。从这个角度,这个世界看起来应该是什么样子呢?

 

一个面片的视角

    将我的眼睛贴紧在这个面片之上,然后看出去,我就能看见这个面片所看见的东西。这个房间非常黑,因为还没有光线进入。但是我把这些边缘画了出来以方便你辨认。

     通过将它所看见的所有光强加在一起,我们能够计算出从场景中发出的所有能够击中这个面片的光强。我们把它成为总入射光强

    这个面片只能看见房间以及窗外漆黑的风景。把所有的入射光强加起来,我们可以看出没有光线射到这里。这个面片应该是一片黑暗。

 

 

一个较低处的面片的视角

     选择柱子上的一个稍低一些的面片。这个面片能够看到窗外明亮的太阳。这一次,所有的入射光强相加的结果表明有很多的光线到达这里(尽管太阳很小,但是它很亮)。这个面片被照亮了。

 

 

墙拄上的光照

    为墙拄上的每个面片重复这个过程,每次都为面片计算总入射光强之后,我们可以回头看看现在的柱子是什么样子。

    在柱子顶部的面片,由于看不见太阳,处在阴影当中。那些能看见太阳的被照得很亮。而那些只能看见太阳的一部分的面片被部分地照亮了。

    如此一来,辐射度算法对于场景中的每个其他的面片都用几乎一样的方式重复。正如同你所看到的,阴影逐渐地在那些不能看见光源的地方显现了。

 

整个房间的光照: 第一次遍历

    为每个面片重复这个过程,给我门带了这样的场景。除了那些能够从太阳直接接受光线的表面这外,所有的东西都是黑的。

    因此,这看起来并不像是被很好地照亮了的场景。忽略那些光照看起来似乎是一块一块的效果。我们可以通过将场景分割为更多的面片来解决这个问题。更值得注意的是除了被太阳直接照射的地方都是全黑的。在这个时候,辐射度渲染器并没有体现出它与其他普通渲染器的不同。然而,我们没有就此而止。既然场景中的某些面片被照得十分明亮,它们自己也变成了光源,并且也能够向场景中的其他部分投射光线。

 

在第一次遍历之后面片的视角

    那些在上次遍历时不能看见太阳而没有接受到光线的面片,现在可以看到其他面片在发光了。因此在下次遍历之后,这些面片将变得明亮一些。

 

整个房间的光照:第二次遍历

    这一次,当你为每个面片计算完入射光强之后,上次全黑的面片现在正被照亮。这个房间开始变得有些真实了。

    现在所发生的是太阳光照射到表面之后反射一次时,场景的效果。

 

整个房间的光照:第三次遍历

    第三次遍历产生了光线折射两次的效果。所有的东西看起来大致相同,只是轻微的亮了一些。

    下一次遍历也仅仅时让场景更加明亮,甚至第16次遍历也并没有带来很大的不同。在那之后已经没有必要做更多的遍历了。

    辐射度过程集中在一个光照解决方案上缓慢地进展。每一次遍历都给场景带来一些轻微地变化,直到产生的变化趋于稳定。根据场景复杂度的不同,以及表面的光照特性,可能需要几次或几千次遍历不等。这取决于你什么时候停止遍历,告诉它已经完成了。

 

第四次遍历

第十六次遍历

 

更加详细的算法描述: 面片

辐射光强(Emmision)
    
尽管我曾说过我们应该认为光源和普通物体是一样的,但场景中显然要有光发出的源头。在真实世界中,一些物体会辐射出光线,但有些不会。并且所有的物体会吸收某些波段的光。我们必须有某种方法区分出场景中那些能够辐射光线的物体。我们在辐射度算法中通过辐射光强来表 述这一点。我们认为,所有的面片都会辐射出光强,然而大多数面片辐射出的光强为0。这个面片的属性称为辐射光强(Emmision)

反射率(Reflectance)
   
当光线击中表面时,一些光线被吸收并且转化为热能(我们可以忽略这一点),剩下的则被反射开去。我们称反射出去的光强比例为反射率(Reflectance)

入射和出射光强(incident and excident lights)
   
在每一次遍历的过程中,记录另外两个东西是有必要的:有多少光强抵达一个面片,有多少光强因反射而离开面片。我们把它们称为入射光强和出射光强。出射光强是面片对外表现的属性。当我们观看某个面片时,其实是面片的出射光被我们看见了。

    incident_light(入射光强) = sum of all light that a patch can see
    excident_light(出射光强) = (incident_light*reflectance) + emmision
         

面片的数据结构
   
既然我们了解了一个面片的所有必要属性,我们就应该定义面片的数据结构了。稍后,我将解释这四个变量的细节。

  structure PATCH
    vec4  emmision
    float reflectance
    vec4  incident
    vec4  excident
  end structure

    我已经讲解了算法的基础,下面将再次使用伪代码的形式加以讲解,让它更加具体。很显然这还是在一个较高的层次上,但我会在后面讲述更多的细节。

   

辐射度算法 伪代码: 级别 1

  load scene
 
  divide each surface into roughly equal sized patches
 
 
  initialise_patches:
  for each Patch in the scene
    if this patch is a light then
      patch.emmision = some amount of light
    else
      patch.emmision = black
    end if
    patch.excident = patch.emmision
  end Patch loop
  
 
 
  Passes_Loop:
 
  each patch collects light from the scene
  for each Patch in the scene
    render the scene from the point of view of this patch
    patch.incident = sum of incident light in rendering
  end Patch loop
 
 
  calculate excident light from each patch:
  for each Patch in the scene
    I = patch.incident
    R = patch.reflectance
    E = patch.emmision
    patch.excident = (I*R) + E
  end Patch loop
 
  Have we done enough passes?
    if not then goto Passes_Loop
 

 

代码解释

initialize patches:(初始化面片)
 
一开始,所有的面片都是黑的,除了那些能自身辐射出光线的面片。因此,那些能辐射光线的面片的出射光强的初始值应被初始化伪它的辐射光强。其他面片的辐射光强都应为0

Passes Loop(遍历循环):
  
代码多次重复这个循环直到场景有了可接受的光照效果。每次循环之后,也就多模拟了一次光在场景中的反射。

each patch collects light from the scene(每个面片从场景中收集光强)
   
如果我之前在文章中解释的那样,每个面片都被它能够看见的其他面片照亮。要达到这个目的,可以简单地把把场景渲染到面片的视角,然后把它所看的光强相加。我将在下一小节更详细地解释这一步。

calculate excident light from each patch(为每个面片计算出射光强):
   
计算出有多少光强抵达面片之后,我们现在可以计算出有多少光强离开面片(被反射)


  
这个过程必须被循环多次以达到一个好的效果。如果渲染器还需要一个循环,我们就调转到标记"Passes Loop"

实现辐射度: 半立方体(Hemi Cubes)

    在实现辐射度算法的过程中,我们首先要解决的问题是,从面片的角度渲染世界。到目前为止我用了鱼眼视角来表示面片看到的场景,但这并不容易,也很难实现。有个更好的方法,半立方体!

 

半球体
   
想象把一个鱼眼视图贴到一个半球体表面上。把半球体放在一个面片的前面,那么从那个面片的角度来看,被扭曲场景贴到一个半球体表面上,又变回原来的样子,这和 从那个面片正常渲染的场景是一样的,没有任何区别。

   将一个摄影机放置在半球体的中心,你可以看到视图看起来和普通的渲染是一样的。(右图)

    如果你能找到一个办法能够很轻松地渲染到一个鱼眼视角,那么你可以把每个象素地亮度加起来以确定这个面片地入射光强。然而渲染一个鱼眼视角并不容易,所以我么你必须另寻方法。


从半球体的中心渲染

 

半立方体
   
让人惊奇的是(或者让人很不惊奇,取决于你的数学水平) 一个半立方体看起来和一个半球体完全一致。


从半立方体的中心渲染

 

展开半立方体

    想象着将一个半立方体展开. 你得到了什么? 一个正方形图片和四个长方形图片。中间的正方形的图片是从面片的位置,朝向面片的法向量直接渲染得到的。其他的四个

分享到:
评论

相关推荐

    遗传算法解决TSP问题.rar

    /* 用遗传算法(GA)解决TSP(旅行商)问题 ...Blog: blog.gameres.com/show.asp?BlogID=1450&column=0 E-mail: starsftk@yahoo.com.cn ps:初学遗传算法,很多都不懂,程序还有很多不足,若你改进了别忘了告诉我 */

    GIF文档 - GameRes_com

    GIF文档 - 中文 详细 全面 希望对大家有帮助 GIF文档 - 中文 详细 全面 希望对大家有帮助 GIF文档 - 中文 详细 全面 希望对大家有帮助 GIF文档 - 中文 详细 全面 希望对大家有帮助

    一种简单、快速、高效的多边形减面算法

    一种简单、快速、高效的多边形减面算法 - GameRes_com.mht 看看

    Windows游戏编程快速入门 - GameRes文档中心- 游戏制作文章、游戏开发 ....doc

    《Windows游戏编程快速入门》是面向初学者的一篇教程,旨在引导读者踏入游戏开发的世界。本文档主要通过实际操作步骤,介绍了如何使用Visual Studio 2003 .NET来创建一个简单的Windows游戏项目,以此来教授基本的...

    游戏策划之路-GameRes文档中心-游戏制作文章、游.doc

    - **售后服务**:解决玩家遇到的问题,提供技术支持和更新,维护游戏社区的活跃度。 ### 第三章:游戏的策划工作如何进行 3.1部分详细讨论了游戏策划的核心任务,包括确定游戏的题材、类型和风格: - **游戏题材*...

    sef.rar_sef_sef软件_图形图像处理

    - `计算几何算法概览 - GameRes_com.htm`:这可能是一个网页文档,提供计算几何的基本概念、常用算法的概述,以及在游戏开发中的应用。 - `e913ACM.pps`:可能是PowerPoint演示文稿,详细介绍了ACM竞赛中的计算...

    泡泡堂单机版[模拟] 源代码

    - 2ccc.com.nfo:NFO文件通常包含有关软件或分享内容的作者信息和版权声明。 - 开发日志.txt:记录了项目开发过程中的笔记、进度和问题。 - res 文件夹:可能包含游戏的其他资源文件,如图片、声音等。 - Map 文件夹...

    游戏脚本系统的demo

    游戏脚本系统是一种在游戏中实现动态行为和交互的关键技术,它允许开发者通过编程语言来控制游戏逻辑,而无需深入到游戏引擎的核心代码。"游戏脚本系统的demo"是针对这一主题的一个示例,提供了实践操作的经验,尽管...

    赫赫大名的A*寻路算法(vb.net版本)

    在网上看到一篇A*寻路算法的译文 http://data.gameres.com/message.asp?TopicID=25439 按此原理写了以下程序另外补充:1.此算法不是最短路径算法. 2.在实际应用中肯定还需要优化,以适合具体游戏. 3.(vb.net2005测试...

    VC版重装机兵(MetalMax)(游戏+源码)

    作品发布:http://data.gameres.com/showmessage.asp?TopicID=163262 如下为作品相关信息,如有转载,请详细标明如下相关信息,以示尊重!! ==================作品信息================== 名称:MetalMax 作者:...

    编程新手真言,转自gameres

    编程新手真言,转自gameres

    huluwa.rar_GBGM.m_c++ 锁_huluwa官网_www. huluwa .com

    本压缩包文件"**huluwa.rar_GBGM.m_c++ 锁_huluwa官网_www.huluwa.com**"可能与一个名为"Huluwa"的游戏或软件相关,它似乎使用了C++语言,并涉及到了互斥锁(Mutex)的概念。"GBGM.m"可能是项目中的一个模块或者类名...

    游戏中Windows中文输入法的处理类

    代码说明 ---------------------------------------------------- 这是一个Win32的窗口应用程序工程。 用于调试与游戏环境类似的非图象部分...该程序参考了www.gameres.com中的一个输入法例程,在此对原作者表示感谢。

    50个论坛地址

    - **GameRes**(http://www.gameres.com/): 游戏开发资源和技术交流的论坛。 - **17173**(http://www.17173.com/): 以游戏资讯为主的论坛,但也包含一定的游戏开发内容。 - **52RD**(http://www.52rd.com/): ...

    正则表达式解析库

    9. `GameRes Readme.txt`: 虽然名字中没有明显的正则表达式关联,但这可能是一个关于游戏资源的说明文件,可能与正则表达式库的使用场景有关。 通过这些文件,我们可以学习到正则表达式库的实现细节、使用方法以及...

    Dx9 SDK DirectShow开发

    - www.pudn.com.txt:这个文件可能是从网站pudn.com下载资源的说明,可能提到了获取更多DirectShow开发相关资源的链接或信息。 - Include:这个目录应该包含了DirectX 9 SDK的头文件,供你的项目引用。 - Lib:这个...

    Parallel World 3D场景漫游

    《Parallel World》为网友dongch007原创的3D场景漫游示例Demo,内含天空、陆地、湖泊、植被、建筑、角色动画、UI及场景特效等诸多场景必备元素,...原帖地址:http://data.gameres.com/showmessage.asp?TopicID=131818

    Pro.OGRE.3D.Programming 中文PDF版本

    - **论坛**:[http://bbs.gameres.com/showforum.asp?forumid=99](http://bbs.gameres.com/showforum.asp?forumid=99) 以上资源为学习OGRE 3D提供了丰富的参考资料和技术支持。 #### 六、总结 《Pro.OGRE.3D....

Global site tag (gtag.js) - Google Analytics