本文主要是介绍Ambient Occlusion,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
在以往的光照模型中,我们没有考虑间接光
以往的环境光照模型为: A=La∗ma
La 表示物体接受到的间接光的总量
ma 表示物体的材质,指定了射入环境光的反射和吸收的数量
并且认为光线从各个方向均匀的入射到物体表面
但这种计算模型完全不真实
环境光遮蔽的思想是:物体表面某点 p 的间接光数目是与以 p 为球心的半球对入射光线的遮蔽程度成比例的
(图片来源:Introduction to 3D Game Programming With DirectX 11)
左边的图表示入射光线全部通过半球射入到 p 点上
右边的图表明一部分光线被遮挡未能入射到 p 点上,即 p 点被遮蔽了
在现实中我们知道这样一个现象,墙角处看起来总是比旁边要暗一下,那是因为墙角收到墙面的遮蔽,因此为了模拟这样一个情况,我们需要计算出物体顶点的遮蔽程度,或者说遮蔽值
那么我们怎样去计算每个顶点的遮蔽程度呢?
假设 p 为物体表面上一点,以 p 点为球心构造一个半球,半球的方向为该点法线的方向,无需考虑负半球,其无法对 p 点造成遮蔽。
构造随机的入射光线是比较困难的,我们可以逆向处理,如:由 p 点发出N条光线,若由H条方向被遮蔽了,则我们认为该点的遮蔽程度为 HN ,我们也将接受到的光线比例称为ambient access.因此ambient access = 1.0 - HN
AO计算公式:
A(p,n)=1π∫ΩV(w,p)max(w⋅n,0)dw
p 表示表面上一点
Ω 表示以 p 点为球心的正半球,即法线指向的方向
n 表示 p 点的法线
w 表示半球体 Ω 从 p 点发出光线的方向向量
V 表示可视函数
因此Ambient Occlusion的计算是通过对围绕着以 p 点为球心的半球 Ω 上的局部AO值进行积分得到,由此可见, V(w,p) 是一个二值函数,因为对于任意一条光线,要么对点 p 产生遮蔽要么不产生,因此取值只有0 , 1 。但实现起来这显然是不可能也不必要的。我们只需进行一部分采样来进行模拟得到
假设通过点 p 沿着半球 Ω 发出的一条光线,与物体相交于点 q ,但是假如点 q 离 p 点太远时,我们可以认为点 q 对 p 点并没有产生遮蔽,因此我们可以设置一个阀值(threshold value),当距离大于阀值时,认为不产生遮蔽作用
我们可以在进入着色器之前为每个顶点预先计算出其遮蔽值~~
伪代码如下:
std::vector<UINT> VertexSharedCount(VertexCount); // 用于保存顶点被共享的次数,最终用于计算顶点的平均Ambient Accessfor(int i = 0; i < TriangleCount; i++){int i0 = indices[3 * i + 0];int i1 = indices[3 * i + 1];int i2 = indices[3 * i + 2];Vertex v0 = vertices[i0];Vertex v1 = vertices[i1];Vertex i2 = vertices[i2];Edge e1 = v1 - v0;Edge e2 = v2 - v0;normal = e1 * e2;center = (v0 + v1 + v2) / 3;for(int j = 0; j < SampleLightCount; j++){RayDirection = GenerateRandomVector();if(!octree.RayIntersect(center , normal)){UnOcclusionNum++;}}float access = 1.0f - (UnOcclusionNum / SampleLightCount);vertices[i0].access += access;vertices[i1].access += access;vertices[i2].access += access;VertexSharedCount[i0]++;VertexSharedCount[i1]++;VertexSharedCount[i2]++;}for(int i = 0; i < VertexCount; i++){vertices[i].access /= VertexSharedCount[i];}
这篇关于Ambient Occlusion的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!