SSAO shader

#version 330 core
out float FragColor;
in vec2 TexCoords;

uniform sampler2D gPositionDepth;	// view空间中的pos
uniform sampler2D gNormal;			// view空间中的normal
uniform sampler2D texNoise;			// noise是4x4的随机旋转向量纹理

uniform vec3 samples[64];

// parameters (you'd probably want to use them as uniforms to more easily tweak the effect)
int kernelSize = 64;
float radius = 1.0;

// tile noise texture over screen based on screen dimensions divided by noise size
const vec2 noiseScale = vec2(800.0f/4.0f, 600.0f/4.0f); 

uniform mat4 projection;

void main()
{
    // Get input for SSAO algorithm
    vec3 fragPos = texture(gPositionDepth, TexCoords).xyz;
    vec3 normal = texture(gNormal, TexCoords).rgb;
    vec3 randomVec = texture(texNoise, TexCoords * noiseScale).xyz;
    
    // Create TBN change-of-basis matrix: from tangent-space to view-space
	// 因为这里的normal是view space中的,因此算出来的TBN矩阵是从切线空间->视图空间
    vec3 tangent = normalize(randomVec - normal * dot(randomVec, normal));
    vec3 bitangent = cross(normal, tangent);
    mat3 TBN = mat3(tangent, bitangent, normal);
    
    // Iterate over the sample kernel and calculate occlusion factor
    float occlusion = 0.0;
    for(int i = 0; i < kernelSize; ++i)
    {
        // get sample position
        vec3 sample = TBN * samples[i]; // From tangent to view-space
        sample = fragPos + sample * radius; 
        
        // 把视图空间中的采样点位置坐标投影到屏幕上,得到他在屏幕空间中的坐标
        vec4 offset = vec4(sample, 1.0);
        offset = projection * offset; // from view to clip-space
        offset.xyz /= offset.w; // perspective divide
        offset.xyz = offset.xyz * 0.5 + 0.5; // transform to range 0.0 - 1.0
        
        // 获取采样点所位于的像素位置上的深度值
        float sampleDepth = -texture(gPositionDepth, offset.xy).w; // Get depth value of kernel sample
        
        // range check & accumulate
        float rangeCheck = smoothstep(0.0, 1.0, radius / abs(fragPos.z - sampleDepth ));
        occlusion += (sampleDepth >= sample.z ? 1.0 : 0.0) * rangeCheck;           
    }
    occlusion = 1.0 - (occlusion / kernelSize);
    
    FragColor = occlusion;
}

在 SSAO 算法中,我们生成了若干个随机采样点,比如上面的64个samples,虽然这些samples是随机的,但是却每个像素都相同,这样效果并不好,因此可以通过引入随机旋转向量,使每个像素在计算时,把采样点们绕着法线旋转随机角度来引入一些随机性。这里的随机旋转向量是用4x4的纹理来存放的(texNoise,4x4的纹理 16个随机旋转向量基本够用,弄个800x600的全部都是随机的旋转向量没必要,代价太大,效果提升很小。 ),并传入ssao过程的shader中。这些随机旋转向量通常是在切线空间中定义的,每个像素在计算时都从noise纹理中采样一个随机旋转向量randomVec并且用它跟视图空间中的normal来构造一个切线空间,计算获得TBN矩阵,然后每个像素采样得到的旋转向量是不同的 因此TBN是不太一样的。这样,在把samples 转换到 视图空间的时候,分布就会不太一样。

TBN矩阵:我们平常来说是通过模型的模型空间中的normal跟模型空间的顶点坐标、纹理坐标计算得到的Trangent向量来生成的TBN矩阵,这个TBN矩阵是把切线空间中的向量转到模型空间的。而在SSAO里,构造的这个TBN矩阵是用的view空间的normal和view空间的一个垂直于法线的向量来构造的,因此该TBN矩阵作用是实现切线空间到view空间的转换

在Unity3D中,常用的屏幕空间环境光遮蔽(Screen Space Ambient Occlusion,SSAO)算法是通过采样深度图来估计场景中每个像素的遮蔽量。下面是一个基本的SSAO Shader算法示例: ```shader Shader "Custom/SSAOShader" { Properties { _MainTex ("Texture", 2D) = "white" {} _DepthTex ("Depth Texture", 2D) = "white" {} _SampleRadius("Sample Radius", Range(0, 10)) = 1 _Intensity("Intensity", Range(0, 10)) = 1 _Bias("Bias", Range(0, 0.1)) = 0.02 } SubShader { Tags { "RenderType"="Opaque" } LOD 200 CGPROGRAM #pragma surface surf Lambert sampler2D _MainTex; sampler2D _DepthTex; float _SampleRadius; float _Intensity; float _Bias; struct Input { float2 uv_MainTex; float2 screenPos; }; void surf (Input IN, inout SurfaceOutput o) { // Sample the depth texture float depth = tex2D(_DepthTex, IN.uv_MainTex).r; // Compute ambient occlusion float ao = 0.0; float radius = _SampleRadius / depth; float samples = 16.0; for (float i = 0.0; i < samples; i++) { float angle = i * (6.28318530718 / samples); float2 offset = float2(cos(angle), sin(angle)) * radius; float sampleDepth = tex2D(_DepthTex, IN.uv_MainTex + offset).r; ao += step(sampleDepth, depth + _Bias); } ao /= samples; ao = 1.0 - (_Intensity * ao); // Apply ambient occlusion to the surface output fixed4 c = tex2D(_MainTex, IN.uv_MainTex); o.Albedo = c.rgb * ao; o.Alpha = c.a; } ENDCG } FallBack "Diffuse" } ``` 在这个示例中,我们首先采样深度图来获取当前像素的深度值。然后,我们使用环形采样模式在周围的像素中进行采样,并将当前像素与采样像素的深度值进行比较。如果采样像素的深度值大于当前像素的深度值加上一个偏差值(即遮蔽物体边缘),则将遮蔽值增加。最后,通过对采样值取平均值,并根据强度参数进行缩放,得到最终的环境光遮蔽值。 请注意,这只是一个基本的SSAO算法示例,可能需要根据具体需求进行调整和优化。例如,您可以尝试调整采样半径、偏差和采样数量来获得更好的效果。此外,还可以使用随机采样模式或更复杂的采样模式来改进SSAO算法。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

宗浩多捞

您的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值