Global Illumination_Screen-Space Directional Occlusion(SSDO)

之前我们了解过AO、SSAO、HBAO(可参照之前文章Vulkan_SSAO—屏幕空间环境光遮蔽DirectX11进阶9_AO、SSAO、Particle System(GPU)Global Illumination_Horizon-Based Ambient Occlusion(HBAO))。

我们本次来看一下一种基于屏幕空间的全局光照算法。SSAO因为与SSDO、HBAO极其相似,仅是在最后一步时充分利用了随机查询点处的信息来模拟间接光照。
在这里插入图片描述

一、基于屏幕空间的间接光照算法

在这里插入图片描述

从上图(左数前两个)看出,对于着色点p在生成随机的点ABCD时,只有处于实际物体下方的点(A,B,D)都是用来计算SSAO的,算法认为可以把处于实际物体上方的点(C点)作为虚拟点光源(VPL)用于计算着色点p的间接光照。

从上图(左数第三个)看出看出,本算法也是存在一定缺陷的:比如A点实际未在物体内部,但因为深度遮挡关系导致改点会出错,B点虽然未被遮挡,但却无法接收环境光的光照效果。虽然有这些问题但问题不大,毕竟CG的一大定律:看着没问题就行。

在这里插入图片描述
其中,

  • di 为随机点 pi 到着色点 p 的距离;

  • 如果随机点 pi 在实际物体下方 ,则 L 返回0;[公式]

  • Qsi 为直接光源方向与随机点 pi 的夹角;

  • Qri 为随机点 pi 到着色点 p 向量与着色点 p 法线的夹角;

  • As 为 (当然这个公式这是一个参考,可以根据具体情况对公式进行改变):
    在这里插入图片描述

二、算法实现

很简单基于延迟渲染,下边简述其shader流程:

2.1 G-Buffer提取

顶点着色器:

#version 430 core
layout(location = 0) in vec3 _Position;
layout(location = 1) in vec3 _Normal;
layout(location = 2) in vec2 _TexCoord;

layout(std140, binding = 0) uniform u_Matrices4ProjectionWorld
{
	mat4 u_ProjectionMatrix;
	mat4 u_ViewMatrix;
};

uniform mat4 u_ModelMatrix;

out vec2 v2f_TexCoords;
out vec3 v2f_Normal;
out vec3 v2f_FragPosInViewSpace;

void main()
{
	vec4 FragPosInViewSpace = u_ViewMatrix * u_ModelMatrix * vec4(_Position, 1.0f);
	gl_Position = u_ProjectionMatrix * FragPosInViewSpace;
	v2f_TexCoords = _TexCoord;
	v2f_Normal = normalize(mat3(transpose(inverse(u_ViewMatrix * u_ModelMatrix))) * _Normal);	
	v2f_FragPosInViewSpace = FragPosInViewSpace.xyz;
}

片元着色器:

#version 430 core

in  vec3 v2f_FragPosInViewSpace;
in  vec2 v2f_TexCoords;
in  vec3 v2f_Normal;

layout (location = 0) out vec3 Albedo_;
layout (location = 1) out vec3 Normal_;
layout (location = 2) out vec4 Position_;

uniform sampler2D u_DiffuseTexture;
uniform vec4 u_DiffuseColor;
uniform float u_Near = 0.1;
uniform float u_Far = 1000.0f;
uniform vec3 u_LightPos = vec3(-18.5264, 19.4874, 78.5421);
//暂未用,多光源时处理
layout(std140, binding = 0) uniform u_Matrices4ProjectionWorld
{
	mat4 u_ProjectionMatrix;
	mat4 u_ViewMatrix;
};

float LinearizeDepth(float vDepth)
{
    float z = vDepth * 2.0 - 1.0; 
    return (2.0 * u_Near * u_Far) / (u_Far + u_Near - z * (u_Far - u_Near));    
}
void main()
{
	Position_ = vec4(v2f_FragPosInViewSpace.xyz,LinearizeDepth(gl_FragCoord.z));
	Normal_ = normalize(v2f_Normal);
	Albedo_ = texture(u_DiffuseTexture, v2f_TexCoords).xyz ;
}

2.2 SSAO计算

常规SSAO计算流程,不再赘述,想具体了解的看一下之前的详细教程。

顶点着色器:

#version 430 core

layout (location = 0) in vec2 _Position;
layout (location = 1) in vec2 _TexCoords;

out vec2 v2f_TexCoords;

void main()
{
	gl_Position = vec4(_Position, 0.0, 1.0);
	v2f_TexCoords = _TexCoords;
}

片元着色器:

#version 430 core

in  vec2 v2f_TexCoords;
layout (location = 0) out float FragColor_;

uniform sampler2D u_PositionTexture;
uniform sampler2D u_NormalTexture;
uniform sampler2D u_NoiseTexture;
uniform float u_Near = 0.1;
uniform float u_Far = 100.0f;
uniform float u_WindowWidth;
uniform float u_WindowHeight;
uniform vec3 u_Samples[64];

int KernelSize = 64;
float Radius = 1.0;

layout(std140, binding = 0) uniform u_Matrices4ProjectionWorld
{
	mat4 u_ProjectionMatrix;
	mat4 u_ViewMatrix;
};

float LinearizeDepth(float vDepth)
{
    float z = vDepth * 2.0 - 1.0; 
    return (2.0 * u_Near * u_Far) / (u_Far + u_Near - z * (u_Far - u_Near));    
}

void main()
{
	vec2 NoiseScale = vec2(u_WindowWidth/4.0f, u_WindowHeight/4.0f);
    vec3 FragPos = texture(u_PositionTexture, v2f_TexCoords).xyz;
    vec3 Normal = texture(u_NormalTexture, v2f_TexCoords).rgb;
    vec3 RandomVec = texture(u_NoiseTexture, NoiseScale * v2f_TexCoords).xyz;
    vec3 Tangent = normalize(RandomVec - Normal * dot(RandomVec, Normal));
    vec3 Bitangent = cross(Normal, Tangent);
    mat3 TBN = mat3(Tangent, Bitangent, Normal);
    float Occlusion = 0.0;
    for(int i = 0; i < KernelSize; ++i)
    {
        vec3 samples = TBN * u_Samples[i]; 
        samples = FragPos + samples * Radius; 
        vec4 Offset = vec4(samples, 1.0);
        Offset = u_ProjectionMatrix * Offset; 
        Offset.xyz /= Offset.w; 
        Offset.xyz = Offset.xyz * 0.5 + 0.5; 
        
        float SampleDepth = -texture(u_PositionTexture, Offset.xy).w; 
        
        float RangeCheck = smoothstep(0.0, 1.0, Radius / abs(FragPos.z - SampleDepth ));
        Occlusion += (SampleDepth >= samples.z ? 1.0 : 0.0) * RangeCheck;           
    }
    Occlusion = 1.0 - (Occlusion / KernelSize);
	FragColor_  = Occlusion;
}

在这里插入图片描述

具体的滤波操作与下面的SSDO一样便不再赘述,仅见下图:
在这里插入图片描述

2.3 SSDO计算

顶点着色器:

#version 430 core

layout (location = 0) in vec2 _Position;
layout (location = 1) in vec2 _TexCoords;

out vec2 v2f_TexCoords;

void main()
{
	gl_Position = vec4(_Position, 0.0, 1.0);
	v2f_TexCoords = _TexCoords;
}

片元着色器:

#version 430 core

in  vec2 v2f_TexCoords;
layout (location = 0) out float FragColor_;

uniform sampler2D u_PositionTexture;
uniform sampler2D u_NormalTexture;
uniform sampler2D u_NoiseTexture;
uniform float u_Near = 0.1;
uniform float u_Far = 100.0f;
uniform float u_WindowWidth;
uniform float u_WindowHeight;
uniform vec3 u_Samples[64];

int KernelSize = 64;
float Radius = 1.0;

layout(std140, binding = 0) uniform u_Matrices4ProjectionWorld
{
	mat4 u_ProjectionMatrix;
	mat4 u_ViewMatrix;
};

float LinearizeDepth(float vDepth)
{
    float z = vDepth * 2.0 - 1.0; 
    return (2.0 * u_Near * u_Far) / (u_Far + u_Near - z * (u_Far - u_Near));    
}

void main()
{
	vec2 NoiseScale = vec2(u_WindowWidth/4.0f, u_WindowHeight/4.0f);
    vec3 FragPos = texture(u_PositionTexture, v2f_TexCoords).xyz;
    vec3 Normal = texture(u_NormalTexture, v2f_TexCoords).rgb;
    vec3 RandomVec = texture(u_NoiseTexture, NoiseScale * v2f_TexCoords).xyz;
    vec3 Tangent = normalize(RandomVec - Normal * dot(RandomVec, Normal));
    vec3 Bitangent = cross(Normal, Tangent);
    mat3 TBN = mat3(Tangent, Bitangent, Normal);
    float Occlusion = 0.0;
    for(int i = 0; i < KernelSize; ++i)
    {
        vec3 samples = TBN * u_Samples[i]; 
        samples = FragPos + samples * Radius; 
        vec4 Offset = vec4(samples, 1.0);
        Offset = u_ProjectionMatrix * Offset; 
        Offset.xyz /= Offset.w; 
        Offset.xyz = Offset.xyz * 0.5 + 0.5; 
        
        float SampleDepth = -texture(u_PositionTexture, Offset.xy).w; 
        
        float RangeCheck = smoothstep(0.0, 1.0, Radius / abs(FragPos.z - SampleDepth ));
        Occlusion += (SampleDepth >= samples.z ? 1.0 : 0.0) * RangeCheck;           
    }
    Occlusion = 1.0 - (Occlusion / KernelSize);
	FragColor_  = Occlusion;
}

此部分为算法主要实现,根据首先判断点的贡献度,然后根据点类型作为次级光源贡献到着色点。
在这里插入图片描述

2.3 滤波

顶点着色器:

#version 430 core

layout (location = 0) in vec2 _Position;
layout (location = 1) in vec2 _TexCoords;

out vec2 v2f_TexCoords;

void main()
{
	gl_Position = vec4(_Position, 0.0, 1.0);
	v2f_TexCoords = _TexCoords;
}

片元着色器:

#version 430 core

in  vec2 v2f_TexCoords;
layout (location = 0) out float FragColor_;

uniform sampler2D u_SSAOTexture;


void main()
{
    vec2 TexelSize = 1.0 / vec2(textureSize(u_SSAOTexture, 0));
    float Result = 0.0;
    for (int x = -2; x < 2; ++x) 
    {
        for (int y = -2; y < 2; ++y) 
        {
            vec2 Offset = vec2(float(x), float(y)) * TexelSize;
            Result += texture(u_SSAOTexture, v2f_TexCoords + Offset).r;
        }
    }
    FragColor_ = Result / (4.0 * 4.0);
}

在这里插入图片描述

2.4 显示

顶点着色器:

#version 430 core

layout (location = 0) in vec2 _Position;
layout (location = 1) in vec2 _TexCoords;

out vec2 v2f_TexCoords;

void main()
{
	gl_Position = vec4(_Position, 0.0, 1.0);
	v2f_TexCoords = _TexCoords;
}

片元着色器:

#version 430 core

in  vec2 v2f_TexCoords;
out vec4 Color_;

uniform sampler2D u_SSAOTexture;
uniform sampler2D u_Albedo;
uniform sampler2D u_SSDOTexture;
uniform sampler2D u_DirectLightSSDOTexture;

void main()
{
	float Ambient = (texture(u_SSAOTexture, v2f_TexCoords, 0).r);
	vec3 Albedo = texture(u_Albedo, v2f_TexCoords).rgb;
	Color_ = vec4( texture(u_SSDOTexture, v2f_TexCoords).rgb,1);
}

直接来看一下环境光部分:
在这里插入图片描述
再看一下直接使用SSAO的效果:
在这里插入图片描述
最后看一下SSDO的效果:
在这里插入图片描述

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值