文章目录

  • 目的
  • 环境
  • PBR 主要渲染方程
    • D 项
      • GGB(desmos)
      • D_Term 完整 Shader
    • G 项
      • GGB
      • G_Term 完整 Shader
    • F 项
      • GGB
      • F_Term 完整 Shader
    • D, G, F 带入公式
      • PBR_Test_DGF.hlsl
      • DGF_Term
  • 应用到具体 PBR 素材上
    • 完整 Shader - 只有 PBR + SH(Reflection Probe) + Emissive
      • PBR__Emissive_Lib.hlsl
      • URP_PBR_Emissive.shader
  • References

目的

这篇文章的目的:只为了备忘、和便于回顾、复习

只适合本人自己查看


环境

下面代码的运行环境

  • Unity : 2019.3.11f1
  • Pipeline : URP

这篇文章,在 2021/12 - 2022/2 初,断断续续的时间片抄完的笔记

参考资料具体查看:Reference

下面多数是抄出来的作业,少部分有自己的理解、调整的东西

这篇 PBR 中,很多都是经过各个 optimization 之后的分支代码


PBR 主要渲染方程

Lo(p,wo)=∫Ω(kdcπ+ksDGF4(wo⋅n)(wi⋅n))Li(p,wi)(wi⋅n)dwiL_o(p,w_o)=\int_{\Omega}(k_d \frac{c}{\pi} + k_s \frac {D G F}{4(w_o \cdot n)(w_i \cdot n)}) L_i(p,w_i)(w_i \cdot n) dw_i Lo​(p,wo​)=∫Ω​(kd​πc​+ks​4(wo​⋅n)(wi​⋅n)DGF​)Li​(p,wi​)(wi​⋅n)dwi​

  • D - 法线分布函数:描述微表面法线 N 和 半角 H 同向性的比重,光滑度越高,也就是粗糙度越低(越光滑),那么N,H 的同向性越大(反射越清晰)
  • G - 后面补上,看下面抄的作业中的描述,也时可以,但是还不够详细
  • F - 同上

D 项

法线分布函数GGX(Normal Distribution Function),即核心方程的D项

D - 法线分布函数:描述微表面法线 N 和 半角 H 同向性的比重,光滑度越高,也就是粗糙度越低(越光滑),那么N,H 的同向性越大(反射越清晰)

D=a2π((n⋅h)2(a2−1)+1)2D = \frac {a^2} {\pi ((n \cdot h)^2 (a^2-1)+1)^2} D=π((n⋅h)2(a2−1)+1)2a2​

  • aaa - 粗糙度
  • nnn - 法线
  • hhh - normalize(L + V),是方向与光源方向的半角向量,这里的光源方向指 计算点到光源的方向,不是光源照射的方向
  • n⋅hn \cdot hn⋅h - NdotH - 法线与半角向量的点乘

核心函数

// #define PI 3.1415926
// D 项 法线微表面分布函数
float D_Function(float NdotH, float roughness)
{float a2 = roughness * roughness;float NdotH2 = NdotH * NdotH;float d = (NdotH2 * (a2 - 1) + 1);d = d * d;// * PI;return a2 / d;
}

GGB(desmos)

这次使用的是 desmos 不是 GGM,也是也差不多,可以看到 D_Term 只是一个拟合曲线(只要横向在 0-1 的部分曲线),来达到近视对应 物理仪器采集的数值对应的模型,下面是可以看到 NdotH 0 - 1 的变化:下压 到 上拱

D_Term 完整 Shader

// jave.lin 2022/01/18
// PBR GGX D TermShader "Test/URP_PBR_D_Term"
{Properties{_Roughness ("Roughness", Range(0, 1)) = 0.5}SubShader{Tags{"RenderPipeline" = "UniversalRenderPipeline"}Pass{HLSLPROGRAM#pragma vertex vert#pragma fragment frag#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"struct appdata{float4 positionOS : POSITION;half3 normalOS : NORMAL;};struct v2f{float4 positionCS : SV_POSITION;half3 normalWS : TEXCOORD0;float3 positionWS : TEXCOORD1;};half _Roughness;// #define PI 3.1415926// D 项 法线微表面分布函数float D_Function(float NdotH, float roughness){float a2 = roughness * roughness;float NdotH2 = NdotH * NdotH;float d = (NdotH2 * (a2 - 1) + 1);d = d * d;// * PI;return a2 / d;}v2f vert (appdata v){v2f o;o.positionCS = TransformObjectToHClip(v.positionOS.xyz);o.positionWS = TransformObjectToWorld(v.positionOS.xyz);o.normalWS = TransformObjectToWorldNormal(v.normalOS.xyz);return o;}half4 frag (v2f i) : SV_Target{half3 N = normalize(i.normalWS);// return half4(N, 1);Light mainLight = GetMainLight();half3 L = normalize(mainLight.direction);// return half4(L, 1);half3 V = SafeNormalize(_WorldSpaceCameraPos - i.positionWS);// return half4(V, 1);half3 H = normalize(L + V);// return half4(H, 1);half HdotN = max(dot(H, N), 1e-5);return D_Function(HdotN, _Roughness);}ENDHLSL}}
}


G 项

几何函数G,(Geometry function),即核心方程的G项,它是描述入射射线(即光照方向)和出射射线(视线方向)被自己的微观几何形状遮挡的比重。

G=n⋅llerp(n⋅l,1,k)×n⋅vlerp(n⋅v,1,k)G = \frac {n \cdot l} {lerp(n \cdot l, 1, k)} \times \frac {n \cdot v} {lerp(n \cdot v, 1, k)} G=lerp(n⋅l,1,k)n⋅l​×lerp(n⋅v,1,k)n⋅v​

  • nnn - 法线
  • lll - 灯光方向(不是入射方向,即:入射反向,从计算点到光源的方向)
  • kkk - NdotL 的过去系数,这里使用拟合曲线:float k = pow(1 + roughness, 2) * 0.5;,下面的 GGB 有图形化该曲线样式

G项是由2个几乎一样的子项相乘得来,故我们可以先定义子项的函数

// G项子项
inline float G_subSection(float dot, float k)
{return dot / lerp(dot, 1, k);
}

再去定义真正的G项,把dot(N,L)dot(N, L)dot(N,L)和dot(N,V)dot(N, V)dot(N,V)代入子项,相乘即可

// G项float G_Function(float NdotL, float NdotV, float roughness){// method1-k:// // float k = pow(1 + roughness, 2) / 8.0;// // method2-k:// const float d = 1.0 / 8.0;// float k = pow(1 + roughness, 2) * d;// method3-k:float k = pow(1 + roughness, 2) * 0.5;return G_subSection(NdotL, k) * G_subSection(NdotV, k);}

GGB

k 曲线,变量 roughness 在 0~1 的范围,值域为:0.5~2.0

G_Term 完整 Shader

// jave.lin 2022/01/19
// PBR G TermShader "Test/URP_PBR_G_Term"
{Properties{_Roughness ("Roughness", Range(0, 1)) = 0.5}SubShader{Tags{"RenderPipeline" = "UniversalRenderPipeline"}Pass{HLSLPROGRAM#pragma vertex vert#pragma fragment frag#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"struct appdata{float4 positionOS : POSITION;half3 normalOS : NORMAL;};struct v2f{float4 positionCS : SV_POSITION;half3 normalWS : TEXCOORD0;float3 positionWS : TEXCOORD1;};half _Roughness;// G项子项inline float G_subSection(float dot, float k){return dot / lerp(dot, 1, k);}// G项float G_Function(float NdotL, float NdotV, float roughness){// method1-k:// // float k = pow(1 + roughness, 2) / 8.0;// // method2-k:// const float d = 1.0 / 8.0;// float k = pow(1 + roughness, 2) * d;// method3-k:float k = pow(1 + roughness, 2) * 0.5;return G_subSection(NdotL, k) * G_subSection(NdotV, k);}v2f vert (appdata v){v2f o;o.positionCS = TransformObjectToHClip(v.positionOS.xyz);o.positionWS = TransformObjectToWorld(v.positionOS.xyz);o.normalWS = TransformObjectToWorldNormal(v.normalOS.xyz);return o;}half4 frag (v2f i) : SV_Target{half3 N = normalize(i.normalWS);// return half4(N, 1);Light mainLight = GetMainLight();half3 L = normalize(mainLight.direction);// return half4(L, 1);half3 V = SafeNormalize(_WorldSpaceCameraPos - i.positionWS);// return half4(V, 1);// half3 H = normalize(L + V);// return half4(H, 1);half NdotL = max(dot(N, L), 1e-5);half NdotV = max(dot(N, V), 1e-5);return G_Function(NdotL, NdotV, _Roughness);}ENDHLSL}}
}


F 项

菲涅尔函数F(Fresnel equation),即核心方程的F项

菲涅尔反射(Fresnel Reflectance)。光线以不同角度入射会有不同的反射率。相同的入射角度,不同的物质也会有不同的反射率。万物皆有菲涅尔反射。F0是即 0 度角入射的菲涅尔反射值。大多数非金属F0范围是 0.02 - 0.04,大多数金属F0范围是 0.7 - 1.0

如下图(UE4 PBR文档的图片):

下面是 Schlick 的模型的公式对应的代码:

F=lerp((1−(n⋅v))5,1,F0)F = lerp((1-(n \cdot v))^5, 1, F_0) F=lerp((1−(n⋅v))5,1,F0​)

但是由于我们需要的法线方向并不是模型本身的宏观法线n,而是经过D项筛选通过的微观法线H,故需把N改成H,再将 Schlick 调整为如下数学公式(为何我觉得上面的公司更为直观,-_-,大概是因为更代码化)

FSchlick(h,v,F0)=F0+(1−F0)(1−(h⋅v))5F_{Schlick} (h, v, F_0) = F_0 + (1 - F_0)(1 - (h \cdot v))^5 FSchlick​(h,v,F0​)=F0​+(1−F0​)(1−(h⋅v))5

后来unity对它进行了优化(Optimizing GGX Shaders with dot(L,H)),视线方向V换成了L,如下所示,这是我们所使用的函数

F(l,h)=F0+(1−F0)(1−l⋅h)5F(l, h) = F_0 + (1 - F_0) (1 - l \cdot h)^5 F(l,h)=F0​+(1−F0​)(1−l⋅h)5

其中的5次方计算量比较大,把它变成自然对数函数进行计算可以节省计算量,后续文章里所有的5次方计算都可以换算成对数计算

xy=eylnxx^y = e^{y \space ln \space x} xy=ey ln x

故最终,我们的菲涅尔函数如下。

// method1:
// F项inline half3 F0_Function(half3 albedo, half metallic)
{// jave.lin :// 非金属一般都是纯灰度:0.02 ~ 0.04// 金属一般都是纯颜色:0.7 ~ 1.0 之间的,这里我们简单的使用一个 lerp 来模拟// 后续其他版本的 pbr 可以调整为更加效果的return lerp(0.04, albedo, metallic);
}half3 F_Function(float HdotL, half3 F)
{// jave.lin : (1-x)^5 转为性能更高的来模拟:2^((-5.55473 * x- 6.98316) * x)half Fre = exp2((-5.55473 * HdotL - 6.98316) * HdotL);return lerp(Fre, 1, F);
}// // method2:
// // F项
// half3 F_Function(float HdotL, half3 F)
// {//     half Fre = pow(1 - HdotL, 5);
//     return lerp(Fre, 1, F);
// }

GGB

F_Term 完整 Shader

// jave.lin 2022/01/19
// PBR F TermShader "Test/URP_PBR_F_Term"
{Properties{_Color ("Color", Color) = (1, 1, 1, 1)_Metallic ("_Metallic", Range(0, 1)) = 0.5}SubShader{Tags{"RenderPipeline" = "UniversalRenderPipeline"}Pass{HLSLPROGRAM#pragma vertex vert#pragma fragment frag#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"struct appdata{float4 positionOS : POSITION;half3 normalOS : NORMAL;};struct v2f{float4 positionCS : SV_POSITION;half3 normalWS : TEXCOORD0;float3 positionWS : TEXCOORD1;};half4 _Color;half _Metallic;// method1:// F项inline half3 F0_Function(half3 albedo, half metallic){// jave.lin :// 非金属一般都是纯灰度:0.02 ~ 0.04// 金属一般都是纯颜色:0.7 ~ 1.0 之间的,这里我们简单的使用一个 lerp 来模拟// 后续其他版本的 pbr 可以调整为更加效果的return lerp(0.04, albedo, metallic);}half3 F_Function(float HdotL, half3 F){// jave.lin : (1-x)^5 转为性能更高的来模拟:2^((-5.55473 * x- 6.98316) * x)half Fre = exp2((-5.55473 * HdotL - 6.98316) * HdotL);return lerp(Fre, 1, F);}// // method2:// // F项// half3 F_Function(float HdotL, half3 F)// {//     half Fre = pow(1 - HdotL, 5);//     return lerp(Fre, 1, F);// }v2f vert (appdata v){v2f o;o.positionCS = TransformObjectToHClip(v.positionOS.xyz);o.positionWS = TransformObjectToWorld(v.positionOS.xyz);o.normalWS = TransformObjectToWorldNormal(v.normalOS.xyz);return o;}half4 frag (v2f i) : SV_Target{// half3 N = normalize(i.normalWS);// return half4(N, 1);Light mainLight = GetMainLight();half3 L = normalize(mainLight.direction);// return half4(L, 1);half3 V = SafeNormalize(_WorldSpaceCameraPos - i.positionWS);// return half4(V, 1);half3 H = normalize(L + V);// return half4(H, 1);// half3 F = lerp(0.04, _Color.rgb, _Metallic);half HdotL = max(dot(H, L), 1e-5);half3 F = F0_Function(_Color.rgb, _Metallic);return half4(F_Function(HdotL, F), 1);}ENDHLSL}}
}


D, G, F 带入公式

直接光的高光部分:我们把D,G,F代入公式,先计算出高光部分。注意这里经过半球积分后会乘PI,不要丢了;注意这里并未再次乘KS,因为KS=F,已经计算过了一次不需要重复计算。

halfHdotN = max(dot(H, N), 1e-5);
half NdotL = max(dot(N, L), 1e-5);
half NdotV = max(dot(N, V), 1e-5);
half HdotL = max(dot(H, L), 1e-5);// ======= Direct START ======half3 F = F0_Function(_Color.rgb, _Metallic);
half Direct_D = D_Function(HdotN, _Roughness);
half Direct_G = G_Function(NdotL, NdotV, _Roughness);
half3 Direct_F = F_Function(HdotL, F);
half3 BRDFSpecSection = (Direct_D * Direct_G) * Direct_F / (4 * NdotL * NdotV);
half3 DirectSpeColor = BRDFSpecSection * mainLight.color * NdotL * PI;
return half4(DirectSpeColor, 1);
// ======= Direct END ======

PBR_Test_DGF.hlsl

#ifndef __PBR_TEST_DGF_H__
#define __PBR_TEST_DGF_H__// jave.lin : 2022/01/19half4 _Color;
half _Metallic;
half _Roughness; // 后续改到 ubo / cbuffer 中// D 项 法线微表面分布函数
float D_Function(float NdotH, float roughness)
{float a2 = roughness * roughness;float NdotH2 = NdotH * NdotH;float d = (NdotH2 * (a2 - 1) + 1);d = d * d;// * PI;return a2 / d;
}// G项子项
inline float G_subSection(float dot, float k)
{return dot / lerp(dot, 1, k);
}
// G项
float G_Function(float NdotL, float NdotV, float roughness)
{// method1-k:// // float k = pow(1 + roughness, 2) / 8.0;// // method2-k:// const float d = 1.0 / 8.0;// float k = pow(1 + roughness, 2) * d;// method3-k:float k = pow(1 + roughness, 2) * 0.5;return G_subSection(NdotL, k) * G_subSection(NdotV, k);
}// method1:
// F项
half3 F_Function(float HdotL, half3 F0)
{// jave.lin : (1-x)^5 转为性能更高的来模拟:2^((-5.55473 * x- 6.98316) * x)half Fre = exp2((-5.55473 * HdotL - 6.98316) * HdotL);return lerp(Fre, 1, F0);
}// // method2:
// // F项
// half F_Function(float HdotL, half F0)
// {//     half Fre = pow(1 - HdotL, 5);
//     return lerp(Fre, 1, F0);
// }inline half3 F0_Function(half3 albedo, half metallic)
{// jave.lin :// 非金属一般都是纯灰度:0.02 ~ 0.04// 金属一般都是纯颜色:0.7 ~ 1.0 之间的,这里我们简单的使用一个 lerp 来模拟// 后续其他版本的 pbr 可以调整为更加效果的return lerp(0.04, albedo, metallic);
}#endif

DGF_Term

// jave.lin 2022/01/19
// PBR DGF TermShader "Test/URP_PBR_DGF_Term"
{Properties{_Color ("Color", Color) = (1, 1, 1, 1)_Metallic ("Metallic", Range(0, 1)) = 0.5_Roughness ("Roughness", Range(0, 1)) = 0.5}SubShader{Tags{"RenderPipeline" = "UniversalRenderPipeline"}Pass{HLSLPROGRAM#pragma vertex vert#pragma fragment frag#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"#include "Includes/PBR_Test_DGF.hlsl"struct appdata{float4 positionOS : POSITION;half3 normalOS : NORMAL;};struct v2f{float4 positionCS : SV_POSITION;half3 normalWS : TEXCOORD0;float3 positionWS : TEXCOORD1;};v2f vert (appdata v){v2f o;o.positionCS = TransformObjectToHClip(v.positionOS.xyz);o.positionWS = TransformObjectToWorld(v.positionOS.xyz);o.normalWS = TransformObjectToWorldNormal(v.normalOS.xyz);return o;}half4 frag (v2f i) : SV_Target{half3 N = normalize(i.normalWS);// return half4(N, 1);Light mainLight = GetMainLight();half3 L = normalize(mainLight.direction);// return half4(L, 1);half3 V = SafeNormalize(_WorldSpaceCameraPos - i.positionWS);// return half4(V, 1);half3 H = normalize(L + V);// return half4(H, 1);half HdotN = max(dot(H, N), 1e-5);half NdotL = max(dot(N, L), 1e-5);half NdotV = max(dot(N, V), 1e-5);half HdotL = max(dot(H, L), 1e-5);// ======= Direct START ======half3 F = F0_Function(_Color.rgb, _Metallic);half Direct_D = D_Function(HdotN, _Roughness);half Direct_G = G_Function(NdotL, NdotV, _Roughness);half3 Direct_F = F_Function(HdotL, F);half3 BRDFSpecSection = (Direct_D * Direct_G) * Direct_F / (4 * NdotL * NdotV);half3 DirectSpeColor = BRDFSpecSection * mainLight.color * NdotL * PI;return half4(DirectSpeColor, 1);// ======= Direct END ======}ENDHLSL}}
}


应用到具体 PBR 素材上

Vela 提供的模型 - 已备份到百度网盘,但不公开(700MB+)

在 substance 官方也可以直接下载到:Vela Template

左边是:SP,右边是 Unity URP 的 Custom PBR Shader(法线是 F0 的模拟效果不太理想,后续可以将 F0 修改为一些一些色阶图来返回 F0 的纯灰度 或是 纯金属的反射颜色才比较好的效果)

天空盒 和 灯光方向、颜色、等参数都不太一样,所以效果差异就更大了


完整 Shader - 只有 PBR + SH(Reflection Probe) + Emissive

阴影还没添加,后续再添加了


PBR__Emissive_Lib.hlsl


#ifndef __PBR_Emissive_LIB_H__#define __PBR_Emissive_LIB_H__// #ifdef HAS_HALF// #undef HAS_HALF// #define HAS_HALF 1// #endif// jave.lin : 2022/02/06#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"struct a2v{float4 positionOS : POSITION;float2 uv : TEXCOORD0;half3 normalOS : NORMAL;half4 tangentOS : TANGENT;float2 lightmapUV : TEXCOORD1;};struct v2f{float4 positionCS : SV_POSITION;float4 uv : TEXCOORD0; // xy: base map uv, zw: lightmap uvfloat4 normalWS : TEXCOORD1; // w: posWS.xfloat4 tangentWS : TEXCOORD2; // w: posWS.yfloat4 bitangentWS : TEXCOORD3; // w: posWS.z};TEXTURE2D(_BaseMap); SAMPLER(sampler_BaseMap);TEXTURE2D(_MaskMap); SAMPLER(sampler_MaskMap);TEXTURE2D(_NormalMap); SAMPLER(sampler_NormalMap);TEXTURE2D(_EmissiveMap); SAMPLER(sampler_EmissiveMap);CBUFFER_START(UnityPerMaterial)float4 _BaseMap_ST;half4 _BaseColor;half _Metallic;half _AO;half _Roughness;half _NormalScale;half _Emissive;half _EmissiveTwinkleFrequence;CBUFFER_END// ========== Direct START =============// 直接光照部分// ========== 直接光照 D项 START =============// 直接光照 D项 法线微表面分布函数half Direct_D_Function(half NdotH, half roughness){half a2 = Pow4(roughness);half d = (NdotH * NdotH * (a2 - 1.0) + 1.0);d = d * d;// *PI;return saturate(a2 / d);}// ========== 直接光照 D项 END =============// ========== 直接光照 G项 START =============// 直接光照 G项子项inline real Direct_G_subSection(half dot, half k){return dot / lerp(dot, 1, k);}// 直接光照 G项half Direct_G_Function(half NdotL, half NdotV, half roughness){// method1-k:// // half k = pow(1 + roughness, 2) / 8.0;// // method2-k:// const half d = 1.0 / 8.0;// half k = pow(1 + roughness, 2) * d;// method3-k:half k = pow(1 + roughness, 2) * 0.5;return Direct_G_subSection(NdotL, k) * Direct_G_subSection(NdotV, k);}// 模拟 G项:Kelemen-Szirmay-Kalos Geometry Factor// http://renderwonk.com/publications/s2010-shading-course/hoffman/s2010_physically_based_shading_hoffman_b.pdf// 搜索:Kelemen-Szirmay-Kalos Geometry Factorinline half Direct_G_Function_Kalos(half LdotH, half roughness){half k = pow(1 + roughness, 2) * 0.5;return Direct_G_subSection(LdotH, k);}// ========== 直接光照 G项 END =============// ========== 直接光照 F项 START =============// method1:// 直接光照 F项half3 Direct_F_Function(half HdotL, half3 F0){half Fre = exp2((-5.55473 * HdotL - 6.98316) * HdotL);return lerp(Fre, 1, F0);}// // method2:// // 直接光照 F项// half3 Direct_F_Function(half HdotL, half3 F0)// {//     half Fre = pow(1 - HdotL, 5);//     return lerp(Fre, 1, F0);// }// ========== 直接光照 F项 END =============// ========== 直接光照 F0 START =============inline half3 Direct_F0_Function(half3 albedo, half metallic){return lerp(0.04, albedo, metallic);}// ========== 直接光照 F0 END =============// ========== Direct END =============// ========== Indirect START =============real3 SH_IndirectionDiff(real3 normalWS){real4 SHCoefficients[7];SHCoefficients[0] = unity_SHAr;SHCoefficients[1] = unity_SHAg;SHCoefficients[2] = unity_SHAb;SHCoefficients[3] = unity_SHBr;SHCoefficients[4] = unity_SHBg;SHCoefficients[5] = unity_SHBb;SHCoefficients[6] = unity_SHC;real3 color = SampleSH9(SHCoefficients, normalWS);return max(0, color);}half3 Indirect_F_Function(half NdotV, half3 F0, half roughness){half fre = exp2((-5.55473 * NdotV - 6.98316) * NdotV);return F0 + fre * saturate(1 - roughness - F0);}half3 IndirectSpeCube(half3 normalWS, half3 viewWS, float roughness, half AO){half3 reflectDirWS = reflect(-viewWS, normalWS);roughness = roughness * (1.7 - 0.7 * roughness); // unity 内部不是线性 调整下 拟合曲线求近似,可以再 GGB 可视化曲线half mipmapLevel = roughness * 6; // 把粗糙度 remap 到 0~6 的 7个阶段,然后进行 texture lod 采样half4 specularColor = SAMPLE_TEXTURECUBE_LOD(unity_SpecCube0, samplerunity_SpecCube0, reflectDirWS, mipmapLevel); // 根据不同的 mipmap level 等级进行采样#if !defined(UNITY_USE_NATIVE_HDR)// 用 DecodeHDREnvironment 将解码 HDR 颜色值。// 可以看到采样出的 RGBM 是一个 4 通道的值// 最后的一个 M 存的是一个参数// 解码时将前三个通道表示的颜色乘上 x*(M^y)// x y 都是有环境贴图定义的系数// 存储在 unity_SpecCube0_HDR 这个结构中return DecodeHDREnvironment(specularColor, unity_SpecCube0_HDR) * AO;#elsereturn specularColor.rgb * AO;#endif}half3 IndirectSpeFactor(half roughness, half smoothness, half3 BRDFspe, half3 F0, half NdotV){#ifdef UNITY_COLORSPACE_GAMMAhalf SurReduction = 1 - 0.28 * roughness * roughness;#elsehalf SurReduction = 1 / (roughness * roughness + 1);#endif#if defined(SHADER_API_GLES) // Lighting.hlsl 261 行half Reflectivity = BRDFspe.x;#elsehalf Reflectivity = max(max(BRDFspe.x, BRDFspe.y), BRDFspe.z);#endifhalf GrazingTSection = saturate(Reflectivity + smoothness);half fre = Pow4(1 - NdotV); // Lighting.hlsl 第 501 行// half fre = exp2((-5.55473 * NdotV - 6.98316) * NdotV); // Lighting.hlsl 第 501 行,他是 4 次方,我们是 5 次方return lerp(F0, GrazingTSection, fre) * SurReduction;}// ========== Indirect END =============// ========== vert START ===========v2f vert(a2v i){v2f o = (v2f)0;float3 positionWS = TransformObjectToWorld(i.positionOS.xyz);o.positionCS = TransformWorldToHClip(positionWS);o.uv.xy = TRANSFORM_TEX(i.uv, _BaseMap);o.uv.zw = i.lightmapUV;o.normalWS.xyz = normalize(TransformObjectToWorldNormal(i.normalOS.xyz));o.tangentWS.xyz = normalize(TransformObjectToWorldDir(i.tangentOS.xyz));o.bitangentWS.xyz = cross(o.normalWS.xyz, o.tangentWS.xyz) * i.tangentOS.w * unity_WorldTransformParams.w;o.normalWS.w = positionWS.x;o.tangentWS.w = positionWS.y;o.bitangentWS.w = positionWS.z;return o;}// ========== vert END ===========// ========== frag START ===========half4 frag(v2f i) : SV_Target{half4 normalTex = SAMPLE_TEXTURE2D(_NormalMap, sampler_NormalMap, i.uv.xy);half3 normalTS = UnpackNormalScale(normalTex, _NormalScale);normalTS.z = sqrt(1 - saturate(dot(normalTS.xy, normalTS.xy)));float3x3 T2W = { i.tangentWS.xyz, i.bitangentWS.xyz, i.normalWS.xyz };T2W = transpose(T2W);half3 N = NormalizeNormalPerPixel(mul(T2W, normalTS));// return half4(N, 1);Light mainLight = GetMainLight();half3 L = normalize(mainLight.direction);// return half4(L, 1);float3 positionWS = float3(i.normalWS.w, i.tangentWS.w, i.bitangentWS.w);half3 V = SafeNormalize(_WorldSpaceCameraPos - positionWS);// return half4(V, 1);half3 H = normalize(L + V);// return half4(H, 1);half HdotN = max(dot(H, N), 1e-5);half NdotL = max(dot(N, L), 1e-5);half NdotV = max(dot(N, V), 1e-5);half HdotL = max(dot(H, L), 1e-5);// ======= Direct START ======half3 albedoTex = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, i.uv.xy).rgb * _BaseColor.rgb;half3 emissiveTex = SAMPLE_TEXTURE2D(_EmissiveMap, sampler_EmissiveMap, i.uv.xy).rgb;half3 maskTex = SAMPLE_TEXTURE2D(_MaskMap, sampler_MaskMap, i.uv.xy).rgb;half metallic = maskTex.r * _Metallic;half AO = lerp(1, maskTex.g, _AO);half smoothness = maskTex.b;half roughness = (1 - smoothness) * _Roughness;// direct specularhalf Direct_D = Direct_D_Function(HdotN, roughness);//return Direct_D;// jave.lin : 使用 Kelemen-Szirmay-Kalos Geometry Factor 优化(可以减少一个 sub G func)// 参考:http://renderwonk.com/publications/s2010-shading-course/hoffman/s2010_physically_based_shading_hoffman_b.pdf - 搜索:Kelemen-Szirmay-Kalos Geometry Factor
#if defined(_KALOS_G_FACTOR_ON)half Direct_G = Direct_G_Function_Kalos(HdotL, roughness);
#elsehalf Direct_G = Direct_G_Function(NdotL, NdotV, roughness);
#endif//return Direct_G;half3 F0 = Direct_F0_Function(albedoTex, metallic);//return half4(F0, 1);half3 Direct_F = Direct_F_Function(HdotL, F0);//return half4(Direct_F, 1);#if defined(_KALOS_G_FACTOR_ON)half3 BRDFSpecSection = (Direct_D * Direct_G) * Direct_F / (4 * HdotL);
#elsehalf3 BRDFSpecSection = (Direct_D * Direct_G) * Direct_F / (4 * NdotL * NdotV);
#endif//return half4(BRDFSpecSection, 1);half3 DirectSpeColor = BRDFSpecSection * mainLight.color * (NdotL * PI * AO);//return half4(DirectSpeColor, 1);// direct diffusehalf3 KS = Direct_F;half3 KD = (1 - KS) * (1 - metallic);// return half4(KD, 1);half3 emissiveColor = emissiveTex * pow(2, _Emissive);
#if defined(_EMISSIVE_TWINKLE_ON)emissiveColor *= sin(_Time.y * _EmissiveTwinkleFrequence * 10) * 0.5 + 0.5;
#endifhalf3 DirectDiffColor = KD * albedoTex * mainLight.color * NdotL + emissiveColor;// return half4(DirectDiffColor, 1);// direct lightshalf3 DirectColor = DirectDiffColor + DirectSpeColor;// return half4(DirectColor, 1);// ======= Direct END ======// ======= Indirect START ======// indirect diffusehalf3 shColor = SH_IndirectionDiff(N) * AO;half3 Indirect_KS = Indirect_F_Function(NdotV, F0, roughness);half3 Indirect_KD = (1 - Indirect_KS) * (1 - metallic);half3 IndirectDiffColor = shColor * Indirect_KD * albedoTex;// return half4(IndirectDiffColor, 1); // jave.lin : 添加一个 反射探针 即可看到效果:reflection probe// indirect specular// 反射探针的间接光half3 IndirectSpeCubeColor = IndirectSpeCube(N, V, roughness, AO);// return half4(IndirectSpeCubeColor, 1);half3 IndirectSpeCubeFactor = IndirectSpeFactor(roughness, smoothness, BRDFSpecSection, F0, NdotV);//return half4(IndirectSpeCubeFactor, 1);half3 IndirectSpeColor = IndirectSpeCubeColor * IndirectSpeCubeFactor;// return half4(IndirectSpeColor, 1);half3 IndirectColor = IndirectDiffColor + IndirectSpeColor;// return half4(IndirectColor, 1);// ======= Indirect END ======half3 finalCol = DirectColor + IndirectColor;return half4(finalCol, 1);}// ========== frag END ===========#endif

URP_PBR_Emissive.shader

// jave.lin 2022/02/06
// URP PBR EmissiveShader "Test/URP_PBR_Emissive"
{Properties{_BaseColor ("Color", Color) = (1, 1, 1, 1)_BaseMap ("Albedo", 2D) = "white" {}[NoScaleOffset] _MaskMap ("Mask: Metalic(R), AO(G), Smoothness(B)", 2D) = "black" {}[NoScaleOffset] [Normal] _NormalMap ("Normal (RGB)", 2D) = "bump" {}[NoScaleOffset] _EmissiveMap ("Emissive (RGB)", 2D) = "black" {}_Metallic ("Metallic", Range(0, 1)) = 1_AO ("AO", Range(0, 1)) = 1_Roughness ("Roughness", Range(0, 1)) = 1_NormalScale("NormalScale", Range(0, 1)) = 1_Emissive ("Emissive", Range(0, 100)) = 1[Toggle(_EMISSIVE_TWINKLE_ON)] _Emissve_Twinkle("Emissive Twinkle", Int) = 1_EmissiveTwinkleFrequence ("Emissive Twinkle Frequence", Range(0, 1)) = 1[Toggle(_KALOS_G_FACTOR_ON)] _Kalos_G_Factor ("Optimize with Kalos G Factor", Int) = 1}SubShader{Tags{"RenderPipeline" = "UniversalRenderPipeline"}Pass{HLSLPROGRAM#pragma vertex vert#pragma fragment frag#pragma shader_feature _ _EMISSIVE_TWINKLE_ON#pragma shader_feature _ _KALOS_G_FACTOR_ON#include "Includes/PBR__Emissive_Lib.hlsl"ENDHLSL}}
}

jave.lin : 另外说明一下

PBR 也是经验模型,是基于物理观察后的数据做的数据模型来模拟的

所以比一般非物理观测模型的更加逼真一些

因此才叫:PBR(基于物理(观察后)渲染(数学模型))

上面我们提到的公式,都是经过再优化(在原来的 PBR 的某个分支的模型基础上再次简化计算的模型)


References

  • 毛神的 PBR 总结 - (毛神一路走好)

    • 【基于物理的渲染(PBR)白皮书】(一) 开篇:PBR核心知识体系总结与概览
    • 【基于物理的渲染(PBR)白皮书】(二) PBR核心理论与渲染光学原理总结
    • 【基于物理的渲染(PBR)白皮书】(三)迪士尼原则的BRDF与BSDF相关总结
    • 【基于物理的渲染(PBR)白皮书】(四)法线分布函数相关总结
    • 【基于物理的渲染(PBR)白皮书】(五)几何函数相关总结
  • URP管线的自学HLSL之路 第三十七篇 造一个PBR的轮子
  • Optimizing GGX Shaders with dot(L,H)
  • GAMES202 笔记 -Real-Time Physically-Based Materials

最后附上近期自己用圆珠笔话的一张画,耗时大概 40 分钟

自从学习 shader 得基础光照着色后,对画画时得细节了解也会增加

(希望自己不要潜得太久,就算不太顺利,也要往前看~,给自己加油!!!)

Unity Shader - 搬砖日志 - URP PBR (抄作业篇,持续更新~)相关推荐

  1. Unity Shader - 搬砖日志 - Dithering

    文章目录 什么时 Dithering 色阶纹理图案 - Texture Dither Pattern 程序化 动态 Dithering - 让 RGBA8888 压缩到 RGBA4444 而没有明显色 ...

  2. Unity Shader - 搬砖日志 - 3D Noise, Noise 3D 相关

    文章目录 GLSL noise 3d Project References 备忘,2D 的 noise 有纹理或是现成的比较多 3D noise 在 unity SG 出奇的没有封装 显示再 shad ...

  3. Unity Shader - 板砖日志 - 简单的树、草 等植物的 随风飘扬 动画

    文章目录 目的 思路 Script include cginc appled shader csharp 效果 目的 便于后续自己的 CTRL+C,V的面向复制.粘贴编程 思路 非常简单:可以使用 p ...

  4. 《Unity Shader入门精要》笔记:高级篇(3)以及扩展

    本篇博客主要为个人学习所编写读书笔记,不用于任何商业用途,以及不允许任何人以任何形式进行转载. 本篇博客会补充一些扩展内容(例如其他博客链接). 本篇博客还会提供一些边读边做的效果截图.文章内所有数学 ...

  5. 《Unity Shader入门精要》笔记:初级篇(2)

    本篇博客主要为个人学习所编写读书笔记,不用于任何商业用途,以及不允许任何人以任何形式进行转载. 本篇博客会补充一些扩展内容(例如其他博客链接). 本篇博客还会提供一些边读边做的效果截图.文章内所有数学 ...

  6. 《Unity Shader入门精要》笔记:初级篇(1)

    本篇博客主要为个人学习所编写读书笔记,不用于任何商业用途,以及不允许任何人以任何形式进行转载. 本篇博客会补充一些扩展内容(例如其他博客链接). 本篇博客还会提供一些边读边做的效果截图.文章内所有数学 ...

  7. 整理全网Shell脚本合集,Java脚本,运维脚本,告警脚本,监控脚本,日志脚本,docker脚本等---------持续更新!

    整理全网Shell脚本合集,Java脚本,运维脚本,告警脚本,监控脚本,日志脚本,docker脚本等---------持续更新! 一.ffmpeg脚本 1.1 打开进程,并判断进程数量 1.2 关闭进 ...

  8. Unity - 搬砖日志 - BRP 管线下的自定义阴影尺寸(脱离ProjectSettings/Quality/ShadowResolution设置)

    文章目录 环境 原因 解决 CSharp 脚本 效果预览 - Light.shadowCustomResolution 效果预览 - Using Quality Settings 应用 Control ...

  9. Unity - 搬砖日志 - 打开项目时崩溃/或是运行不起unity的日志

    崩溃日志目录所在:C:/Users/admins/AppData/Local/Unity/Editor/Editor.log 其中 admins 是你的 windows 登录账号名 打开该 Edito ...

最新文章

  1. 迁移数据文件到ASM【转】
  2. 怎样才算熟悉python-怎么样才算是精通 Python?
  3. JavaScript实现integerPartition整数划分算法(附完整源码)
  4. Linux 下shell编程
  5. 国科大prml15-BP
  6. 如何利用python自动化办公项目_python办公自动化:自动进行word文档处理和排版
  7. python做股票系统_GitHub - yeyaowen/stock: stock,股票系统。使用python进行开发。
  8. HttpClient 忽略证书直接访问https站点
  9. 第二章 Jackson属性名转换+属性忽略
  10. Element Form表单布局(一行多列)
  11. 如何手动合成年度夜间灯光影像
  12. matlab 2017安装教程
  13. TI AM3352/54/59 工业核心板硬件说明书
  14. HFSS同轴馈电矩形贴片天线馈电点以及尺寸的计算
  15. 《C语言程序设计》江宝钏主编-习题6-1-温度转换
  16. 在Centos/Linux系统下使用Phalcon开发PHP项目
  17. 中国联通(广东省分公司)研发技术初面
  18. 无法启动此程序,因为计算机中丢失opencv_core*d.dll vs2010
  19. 全志T507操作小技巧连载2-T507以太网配置方法- 飞凌嵌入式国产全志T507开发板
  20. 利用opencv进行图片水印消除

热门文章

  1. 什么是TypeScript?本文介绍TypeScript基本用法和语法。
  2. 解决MainActivity.onCreate(Unknown Source)的混淆错误
  3. 精准控制的开关电脉冲表征GST薄膜的相变行为(2121.8.29,cyy)
  4. C语言度化为度分秒的方法,一句话转换度:分:秒格式为度.度度度
  5. 获取海拔高度. 实时气压
  6. Cesium中实时根据鼠标的位置显示经度,纬度,视角高度以及海拔高度
  7. 干货!APP推广全周期解决方案 只需做好这6步
  8. iOS-APP性能测试
  9. 支持M1芯片AE2022已发布,After Effects 2022 for MAC中文安装教程,支持Monterey系统不闪退
  10. 数学 余式定理 简介