十九,镜面IBL--BRDF积分贴图

news/2024/6/17 8:50:52 标签: osg

再回顾下镜面部分的分割求和近似法
在这里插入图片描述
现在关注第二部分
在这里插入图片描述
最后可化为
在这里插入图片描述
也就是说,这两部分积分可以获得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 = roughness
roughness; \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 - cosTheta
cosTheta); \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());

//shader

osg::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();

}


http://www.niftyadmin.cn/n/5064036.html

相关文章

算法-动态规划/trie树-单词拆分

算法-动态规划/trie树-单词拆分 1 题目概述 1.1 题目出处 https://leetcode.cn/problems/word-break/description/?envTypestudy-plan-v2&envIdtop-interview-150 1.2 题目描述 2 动态规划 2.1 解题思路 dp[i]表示[0, i)字符串可否构建那么dp[i]可构建的条件是&…

基于SpringBoot的养老监护管理平台设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作…

操作系统--分页存储管理

一、概念介绍 分页存储&#xff1a;一是分内存地址&#xff0c;二是分逻辑地址。 1.分内存地址 将内存空间分为一个个大小相等的分区。比如&#xff0c;每个分区4KB。 每个分区就是一个“页框”&#xff0c;每个页框有个编号&#xff0c;即“页框号”&#xff0c;“页框号”…

RFID与人工智能的融合:物联网时代的智能化变革

随着物联网技术的不断发展&#xff0c;现实世界与数字世界的桥梁已经被打通。物联网通过各种传感器&#xff0c;将现实世界中的光、电、热等信号转化为有价值的数据。这些数据可以通过RFID技术进行自动收集和传输&#xff0c;然后经由人工智能算法进行分析、建模和预测&#xf…

MySQL进阶_查询优化和索引优化

文章目录 第一节、索引失效案例1.1 数据准备1.2 全值匹配我最爱1.3 最佳左前缀法则 第一节、索引失效案例 可以从以下维度对数据库进行优化&#xff1a; 索引失效、没有充分利用到索引–索引建立关联查询太多JOIN (设计缺陷或不得已的需求)–SQL优化服务器调优及各个参数设置…

贪心 + 前后缀分解 + 动态规划(123. 买卖股票的最佳时机 III)

class Solution {//贪心 前后缀分解 动态规划public int maxProfit(int[] prices) {int n prices.length;int left[] new int[n];int right[] new int[n];for(int i 1,Min prices[0];i < n;i){left[i] Math.max(left[i - 1],prices[i] - Min);Min Math.min(Min,pri…

【LeetCode热题100】--98.验证二叉搜索树

98.验证二叉搜索树 给你一个二叉树的根节点 root &#xff0c;判断其是否是一个有效的二叉搜索树。 有效 二叉搜索树定义如下&#xff1a; 节点的左子树只包含 小于 当前节点的数。节点的右子树只包含 大于 当前节点的数。所有左子树和右子树自身必须也是二叉搜索树。 由于二…

NPDP产品经理知识(文化,团队与领导力)

文化和氛围的区别&#xff1a; -- 文化 组织中人们共同拥有的信仰&#xff0c;核心价值观&#xff0c;假设和期望&#xff0c;反映组织的价值观 >反映组织价值观 >在习俗&#xff0c;仪式中遵守 >决定如何完成工作 >表达组织的长期可持续性 -- 氛围 员…