OpenGL3.3-SSAO

什么是SSAO

screen space ambient occlusion 屏幕空间环境光遮蔽
环境光主要是让整个环境更亮一点 模拟光线的二次散射 因为phong光照模型中只有环境光 漫反射光 镜面反射光 如果我们取消环境光 并且我们的物体并不是在光线能直接照射的地方 就会发现这个物体完全是黑暗的 所以我们就引入了环境光 但是此时就会有一个问题
我们会发现在任何非光照直射的地方亮度也就是环境光的亮度都是一样的 比如桌子下面和桌子不在光照直射范围的地方 但是很明显 桌子下面应该更暗一些 SSAO就是为了解决这个问题

SSAO核心思想

最核心思想就是逐像素的遍历屏幕空间的像素生成随机点 例如16个 然后将随机点和几何体在视空间比较 看有多少在视空间上 越多就越亮 计算出来的就是环境光遮挡因子
SSAO基于延迟渲染 因为如果使用正向渲染消耗太大
SSAO是在gbuffer之后 真正渲染之前 应该就是几何渲染之后 光照渲染之前
输出也是一个数据纹理
遍历屏幕空间中的每一个像素,并提取每个像素在视图空间下的坐标信息,并在这个坐标点周围随机采样一些点,判断这些随机点位于几何体内部还是外部
如果许多点都是位于几何体内部,这就表示原始像素应该是位于角落中的因而接收到的光照也较少;如果大部分点都位于几何体之外,这就表示原始像素被 “高度曝光” ,因此接收到的光照也较多
所以他要做的就是和gbuff类似的 除了世界坐标 再存一个视图坐标 应该是摄像机坐标 视空间
向着色器程序中传入一个随机向量数组(作为一致变量)并分别将这些向量与当前片元在视图空间下的坐标相加,这样就能得到一系列的随机点
将随机点的 Z 分量值与场景中离相机最近的实际点的 Z 值进行比较
请添加图片描述
那两个小的点就是随机点 而红线就是几何体 我们因为存储的是视图坐标(就是摄像机的视空间 视空间 ) 所以是从摄像机那看过去的 自然就是从摄像机那连一根射线 这个射线和几何体的交点 将随机点和这个交点的z值进行比较 看谁在前面了
但是我们能很容易知道随机点的z值 因为我们是根据像素的坐标随机生成的
但是我们不知道例如 R1和几何体也就是红线的交点的z值 那么我们因为在纹理中存储了视图坐标 所以我们就想从纹理中获取 那我们就需要这个交点在这个纹理图中的纹理坐标 然后通过纹理坐标知道这个点的z值

原文
现在我们先对视图空间的位置坐标纹理的创建做一个快速的回顾。在将顶点坐标从模型的局部坐标系变换到视图坐标系之后,还需要乘上透视投影矩阵(实际上整个变换过程都是通过一个矩阵实现的)。这些都发生在顶点着色器中,在将它们传入到片元着色器时 GPU 会自动进行透视除法来完成整个投影过程。通过投影之后视图空间所有顶点都被投影到了近裁剪面上,而且所有位于视锥体中的顶点的 XYZ 分量都位于(-1,1)的范围之间。在片元着色器中将视图空间中的坐标信息写出到纹理中时(上面的计算只会作用于 gl_Position 中的存放的数据,而要写入到纹理中的数据则是被存放在另一个变量中),当前像素的 XY 分量会被变换到(0,1)的范围中,这个结果就是视图坐标数据被写入到纹理中时的纹理坐标。

上面回顾就是告诉你这个视图坐标是通过了一系列什么被存到了纹理中 变成了纹理坐标 那么我们要做同样的事情将那两个随机点也变成纹理坐标
向着色器程序中传入透视投影矩阵并用这个矩阵将红点和绿点投影到近裁剪面上并手动执行投影除法。之后我们需要将结果变换到 (0,1)的范围中去,这样就能得到纹理坐标。之后我们就能通过采样操作从纹理中获得与射线的相交的顶点的 Z 值,并通过这个值来判断我们生成的随机点是否位于几何体内部

代码

几何渲染阶段

光照渲染阶段

顶点着色器

(ssao.vs)
 #version 330
layout (location = 0) in vec3 Position; 
out vec2 TexCoord;
void main()
{ 
    gl_Position = vec4(Position, 1.0);//绘制了一个全屏四边形的坐标范围在 (-1,-1) 到 (1,1)之间
    TexCoord = (Position.xy + vec2(1.0)) / 2.0;//然后给每个顶点纹理坐标 方便在上面传过来的视空间纹理中采样
}

片元着色器

(ssao.fs)
 #version 330
in vec2 TexCoord;//上面顶点着色器传来的纹理坐标
out vec4 FragColor;
uniform sampler2D gPositionMap;//前面几何空间做的视空间纹理
uniform float gSampleRad;
uniform mat4 gProj;//透视投影矩阵 方便将随机点投影到近平面上进行比较
const int MAX_KERNEL_SIZE = 128;
uniform vec3 gKernel[MAX_KERNEL_SIZE];//随机数组 数组中存放的都是范围在 (-1,1)之间的随机向量
void main()
{
    vec3 Pos = texture(gPositionMap, TexCoord).xyz;//获取这个像素在视空间的坐标
    float AO = 0.0;//设置遮挡因子为0
    /*循环生成目标个随机点*/
    for (int i = 0 ; i < MAX_KERNEL_SIZE ; i++) {
        vec3 samplePos = Pos + gKernel[i]; //生成随机点坐标 是在视空间的基础上
        vec4 offset = vec4(samplePos, 1.0); // 变为四维
        offset = gProj * offset; // 将随机点投影到近平面上 这个目的是获取这个随机点遮盖 或者遮盖随机点的几何体的那个像素 因为从摄像机那连线过来 就是视空间里同x,y嘛
        offset.xy /= offset.w; // 透视除法 单纯的对xy做 因为我们不需要深度测试了
        /*这样就可以计算出在视中x,y的深度值z*/
        //下面计算的就是纹理坐标
        offset.xy = offset.xy * 0.5 + vec2(0.5); // transform to (0,1) range
        //对纹理进行采样获取几何体上同像素位置的深度值
        float sampleDepth = texture(gPositionMap, offset.xy).b;
        if (abs(Pos.z - sampleDepth) < gSampleRad) {//遮盖pos.z不是像素点的吗 应该用samplePos啊
            AO += step(sampleDepth,samplePos.z);
        }
    }
    AO = 1.0 - AO/128.0;
    FragColor = vec4(pow(AO, 2.0))//然后把这个结果存进去;
}

现在要解决的就是坐标问题

问题

为什么是摄像机空间 而不是光源视角的空间

因为给我的感觉是 如果是光源空间 我们计算的环境光遮挡因子 如果是光线能直射到的地方一般就是漫反射光线影响的地方 所以不太需要环境光遮蔽 所以这个随机点在摄像机的视角下是否被遮挡更重要

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值