笔者介绍:姜雪伟,IT公司技术合伙人。IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者,国家专利发明人;已出版书籍:《手把手教你架构3D游戏引擎》电子工业出版社和《Unity3D实战核心技术具体解释》电子工业出版社等。

CSDN视频网址:http://edu.csdn.net/lecturer/144

在使用引擎开发产品时,我们常常会使用环境光作为游戏场景的太阳光使用。环境光照是我们添加场景整体光照中的一个固定光照常量。它被用来模拟光的散射(Scattering)。在现实中。光线会以随意方向散射,它的强度是会一直改变的。所以间接被照到的那部分场景也应该有变化的强度,而不是一成不变的环境光。

当中一种间接光照的模拟叫做环境光遮蔽(Ambient Occlusion)。它的原理是通过将褶皱、孔洞和非常靠近的墙面变暗的方法近似模拟出间接光照。这些区域非常大程度上是被周围的几何体遮蔽的,光线会非常难流失,所以这些地方看起来会更暗一些。

站起来看一看你房间的拐角或者是褶皱。是不是这些地方会看起来有一点暗?

以下这幅图展示了在使用和不使用SSAO时场景的不同。特别注意对照褶皱部分。你会发现(环境)光被遮蔽了很多:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvanh3MTY3/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" />

虽然这不是一个非常明显的效果,启用SSAO的图像确实给我们更真实的感觉。这些小的遮蔽细节给整个场景带来了更强的深度感。

环境光遮蔽这一技术会带来非常大的性能开销,由于它还须要考虑周围的几何体。我们能够对空间中每一点发射大量光线来确定其遮蔽量,可是这在实时运算中会非常快变成大问题。在2007年。Crytek公司公布了一款叫做屏幕空间环境光遮蔽(Screen-Space Ambient Occlusion, SSAO)的技术。并用在了他们的看家作孤岛危机上。这一技术使用了屏幕空间场景的深度而不是真实的几何体数据来确定遮蔽量。

这一做法相对于真正的环境光遮蔽不但速度快,并且还能获得非常好的效果,使得它成为近似实时环境光遮蔽的标准。

SSAO背后的原理非常easy:对于铺屏四边形(Screen-filled Quad)上的每个片段。我们都会依据周边深度值计算一个遮蔽因子(Occlusion Factor)。这个遮蔽因子之后会被用来降低或者抵消片段的环境光照分量。遮蔽因子是通过採集片段周围球型核心(Kernel)的多个深度样本。并和当前片段深度值对照而得到的。

高于片段深度值样本的个数就是我们想要的遮蔽因子。

上图中在几何体内灰色的深度样本都是高于片段深度值的,他们会添加遮蔽因子;几何体内样本个数越多,片段获得的环境光照也就越少。

非常明显,渲染效果的质量和精度与我们採样的样本数量有直接关系。假设样本数量太低,渲染的精度会急剧降低,我们会得到一种叫做波纹(Banding)的效果。假设它太高了,反而会影响性能。

我们能够通过引入随机性到採样核心(Sample Kernel)的採样中从而降低样本的数目。

通过随机旋转採样核心。我们能在有限样本数量中得到高质量的结果。

然而这仍然会有一定的麻烦。由于随机性引入了一个非常明显的噪声图案,我们将须要通过模糊结果来修复这一问题。以下这幅图片展示了波纹效果还有随机性造成的效果:

你能够看到,虽然我们在低样本数的情况下得到了非常明显的波纹效果,引入随机性之后这些波纹效果就全然消失了。

Crytek公司开发的SSAO技术会产生一种特殊的视觉风格。

由于使用的採样核心是一个球体。它导致平整的墙面也会显得灰蒙蒙的,由于核心中一半的样本都会在墙这个几何体上。以下这幅图展示了孤岛危机的SSAO。它清晰地展示了这样的灰蒙蒙的感觉:

由于这个原因,我们将不会使用球体的採样核心,而使用一个沿着表面法向量的半球体採样核心。

通过在法向半球体(Normal-oriented Hemisphere)周围採样,我们将不会考虑到片段底部的几何体.它消除了环境光遮蔽灰蒙蒙的感觉,从而产生更真实的结果。

SSAO须要获取几何体的信息。由于我们须要一些方式来确定一个片段的遮蔽因子。对于每个片段。我们将须要这些数据: - 逐片段位置向量 - 逐片段的法线向量 - 线性深度纹理 - 採样核心 - 用来旋转採样核心的逐片段随机旋转矢量

通过使用一个逐片段观察空间位置,我们能够将一个採样半球核心对准片段的观察空间表面法线。

对于每个核心样本我们会採样线性深度纹理来比較结果。採样核心会依据旋转矢量略微偏转一点;我们所获得的遮蔽因子将会之后用来限制终于的环境光照分量。

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvanh3MTY3/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" />

由于SSAO是一种屏幕空间技巧,我们对铺屏2D四边形上每个片段计算这一效果;也就是说我们没有场景中几何体的信息。

我们能做的仅仅是渲染几何体数据到屏幕空间纹理中,我们之后再会将此数据发送到SSAO着色器中,之后我们就能訪问到这些几何体数据了。

假设你看了前面一篇教程,你会发现这和延迟渲染非常类似。

这也就是说SSAO和延迟渲染能完美地兼容,由于我们已经存位置和法线向量到G缓冲中了。

由于我们已经有了逐片段位置和法线数据(G缓冲中),我们仅仅须要更新一下几何着色器,让它包括片段的线性深度即可了。回顾我们在深度測试那一节学过的知识,我们能够从gl_FragCoord.z中提取线性深度:

#version 330 core
layout (location = 0) out vec4 gPositionDepth;
layout (location = 1) out vec3 gNormal;
layout (location = 2) out vec4 gAlbedoSpec; in vec2 TexCoords;
in vec3 FragPos;
in vec3 Normal; const float NEAR = 0.1; // 投影矩阵的近平面
const float FAR = 50.0f; // 投影矩阵的远平面
float LinearizeDepth(float depth)
{
float z = depth * 2.0 - 1.0; // 回到NDC
return (2.0 * NEAR * FAR) / (FAR + NEAR - z * (FAR - NEAR));
} void main()
{
// 储存片段的位置矢量到第一个G缓冲纹理
gPositionDepth.xyz = FragPos;
// 储存线性深度到gPositionDepth的alpha分量
gPositionDepth.a = LinearizeDepth(gl_FragCoord.z);
// 储存法线信息到G缓冲
gNormal = normalize(Normal);
// 和漫反射颜色
gAlbedoSpec.rgb = vec3(0.95);
}

提取出来的线性深度是在观察空间中的,所以之后的运算也是在观察空间中。确保G缓冲中的位置和法线都在观察空间中(乘上观察矩阵也一样)。观察空间线性深度值之后会被保存在gPositionDepth颜色缓冲的alpha分量中,省得我们再声明一个新的颜色缓冲纹理。

gPositionDepth颜色缓冲纹理被设置成了以下这样:

glGenTextures(1, &gPositionDepth);
glBindTexture(GL_TEXTURE_2D, gPositionDepth);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGBA, GL_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

这给了我们一个线性深度纹理,我们能够用它来对每个核心样本获取深度值。注意我们把线性深度值存储为了浮点数据;这样从0.1到50.0范围深度值都不会被限制在[0.0, 1.0]之间了。假设你不用浮点值存储这些深度数据。确保你首先将值除以FAR来标准化它们,再存储到gPositionDepth纹理中。并在以后的着色器中用类似的方法重建它们。

相同须要注意的是GL_CLAMP_TO_EDGE的纹理封装方法。这保证了我们不会不小心採样到在屏幕空间中纹理默认坐标区域之外的深度值。

接下来我们须要真正的半球採样核心和一些方法来随机旋转它。

OpenGL核心之SSAO技术解说(一)的更多相关文章

  1. 【OpenGL游戏开发之三】OpenGl核心函数库汇总

    OpenGl核心函数库 glAccum 操作累加缓冲区 glAddSwapHintRectWIN 定义一组被SwapBuffers拷贝的三角形 glAlphaFunc允许设置alpha检测功能 glA ...

  2. spring核心及常用技术

    一.核心内容 1.依赖注入(控制反转) 1)什么是依赖注入 spring将实例的创建交给spring容器(BeanFactory或ApplicationContext)管理,当实例创建后通过设值或构造 ...

  3. windows核心编程 DLL技术 【转】

    注:本文章转载于网络,源地址为:http://blog.csdn.net/ithzhang/article/details/7051558 本篇文章将介绍DLL显式链接的过程和模块基地址重定位及模块绑 ...

  4. OpenGL核心技术之混合技术

    笔者介绍:姜雪伟,IT公司技术合伙人,IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者.国家专利发明人;已出版书籍:<手把手教你架构3D游戏引擎>电子工业出版社和<Unity3D ...

  5. JAVA智能设备基于OpenGL的3D开发技术 之AABB碰撞检测算法论述

    摘要:无论是PC机的3D还是智能设备应用上,碰撞检测始终是程序开发的难点,甚至可以用碰撞检测作为衡量3D引擎是否完善的标准.现有许多3D碰撞检测算法,其中AABB碰撞检测是一种卓有成效而又经典的检测算 ...

  6. OpenGL中的光照技术(翻译)

    Lighting:https://www.evl.uic.edu/julian/cs488/2005-11-03/index.html 光照 OpenGL中的光照(Linghting)是很重要的,为什 ...

  7. hibernate核心及常用技术

    一.hibernate介绍 1.hibernate概述 hibernate是轻量级Java EE持久层解决方案,管理java类到数据库表的映射(ORM:对象关系型数据映射),并提供数据查询获取的方法. ...

  8. OpenGL核心之视差映射

    笔者介绍:姜雪伟,IT公司技术合伙人.IT高级讲师,CSDN社区专家,特邀编辑.畅销书作者;已出版书籍:<手把手教你¯的纹理坐标偏移T3来对fragment的纹理坐标进行位移.你能够看到随着深度 ...

  9. Web核心之会话技术Cookie&Session

    什么是会话技术? http协议是无状态协议.为了满足在多次请求之间数据进行交互,推出了会话技术. 会话概念:一次会话,指的是从客户端和服务器建立起连接开始,到客户端或服务器断开连接为止.中间可能进行多 ...

随机推荐

  1. Redis自学笔记—PHP

    connect 实例连接到一个Redis. $redis = new redis(); $result = $redis->connect('127.0.0.1', 6379); var_dum ...

  2. 【C/C++】:用C实现输出日期的阴历日子

    前言 输出阴历一直是个老大难的问题.由于阴历日子没有规律.所以这里须要做的就是通过打表的算法做到输出阴历日子,可是非常多人都不太了解原理,我这里就给大家送上了一个福利.把自己做好的基于打表的阴历的日子 ...

  3. 关于COM的Unicode string的精彩论述

    I need to make a detour for a few moments, and discuss how to handle strings in COM code. If you are ...

  4. 代码生成器的关键代码(读取PDM文件)

    /// <summary> /// 处理PDM文件 /// </summary> public class DoPDMDal:IDoDataBaseDal { public L ...

  5. Inside GDALAllRegister之四: 跳过driver

    这个函数很短小: /** * \brief This method unload undesirable drivers. * * All drivers specified in the space ...

  6. web中的安全编码

    个人记录 一.Web安全验证 输入验证 防范跨站脚本XSS攻击 防止SQL注入 图片验证码 二.输入验证 经典的安全法则:永远不要相信用户提交的数据 验证内容: 用户名,密码等格式 验证长度防止数据库 ...

  7. serialport控件的详细用法

    http://www.cnblogs.com/jerry-bian/archive/2012/01/10/2317861.html 最近在做通讯协议,关于SerialPort类 DataReceive ...

  8. Linux高级权限管理

    传统的UGO(rwx-wx-wx)权限模型,无法解决当多个组需要对一个文件执行某些权限的问题. ACL :访问控制列表access control list一种高级的权限机制,允许我们对文件或者文件夹 ...

  9. js replace 如何替换字符串中的最后一个匹配项

    1.正则表达时,贪婪模式,.*会一直匹配到最后一个 // 验证 let str = "123[空]345[空]789[空]0"; let res = str.replace(/(. ...

  10. ElasticSearch异常归纳(能力工场小马哥)

    异常1: can not run elasticsearch as root [WARN ][o.e.b.ElasticsearchUncaughtExceptionHandler] [node-2] ...