一般在光照模型中,ambient light的计算方法为:A = l * m,其中l表示表面接收到的来自光源的ambient light的总量,而m表示表面接收到ambient light后,反射和吸收的量。出于性能考虑,在计算光照时,我们是不考虑那些从场景中其他物体反弹过来的光的,因为通常我们认为这些光在场景中被发散和弹射许许多多次以至于最后从各个方向照射到物体上的量是相同的。所以ambient light所做的就是提亮物体,它没有任何真实的物理光照计算,所以它最终的渲染效果就是一个常量颜色:

而这里要介绍的Ambient Occlusion技术的作用就是改善ambient light的效果,使物体看起来饱满有层次感。

前面提到,在普通的ambient light计算中,我们认为所有的光线从各个角度照射到物体上时是等量的:

而在Ambient Occlusion技术中,我们将遮挡考虑进去,也就是说,表面从各个方向接受到的光的总量不再是等量的,而是取决于从表面的上半球体照射到表面的光线被遮挡了多少(这里为什么只考虑表面的上半球体呢,是因为从表面下半部分照射到表面的光是不会照亮表面的,所以不需要考虑):

那么在程序中我们该如何模拟这种遮挡呢?具体来说,就是我们从顶点随机生成围绕表面上半球体的射线,然后检测这些射线是否和网格相交:

根据上图,我们发射了7条射线,其中有5条是和网格相交的,那么对于这个顶点p,他的遮挡值occlusion = 5 / 7。所以,我们对于遮挡的定义如下:对于顶点p,如果我们发射了N条射线,其中有h条和网格相交,那么顶点p的遮挡值就是occlusion = h / N。但在计算时我们还有个需要注意的地方是需要定义一个距离distance,只有当顶点到射线和网格的交点的距离小于distance时,我们才认为顶点是被遮挡的,原因是如果距离太远,尽管射线和网格相交了,但是我们认为这个网格其实是遮挡不住顶点的。

好了,至此,我们已经了解了Ambient Occlusion的基本原理,可以开始动手实现了,基本的程序流程是这样的:

对于每个三角面,我们计算每个顶点的遮挡值,但是这个顶点可能被多个三角面共享,因此,我们的处理方式是加权平均,假设顶点v被2个三角面共享,对于三角面1,我们计算出来他的遮挡值是0.7,而对于三角面2,我们计算出他的遮挡值是0.5,那么他最终的遮挡值就是:(0.7 + 0.5) / 2 = 0.6。下面展示我写的计算代码片段,其中,对于每个三角面,我会发射32条射线用于做相交性检测:

         UINT uTriangleCount = vIndices.size() / ;
std::vector<UINT> vVertexSharedCount( uVertexCount ); // Used to count how many triangles contain the same vertex for ( UINT triangleIndex = ; triangleIndex < uTriangleCount; ++triangleIndex )
{
UINT index_0 = vIndices[triangleIndex * ];
UINT index_1 = vIndices[triangleIndex * + ];
UINT index_2 = vIndices[triangleIndex * + ]; XMVECTOR vertex_0 = XMLoadFloat3( &vVertices[index_0].v3Position );
XMVECTOR vertex_1 = XMLoadFloat3( &vVertices[index_1].v3Position );
XMVECTOR vertex_2 = XMLoadFloat3( &vVertices[index_2].v3Position ); // Calculate normal and centroid of this triangle
XMVECTOR edge_0 = vertex_1 - vertex_0;
XMVECTOR edge_1 = vertex_2 - vertex_0;
XMVECTOR normal = XMVector3Normalize( XMVector3Cross(edge_0, edge_1) ); XMVECTOR centroid = (vertex_0 + vertex_1 + vertex_2) / 3.0f;
centroid += 0.001f * normal; // Offset to avoid self intersection //
UINT UnoccludedCount = ;
static const UINT SAMPLE_RAY_COUNT = ;
for ( UINT index = ; index < SAMPLE_RAY_COUNT; ++index )
{
XMVECTOR vRandomDir = CUtils::RandHemisphereUnitVector3( normal ); if ( !g_pOctree->RayOctreeIntersect(centroid, vRandomDir) )
{
++UnoccludedCount;
}
} FLOAT fAmbientAccess = static_cast<FLOAT>(UnoccludedCount) / static_cast<FLOAT>(SAMPLE_RAY_COUNT); // Average with vertices that share this triangle
vVertexAmbientAccesses[index_0] += fAmbientAccess;
vVertexAmbientAccesses[index_1] += fAmbientAccess;
vVertexAmbientAccesses[index_2] += fAmbientAccess; ++vVertexSharedCount[index_0];
++vVertexSharedCount[index_1];
++vVertexSharedCount[index_2];
} for ( UINT vertexIndex = ; vertexIndex < uVertexCount; ++vertexIndex )
{
vVertexAmbientAccesses[vertexIndex] /= vVertexSharedCount[vertexIndex];
}

好了,至此我们已经讲解完Ambient Occlusion技术了,这里还要补充的是,Ambient Occlusion的计算开销其实是非常大的,在我写的Demo中,有32000多个顶点,60000多个三角面,对于每个三角面发射32条射线,在我使用了八叉树进行优化的情况下,仍然需要5分钟左右的时间才能计算完毕,因此,我们通常会事先计算完遮挡值,存在文件中,然后运行时直接读取而不再计算,所以这个技术通常只能用于静态网格模型,因为对于动态网格模型他不可能实时运算。

在我的Demo中,我将每个顶点的遮挡值存在一张纹理中,其中每个像素对应一个顶点的遮挡值:

Demo最终的效果如下:

Ambient Occlusion的更多相关文章

  1. GLSL实现Ambient Occlusion 【转】

    http://blog.csdn.net/a3070173/archive/2008/11/04/3221181.aspx 相信使用OpenGl或DirectX3D的朋友都知道到固定功能管线在光照处理 ...

  2. TSSAO Temporal Screen-Space Ambient Occlusion (Unity3d 5 示例实现)

    前提 环境光(ambient occlusion)是一种GI,其简化形式SSAO可以用“微量高效”来形容,消耗得很少,得到的效果很好.环 境光遮蔽(ambient occlusion)的本质是计算在一 ...

  3. [帖子收集]环境光遮蔽(Ambient Occlusion)

    环境光遮蔽,效果示例图 图片左边是一条龙的简单模型,呈现在一个均匀照明的环境中.尽管模型中有一些明暗不同的区域,但大部分光照都是均匀的.虽然模型有着相当复杂的几何形状,但看上去比较光滑平坦,没有明显的 ...

  4. Dynamic Ambient Occlusion and Indirect Lighting

    This sample was presented on the Nvida witesite, which detail a new idea to calculate the ambient oc ...

  5. Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第二十一章:环境光遮蔽(AMBIENT OCCLUSION)

    原文:Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第二十一章:环境光遮蔽(AMBIENT OCCLUSION) 学习目标 ...

  6. Cesium源码剖析---Ambient Occlusion(环境光遮蔽)

    Ambient Occlusion简称AO,中文没有太确定的叫法,一般译作环境光遮蔽.百度百科上对AO的解释是这样的:AO是来描绘物体和物体相交或靠近的时候遮挡周围漫反射光线的效果,可以解决或改善漏光 ...

  7. [译]基于GPU的体渲染高级技术之raycasting算法

    [译]基于GPU的体渲染高级技术之raycasting算法 PS:我决定翻译一下<Advanced Illumination Techniques for GPU-Based Volume Ra ...

  8. Gamma校正与线性空间

    基础知识部分 为了方便理解,首先会对(Luminance)的相关概念做一个简单介绍.如果已经了解就跳到后面吧. 我们用Radiant energy(辐射能量)来描述光照的能量,单位是焦耳(J),因为光 ...

  9. [转]显卡帝揭秘3D游戏画质特效

    显卡帝揭秘3D游戏画质特效 近几年来,大量采用最新技术制作的大型3D游戏让大部分玩家都享受到了前所未有的游戏画质体验,同时在显卡硬件方面的技术革新也日新月异.对于经常玩游戏的玩家来说,可能对游戏画质提 ...

随机推荐

  1. python学习第二天第一部分

    备注:写程序不能写重复性的代码 学习内容:数据类型.for循环.while循环.字符编码.文件处理 一.for循环 1.简单的for循环 for i in range(10): # 此处意思为:循环r ...

  2. C#调用C++ Dll

    现在项目基本都是旁边C++的哥们做好dll扔给我,然后我调用.好久之前晚上down了一份c#调用c++dll的方法,出处早已经遗忘.闲来无事,放上来好了.原作者看到后可以留言,我会把您链接放上的,帮了 ...

  3. Node.js学习笔记 01 搭建静态服务器

    希望这篇文章能解决你这样一个问题:“我现在已经了解了一些Node.Js基本概念了,怎么搭一台静态服务器呢?” 请参考一下博主的前两篇文章: 完全面向于初学者的Node.js指南 Node.Js的Mod ...

  4. 谈谈对MVC的理解

    MVC是Model-View-Controler的简称,即模型-视图-控制器.其实MVC是一种设计模式,它强制性的把应用程序的输入.处理和输出分开.MVC中的模型.视图.控制器它们分别承担着不同的任务 ...

  5. hdu 4715 Difference Between Primes

    题目连接 http://acm.hdu.edu.cn/showproblem.php?pid=4715 Difference Between Primes Description All you kn ...

  6. bzoj 1054 移动玩具

    题目连接 http://www.lydsy.com/JudgeOnline/problem.php?id=1054 移动玩具 Description 在一个4*4的方框内摆放了若干个相同的玩具,某人想 ...

  7. JavaScript高级程序设计之表单基础

    A FORM <form id='form' action='http://a-response-url' method="post"> <!--maxlengt ...

  8. [转]ubuntu 10.04下的配置tftp服务器

    [转]ubuntu 10.04下的配置tftp服务器 http://www.cnblogs.com/geneil/archive/2011/11/24/2261653.html 第1步:安装tftp所 ...

  9. netstat用法

    netstat - 显示网络连接,路由表,接口状态,伪装连接,网络链路信息和组播成员组. 总 netstat [address_family_options] [--tcp|-t] [--udp|-u ...

  10. 查看图片真正的格式,在不知道扩展名的情况下区分是jpeg还是bmp

    用系统自带的画图软件打开图片,然后按文件-->另存为就会弹出保存窗口.保存窗口的保存类形就是"照片真正的格式".