十九,镜面IBL--BRDF积分贴图
再回顾下镜面部分的分割求和近似法
现在关注第二部分
最后可化为
也就是说,这两部分积分可以获得F0的系数和F0的偏差。
这两个值可以存储到BRDF积分贴图的RG部分。
void main()
{
vec2 integratedBRDF = IntegrateBRDF(TexCoords.x, TexCoords.y);
FragColor = integratedBRDF;
}
再看函数vec2 IntegrateBRDF(float NdotV, float roughness) ,可知积分贴图的横坐标是NotV,纵坐标是粗糙度。
查看这个RG是如何计算的。
循环采样后A /= float(SAMPLE_COUNT); B /= float(SAMPLE_COUNT); return vec2(A, B); 即A,B分别是F0和系数和F0的偏差,也就是积分贴图的RG部分。结合公式
在对每个采样向量中,
float Fc = pow(1.0 - VdotH, 5.0);
A += (1.0 - Fc) * G_Vis;
B += Fc * G_Vis;
继续向下float G = GeometrySmith(N, V, L, roughness); float G_Vis = (G * VdotH) / (NdotH * NdotV); 可知,使用BRDF的几何函数处理了采样向量。继续向下vec2 Xi = Hammersley(i, SAMPLE_COUNT);
vec3 H = ImportanceSampleGGX(Xi, N, roughness);
vec3 L = normalize(2.0 * dot(V, H) * H - V);
类似于预过滤环境贴图,也是通过低差异序列进行重要性采样获取采样向量。
在c++部分。
设置brdf积分贴图为512x512,纹理设置为GL_RG
texture->setSourceFormat(GL_RG);
运行结果如下:
代码如下:
#include <osg/TextureCubeMap>
#include <osg/TexGen>
#include <osg/TexEnvCombine>
#include <osgUtil/ReflectionMapGenerator>
#include <osgDB/ReadFile>
#include <osgViewer/Viewer>
#include <osg/NodeVisitor>
#include <osg/ShapeDrawable>
#include <osg/Texture2D>
#include <osgDB/WriteFile>
static const char * vertexShader =
{
“in vec3 aPos;\n”
“in vec2 texcoord;”
“varying vec2 TexCoords;\n”
“void main(void)\n”
“{\n”
“TexCoords = texcoord;\n”
“gl_Position = ftransform();\n”
//“gl_Position = view * view * vec4(aPos,1.0);”
“}\n”
};
static const char psShader =
{
“#version 330 core \n”
“out vec2 FragColor; \n”
“in vec2 TexCoords; \n”
" \n"
“const float PI = 3.14159265359; \n”
“// ---------------------------------------------------------------------------- \n”
“// http://holger.dammertz.org/stuff/notes_HammersleyOnHemisphere.html \n”
“// efficient VanDerCorpus calculation. \n”
“float RadicalInverse_VdC(uint bits) \n”
“{ \n”
" bits = (bits << 16u) | (bits >> 16u); \n"
" bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); \n"
" bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); \n"
" bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); \n"
" bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); \n"
" return float(bits) * 2.3283064365386963e-10; // / 0x100000000 \n"
“} \n”
“// ---------------------------------------------------------------------------- \n”
“vec2 Hammersley(uint i, uint N) \n”
“{ \n”
" return vec2(float(i) / float(N), RadicalInverse_VdC(i)); \n"
“} \n”
“// ---------------------------------------------------------------------------- \n”
“vec3 ImportanceSampleGGX(vec2 Xi, vec3 N, float roughness) \n”
“{ \n”
" float a = roughnessroughness; \n"
" \n"
" float phi = 2.0 * PI * Xi.x; \n"
" float cosTheta = sqrt((1.0 - Xi.y) / (1.0 + (aa - 1.0) * Xi.y)); \n"
" float sinTheta = sqrt(1.0 - cosThetacosTheta); \n"
" \n"
" // from spherical coordinates to cartesian coordinates - halfway vector \n"
" vec3 H; \n"
" H.x = cos(phi) * sinTheta; \n"
" H.y = sin(phi) * sinTheta; \n"
" H.z = cosTheta; \n"
" \n"
" // from tangent-space H vector to world-space sample vector \n"
" vec3 up = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0); \n"
" vec3 tangent = normalize(cross(up, N)); \n"
" vec3 bitangent = cross(N, tangent); \n"
" \n"
" vec3 sampleVec = tangent * H.x + bitangent * H.y + N * H.z; \n"
" return normalize(sampleVec); \n"
“} \n”
“// ---------------------------------------------------------------------------- \n”
“float GeometrySchlickGGX(float NdotV, float roughness) \n”
“{ \n”
" // note that we use a different k for IBL \n"
" float a = roughness; \n"
" float k = (a * a) / 2.0; \n"
" \n"
" float nom = NdotV; \n"
" float denom = NdotV * (1.0 - k) + k; \n"
" \n"
" return nom / denom; \n"
“} \n”
“// ---------------------------------------------------------------------------- \n”
“float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness) \n”
“{ \n”
" float NdotV = max(dot(N, V), 0.0); \n"
" float NdotL = max(dot(N, L), 0.0); \n"
" float ggx2 = GeometrySchlickGGX(NdotV, roughness); \n"
" float ggx1 = GeometrySchlickGGX(NdotL, roughness); \n"
" \n"
" return ggx1 * ggx2; \n"
“} \n”
“// ---------------------------------------------------------------------------- \n”
“vec2 IntegrateBRDF(float NdotV, float roughness) \n”
“{ \n”
" vec3 V; \n"
" V.x = sqrt(1.0 - NdotV*NdotV); \n"
" V.y = 0.0; \n"
" V.z = NdotV; \n"
" \n"
" float A = 0.0; \n"
" float B = 0.0; \n"
" \n"
" vec3 N = vec3(0.0, 0.0, 1.0); \n"
" \n"
" const uint SAMPLE_COUNT = 1024u; \n"
" for (uint i = 0u; i < SAMPLE_COUNT; ++i) \n"
" { \n"
" // generates a sample vector that’s biased towards the \n"
" // preferred alignment direction (importance sampling). \n"
" vec2 Xi = Hammersley(i, SAMPLE_COUNT); \n"
" vec3 H = ImportanceSampleGGX(Xi, N, roughness); \n"
" vec3 L = normalize(2.0 * dot(V, H) * H - V); \n"
" \n"
" float NdotL = max(L.z, 0.0); \n"
" float NdotH = max(H.z, 0.0); \n"
" float VdotH = max(dot(V, H), 0.0); \n"
" \n"
" if (NdotL > 0.0) \n"
" { \n"
" float G = GeometrySmith(N, V, L, roughness); \n"
" float G_Vis = (G * VdotH) / (NdotH * NdotV); \n"
" float Fc = pow(1.0 - VdotH, 5.0); \n"
" \n"
" A += (1.0 - Fc) * G_Vis; \n"
" B += Fc * G_Vis; \n"
" } \n"
" } \n"
" A /= float(SAMPLE_COUNT); \n"
" B /= float(SAMPLE_COUNT); \n"
" return vec2(A, B); \n"
“} \n”
“// ---------------------------------------------------------------------------- \n”
“void main() \n”
“{ \n”
" vec2 integratedBRDF = IntegrateBRDF(TexCoords.x, TexCoords.y); \n"
" FragColor = integratedBRDF; \n"
“} \n”
};
int main()
{
int imageWidth = 512;
int imageHeight = 512;
osg::ref_ptrosg::Vec3Array vertices = new osg::Vec3Array;
vertices->push_back(osg::Vec3(-imageWidth, 0.0f, -imageHeight));
vertices->push_back(osg::Vec3(imageWidth, 0.0f, -imageHeight));
vertices->push_back(osg::Vec3(imageWidth, 0.0f, imageHeight));
vertices->push_back(osg::Vec3(-imageWidth, 0.0f, imageHeight));
osg::ref_ptr<osg::Vec3Array> normals = new osg::Vec3Array;
normals->push_back(osg::Vec3(0.0f, -1.0f, 0.0f));
osg::ref_ptr<osg::Vec2Array> texcoords = new osg::Vec2Array;
texcoords->push_back(osg::Vec2(0.0f, 0.0f));
texcoords->push_back(osg::Vec2(1.0f, 0.0f));
texcoords->push_back(osg::Vec2(1.0f, 1.0f));
texcoords->push_back(osg::Vec2(0.0f, 1.0f));
osg::ref_ptr<osg::Geometry> quad = new osg::Geometry;
quad->setVertexArray(vertices.get());quad->setNormalArray(normals.get());
quad->setNormalBinding(osg::Geometry::BIND_OVERALL);
quad->setTexCoordArray(0, texcoords.get());
quad->addPrimitiveSet(new osg::DrawArrays(GL_QUADS, 0, 4));quad->setVertexAttribArray(1, vertices, osg::Array::BIND_PER_VERTEX);
quad->setVertexAttribArray(2, texcoords, osg::Array::BIND_PER_VERTEX);
osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D;
texture->setSourceFormat(GL_RG);
texture->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::LINEAR);
texture->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::LINEAR);
texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
osg::ref_ptr<osg::Geode> geode = new osg::Geode;
geode->addDrawable(quad.get());
osg::ref_ptr<osg::StateSet> stateset = geode->getOrCreateStateSet();
stateset->setTextureAttributeAndModes(0, texture.get());//shaderosg::ref_ptr<osg::Shader> vs1 = new osg::Shader(osg::Shader::VERTEX, vertexShader);
osg::ref_ptr<osg::Shader> ps1 = new osg::Shader(osg::Shader::FRAGMENT, psShader);
osg::ref_ptr<osg::Program> program1 = new osg::Program;
program1->addShader(vs1);
program1->addShader(ps1);
program1->addBindAttribLocation("aPos", 1);
program1->addBindAttribLocation("texcoord", 2);osg::ref_ptr<osg::Uniform> tex0Uniform = new osg::Uniform("tex0", 0);
stateset->addUniform(tex0Uniform);
stateset->setAttribute(program1, osg::StateAttribute::ON);//osgDB::writeImageFile(*image, strBRDFLUTImageName);
osgViewer::Viewer viewer;
viewer.setSceneData(geode.get());bool bPrinted = false;
return viewer.run();
}
相关文章:

十九,镜面IBL--BRDF积分贴图
再回顾下镜面部分的分割求和近似法 现在关注第二部分 最后可化为 也就是说,这两部分积分可以获得F0的系数和F0的偏差。 这两个值可以存储到BRDF积分贴图的RG部分。void main() { vec2 integratedBRDF IntegrateBRDF(TexCoords.x, TexCoords.y); FragColor …...

Linux 创建 终止线程(thread)
进程线程区别 创建线程 #include <pthread.h> int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg); -功能:创建一个子线程,一般情况下main函数所在的线程称为主线程,…...
【IPC 通信】信号处理接口 Signal API(6)
收发信号思想是 Linux 程序设计特性之一,一个信号可以认为是一种软中断,通过用来向进程通知异步事件。 本文讲述的 信号处理内容源自 Linux man。本文主要对各 API 进行详细介绍,从而更好的理解信号编程。 kill(2) 遵循 POSIX.1 - 2008 1.库 …...

ipaguard界面概览
ipaguard界面概览 ipaguard界面分左右2块:左边菜单导航栏,右边的功能区 左侧菜单:按模块分成启动界面,代码模块,文件模块,重签名与测试模块 右侧主功能区会随着功能变化,但是整体分3块…...

萌新的FPGA学习绪论-1
萌新的FPGA学习绪论-1 其实很多的课和内容都是相通的 我在跑完单周期的RiscV时候 虽然最后还差点意思但是基本的逻辑实现没有特别大的问题 过两天写一个Spec文档说明一下 由于开始一个新的设计 所以对于RiscV的设计暂时放到一边希望我能在接下来的时间内尽快完成 暂时不说这个…...

目标检测算法改进系列之Backbone替换为EMO
EMO:结合 Attention 重新思考移动端小模型中的基本模块 近年来,由于存储和计算资源的限制,移动应用的需求不断增加,因此,本文的研究对象是端侧轻量级小模型 (参数量一般在 10M 以下)。在众多小模型的设计中࿰…...
Laravel一些优雅的写法
1. 新增操作 // 原则,所有服务类只有一个public入口,或者多个public入口,但是他们做都是同一件事情 Class CreateService {// 创建类的入口, 根据dto去新建public function create(Dto $dto){// 先构建model对象, 不要在事务期间构建,减少事务…...

vue+three.js中使用Ammo.js
直接通过npm i ammo.js安装进webpack的项目里调用时,会出现如下报错: ERROR in ./node_modules/ammo.js/ammo.js 1:1683-1696 Mo…...

【k8s】kubectl命令详解
文章目录 命令行工具 kubectl在slave下配置kubectl资源操作创建对象 API概述类型访问控制认证授权 废弃API说明 资源管理资源管理介绍资源管理方式命令式对象管理命令式对象配置创建yaml文件创建资源查看资源删除资源 声明式对象配置 kebectl在node节点上运行查看每种资源的可配…...

Centos 7 部署SVN服务器
一、安装SVN 1、安装Subversion sudo yum -y install subversion2、验证是否安装成功(查看svn版本号) svnserve --version二、创建版本库 1、先建立目录,目录位置可修改 mkdir -p /var/svn cd /var/svn2、创建版本库,添加权限…...

SEO方案尝试--Nuxtjs项目基础配置
Nuxtjs 最新版 Nuxt3 项目配置 安装nuxtjs 最新版 Nuxt3 参考官网安装安装插件安装ElementPlus页面怎么跳转,路由怎么实现404页面该怎么配置配置 网页的title 安装nuxtjs 最新版 Nuxt3 参考官网安装 安装插件 安装ElementPlus 安装 Element Plus 和图标库 # 首先&…...

【算法分析与设计】动态规划(上)
目录 一、学习要点二、算法总体思想三、动态规划基本步骤四、矩阵连乘问题4.1 完全加括号的矩阵连乘积4.2 穷举法4.3 动态规划4.3.1 分析最优解的结构4.3.2 建立递归关系4.3.3 计算最优值4.3.4 用动态规划法求最优解 五、动态规划算法的基本要素5.1 最优子结构5.2 重叠子问题5.…...

Java多线程篇(6)——AQS之ReentrantLock
文章目录 1、管程2、AQS3、ReentrantLock3.1、lock/unlock3.1.1、lock3.1.2、unlock 3.2、一些思考 1、管程 什么是管程? 管理协调多个线程对共享资源的访问,是一种高级的同步机制。 有哪些管程模型? hansen:唤醒其他线程的代码…...

【计算机网络】IP协议第二讲(Mac帧、IP地址、碰撞检测、ARP协议介绍)
IP协议第二讲 1.IP和Mac帧2.碰撞检测2.1介绍2.2如何减少碰撞发生2.3MTU2.4一些补充 3.ARP协议3.1协议介绍3.2报文格式分析 1.IP和Mac帧 IP(Internet Protocol)和MAC(Media Access Control)帧是计算机网络中两个不同层次的概念&am…...

TouchGFX界面开发 | 按钮控件应用示例
按钮控件应用示例 按钮是最常见的部件之一,有了按钮就可以点击,从而响应事件,达到人机交互的目的。TouchGFX Designer内置了七种按钮部件: 下压按钮:能够在被释放时发送回调,按下和释放状态都关联了图像标…...

BSVD论文理解:Real-time Streaming Video Denoising with Bidirectional Buffers
BSVD是来自香港科技大学的一篇比较新的视频去噪论文,经实践,去噪效果不错,在这里分享一下对这篇论文的理解。 论文地址:https://arxiv.org/abs/2207.06937 代码地址:GitHub - ChenyangQiQi/BSVD: [ACM MM 2022] Real…...

共同见证丨酷雷曼武汉运营中心成立2周年
酷雷曼武汉运营中心2周年 全国合作商齐贺武汉公司2周年庆 2021年 作为酷雷曼辐射全国版图的又一重要据点 酷雷曼武汉运营中心 在“中国光谷”正式成立 沉浸式参观酷雷曼武汉公司 2年时间 尽管历经诸多客观因素的挑战 但后浪扬帆,依然交出了不斐的成绩 解决…...

一种单键开关机电路图
我们设计产品时,通常需要设计单键开关机功能。 单键开关机,通常需要单片机的两个IO完成,一个IO用于保持开机状态。另外,一个IO用于判定关机状态。 下面就是一种单键开关机电路原理图: 此单键开关电路已经在S2W-M02、S2…...

设计模式2、抽象工厂模式 Abstract Factory
解释说明:提供一个创建一系列相关或相互依赖对象的接口,而无需指定他们具体的类。 简言之,一个工厂可以提供创建多种相关产品的接口,而无需像工厂方法一样,为每一个产品都提供一个具体工厂 抽象工厂(Abstra…...
C++ 32盏灯,利用进制和 与 或 进行设计
一共32盏灯,设计一个灯光控制系统,其中 台球部8盏灯 桌游区8盏灯 酒吧区8盏灯 休息区8盏灯 满足以下功能 1、能够独立控制每一盏灯 2、能够一次性打开或关闭一个区域的全部灯光 3、能够获取各个区域的灯光打开关闭情况 4、能够一次性关闭打开的灯&#x…...
【杂谈】-递归进化:人工智能的自我改进与监管挑战
递归进化:人工智能的自我改进与监管挑战 文章目录 递归进化:人工智能的自我改进与监管挑战1、自我改进型人工智能的崛起2、人工智能如何挑战人类监管?3、确保人工智能受控的策略4、人类在人工智能发展中的角色5、平衡自主性与控制力6、总结与…...

阿里云ACP云计算备考笔记 (5)——弹性伸缩
目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...

【第二十一章 SDIO接口(SDIO)】
第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...

微信小程序 - 手机震动
一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注:文档 https://developers.weixin.qq…...
spring:实例工厂方法获取bean
spring处理使用静态工厂方法获取bean实例,也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下: 定义实例工厂类(Java代码),定义实例工厂(xml),定义调用实例工厂ÿ…...
【git】把本地更改提交远程新分支feature_g
创建并切换新分支 git checkout -b feature_g 添加并提交更改 git add . git commit -m “实现图片上传功能” 推送到远程 git push -u origin feature_g...
Linux离线(zip方式)安装docker
目录 基础信息操作系统信息docker信息 安装实例安装步骤示例 遇到的问题问题1:修改默认工作路径启动失败问题2 找不到对应组 基础信息 操作系统信息 OS版本:CentOS 7 64位 内核版本:3.10.0 相关命令: uname -rcat /etc/os-rele…...

从“安全密码”到测试体系:Gitee Test 赋能关键领域软件质量保障
关键领域软件测试的"安全密码":Gitee Test如何破解行业痛点 在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的"神经中枢"。从国防军工到能源电力,从金融交易到交通管控,这些关乎国计民生的关键领域…...
Kafka主题运维全指南:从基础配置到故障处理
#作者:张桐瑞 文章目录 主题日常管理1. 修改主题分区。2. 修改主题级别参数。3. 变更副本数。4. 修改主题限速。5.主题分区迁移。6. 常见主题错误处理常见错误1:主题删除失败。常见错误2:__consumer_offsets占用太多的磁盘。 主题日常管理 …...
Spring Security 认证流程——补充
一、认证流程概述 Spring Security 的认证流程基于 过滤器链(Filter Chain),核心组件包括 UsernamePasswordAuthenticationFilter、AuthenticationManager、UserDetailsService 等。整个流程可分为以下步骤: 用户提交登录请求拦…...