GAMES202——作业4 Kulla-Conty BRDF(BRDF的预计算、重要性采样)

目录

任务

实现

        预计算E(µ)

        预计算Eavg

        Bonus1:重要性采样

        在实时渲染中使用预计算数据

结果


任务

        完成 Kulla-Conty BRDF 模型,关键在于计算 BRDF 的补偿项 f ms ,而 f ms 的计算需要 E ( µ ) E avg 两个前置变量。

        1.预计算E(µ)

        2.预计算Eavg

        3.在实时渲染中使用预计算的数据。

        Bonus1:使用重要性采样方法

实现

        该作业框架采用的F,D,G三种模型

        

        F项采用Schlick近似

        

        D项采用GGX法线分布

        G项采用GGX法线分布匹配的Smith模型

        预计算E(µ)

        这里使用框架里提到的Revisiting Physically Based Shading at Image works SIGGRAPH 2017 course,by Kulla and Conty

        当我们使用Mircofacet模型时,当材质的粗糙度越大,通过白炉测试会发现损失的能量越来越多。因为Mircofacet模型只涉及一次的光线弹射,当物体很粗糙时,一根光线很容易会与表面发生多次作用,Mircofacet忽略了这一点,因此粗糙度大的物体,渲染结果会有点暗。而Kulla Conty的模型解决了这一问题。

        将入射光看作四面八方radiance都为1的光,对所有方向的入射光求积分求它的irradiance,可以得到下面的式子,

        这种形式是将dw拆分成了两项,并将原来的式子中的cosθ移到了dθ里,因此变成sinθd(sinθ),将sinθ换元成μ,就得到了最终上面的式子。好处就是不需要再关注 Φ了,就留下一个μ。

        假设入射光为1,那么损失的能量是1-E(μ),因为BRDF的对称性,因此需要考虑入射与出射方向。但是1-E(μ)是<0的,多乘一次,会变得更小,使得损失的能量计算错误,因此需要在补一项,最终得到下面的式子。

        通过下面的验证,证明了该公式的合理。

        因此对于任意一个Mircofacet的补偿能量,我们需要知道1-E(μ)和1-Eavg,就能求出需要补偿的能量。E(μ)依赖三个参数,入射角μ,粗糙度α,和折射率。但是三个变量会产生很大的存储空间,因此简单将菲涅尔项先当作1处理,因此通过μ和α可以求出最终的预计算的表。

        但是对于有色的物体,其自身也会对光进行吸收,也会存在能量损失。

        因此对于有色物体,在补偿的能量上还需要乘以颜色带来的能量损失。因此最终的BRDF是

                 

                    

           

//Emu_MC.cppfloat DistributionGGX(Vec3f N, Vec3f H, float roughness)
{float a = roughness*roughness;float a2 = a*a;float NdotH = std::max(dot(N, H), 0.0f);float NdotH2 = NdotH*NdotH;float nom   = a2;float denom = (NdotH2 * (a2 - 1.0) + 1.0);denom = PI * denom * denom;return nom / std::max(denom, 0.0001f);
}float GeometrySchlickGGX(float NdotV, float roughness) {float a = roughness;float k = (a * a) / 2.0f;float nom = NdotV;float denom = NdotV * (1.0f - k) + k;return nom / denom;
}float GeometrySmith(float roughness, float NoV, float NoL) {float ggx2 = GeometrySchlickGGX(NoV, roughness);float ggx1 = GeometrySchlickGGX(NoL, roughness);return ggx1 * ggx2;
}Vec3f IntegrateBRDF(Vec3f V, float roughness, float NdotV) {float A = 0.0;float B = 0.0;float C = 0.0;const int sample_count = 1024;Vec3f N = Vec3f(0.0, 0.0, 1.0);samplePoints sampleList = squareToCosineHemisphere(sample_count);for (int i = 0; i < sample_count; i++) {// TODO: To calculate (fr * ni) / p_o hereVec3f L = normalize(sampleList.directions[i]);float pdf = sampleList.PDFs[i];Vec3f H = normalize(L + V);float NdotL = dot(N,L);float F = 1.0;float G = GeometrySmith(roughness,NdotV,NdotL);float D = DistributionGGX(N,H,roughness);float denominator = 4 * NdotL * NdotV;float result = F * G * D / denominator * NdotL / pdf;A += result;B += result;C += result;}return {A / sample_count, B / sample_count, C / sample_count};
}
        预计算Eavg

        其实按照题目给的要求,完全不需要采样,直接在main里面求和然后再求平均就好了。

int main() {unsigned char *Edata = stbi_load("./GGX_E_MC_LUT.png", &resolution, &resolution, &channel, 3);if (Edata == NULL) {std::cout << "ERROE_FILE_NOT_LOAD" << std::endl;return -1;}else {std::cout << resolution << " " << resolution << " " << channel << std::endl;// | -----> mu(j)// | // | rough(i)// flip it if you want to write the data on picture uint8_t data[resolution * resolution * 3];float step = 1.0 / resolution;Vec3f Eavg = Vec3f(0.0);for (int i = 0; i < resolution; i++) {float roughness = step * (static_cast<float>(i) + 0.5f);for (int j = 0; j < resolution; j++) {float NdotV = step * (static_cast<float>(j) + 0.5f);Vec3f V = Vec3f(std::sqrt(1.f - NdotV * NdotV), 0.f, NdotV);Vec3f Ei = getEmu((resolution - 1 - i), j, 0, Edata, NdotV, roughness);// Eavg += IntegrateEmu(V, roughness, NdotV, Ei) * step;Eavg +=  Ei * NdotV * 2.0 * step;setRGB(i, j, 0.0, data);}for(int k = 0; k < resolution; k++){setRGB(i, k, Eavg, data);}Eavg = Vec3f(0.0);}// stbi_flip_vertically_on_write(true);stbi_write_png("GGX_Eavg_LUT.png", resolution, resolution, channel, data, 0);}stbi_image_free(Edata);return 0;
}

       

        Bonus1:重要性采样

        这里直接就采用作业文档里给出的公式了。

        通过采样法线,通过反射来计算入射光的方向。

        法线的采样

        pdf的计算

        

        最终的权重

//Emu_IS.cppVec3f ImportanceSampleGGX(Vec2f Xi, Vec3f N, float roughness) {float a = roughness * roughness;//TODO: in spherical space - Bonus 1float theta = atan(a * sqrt(Xi.x) / sqrt(1.0 - Xi.x));float phi = 2.0 * PI * Xi.y;//TODO: from spherical space to cartesian space - Bonus 1Vec3f H = Vec3f(cos(phi) * sin(theta) , sin(phi) * sin(theta) , cos(theta)  );//TODO: tangent coordinates - Bonus 1Vec3f temp = Vec3f(0.0,0.0,1.0);if( abs(N.z ) > 0.999)temp = Vec3f(1.0,0.0,0.0);Vec3f tangent = normalize( cross(temp,N) );Vec3f bitangent = normalize( cross(N,tangent));//TODO: transform H to tangent space - Bonus 1Vec3f sample = tangent * H.x + bitangent * H.y + N * H.z ;return normalize(sample); }
//Emu_IS.cppVec3f IntegrateBRDF(Vec3f V, float roughness) {const int sample_count = 1024;Vec3f N = Vec3f(0.0, 0.0, 1.0);Vec3f Emu = Vec3f(0.0);for (int i = 0; i < sample_count; i++) {Vec2f Xi = Hammersley(i, sample_count);Vec3f H = ImportanceSampleGGX(Xi, N, roughness);Vec3f L = normalize(H * 2.0f * dot(V, H) - V);float NoL = std::max(L.z, 0.0f);float NoH = std::max(H.z, 0.0f);float VoH = std::max(dot(V, H), 0.0f);float NoV = std::max(dot(N, V), 0.0f);// TODO: To calculate (fr * ni) / p_o here - Bonus 1float G = GeometrySmith(roughness , NoV , NoL);float weight = VoH * G / NoV / NoH;Emu += Vec3f(1.0) * weight;// Split Sum - Bonus 2}std::cout << Emu.x << Emu.y << Emu.z << std::endl;return Emu / sample_count;
}

        在实时渲染中使用预计算数据
//KullaContyFragment.glsl#ifdef GL_ES
precision mediump float;
#endifuniform vec3 uLightPos;
uniform vec3 uCameraPos;
uniform vec3 uLightRadiance;
uniform vec3 uLightDir;uniform sampler2D uAlbedoMap;
uniform float uMetallic;
uniform float uRoughness;
uniform sampler2D uBRDFLut;
uniform sampler2D uEavgLut;
uniform samplerCube uCubeTexture;varying highp vec2 vTextureCoord;
varying highp vec3 vFragPos;
varying highp vec3 vNormal;const float PI = 3.14159265359;float DistributionGGX(vec3 N, vec3 H, float roughness)
{// TODO: To calculate GGX NDF herefloat a2 = roughness * roughness;float NdotH = max(dot(N, H), 0.0);float NdotH2 = NdotH*NdotH;float nom = a2;float denom = (NdotH2 * (a2 - 1.0) + 1.0);denom = PI * denom * denom;return nom / denom;}float GeometrySchlickGGX(float NdotV, float roughness)
{// TODO: To calculate Schlick G1 herefloat a = roughness;float k = (a * a) / 2.0;float nom   = NdotV;float denom = NdotV * (1.0 - k) + k;return nom / denom;
}float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness)
{// TODO: To calculate Smith G herefloat NdotV = max(dot(N, V), 0.0);float NdotL = max(dot(N, L), 0.0);float ggx2 = GeometrySchlickGGX(NdotV, roughness);float ggx1 = GeometrySchlickGGX(NdotL, roughness);return ggx1 * ggx2;
}vec3 fresnelSchlick(vec3 F0, vec3 V, vec3 H)
{// TODO: To calculate Schlick F herereturn F0 + (1.0 - F0) * pow(1.0 - dot(V, H), 5.0);
}//https://blog.selfshadow.com/publications/s2017-shading-course/imageworks/s2017_pbs_imageworks_slides_v2.pdf
vec3 AverageFresnel(vec3 r, vec3 g)
{return vec3(0.087237) + 0.0230685*g - 0.0864902*g*g + 0.0774594*g*g*g+ 0.782654*r - 0.136432*r*r + 0.278708*r*r*r+ 0.19744*g*r + 0.0360605*g*g*r - 0.2586*g*r*r;
}vec3 MultiScatterBRDF(float NdotL, float NdotV)
{vec3 albedo = pow(texture2D(uAlbedoMap, vTextureCoord).rgb, vec3(2.2));vec3 E_o = texture2D(uBRDFLut, vec2(NdotL, uRoughness)).xyz;vec3 E_i = texture2D(uBRDFLut, vec2(NdotV, uRoughness)).xyz;vec3 E_avg = texture2D(uEavgLut, vec2(0, uRoughness)).xyz;// coppervec3 edgetint = vec3(0.827, 0.792, 0.678);vec3 F_avg = AverageFresnel(albedo, edgetint);// TODO: To calculate fms and missing energy herevec3 fms = ( vec3(1.0) - E_o ) * (vec3(1.0) - E_i) / ( PI * (vec3(1.0) - E_avg) );vec3 fadd = F_avg * E_avg / ( vec3(1.0) - F_avg * ( vec3(1.0) - E_avg ) );return fms * fadd;return vec3(1.0);}void main(void) {vec3 albedo = pow(texture2D(uAlbedoMap, vTextureCoord).rgb, vec3(2.2));vec3 N = normalize(vNormal);vec3 V = normalize(uCameraPos - vFragPos);float NdotV = max(dot(N, V), 0.0);vec3 F0 = vec3(0.04); F0 = mix(F0, albedo, uMetallic);vec3 Lo = vec3(0.0);// calculate per-light radiancevec3 L = normalize(uLightDir);vec3 H = normalize(V + L);float distance = length(uLightPos - vFragPos);float attenuation = 1.0 / (distance * distance);vec3 radiance = uLightRadiance;float NDF = DistributionGGX(N, H, uRoughness);   float G   = GeometrySmith(N, V, L, uRoughness);vec3 F = fresnelSchlick(F0, V, H);vec3 numerator    = NDF * G * F; float denominator = 4.0 * max(dot(N, V), 0.0) * max(dot(N, L), 0.0);vec3 Fmicro = numerator / max(denominator, 0.001); float NdotL = max(dot(N, L), 0.0);        vec3 Fms = MultiScatterBRDF(NdotL, NdotV);vec3 BRDF = Fmicro + Fms;Lo += BRDF * radiance * NdotL;vec3 color = Lo;color = color / (color + vec3(1.0));color = pow(color, vec3(1.0/2.2)); gl_FragColor = vec4(color, 1.0);}

结果

        没有采用重要性采样得到的E(μ)的结果

        

        采用重要性采样后得到的E(μ)的结果

        

        Eavg的结果

        

        在渲染端使用预计算后补偿能量的结果

         

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/415578.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【pgAdmin4】创建/删除:数据库Database和数据库表Table

目录 0.环境 1.简介 2.详细步骤 1&#xff09;创建数据库 法一&#xff1a;UI界面创建 法二&#xff1a;sql语句创建数据库 2&#xff09;创建数据库表 查看数据库表 查看数据库表内容 法一&#xff1a;UI界面创建数据库表 法二&#xff1a;sql语句创建数据库表 3&…

快专利与慢专利:速度与质量的天平

在当今快速发展的科技时代&#xff0c;专利成为了创新成果的重要保护手段。然而&#xff0c;不同的创新有着不同的节奏&#xff0c;由此也产生了“快专利”与“慢专利”之分。快专利以其迅速的申请和应用&#xff0c;为创新者抢占市场先机&#xff1b;慢专利则凭借深度的研发和…

【Redis之一:下载安装Redis】

Redis下载与安装 一、下载 Redis 安装包1、 Windows 安装包下载 二、安装Redis1、 Windows 安装Redis 三、配置 Redis1、 Windows 中配置 Redis&#xff08;1&#xff09;配置访问密码&#xff08;2&#xff09;重启 Redis 服务 三、访问 Redis1、命令行访问 Redis&#xff08;…

【福利】最新可用!谷歌搜索和谷歌学术的镜像网站

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你&#xff0c;欢迎[点赞、收藏、关注]哦~ 内容来自&#xff1a;https://www.80srz.com/posts/1633.html 谷歌搜索镜像 Google搜索镜像1&#xff1a;https://g.savalone.com/ Google搜索镜像2&…

Python基础笔记

一、python基础1.1 基础知识1.1.1 注释 注释&#xff1a;在程序中对程序代码进行解释说明的文字。 作用&#xff1a;注释不是程序&#xff0c;不能被执行&#xff0c;只是对程序代码进行解释说明&#xff0c;让别人可以看懂程序代码的作用&#xff0c;能够大大增强程序的可读性…

LabVIEW电机多次调用

在LabVIEW中&#xff0c;为实现对多个电机的独立控制&#xff0c;工程师可以采用可重入VI、动态VI调用、多任务结构或面向对象编程等方法。每种方法都有其优点和适用场景&#xff0c;选择合适的方法能有效提升系统的性能和可维护性。 在LabVIEW中&#xff0c;如果需要多次调用…

遥控器显示分别对应的无人机状态详解!!

1. 电量显示 遥控器电量&#xff1a;遥控器上通常会显示自身的电池电量&#xff0c;以提醒用户及时充电。 无人机电量&#xff1a;部分高端遥控器还会显示无人机的电池电量&#xff0c;以进度条或百分比的形式表示&#xff0c;帮助用户了解无人机的续航能力。 2. 飞行模式与…

Mybatis框架——缓存(一级缓存,二级缓存)

本章将简单介绍Mybatis框架中的缓存&#xff0c;欢迎大家点赞➕收藏&#xff0c;蟹蟹&#xff01;&#xff01;&#xff01;&#x1f495; &#x1f308;个人主页&#xff1a;404_NOT_FOUND &#x1f308;MyBatis环境搭建步骤&#xff08;超全解析&#xff01;&#xff01;&am…

hackme靶机攻略

1.通过nmap扫描靶场ip 2.目录扫描 3.找出文件存储位置&#xff0c;看看哪里可以上传文件 4.注册账号登录一下 点击search 5.输入1 and 11 -- 1 and 12 --看看有无SQL注入 6.判断字段数 1 order by 3 -- 说明字段数是3 7.查看数据库 -1 union select database(),2,3 # 8.查…

【Linux】使用Linux实现小程序 - 进度条

目录 一、缓冲区二、回车换行的概念三、进度条的设计3.1 版本1&#xff08;没有配合场景&#xff09;3.2 版本2&#xff08;配合场景&#xff09;3.3 版本3&#xff08;美化进度条&#xff09; 结尾 一、缓冲区 C/C语言&#xff0c;会针对标准输出&#xff0c;给我们提供默认的…

Python数据分析实战,兰州市二手房市场深度分析

作为购房者&#xff0c;除了关注地段与价格外&#xff0c;房屋的总价与面积的关系&#xff0c;以及房屋朝向的选择&#xff0c;同样是决策过程中的关键因素。那么&#xff0c;兰州市的二手房市场中&#xff0c;房屋总价与面积之间究竟存在怎样的关系&#xff1f;各个朝向的房源…

day-48 分割回文串

思路 利用dfs算法&#xff0c;用ids表示当前所指向字符的位置&#xff0c;依次判断s.charAt(ids),s.charAt(ids)s.charAt(ids1)…是否为回文字符串&#xff0c;如果是则加入链表p,再递归调用dfs函数 解题过程 每次调用dfs函数后记得还原现场 Code class Solution {public St…

宝藏!《联盟自控基础班筑基题库》(凤凰篇) 1-8章:甄选部分

本文内容&#xff0c;全部选自自动化考研联盟的&#xff1a;初试《自控基础班筑基题库》(凤凰篇)。 Part1&#xff1a;资料封面&目录 Part2&#xff1a;资料各个章节具体内容 第1章 自动控制的基本概念 第2章 控制系统的数学模型 第3章 控制系统的时域分析 第4章 根轨迹法…

探索ArrayList的线程不安全性

文章目录 概要示例代码原因解决用 synchronized 保证安全添加元素其他方法 总结 概要 要测试ArrayList的线程不安全性&#xff0c;可以创建多个线程同时对 ArrayList 进行修改操作&#xff08;如添加、删除元素&#xff09;&#xff0c;并观察是否会引发异常或数据不一致的问题…

unity游戏开发——标记物体 一目了然

Unity游戏开发:标记物体,让开发变得一目了然 “好读书&#xff0c;不求甚解&#xff1b;每有会意&#xff0c;便欣然忘食。” 本文目录&#xff1a; Unity游戏开发 Unity游戏开发:标记物体,让开发变得一目了然前言1. 什么是Tag&#xff1f;2. Unity中如何添加和管理Tag步骤1&am…

微电网管理系统

微电网管理系统 1. 相关概念简介 基本概念及分析意义&#xff1a; 微电网基本概念&#xff1a;微电网&#xff08;MG&#xff09;由分布式电源、用电负荷、能量管理系统等组成&#xff0c;是一个能够基本实现内部电力电量平衡的供用电系统。 通过整合分布式电源、储能、负荷…

阿里巴巴数学竞赛成绩未公布:背后的权衡与期待

文 | 头部财经首席评论员白立新 发布 | 头部财经 top168.com 导语&#xff1a;2024 年阿里巴巴数学竞赛成绩迟未公布&#xff0c;引发广泛猜测。中专生姜萍的表现备受瞩目&#xff0c;达摩院陷入两难困境。这场竞赛结果的公布&#xff0c;关乎多方利益与社会影响&#xff0c;…

学习之git

github 创建远程仓库 代码推送 Push 代码拉取 Pull 代码克隆 Clone SSH免密登录 Idea集成GitHubGitee码云 码云创建远程仓库 Idea集成Gitee码云 码云连接Github进行代码的复制和迁移GitLab gitlab服务器的搭建和部署 Idea集成GitLabgit概述 一切皆本地 版本控制工具 集中…

服务器数据恢复—磁盘坏扇区导致raid6阵列崩溃的数据恢复案例

服务器存储数据恢复环境&#xff1a; 一台存储中有一组由12块SAS硬盘组建的raid6磁盘阵列&#xff0c;划分了1个卷&#xff0c;由数台Vmware ESXI主机共享存储。卷中存放了大量的Windows系统虚拟机。这些虚拟机系统盘大小一致&#xff0c;数据盘大小不确定&#xff0c;数据盘都…

8连接数据与决策:信息系统基础概念解读

信息系统概述 信息系统是由计算机硬件、网络和通信设备、计算机软件、信息资源、信息用户和规章制度组成的以处理信息流为目的的人机一体化系统。 信息系统的5个基本功能&#xff1a;输入、存储、处理、输出和控制。 信息系统的性质影响着系统开发者和系统用户的知识需求。“…