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游戏让大部分玩家都享受到了前所未有的游戏画质体验,同时在显卡硬件方面的技术革新也日新月异.对于经常玩游戏的玩家来说,可能对游戏画质提 ...
随机推荐
- linux积累
在多文件中批量替换字符串grep -rl 'windows' ./ | xargs sed -i 's/windows/linux/g'
- 菜鸟学习Spring——60s使用annotation实现简单AOP
一.概述. AOP大家都知道切面编程,在Spring中annotation可以实现简单的AOP列子.下面还未大家介绍几个概念: Aspect 对横切性关注点的模块化. Advice 对横切性关注点的具 ...
- [Java][RCP] 记 ProgressView的使用
进度条效果图
- SQLite数据库与Contentprovider(2)
ContentProvider: 在创建ContentProvider时,需要首先使用数据库.文件系统或网络实现底层存储功能, 然后在继承ContentProvider的类中实现基本数据操作的接口函数 ...
- linux清除swap
执行top会显示Cpu(s): 0.7%us, 0.3%sy, 0.0%ni, 99.0%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%stMem: 2044500 ...
- iOS学习之C语言循环结构
一.while循环 while (循环条件) { 循环体: } // 1.定义循环变量 int time = 1; // 2.循环条件 while ( ...
- Linux下安装MySQLdb模块
1,查看是否已安装MySQLdb模块 进入python的命令行,输入 import MySQLdb 如果没有报错,证明此模块已经安装,可以跳过以下步骤. 2,下载最新的MySQLdb安装包: wget ...
- vhdl基础---分频
偶数分频 ibrary IEEE; use IEEE.STD_LOGIC_1164.ALL; use ieee.std_logic_arith; use ieee.std_logic_unsigned ...
- Android实现Button事件的处理
Android实现Button事件的处理 开发工具:Andorid Studio 1.3 运行环境:Android 4.4 KitKat 代码实现 首先是最基本的线性布局,给每个控件设立id值,以供代 ...
- 触摸屏校准tslib的配置文件
./autogen.sh#sleep 10./configure --prefix=/usr/lxl/tslib --host=arm-linux CC=arm-linux-gcc#sleep 100 ...