基于物理的渲染四(PBS)
简略实现PBS
Shader "Unlit/SelfPBR"
{
Properties
{
//漫反射材质控制颜色
_Albedo("Color",Color)=(1,1,1,1)
//漫反射纹理
_AlbedoTex("Albedo",2D)="white"{}
//平滑度
_Glossiness("Smoothness",Range(0.0,1.0))=0.7
//高光反射材质控制颜色
_SpecularCol("Specular Color",Color)=(0.2, 0.2, 0.2)
//高光反射纹理
_SpecularTex("Specular Tex",2D)="white"{}
//自发光控制颜色
_EmissionColor("Emission Color",Color)=(0,0,0)
//自发光纹理
_EmissionMap("Emission Map",2D)="white"{}
//法线控制项
_BumpScale("Bump Scale",float)=1.0
//法线纹理
_BumpMap("Normal Map",2D)="bump"{}
}
SubShader
{
Tags{"RenderType"="Opaque"}
LOD 300
CGINCLUDE
#include "UnityCG.cginc"
#include "HLSLSupport.cginc"
#include "AutoLight.cginc"
#include "Lighting.cginc"
fixed4 _Albedo;
sampler2D _AlbedoTex;
float4 _AlbedoTex_ST;
half _Glossiness;
fixed4 _SpecularCol;
sampler2D _SpecularTex;
fixed4 _EmissionColor;
sampler2D _EmissionMap;
float _BumpScale;
sampler2D _BumpMap;
float4 _BumpMap_ST;
inline half SaturateAndDot(half3 v1,half3 v2)
{
return saturate(dot(v1,v2));
}
//Disney BRDF
inline half3 DisneyDiffuseBRDF(half3 baseColor, half NdotL,half NdotV,half LdotH,half roughness)
{
half fd90 = 0.5 + 2 * roughness * LdotH * LdotH;
half lightScatter = 1 + (fd90 - 1) * pow(1-NdotL,5);
half viewScatter = 1 + (fd90 - 1) * pow(1-NdotV,5);
return baseColor * UNITY_INV_PI * lightScatter * viewScatter;
}
inline half3 FresnelSchlick(half3 c,half HdotL)
{
half t = pow(1 - HdotL, 5);
return c + (1 - c) * t;
}
inline half3 CustomFresnelLerp(half3 c0, half3 c1, half cosA)
{
half3 light = pow(1-cosA,5);
return lerp(c0,c1,light);
}
//法线分布函数
inline half GGX(half NdotH, half roughness)
{
half a2 = roughness * roughness;
half d = (NdotH * a2 - NdotH) * NdotH + 1.0f;
return UNITY_INV_PI * a2 / (d * d + 1e-7f);
}
//阴影遮挡函数
inline half SmithJointGGX(half NdotL, half NdotV, half roughness)
{
half a2 = roughness * roughness;
half lambdaV = NdotL * (NdotV * (1 - a2) + a2);
half lambdaL = NdotV * (NdotL * (1 - a2) + a2);
return 0.5f / (lambdaV + lambdaL + 1e-5f);
}
struct a2v
{
float4 vertex:POSITION;
float3 normal:NORMAL;
float4 tangent:TANGENT;
float4 texcoord:TEXCOORD;
};
struct v2f
{
float4 pos:SV_POSITION;
float2 uv:TEXCOORD0;
float4 T2W0:TEXCOORD1;
float4 T2W1:TEXCOORD2;
float4 T2W2:TEXCOORD3;
//阴影三剑客之一
SHADOW_COORDS(4) // Defined in AutoLight.cginc
UNITY_FOG_COORDS(5) // Defined in UnityCG.cginc
};
v2f vert(a2v v)
{
v2f o;
UNITY_INITIALIZE_OUTPUT(v2f,o);
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.texcoord,_AlbedoTex);
float3 worldPos = mul(unity_ObjectToWorld,v.vertex).xyz;
float3 worldNormal = UnityObjectToWorldNormal(v.normal);
float3 worldTagent = UnityObjectToWorldDir(v.tangent.xyz);
float3 worldBinormal = cross(worldNormal,worldTagent) * v.tangent.w;
//构建从切线空间到世界空间的转换矩阵
//上面构建了切线空间在世界空间下的表示
//那么从切线空间到世界空间的矩阵为:
//[tangent.x,binormal.x,normal.x]
//[tangent.y,binormal.y,normal.y]
//[tangent.z,binormal.z,normal.z]
//即[X,Y,Z],xyz是矢量坐标轴表示
o.T2W0 = float4(worldTagent.x,worldBinormal.x,worldNormal.x,worldPos.x);
o.T2W1 = float4(worldTagent.y,worldBinormal.y,worldNormal.y,worldPos.y);
o.T2W2 = float4(worldTagent.z,worldBinormal.z,worldNormal.z,worldPos.z);
//阴影三剑客之一
TRANSFER_SHADOW(o); // Defined in AutoLight.cginc
//We need this for fog rendering
UNITY_TRANSFER_FOG(o, o.pos); // Defined in UnityCG.cginc
return o;
}
half4 frag(v2f i):SV_Target
{
half4 specularGloss = tex2D(_SpecularTex,i.uv);
specularGloss.a *= _Glossiness; //高光工作流中高光贴图的a通道作为粗糙度参与了运算
half3 specularColor = specularGloss.rgb * _SpecularCol.rgb;
half roughness = 1- specularGloss.a; //粗糙度
//单颜色能量守恒,使用镜面高光颜色的最强分量来减少反射率
half oneMinusReflectivity = 1 - max(max(specularColor.r,specularColor.g),specularColor.b);
half3 diffuseColor = tex2D(_AlbedoTex,i.uv).rgb * _Albedo.rgb * oneMinusReflectivity;
//法线贴图
half3 normalTagent = UnpackNormal(tex2D(_BumpMap,i.uv));
normalTagent.xy *= _BumpScale;
normalTagent.z = sqrt(1.0-saturate(dot(normalTagent.xy,normalTagent.xy)));
//将切线空间的法线转换到世界空间下
half3 worldNormal = normalize(half3(dot(i.T2W0.xyz,normalTagent),dot(i.T2W1.xyz,normalTagent),dot(i.T2W2.xyz,normalTagent)));
float3 worldPos = float3(i.T2W0.w,i.T2W1.w,i.T2W2.w);
half3 worldLightDir = normalize(UnityWorldSpaceLightDir(worldPos));
half3 worldViewDir = normalize(UnityWorldSpaceViewDir(worldPos));
//根据入射光方向向量 I ,和顶点法向量 N ,计算反射光方向向量。其中 I 和 N 必须被归一化,需要非常注意的是,这个 I 是指向顶点的
half3 worldRelectDir = reflect(-worldViewDir,worldNormal);
//这里认为衰减系数是1
//计算阴影和光照衰减值atten
UNITY_LIGHT_ATTENUATION(atten, i, worldPos);
//半角向量
half3 halfDir = normalize(worldLightDir + worldViewDir);
half NdotV = SaturateAndDot(worldNormal,worldViewDir);
half NdotL = SaturateAndDot(worldNormal,worldLightDir);
half NdotH = SaturateAndDot(worldNormal,halfDir);
half LdotV = SaturateAndDot(worldLightDir,worldViewDir);
half LdotH = SaturateAndDot(worldLightDir,halfDir);
//Disneys的BRDF漫反射项
half3 disneyDiffuseTerm = DisneyDiffuseBRDF(diffuseColor,NdotL,NdotV,LdotH,roughness);
//高光反射项 F * G * D
//菲涅尔
half3 F = FresnelSchlick(specularColor,LdotH);
//可见性概率
half G = SmithJointGGX(NdotL,NdotV,roughness);
//法线分布
half D = GGX(NdotH,roughness * roughness);
half3 specularTerm = F * G * D;
//自发光项
half3 emissionColorTerm = tex2D(_EmissionMap,i.uv).rgb * _EmissionColor.rgb;
//环境光IBL
//环境光Mipmap的LOD和粗糙度有关,因为越粗糙,反射的越模糊
//而粗糙度和LOD又不是线性关系,Unity使用了下面的方式进行了转换
half mipRoughness = roughness * (1.7 - 0.7 * roughness);
//乘6后转到LOD
half mip = mipRoughness * 6;
half4 envMap = UNITY_SAMPLE_TEXCUBE_LOD(unity_SpecCube0,worldRelectDir,mip);
//这两项是啥意思????
half grazingTerm = saturate((1 - roughness) + (1 - oneMinusReflectivity));
half surfaceReduction = 1.0 / (roughness * roughness + 1.0);
//计算间接反射光
half3 indirectSpecular = surfaceReduction * envMap.rgb * CustomFresnelLerp(specularColor,grazingTerm,NdotV);
//最终的颜色 直接光+间接光
half3 finalCol = emissionColorTerm + UNITY_PI * (disneyDiffuseTerm + specularTerm) * _LightColor0.rgb * NdotL * atten + indirectSpecular;
UNITY_APPLY_FOG(i.fogCoord, c.rgb);
return half4(finalCol,1);
}
ENDCG
Pass
{
Tags { "LightMode" = "ForwardBase" }
CGPROGRAM
#pragma target 3.0
#pragma multi_compile_fwdbase
#pragma multi_compile_fog
#pragma vertex vert
#pragma fragment frag
ENDCG
}
}
}
从左到右依次为标准金属工作流、高光工作流、简略实现的高光工作流。
其中有几项抄自原作者“改版后的第十八章”,如IBL相关计算,掠射角颜色计算