GraphicsLab Project之Screen Space Ambient Occlusion(SSAO)

作者:i_dovelemon

日期:2017 / 03 / 4

来源:CSDN

主题:SSAO



引言



        GraphicsLab项目已经进行了一段时间,期间经过了几次代码重构,现在的框架要比我初次编写的框架更加容易让我实验一些新的算法。今天,就来和大家讨论下最近集成到框架里面的一个新算法:Screen Space Ambient Occlusion。

        文章中将只会提出使用OpenGL的哪种技术来实现,不会给出具体的使用OpenGL指令的方式,这些内容可以自行通过网络搜索到,或者可以查看 GraphicsLab项目的源码来了解。


Ambient Occlusion



        在讲解具体的算法之前,我们先来了解下什么是Ambient Occlusion(AO)。我们知道,在实时渲染当中,光照模型主要是以Diffuse+Specular+Ambient的形式来进行光照计算。而其中的Ambient项主要模拟的是从环境中反射到物体上的光照。对于实时渲染来说,这种由周围环境反射到物体上的光照很难精确的计算出来,所以在光照模型中,Ambient项对于一个模型来说就是一个固定的颜色。也就是说,如果在不考虑Diffuse和Specular的情况下,仅仅使用Ambient来对物体进行着色的话,你会得到类似如下所示的一种很扁平的光照结果:



        从上图可以看到,对于只有简单Ambient着色的物体来说,就是一个扁平的色块,没有任何的明暗对比,所以看不出来任何的立体效果。但是,在现实世界中,由于物体本身的遮挡关系,必然会导致有些地方接受到的光要少,有些地方较多,也就是会形成一种明暗对比关系,而Ambient Occlusion就是为了体现这种由于遮挡而产生明暗对比关系的效果而提出的。同样的图片,在经过AO处理之后(即本文将要介绍的SSAO算法),将会更加有立体的感觉:




Screen Space Ambeint Occlusion



        传统的AO计算方法,是通过对模型上每一个点投射多个射线的方法,并以此来统计哪些射线被遮挡,哪些射线指向了空白区域。但是这种方法计算量过高,会随着场景复杂度的提高而提高,无法在实时计算中使用。Crytek提出的该算法,它仅仅依靠深度信息,在屏幕空间计算屏幕上每一个像素的AO值,生成一张屏幕空间的AO贴图,当我们在渲染物体的时候,就可以在这张贴图上查找对应像素的AO值。


AO计算方法



        SSAO算法是对中心像素的周围进行采样,然后判断哪些采样像素会遮挡中心像素,哪些不会遮挡。下图即这个过程的示意图:



        上图所示,我们要计算点P的AO值,可以在一定区域内采样指定数目的深度值,然后判断:采样深度值大于点P的时候,表示该采样点不会遮挡点P,反之则会遮挡。通过未遮挡采样像素的数目占总采样像素的比例来决定点P的AO值(这里是很粗糙的做法,读者自己实现的时候,可以加上距离衰减之类的控制)。

        通过上面的描述,我们就大体的知道了如果计算每一个屏幕空间中像素的AO值。而该计算的条件是:一张Depth Map,一种采样内核。下面,我们就分别来讲述下,如何获取这些信息。


Depth Map



        首先要获得Depth Map,我们需要有一种能够渲染到纹理的技术,该技术可以通过OpenGL中的FBO来实现。我们创建一张深度纹理,然后将该纹理绑定到FBO上。然后,我们就可以简单的绘制下场景,这个时候绘制的场景我们仅仅需要的是Depth信息,所以可以关闭对Color Buffer的写入。当关闭对Color Buffer的写入的时候,系统会以更加快速的方式来绘制。除此之外,我们还需要明确的一点是,我们在Depth Map中,保存的不是NDC下的Z坐标,而是在Camera Space中的Z坐标值。由于在Camera Space中,物体的Z值是线性变化的,而在NDC中1/Z值是线性变化的,为了方便以后我们进行深度值的比较,可以保存下Camera Space中的Z坐标值。

        下面我们一一的来给出相应的代码,首先是创建深度图,创建FBO,然后绑定FBO的代码:

void RenderImp::PrepareDepthMap() {
    // Create depth map
    texture::Texture* depth_map = texture::Texture::CreateFloat16DepthTexture(m_Width, m_Height);
    if (depth_map != NULL) {
        m_DepthMap = texture::Mgr::AddTexture(depth_map);
    } else {
        GLB_SAFE_ASSERT(false);
    }

    // Create render target
    m_DepthTarget = RenderTarget::Create(m_Width, m_Height);
    if (m_DepthTarget != NULL) {
        m_DepthTarget->AttachDepthTexture(depth_map);
    } else {
        GLB_SAFE_ASSERT(false);
    }

    // Create shader
    m_DepthShader = shader::Mgr::AddShader("..\\glb\\shader\\depth.vs", "..\\glb\\shader\\depth.ps");
}

        接下来,我们来看下depth shader的代码:

depth.vs
//----------------------------------------------------
// Declaration: Copyright (c), by i_dovelemon, 2016. All right reserved.
// Author: i_dovelemon[1322600812@qq.com]
// Date: 2017 / 02 / 16
// Brief: Depth map
//----------------------------------------------------
#version 330

in vec3 glb_Pos;

uniform mat4 glb_ProjM;
uniform mat4 glb_ViewM;
uniform mat4 glb_WorldM;

out float vs_DepthInViewSpace;

void main() {
	vec4 pos_in_view_space = glb_ViewM * glb_WorldM * vec4(glb_Pos, 1.0);
	gl_Position = glb_ProjM * pos_in_view_space;
	vs_DepthInViewSpace = pos_in_view_space.z / pos_in_view_space.w;
}

depth.ps
//----------------------------------------------------
// Declaration: Copyright (c), by i_dovelemon, 2017. All right reserved.
// Author: i_dovelemon[1322600812@qq.com]
// Date: 2017 / 02 / 16
// Brief: Depth map
//----------------------------------------------------
#version 330

// Input attributes
in float vs_DepthInViewSpace;

uniform float glb_FarClip;

void main() {
	// Note: In opengl, camera look at -z axis
	// so the depth value in view space is negative value
	gl_FragDepth = -vs_DepthInViewSpace / glb_FarClip;
}

        从shader中我们可以看出,我保存的是Came
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值