QT With OpenGL(SSAO)(Screen-Space Ambient Occlusion)

2023-11-13

在G_Buffer中加入深度信息

  1. 将深度转化为线性
float LinearizeDepth(float depth)
{
    float z = depth * 2.0 - 1.0; // 回到NDC
    return (2.0 * NEAR * FAR) / (FAR + NEAR - z * (FAR - NEAR));    
}
  1. 将深度记录在position的alpha值中。
gPositionDepth.a = LinearizeDepth(gl_FragCoord.z); 

注:深度的范围不能局限在【0,1】,所以需要设置纹理数据类型为GL_RGBA16F。纹理封装方法设置为GL_CLAMP_TO_EDGE

使用深度信息得到环境遮蔽的结果

首先我们需要在该片段(像素)的法线半球方向上采样

1. 新建SSAO帧缓存类

#include <QOpenGLFunctions>
#include <QOpenGLFramebufferObject>
#include <QOpenGLShaderProgram>

class SSAO : public QOpenGLFunctions
{
public:
    QOpenGLFramebufferObject* SSAOFBO;
    QOpenGLShaderProgram *SSAOShader;
    QVector<QVector3D> SSAOKernel;

public:
    SSAO(int w,int h);
    void generateKernel();
    void setUniform();
    ~SSAO();
};

2.生成法向半球核心

void SSAO::generateKernel()
{
    for(int i=0;i<64;++i){
        QVector3D sample(random_double(-1,1),random_double(-1,1),random_double(0,1));
        sample.normalize();//生成一个半球面的随机向量
        sample *= random_double(0,1);//将随机向量均匀分布在半球体内
        double scale = double(i)/64.0;
        scale = 0.1 + scale*scale*(0.9);//将随机向量再次缩放,使绝大多数采样点接近原点
        sample *= scale;
        SSAOKernel.push_back(sample);
    }
}

3. 生成随机核心转动纹理

为什么要生成随机核心转动

  • 如果对每一个像素都随机生成64个采样向量,那么性能将会受到很大影响。
  • 如果对于每一个像素都使用同一个64随机采样向量,效果不好???
  • 如果对于每一个像素,都将改64随机采样向量做一次旋转,则会得到更为随机的采样

因此引入随机核心转动
如果我们对场景中每一个片段创建一个随机旋转向量,这会很快将内存耗尽。
所以,更好的方法是创建一个小的随机旋转向量纹理平铺在屏幕上。

创建一个小的随机旋转向量纹理

    for(int i=0;i<16;++i){
        QVector3D noise(random_double(-1,1),random_double(-1,1),0.0);
        SSAONoise.push_back(noise);
    }

使用OpenGL函数绑定到纹理

{
    GLuint noiseTexture;
    glGenTextures(1, &noiseTexture);
    glBindTexture(GL_TEXTURE_2D, noiseTexture);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, 4, 4, 0, GL_RGB, GL_FLOAT, &SSAONoise[0]);
    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_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D,noiseTexture);
}

使用QT::QOpenGLTexture,绑定到QOpenGLTexture类。

{
    texture.setSize(4,4,1);
    texture.setFormat(QOpenGLTexture::RGBFormat);
    texture.allocateStorage(QOpenGLTexture::RGB,QOpenGLTexture::Float32);
    texture.setData(0,QOpenGLTexture::RGB,QOpenGLTexture::Float32,(void*)SSAONoise.data());
    texture.setMinMagFilters(QOpenGLTexture::Nearest,QOpenGLTexture::Nearest);
    texture.setWrapMode(QOpenGLTexture::Repeat);
    glActiveTexture(GL_TEXTURE0);
    texture.bind();
}

效果展示

//test
debugShader = new QOpenGLShaderProgram();
debugShader->addShaderFromSourceFile(QOpenGLShader::Vertex,":/map_depth.vert");
debugShader->addShaderFromSourceFile(QOpenGLShader::Fragment,":/map_depth.frag");
debugShader->link();

debugShader->bind();//shader
debugShader->setUniformValue("depthMap",0);
renderQuad();

在这里插入图片描述
纹理成功生成。

4.使用G_Buffer渲染SSAO纹理

传入参数

  • gPositionDepth (观察空间(View Space))位置深度信息
  • gNormal 法线信息
  • noiseTexture 随机法向半球
  • samples[64]; 64个采样向量

注意: SSAO需要的位置信息是视口(View) 角度下看到的物体的视口坐标信息和深度,这与之前做延迟渲染(Deferred Shader) 不同,延迟渲染需要的信息为视口角度下物体的世界坐标信息。

因此若要统一G_Buffer,需要将G_Buffer提供的位置深度信息统一为视口坐标下的位置深度信息。在延迟渲染中,将 视口矩阵 ∗ 光源信息 视口矩阵*光源信息 视口矩阵光源信息 ,使整个光照渲染都在视口坐标系下进行。

gPosition不再是世界坐标,而是视口坐标(第一张图)
在这里插入图片描述

着色器

1. 获取当前像素在纹理中的信息

width,height为当前屏幕的大小

const vec2 noiseScale = vec2(width/4.0f, height/4.0f);
    // Get input for SSAO algorithm
    vec3 fragPos = texture(gPositionDepth, TexCoords).xyz;
    vec3 normal = texture(gNormal, TexCoords).rgb;
    vec3 randomVec = texture(texNoise, TexCoords * noiseScale).xyz;

若渲染(0,0)像素点,随机转动核心纹理为(0,0)点
若渲染( 1 / w i d t h 1/width 1/width 1 / h e i g h t 1/height 1/height)像素点,随机转动核心纹理为( 1 / 4 1/4 1/4 1 / 4 1/4 1/4)点
即每4X4个像素使用一组随机转动核心纹理。

2.计算TBN矩阵

已知Normal向量,获得了一个随机向量randomVec ( x , y , 0 ) (x,y,0) (x,y,0),求TBN。
在这里插入图片描述

    // Create TBN change-of-basis matrix: from tangent-space to view-space
    vec3 tangent = normalize(randomVec - normal * dot(randomVec, normal));
    vec3 bitangent = cross(normal, tangent);
    mat3 TBN = mat3(tangent, bitangent, normal);

关于切线空间的内容可参考

3. 遍历采样点

将样本从切线空间变换到观察空间

float occlusion = 0.0;
for(int i = 0; i < kernelSize; ++i)
{
    // 获取样本位置
    vec3 sample = TBN * samples[i]; // 切线->观察空间
    sample = fragPos + sample * radius; 

    [...]
}

变换sample从观察空间到屏幕空间

    vec4 offset = vec4(Tsample, 1.0);
    offset = projection * offset; // 观察->裁剪空间
    offset.xyz /= offset.w; // 透视划分
    offset.xyz = offset.xyz * 0.5 + 0.5;// 变换到0.0 - 1.0的值域

获取采样点像素的屏幕深度值

float sampleDepth = -texture(gPositionDepth, offset.xy).w; 

引入范围测试从而保证我们只当被测深度值在取样半径内时影响遮蔽因子
fragPos.z是渲染点深度
sampleDepth是采样点的像素深度

smoothstep的意义可参照Shader实验室: smoothstep函数

float rangeCheck = smoothstep(0.0, 1.0, radius / abs(fragPos.z - sampleDepth ));
occlusion += (sampleDepth >= Tsample.z ? 1.0 : 0.0) * rangeCheck;

如果 ∣ f r a g P o s . z − s a m p l e D e p t h ∣ < r a d i u s |fragPos.z - sampleDepth|<radius fragPos.zsampleDepth<radius,则rangeCheck=1
如果 ∣ f r a g P o s . z − s a m p l e D e p t h ∣ > r a d i u s |fragPos.z - sampleDepth|>radius fragPos.zsampleDepth>radius,则rangeCheck ∈ ( 0 , 1 ) \in(0,1) (0,1)
∣ f r a g P o s . z − s a m p l e D e p t h ∣ > r a d i u s |fragPos.z - sampleDepth|>radius fragPos.zsampleDepth>radius并不直接移除遮蔽贡献是因为,这样会在范围检测边缘看到很明显的边界。因此使用距离远近改变贡献度。
在这里插入图片描述

当采样点像素深度(物体边缘)大于等于检测点深度(空心圈),说明该点未遮蔽了渲染点的环境光照,Occlusion遮蔽值增加。

将遮蔽贡献根据核心的大小标准化
occlusion / kernelSize 得到未遮蔽系数,再用1减去,得到遮蔽系数。

occlusion = 1.0 - (occlusion / kernelSize);
FragColor = occlusion;

SSAO输出测试

在这里插入图片描述
在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

QT With OpenGL(SSAO)(Screen-Space Ambient Occlusion) 的相关文章

随机推荐

  • Kali beef-xss实现Xss详细教程。

    免责声明 本文所介绍的内容仅做学习交流使用 严禁利用文中技术进行非法行为 否则造成一切严重后果自负 1 XSS简介 1 XSS简介 XSS作为OWASP TOP 10之一 XSS中文叫做跨站脚本攻击 Cross site scripting
  • oracle表连接深入浅出

    author skate time 2010 08 20 表的连接 表的连接是指在一个SQL语句中通过表与表之间的关联 从一个或多个表检索出相关的数据 如果一个SQL语句的关联表超过两个 那么连接的顺序如何呢 ORACLE首先连接其中的两个
  • 【AWS实验 】在 AWS Fargate 上使用 Amazon ECS 部署应用程序

    文章目录 实验概览 目标 实验环境 任务 1 连接到实验命令主机 任务 2 将应用程序容器化 任务 3 构建 Web2048 容器 任务 4 创建 Amazon ECR 存储库并推送 Docker 映像 任务 5 创建 ECS 集群 任务
  • IIS6.0容器之解析漏洞复现

    漏洞简介 解析漏洞是指web服务器因对HTTP请求处理不当导致将非可执行的脚本 文件等当作可执行的脚本去执行 该漏洞一般配合web容器 iis nginx apache tomcat等 的文件上传功能去使用 以获取服务器的权限 IIS5 X
  • 【超详细】MMLab分类任务mmclassification:环境配置说明、训练、预测及模型结果可视化展示

    本文详细介绍了使用MMLab的mmclassification进行分类任务的环境配置 训练与预测流程 目录 文件配置说明 下载源码 配置文件 基于预训练模型微调或者续训练自己模型的方式 配置文件说明 数据集配置方式 更改配置文件中的类别名称
  • Docker-harbor私有仓库

    目录 一 Harbor概述 1 Harbor的概念 2 Harbor的特性 3 Harbor的构成 二 Harbor 部署 环境准备 1 部署 Docker Compose 服务 2 部署 Harbor 服务 下载或上传 Harbor 安装
  • Data,DB,DBMS,DBS,DBA

    数据 Data 数据库 Database DB 数据库管理系统 DBMS 数据库系统 DBS 数据库管理员 DBA 1 数据 数据是信息的承载者 数据可以是数字 也可以是文字 图片 音频等等 通过数字化存进计算机 2 数据库 长期存储在计算
  • 【AI实战】开源大语言模型LLMs汇总

    AI实战 开源大语言模型LLM汇总 大语言模型 开源大语言模型 1 LLaMA 2 ChatGLM 6B 3 Alpaca 4 PandaLLM 5 GTP4ALL 6 DoctorGLM MedicalGPT zh v2 7 Medica
  • 串口收发最高位为0

    串口收发最高位为0 昨天在现场 读取电表度数的时候发现一个问题 读取字节最高位不管多少全部为0 搞的我相当郁闷 最后没有办法 只好人工给他置1 今天到了公司打开 linux 程序设计 翻到终端这一章 152页 赫然写着几个大字 ISTRIP
  • input标签设置为不可用状态的三种方法

    一 disabled 禁用input元素 背景颜色变成灰色 不能点击和编辑 但后台可以接收到input中的数据
  • 超声波 HC-SR04 模块入门

    超声波 HC SR04 模块入门 简介 超声波测距模块是根据超声波遇障碍反射的原理进行测距的 能够发送超声波 接收超声波并通过处理 输出一段和发送与接收间隔时间相同的高电平信号 是常用的测距模块之一 HC SR04 是最常用的超声波测距模块
  • fastadmin两级联动

    记录 实现fastadmin 两级联动效果 后端代码 ajax控制器 读取年级数据 联动列表 public function grade status this gt request gt get status where shop id
  • Hive练习题 字符串与时间类型转换

    参考文章 https blog csdn net a805814077 article details 115014708 概念 时间戳 也就是timestamp 是hive中的一种数据类型 与unix timestamp不是一个概念 un
  • Vue 之自定义指令

    Vue 之自定义指令 Vue 自定义指令有全局注册和局部注册两种方式 先来看看注册全局指令的方式 通过 Vue directive id definition 方式注册全局指令 然后在入口文件中进行 Vue use 调用新的改变 在src目
  • 01前端实习面经之第一次面试

    前端实习面试经验 本系列文章为本菜鸟记录自己的前端面试经验 也希望帮助到需要者 文章目录 前端实习面试经验 公司 面试题 总结 公司 base南京 200 天 面试题 1 es6新特性 2 数组的常用方法 3 对promise的了解 是否使
  • Linux安装代码提示或自动补全插件

    在Linux中 当我们输入命令的时候 按 TAB键会有自动补全的功能 然而有些命令不会提示 而我们又忘记了怎么拼写 比如查看防火墙开启的状态命令如下 systemctl status firewalld service firewalld
  • 网页端html使用mqttjs,JavaScript使用MQTT - 农民子弟 - 博客园

    lt html gt lt head gt lt meta charset utf 8 gt lt title gt title gt lt script src https cdnjs cloudflare com ajax libs p
  • 迅捷fw325r虚拟服务器设置,迅捷FW325Rwifi连接Internet上网怎么设置 - falogincn登录页面...

    新购或恢复出厂快fw325r无线路由器的设置 实现网络连接 需要经过以下配置步骤 本文主要介绍了迅捷www falogincn cn 快 fw325r路由器网络连接设置方法 迅捷fw325r路由器网络连接设置方法 1 输入登录地址 在浏览器
  • 一个简单有趣的爬虫-----爬取百度翻译功能

    首先确定爬取的网址 https fanyi baidu com sug 我们爬取翻译功能时应对request进行简单伪装 编写headers base url https fanyi baidu com sug kw input 请输入要查
  • QT With OpenGL(SSAO)(Screen-Space Ambient Occlusion)

    文章目录 在G Buffer中加入深度信息 使用深度信息得到环境遮蔽的结果 1 新建SSAO帧缓存类 2 生成法向半球核心 3 生成随机核心转动纹理 为什么要生成随机核心转动 创建一个小的随机旋转向量纹理 4 使用G Buffer渲染SSA