Ambient Occlusion
一般在光照模型中,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的更多相关文章
- GLSL实现Ambient Occlusion 【转】
http://blog.csdn.net/a3070173/archive/2008/11/04/3221181.aspx 相信使用OpenGl或DirectX3D的朋友都知道到固定功能管线在光照处理 ...
- TSSAO Temporal Screen-Space Ambient Occlusion (Unity3d 5 示例实现)
前提 环境光(ambient occlusion)是一种GI,其简化形式SSAO可以用“微量高效”来形容,消耗得很少,得到的效果很好.环 境光遮蔽(ambient occlusion)的本质是计算在一 ...
- [帖子收集]环境光遮蔽(Ambient Occlusion)
环境光遮蔽,效果示例图 图片左边是一条龙的简单模型,呈现在一个均匀照明的环境中.尽管模型中有一些明暗不同的区域,但大部分光照都是均匀的.虽然模型有着相当复杂的几何形状,但看上去比较光滑平坦,没有明显的 ...
- Dynamic Ambient Occlusion and Indirect Lighting
This sample was presented on the Nvida witesite, which detail a new idea to calculate the ambient oc ...
- Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第二十一章:环境光遮蔽(AMBIENT OCCLUSION)
原文:Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第二十一章:环境光遮蔽(AMBIENT OCCLUSION) 学习目标 ...
- Cesium源码剖析---Ambient Occlusion(环境光遮蔽)
Ambient Occlusion简称AO,中文没有太确定的叫法,一般译作环境光遮蔽.百度百科上对AO的解释是这样的:AO是来描绘物体和物体相交或靠近的时候遮挡周围漫反射光线的效果,可以解决或改善漏光 ...
- [译]基于GPU的体渲染高级技术之raycasting算法
[译]基于GPU的体渲染高级技术之raycasting算法 PS:我决定翻译一下<Advanced Illumination Techniques for GPU-Based Volume Ra ...
- Gamma校正与线性空间
基础知识部分 为了方便理解,首先会对(Luminance)的相关概念做一个简单介绍.如果已经了解就跳到后面吧. 我们用Radiant energy(辐射能量)来描述光照的能量,单位是焦耳(J),因为光 ...
- [转]显卡帝揭秘3D游戏画质特效
显卡帝揭秘3D游戏画质特效 近几年来,大量采用最新技术制作的大型3D游戏让大部分玩家都享受到了前所未有的游戏画质体验,同时在显卡硬件方面的技术革新也日新月异.对于经常玩游戏的玩家来说,可能对游戏画质提 ...
随机推荐
- 内存详解--理解 JVM 如何使用 AIX 上的本机内存
转自---http://www.ibm.com/developerworks/cn/java/j-nativememory-aix/
- C 封装一个简单二叉树基库
引文 今天分享一个喜欢佩服的伟人,应该算人类文明极大突破者.收藏过一张纸币类型如下 那我们继续科普一段关于他的简介 '高斯有些孤傲,但令人惊奇的是,他春风得意地度过了中产阶级的一生,而 没有遭受到冷 ...
- PAT乙级真题1001. 害死人不偿命的(3n+1)猜想 (15)(解题)
卡拉兹(Callatz)猜想: 对任何一个自然数n,如果它是偶数,那么把它砍掉一半:如果它是奇数,那么把(3n+1)砍掉一半.这样一直反复砍下去,最后一定在某一步得到n=1.卡拉兹在1950年的世界数 ...
- 菜鸟学习Spring——60s配置XML方法实现简单AOP
一.概述. 上一篇博客讲述了用注解的形式实现AOP现在讲述另外一种AOP实现的方式利用XML来实现AOP. 二.代码演示. 准备工作参照上一篇博客<菜鸟学习Spring--60s使用annota ...
- ORACLE 分区表简介
参考链接:http://blog.csdn.net/ziwen00/article/details/9158725ORACLE-分区表 此文从以下几个方面来整理关于分区表的概念及操作: 1.表空间及分 ...
- 取精华、去糟粕!适合iOS开发者的15大网站推荐
iOS开发者若想使技艺达到炉火纯青的地步,就要不断借鉴他人的有益经验,紧跟新兴科技和工具的步伐.除了Apple的开发者中心,其他网站上的文章和资源也具备参考价值,若能学得一二,必能锦上添花.不过,时间 ...
- < java.util >-- List接口
List本身是Collection接口的子接口,具备了Collection的所有方法.现在学习List体系特有的共性方法,查阅方法发现List的特有方法都有索引,这是该集合最大的特点. List:有序 ...
- 【工作总结】LLDB调试技巧 - 篇一
备忘命令 : 命令“l”可以查看程序当前运行的位置 (lldb) l } - (void)rightBarButtonAction { 命令“bt”也能查看程序运行的调用栈 (lldb) bt * t ...
- Machine Learning 学习笔记 (2) —— 使用牛顿法寻找极值
本系列文章允许转载,转载请保留全文! [请先阅读][说明&总目录]http://www.cnblogs.com/tbcaaa8/p/4415055.html 1. 用牛顿法解方程 牛顿法是一种 ...
- java笔试题(1)
char型变量中能不能存贮一个中文汉字? char型变量是用来存储Unicode编码的字符的,unicode编码字符集中包含了汉字,所以,char型变量中当然可以存储汉字啦.不过,如果某个特殊的汉字没 ...