question:

brdf中的几何因子考虑了微表面的自遮挡,当表面粗糙度较大或者与法线夹角越大时,这个因子越小,导致颜色越暗。这部分能量相等于直接忽略掉了,实际上被遮挡的光线会被反射,然后经过若干次反射,从另一点以另一角度重新进入视线。因此需要将缺失的能量重新补回来。

Kulla-Conty近似

Kulla-Conty近似额外添加了一个补充的brdf系数,使得总能量守恒。

引入颜色

注意Kulla-Conty实现的是:1次弹射+能量补充,而一次弹射的内容里面是已经有考虑颜色的菲涅尔项了,但是能量的补充我们始终认为是对于一个没有颜色的,F值为1的东西进行的补充【上边推导过程假设了F为1】,这对于有颜色的东西会补充过多,因为F小于1时,多次弹射时每次都会被吸收一部分。因此我们这里考虑颜色是对于补充的那一部分考虑颜色,对补充的能量进行衰减!

现在我们要把这个菲涅尔项考虑进来了,但是这里注意,这个菲涅尔的值,是和角度有关系的,如果在这里再考虑上角度,其实最后我们是很难得到一个实时的结果的。

所以这里再次采用了一种近似的方案,就是把菲涅尔选择一个各角度下的平均值,不管角度是多少都用这一个菲涅尔值。

这个平均和我们之前的E_avg

是一样的思路:

\[F_{avg} = \frac{\int_0^1{F(u)udu}}{\int_0^1{udu}}=2\int_0^1{F(u)udu}
\]

代码

离线计算E(u)

Vec3f IntegrateBRDF(Vec3f V, float roughness) {
float A = 0.0;
float B = 0.0;
float C = 0.0;
const int sample_count = 2048;
Vec3f N = Vec3f(0.0, 0.0, 1.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 1
float g = GeometrySmith(roughness, NoV, NoL);
float w = VoH * g / (NoV*NoH);
A += w;
B += w;
C += w;
// Split Sum - Bonus 2
}
//return { 1.0f - A / sample_count,1.0f - B / sample_count, 1.0f - C / sample_count };
return { A / static_cast<float>(sample_count), B / static_cast<float>(sample_count), C / static_cast<float>(sample_count) };
//return Vec3f(1.0f);
}

结果:

离线计算E_avg:

Vec3f IntegrateEmu(Vec3f V, float roughness, float NdotV, Vec3f Ei) {
Vec3f Eavg = Vec3f(0.0f);
const int sample_count = 1024;
Vec3f N = Vec3f(0.0, 0.0, 1.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 Eavg here - Bonus 1 }
*/
return Ei * NdotV * 2;
//NdotV = 1.0f - NdotV;
//return Ei * sqrt(1 - NdotV * NdotV) * 2;
//return Vec3f(1.0);
} int main() {
unsigned char *Edata = stbi_load("./GGX_E_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 the data written to the texture
uint8_t data[128 * 128 * 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);
//resolution - 1 - i
//这里非常隐晦,与产生图像是顺序相反
//产生图像时,theta从2/Pi到0
//这里theta从0到2/Pi进行访问
//这里的NdotV范围从0到1均匀采样,相当于sin(theta)
Vec3f Ei = getEmu((resolution - 1 - i), j, 0, Edata, NdotV, roughness);
Eavg += IntegrateEmu(V, roughness, NdotV, Ei) * 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;
}

结果:

运行时,KullaContyFragment.glsl:

#ifdef GL_ES
precision mediump float;
#endif uniform 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 here
float a = roughness*roughness;
float a2 = a*a;
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 / max(denom, 0.0001);
} float GeometrySchlickGGX(float NdotV, float roughness)
{
// TODO: To calculate Schlick G1 here
float a = roughness;
//float k = (a +1.0)*(a+1.0) / 8.0;
float k = (a * a) / 2.0;//用这个才能得到assignment4.pdf中的效果图
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 here
return GeometrySchlickGGX(max(dot(N,V),0.0),roughness)*GeometrySchlickGGX(max(dot(N,L),0.0),roughness);
} vec3 fresnelSchlick(vec3 F0, vec3 V, vec3 H)
{
// TODO: To calculate Schlick F here
float HoV = max(dot(V,H), 0.0);
vec3 f = F0 + (vec3(1.0) - F0) * pow((1.0 - HoV), 5.0);
// TODO: To calculate Schlick F here
return f;
} //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;
// copper
vec3 edgetint = vec3(0.827, 0.792, 0.678);
vec3 F_avg = AverageFresnel(albedo, edgetint); // TODO: To calculate fms and missing energy here
float fms = (1.0-E_o.x)*(1.0-E_i.x)/(PI*(1.0-E_avg.x));
vec3 fadd = F_avg*E_avg/(vec3(1.0)-F_avg*(vec3(1.0)-E_avg)); return vec3(fms)*fadd; } 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 radiance
vec3 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); }

结果:

上一排是使用了Kulla-Cony BRDF,下一排使用了没有添加补充的BRDF,可以看到在低粗糙度时,使用了Kulla-Cony BRDF的要更亮一些。

Kulla-Conty BRDF的更多相关文章

  1. 图形学理论知识 BRDF 双向反射分布函数(Bidirectional Reflectance Distribution Function)

    图形学理论知识 BRDF 双向反射分布函数 Bidirectional Reflectance Distribution Function BRDF理论 BRDF表示的是双向反射分布函数(Bidire ...

  2. Ward BRDF实现心得

    最近做了Ward BRDF的实现,相对于之前的lambert,phong来说,Ward是一个真正意义上的各向异性BRDF,但同样的,Ward模型也是一个基于经验的模型,并不是物理上正确的.它由ward ...

  3. Unity3D ShaderLab BRDF模拟

    Unity3D ShaderLab BRDF模拟 在上一篇,说到了使用渐变纹理着色,使用一个值来控制纹理的uv坐标,但是这也就表示我们只能得到一个线性的光照效果. 那么我们能不能通过观察方向的向量结合 ...

  4. Unity3d 基于物理渲染Physically-Based Rendering之specular BRDF

    在实时渲染中Physically-Based Rendering(PBR)中文为基于物理的渲染它能为渲染的物体带来更真实的效果,而且能量守恒 稍微解释一下字母的意思,为对后文的理解有帮助,从右到左L为 ...

  5. 【Unity Shaders】Diffuse Shading——使用2D ramp texture来创建一个假的BRDF(双向反射分布函数)

    本系列主要参考<Unity Shaders and Effects Cookbook>一书(感谢原书作者),同时会加上一点个人理解或拓展. 这里是本书所有的插图.这里是本书所需的代码和资源 ...

  6. Lambert漫反射的BRDF

    Lambert漫反射brdf=Albedo/pi. 推导: 如图,设Lambert面元获得入射照度Ei,各方向均匀释放亮度,每个方向释放的亮度都是Lo. 又设此表面反射率为Albedo,根据反射率定义 ...

  7. (z转)基于CPU的Bank BRDF经验模型,实现各向异性光照效果!

    摘抄“GPU Programming And Cg Language Primer 1rd Edition” 中文 名“GPU编程与CG语言之阳春白雪下里巴人” BRDF 光照模型 10.2.1 什么 ...

  8. (转)图形学理论知识 BRDF 双向反射分布函数(Bidirectional Reflectance Distribution Function)

    BRDF理论 BRDF表示的是双向反射分布函数(Bidirectional Reflectance Distribution Function),它描述了光线如何在物体表面进行反射,可以用来描述材质属 ...

  9. 使用RampTexture实现BRDF效果

    [使用RampTexture实现BRDF效果] BRDF stands for bidirectional reflectance distribution function. While that ...

  10. standard pbr(三)-BRDF

    // Default BRDF to use: #if !defined (UNITY_BRDF_PBS) // allow to explicitly override BRDF in custom ...

随机推荐

  1. 一键语法错误增强工具 ChineseErrorCorrector

    一键语法错误增强工具 欢迎使用我最近开源的使用一键语法错误增强工具,该工具可以进行14种语法错误的增强,不同行业可以根据自己的数据进行错误替换,来训练自己的语法和拼写模型,希望推动行业文本纠错的发展, ...

  2. SpringBoot 配置统一API对象返回

    1.前言 在实际项目开发中,为了便于前端进行响应处理,需要统一返回类格式.特别是在有多个后端开发人员参与的情况下,如果不规范返回类,每个人按照个人习惯返回数据,前端将面临各式各样的返回数据,难以统一处 ...

  3. 【Vue】 vue-element-admin 路由菜单配置

    路由说明见官方文档: https://panjiachen.gitee.io/vue-element-admin-site/zh/guide/essentials/router-and-nav.htm ...

  4. 学术写作: These authors contributed equally to this work. —— 共同一作

    早些年很少看到论文里面有: These authors contributed equally to this work. 不过现在这种方法在论文中出现的还是比较多的,说白了,这种共同一作的声明其实是 ...

  5. 国产的huggingface —— modelscope

    国产的huggingface: https://www.modelscope.cn/

  6. 破局SAP实施难题、降低开发难度,定制化需求怎样快速上线?

    前言 SAP 是全球领先的业务流程管理软件供应商之一,其提供广泛的模块化解决方案和套件,所开发的软件解决方案面向各种规模的企业,帮助客户规划和设计业务流程.分析并高效设计整个价值链,以更好的了解和响应 ...

  7. 恭喜又一白鲸开源成员成为 Apache SeaTunnel PMC Member

    个人简介 王海林 白鲸开源研发工程师 GitHub ID:hailin0 做过性能监控.数据开发平台等,目前聚焦在数据集成同步及其周边生态的研发 问:作为白鲸开源的一员,您为社区做出过哪些贡献?具体方 ...

  8. 快速基于 ClickHouse + Grafana 搭建可观测性解决方案 - 分布式链路追踪篇(ClickHouse 官方博客)

    引言 在 ClickHouse,我们认为可观测性仅仅是另一个实时分析问题.作为一款高性能的实时分析数据库,ClickHouse 被用于多种场景,包括时间序列数据的实时分析.其应用场景的多样性推动了大量 ...

  9. win+jenkins+git+allure+tomcat+jdk部署(万人坑)

    万人坑:之所以叫万人坑,是一些网站真的是非常非常非常坑,且不说按照他们的方法一次都没成功,还遇到了各种千奇百怪的问题,浪费了本人3.1415926h的时间,真想给他们竖起一个超大号的倒拇指.下面开始踩 ...

  10. 华为交换机S5700-52C-EI配置以太网和snmp服务

    配置以太网 通过超级终端Hyper Terminal和console串口线链接华为交换机 # 用超级终端打开,配上串口线,用9600波特率链接 system-view interface Vlanif ...