笔者介绍:姜雪伟,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. natapp搭建外网服务器

    首先在natapp官网注册一个账号:https://natapp.cn/ 注册好后登陆网站,点击左侧菜单中的购买隧道: 点击免费隧道后进入隧道配置页面,我这里已经配置好了直接展示如下: 点击保存后点击 ...

  2. CommonCode升级:把不常用的Sqlite独立出去

    CommonCode大概一年多没有更新了,今天碰到一件闹心的事情,结果用一行代码解决了京东购物车信息提取工具.一不小心,把一贯以来的一个念头又给惹起来了:就是把程序做成又给独立的exe,不要这么多dl ...

  3. [Algorithm] Radix Sort Algorithm

    For example we have the array like this: [, , , , , ] First step is using Counting sort for last dig ...

  4. 直接修改class文件内容即使是文本会导致App异常,正确方式是修改java再用生成的class替换掉原有的class

    前几天来了个小任务,把某项目中某人的邮件地址改了下. 由于对项目不熟悉,于是采用find方式找出app中所有包含某人邮件地址的文件都找出来了. xml,properties大约三四个,还有两个clas ...

  5. lua接收图片并进行md5处理

    需要luacurl(http://luacurl.luaforge.net/)和MD5两个库函数 curl = require("luacurl") require("m ...

  6. 使用GCD创建单例

    + (KKTextHUB *)sharedTextHUB { static KKTextHUB *sharedHub = nil; static dispatch_once_t onceToken; ...

  7. FFmpeg源码结构图 - 编码

    ===================================================== FFmpeg的库函数源码分析文章列表: [架构图] FFmpeg源码结构图 - 解码 FFm ...

  8. 解决 Maven was cached in the local repository, resolution will not be reattempted until the update interv

    问题原因 Maven默认会使用本地缓存的库来编译工程,对于上次下载失败的库,maven会在~/.m2/repository/<group>/<artifact>/<ver ...

  9. 安装 启动sshd服务:

    .先确认是否已安装ssh服务: [root@localhost ~]# rpm -qa | grep openssh-server openssh-server-.3p1-.fc12.i686 (这行 ...

  10. Java类(继承)初始化顺序

    /** * Created by xfyou on 2016/11/2. * Java继承的初始化 */ public class Beetle extends Insect { int k = pr ...