使用资产
通过网盘分享的文件:PBR测试内容 链接: https://pan.baidu.com/s/1MpDbI4TXPY2lKaz2uZC3_w?pwd=2psd 提取码: 2psd
完整代码
Shader "Unlit/PBRShader"
{
properties
{
_MainTex("MainTex", 2D) = "white"{}
_NormalMap("NormalMap", 2D) = "bump"{}
_NormalMapScale("NormalMapScale", Range(0,5)) = 1
_RoughnessTex("RoughnessTex", 2D)= "white"{}
_MetallicTex("MetallicTex", 2D)= "white"{}
_HeightTex("HeightTex", 2D)= "white"{}
_AOTex("AOTex", 2D)= "white"{}
_CubeMap("CubeMap", cube)= "white"{}
_SpecularColor("SpecularColor",color) = (1,1,1,1)
_SpecularIntensity("SpecularIntensity", Range(0,50)) = 1
_SpecularShininess("SpecularShininess", Range(0,10)) = 2
[Header(Roughness Adjustment)]
[Space]
_RoughnessIntensity ("RoughnessIntensity", Range(0, 2)) = 1
[Space]
_RoughnessBias ("Bias", Range(-1.0, 1.0)) = 0
[Space]
_RoughnessRemapMin ("Remap Min", Range(0, 1)) = 0
_RoughnessRemapMax ("Remap Max", Range(0, 1)) = 1
[Header(ParallaxMapping Basic)]
_ParallaxScale ("Parallax Scale 未使用", Range(0, 10)) = 0.1
[Header(ParallaxOcclusionMapping)]
_HeightScale ("Height Scale", Range(0, 0.2)) = 0.05
_MinLayers("Parallax Min Layers", Range(1, 128)) = 10
_MaxLayers("Parallax Max Layers", Range(1, 128)) = 50
_HeightReferencePlane ("Height Reference Plane", Range(0, 1)) = 0.5 // 解决凹凸问题
//_POMFadeDistance ("POM Near Fade Distance", Range(0, 10)) = 2.0
_ShadowSoftness("POM Shadow Strength" ,Range(0, 10)) = 1.0
_ShadowSteps("POM Shadow Steps" ,Range(0, 16)) = 8
[Header(EnvBRDFLUT)]
[Toggle]_UseCustomLUT("Custom BRDF LUT", float) = 1.0
_CustomBRDFLUT("Custom BRDF LUT", 2D) = "white" {}
}
Subshader
{
Tags
{
"RenderType"="Opaque"
"Queue"="Geometry"
}
Pass
{
Name"FORWARD"
Tags{"LightMode"="ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdbase
#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "AutoLight.cginc"
struct appdata
{
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 tangent : TANGENT;
float2 uv : TEXCOORD0;
};
struct v2f
{
float4 pos : SV_POSITION;
float4 positionWS : TEXCOORD0;
float3 normalWS : TEXCOORD1;
float3 tangentWS : TEXCOORD2;
float3 bitangentWS : TEXCOORD3;
float2 texcoord : TEXCOORD4;
float3 viewDirTS : TEXCOORD5;
float3 lightDirTS : TEXCOORD6;
UNITY_SHADOW_COORDS(7)
};
sampler2D _MainTex;
half4 _MainTex_ST;
sampler2D _NormalMap;
sampler2D _RoughnessTex;
sampler2D _MetallicTex;
sampler2D _HeightTex;
sampler2D _AOTex;
samplerCUBE _CubeMap;
float4 _CubeMap_HDR; //此参数声明后,Unity会自动配置
half _NormalMapScale;
half4 _SpecularColor;
half _SpecularIntensity;
half _SpecularShininess;
half _RoughnessIntensity;
half _RoughnessBias;
half _RoughnessRemapMin;
half _RoughnessRemapMax;
half _RoughnessBoost;
half _ParallaxScale;
half _HeightScale;
half _MinLayers;
half _MaxLayers;
half _HeightReferencePlane;
half _POMFadeDistance;
half _ShadowSoftness;
half _ShadowSteps;
half _UseCustomLUT;
sampler2D _CustomBRDFLUT;
float3 ParallaxOcclusionMapping(float2 uv, float3 viewDirTS, float3 lightDirTS)
{
// === 保存原始UV导数 ===
float2 dx = ddx(uv);
float2 dy = ddy(uv);
// 这个系数能保证在极低角度时视差平滑消失
//float angleFade = saturate(viewDirTS.z * 5.0);
float angleFade = saturate(pow(viewDirTS.z, 0.5));
//float angleFade = smoothstep(0.15, 0.4, viewDirTS.z);
// 截断淡出,效果较差
// float2 rawOffset = viewDirTS.xy / max(viewDirTS.z, 0.0001) * _HeightScale;
// float maxLen = _HeightScale * 2.0;
// float currentLen = length(rawOffset);
// float2 maxParallaxOffset = rawOffset * (min(currentLen, maxLen) / max(currentLen, 0.001));
// === 1. 动态调整采样层数 ===
float numLayers = lerp(_MaxLayers, _MinLayers, abs(viewDirTS.z));
// === 2. 计算步进参数 ===
float layerStepDistance = 1.0 / numLayers; // 步进距离
float2 maxParallaxOffset = viewDirTS.xy / max(viewDirTS.z, 0.001) * _HeightScale * angleFade; //最大步进距离
float2 deltaUV = maxParallaxOffset / numLayers; //步进UV
// === 3. 初始化 第一层数据===
float currentLayerDistance = 0.0; //起始层基准
float2 currentUV = uv + maxParallaxOffset * (1.0 - _HeightReferencePlane); // 当前UV坐标
//float currentHeight = 1 - tex2D(_HeightTex, currentUV).r; // 初始表面高度
float currentHeight = 1.0 - tex2Dgrad(_HeightTex, currentUV, dx, dy).r;
// 缓存上一步的数据,避免循环结束后重复采样
float prevHeight = currentHeight;
float prevLayerDistance = 0.0;
//[unroll(50)] // 展开循环优化(数字要≥_MaxLayers)
[loop] //标记循环
for(int i = 0; i < numLayers; i++)
{
// 1.检查是否当前位置数值是否大于当前高度值
if(currentLayerDistance >= currentHeight)
break;
// 2.记录旧状态
prevHeight = currentHeight;
prevLayerDistance = currentLayerDistance;
// 3.步进操作
// 沿视线方向步进,下一层的数据,这里虽然命名是current,但是数据已经是下一层的了
currentUV -= deltaUV; //UV步进1层
currentLayerDistance += layerStepDistance; //当前位置步进1层
// 4.采样新高度
//currentHeight = 1 - tex2Dlod(_HeightTex, float4(currentUV, 0, 0)).r; //深度值随UV步进1层
currentHeight = 1.0 - tex2Dgrad(_HeightTex, currentUV, dx, dy).r;
}
// 步进操作前,也就是当前UV
float2 prevUV = currentUV + deltaUV;
//当前步进距离对高度值影响
float beforeDistance = prevHeight - prevLayerDistance;
//下一层,总步进距离对高度值影响
float afterDistance = currentHeight - currentLayerDistance;
// 插值权重,两个端点之间的线性插值
float weight = afterDistance / (afterDistance - beforeDistance);
// 最终UV = 加权平均
// 对每一次步进的UV插值
float2 finalUV = lerp(currentUV, prevUV, weight);
//float finalHeight = lerp(currentLayerDistance, prevLayerDistance, weight);
// === 自阴影循环 (Soft Shadows) ===
float shadow = 1.0;
//float lightAngleFade = smoothstep(0.05, 0.25, lightDirTS.z);
float lightAngleFade = saturate(pow(lightDirTS.z, 0.5));
if(_ShadowSoftness > 0.0 && angleFade > 0.01 && lightDirTS.z > 0.0)
{
float hitDepth = 1.0 - tex2Dgrad(_HeightTex, finalUV, dx, dy).r;
if(hitDepth > 0.001)
{
// 计算完整的UV偏移方向(从交点到光源方向在UV上的投影)
float2 lightDirUV = lightDirTS.xy / max(lightDirTS.z, 0.001) * _HeightScale;
// 限制最大追踪距离,防止低角度时偏移过大
float maxOffset = _HeightScale * 2.0;
float offsetLen = length(lightDirUV);
if(offsetLen > maxOffset)
{
lightDirUV = lightDirUV / offsetLen * maxOffset;
}
// 按深度比例的总UV偏移
float2 totalUV = lightDirUV * hitDepth;
// 均匀分配到每一步
float2 stepUV = totalUV / _ShadowSteps;
float stepDepth = hitDepth / _ShadowSteps;
float currentDepth = hitDepth;
float2 currentUV = finalUV;
[loop]
for(int j = 0; j < _ShadowSteps; j++)
{
currentDepth -= stepDepth;
currentUV += stepUV;
if(currentDepth <= 0.0)
break;
float terrainDepth = 1.0 - tex2Dgrad(_HeightTex, currentUV, dx, dy).r;
if(terrainDepth < currentDepth)
{
float occlusion = currentDepth - terrainDepth;
shadow = min(shadow, 1.0 - saturate(occlusion * _ShadowSoftness));
if(shadow < 0.01)
{
shadow = 0.0;
break;
}
}
}
}
}
// 根据淡出系数混合阴影
shadow = lerp(1.0, shadow, angleFade * lightAngleFade);
// 根据淡出系数混合阴影(让阴影也随视角淡出)
//shadow = lerp(1.0, shadow, angleFade);
//return finalUV;
return float3(finalUV,shadow);
}
float2 EnvBRDFApprox(float roughness, float NoV)
{
// Epic Games通过拟合大量数据得出的魔法系数
const float4 c0 = float4(-1.0, -0.0275, -0.572, 0.022);
const float4 c1 = float4(1.0, 0.0425, 1.04, -0.04);
float4 r = roughness * c0 + c1;
float a004 = min(r.x * r.x, exp2(-9.28 * NoV)) * r.x + r.y;
float2 AB = float2(-1.04, 1.04) * a004 + r.zw;
return AB; // AB.x = scale, AB.y = bias
}
v2f vert(appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.positionWS = mul(unity_ObjectToWorld, v.vertex);
o.normalWS = UnityObjectToWorldNormal(v.normal);
o.tangentWS = UnityObjectToWorldDir(v.tangent.xyz);
//* v.tangent.w处理平台差异
o.bitangentWS = cross(o.normalWS, o.tangentWS) * v.tangent.w;
o.texcoord = TRANSFORM_TEX(v.uv,_MainTex);
//ParallaxMapping_Basic start
float3x3 TBN = float3x3(o.tangentWS, o.bitangentWS, o.normalWS);
float3 worldViewDir = normalize(_WorldSpaceCameraPos - o.positionWS);
o.viewDirTS = mul(TBN, worldViewDir);
half3 lightWS = normalize(_WorldSpaceLightPos0.xyz);
o.lightDirTS= mul(TBN, lightWS);
//ParallaxMapping_Basic end
TRANSFER_SHADOW(o);
return o;
}
float4 frag(v2f i) : SV_Target
{
//vectors
float3 positionWS = i.positionWS;
float3 normalWS = normalize(i.normalWS);
float3 tangentWS = normalize(i.tangentWS);
float3 bitangentWS = normalize(i.bitangentWS);
float3x3 TBN = float3x3(tangentWS, bitangentWS, normalWS);
half3 lightWS = normalize(_WorldSpaceLightPos0.xyz);
float3 viewDir = normalize(_WorldSpaceCameraPos - positionWS);
half3 lightDirTS = i.lightDirTS;
//************tex*************
//ParallaxMapping_Basic start
//float height = 1.0 - tex2D(_HeightTex, i.texcoord).r;
//float2 offset = i.viewDirTS.xy / i.viewDirTS.z * height * _ParallaxScale * 0.01;
//float2 texcoord = i.texcoord - offset;
//ParallaxMapping_Basic end
float3 POMData = ParallaxOcclusionMapping(i.texcoord, i.viewDirTS, lightDirTS);
float2 texcoord = POMData.xy;
float POMShadow = POMData.z;
float3 lightColor = _LightColor0.xyz;
float4 baseCol = tex2D(_MainTex, texcoord);
float3 normalMapData = UnpackNormal(tex2D(_NormalMap, texcoord)).xyz;
normalMapData.xy *= _NormalMapScale;
float metallic = tex2D(_MetallicTex, texcoord).b;
float roughness = tex2D(_RoughnessTex, texcoord).r + _RoughnessBoost;
//halfLambet
float3 pixelNormal = normalize(mul(normalMapData, TBN));
float nol = max(0, dot(pixelNormal, lightWS));
float halfLambet = nol*0.5 + 0.5;
float3 diffuseColor = baseCol * nol * lightColor;
//Specular
//phong
half3 reflectDir = reflect(-lightWS, pixelNormal);
half phongItem = max(0, dot(reflectDir, viewDir));
//Blinn-phong
half3 halfDir = normalize(lightWS + viewDir);
half blinnPhongItem = max(0, dot(pixelNormal, halfDir));
//整体漫反射剔除金属的的漫反射(错误写法,能量不守恒)
//float3 pbrDiffuseColor = lerp(0.96 * diffuseColor, 0, metallic);
roughness = roughness * _RoughnessIntensity + _RoughnessBias;
roughness = clamp(roughness, _RoughnessRemapMin, _RoughnessRemapMax);
//GGXSpecular
float3 f0 = lerp(float3(0.04,0.04,0.04), baseCol, metallic);
float roughness2 = roughness * roughness;
float d = roughness2*roughness2/(3.14159*(blinnPhongItem * blinnPhongItem * (roughness2 * roughness2 - 1) + 1)*(blinnPhongItem * blinnPhongItem * (roughness2 * roughness2 - 1) + 1));
float k = (roughness + 1) * (roughness + 1) / 8;
float nov = max(0, dot(pixelNormal, viewDir));
float gv = nov / (nov * (1 - k) + k);
float gl = nol / (nol * (1 - k) + k);
float g = gv * gl;
float3 f = f0 + (1-f0) * pow(1 - max(0,(dot(viewDir, halfDir))), 5);
float3 KD_GGX = (1 - f)* (1 - metallic);
float3 ggxBRDF = d * f * g / (4 * nol * nov + 0.001);
float3 pbrSpecularColor = ggxBRDF * _SpecularColor * lightColor * nol;
float3 pbrDiffuseColor = baseCol /3.14159 * KD_GGX * nol * lightColor;
//******环境光处理**********
/******************漫反射环境光*********************/
//EnvironmentColor
//1.环境光(Unity 自动处理)
fixed3 envColor = UNITY_LIGHTMODEL_AMBIENT.rgb * baseCol.rgb;
// ^^^^^^^^^^^^^^^^^^^^^^^ Unity 内置的环境光颜色
//2.SH环境光(支持 Skybox/Gradient/Color)
fixed3 envColorSH = ShadeSH9(half4(normalWS, 1.0)) * baseCol.rgb;
// ^^^^^^^ Unity 的球谐函数,根据法线方向采样环境光
/******************镜面反射环境光*********************/
//1.采样反射探针(Reflection Probe)
half4 skyData = UNITY_SAMPLE_TEXCUBE(unity_SpecCube0, reflectDir);
half3 skyColor = DecodeHDR(skyData, unity_SpecCube0_HDR);
// 计算反射方向
float3 reflviewDir = reflect(-viewDir, normalize(pixelNormal));
//环境光漫反射
float3 f_indirect = f0 + (1-f0) * pow(1 - max(0,(dot(viewDir, pixelNormal))), 5);
float3 kD_indirect = (1.0 - f_indirect) * (1.0 - metallic);
float3 indirectDiffuse = kD_indirect * envColorSH;
//环境光镜面漫反射
float mipLevel = roughness* 7.0;
float4 envSpecularRaw = texCUBElod(_CubeMap, float4(reflviewDir, mipLevel));
half3 envSpecular = DecodeHDR(envSpecularRaw, _CubeMap_HDR);
//1.拟合BRDF(未使用)
float2 envBRDF = EnvBRDFApprox(roughness, nov);
float3 envSpecularEasy = envSpecular * f_indirect;
//2.采样BRDFLUT
float2 brdfLUT = tex2D(unity_NHxRoughness, float2(roughness, nov)).rg;
float2 CustombrdfLUT = tex2D(_CustomBRDFLUT, float2(roughness, nov)).rg;
if(_UseCustomLUT > 0.5)
{
brdfLUT = CustombrdfLUT;
}
envSpecular = envSpecular * (f0 * brdfLUT.x + brdfLUT.y);
//AO
float AO = tex2D(_AOTex, texcoord).r;
//float fakeAO = saturate(dot(normalWS, float3(0,1,0))) * 0.6 + 0.4;
//Shadow
float shadow = SHADOW_ATTENUATION(i);
//output
float3 finalColor = (pbrDiffuseColor + pbrSpecularColor) * shadow + (indirectDiffuse + envSpecular )* AO * POMShadow ;
return float4(finalColor, 1);
}
ENDCG
}
}FallBack"Diffuse"
}
测试大致效果

