文章目录
目的
备份、拾遗
核心代码
half3 blended_normal = normalize(half3(n1.xy + n2.xy, n1.z*n2.z));
PBR - Filament - Normal mapping
Shader
// jave.lin : 测试 法线重定向 (混合)
Shader "Test/TestingNormalmapBlending"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {
}
_BumpMap ("Normal Map", 2D) = "bump" {
}
_BumpDetailMap ("Normal Detail Map", 2D) = "bump" {
}
_BumpBlending ("Bump Blending", Range(0.0, 1.0)) = 1.0
}
SubShader
{
Tags {
"RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
half3 normal : NORMAL;
half4 tangent : TANGENT; // xyz : dir, w : sign
};
struct v2f
{
float4 vertex : SV_POSITION;
float2 uv : TEXCOORD0;
half3 tangentWS : TEXCOORD1; // xyz : dir, w : view dir.y
half3 normalWS : TEXCOORD2; // xyz : dir, w : view dir.x
half3 binormalWS : TEXCOORD3; // xyz : dir, w : view dir.z
float3 posWS : TEXCOORD4; // xyz : world position
};
sampler2D _MainTex;
sampler2D _BumpMap;
sampler2D _BumpDetailMap;
float4 _MainTex_ST;
fixed _BumpBlending;
fixed4 _LightColor0;
half3 CustomUnpackScaleNormal(half4 packednormal, half bumpScale)
{
#if defined(UNITY_NO_DXT5nm)
half3 normal = packednormal.xyz * 2 - 1;
#if (SHADER_TARGET >= 30)
// SM2.0: instruction count limitation
// SM2.0: normal scaler is not supported
normal.xy *= bumpScale;
#endif
return normal;
#elif defined(UNITY_ASTC_NORMALMAP_ENCODING)
half3 normal;
normal.xy = (packednormal.wy * 2 - 1);
normal.z = sqrt(1.0 - saturate(dot(normal.xy, normal.xy)));
normal.xy *= bumpScale;
return normal;
#else
// This do the trick
packednormal.x *= packednormal.w;
half3 normal;
normal.xy = (packednormal.xy * 2 - 1);
#if (SHADER_TARGET >= 30)
// SM2.0: instruction count limitation
// SM2.0: normal scaler is not supported
normal.xy *= bumpScale;
#endif
normal.z = sqrt(1.0 - saturate(dot(normal.xy, normal.xy)));
return normal;
#endif
} // end custom unpack scale normal
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
float4 posWS = mul(unity_ObjectToWorld, v.vertex);
half3 normalWS = UnityObjectToWorldNormal(v.normal);
half4 tangentWS = half4(UnityObjectToWorldDir(v.tangent.xyz), v.tangent.w);
half sign = tangentWS.w * unity_WorldTransformParams.w;
half3 binormalWS = cross(normalWS, tangentWS) * sign;
o.tangentWS = tangentWS.xyz;
o.normalWS = normalWS.xyz;
o.binormalWS = binormalWS.xyz;
o.posWS.xyz = posWS.xyz;
return o;
}
half3 BlendNormal_Hill12(half3 N1, half3 N2)
{
return normalize(N1 * dot(N1, N2) - N2 * N1.z);
}
half3 BlendNormal_UDN(half3 N1, half3 N2)
{
return normalize(half3(N1.xy + N2.xy, N1.z));
}
half3 BlendNormals_Unity_Native(half3 n1, half3 n2)
{
return normalize(half3(n1.xy + n2.xy, n1.z*n2.z));
}
fixed4 frag (v2f i, fixed isFrontFace : VFACE) : SV_Target
{
half3 N = normalize(i.normalWS);
N = isFrontFace >= 0 ? N : -N;
half3 V = normalize(_WorldSpaceCameraPos - i.posWS.xyz);
half3 T = normalize(i.tangentWS);
half3 B = normalize(i.binormalWS);
half3x3 TBN = half3x3(T, B, N);
half3 N1_ts = CustomUnpackScaleNormal(tex2D(_BumpMap, i.uv.xy), 1.0);
half3 N1 = mul(N1_ts, TBN);
// return half4(N1.xyz, 1.0);
half3 N2_ts = CustomUnpackScaleNormal(tex2D(_BumpDetailMap, i.uv.xy), 1.0);
half3 N2 = mul(N2_ts, TBN);
// return half4(N2.xyz, 1.0);
// N = BlendNormal_Hill12(N1, N2);
// N = BlendNormal_UDN(N1, N2);
N = BlendNormals_Unity_Native(N1, N2); // jave.lin : 目前看着是: unity native 的效果是最好的
N = lerp(N1, N, _BumpBlending);
// return half4(N.xyz, 1.0);
half3 L = _WorldSpaceLightPos0.xyz;
half3 H = normalize(L + V);
half NdotL = saturate(dot(N, L));
half NdotH = saturate(dot(N, H));
half diffuse = (NdotL);
// return diffuse;
half specular = pow((NdotH), 32) * 4.0;
// return specular;
fixed4 baseCol = tex2D(_MainTex, i.uv.xy);
// half kd = 1 - specular;
// diffuse *= kd;
// return diffuse + specular;
half3 finalCol = (diffuse + specular) * baseCol.rgb * _LightColor0.rgb + UNITY_LIGHTMODEL_AMBIENT.rgb * baseCol.rgb;
return half4(finalCol.xyz, 1.0);
}
ENDCG
}
}
}
效果
half3 N1_ts = CustomUnpackScaleNormal(tex2D(_BumpMap, i.uv.xy), 1.0);
half3 N1 = mul(N1_ts, TBN);
// return half4(N1.xyz, 1.0);
half3 N2_ts = CustomUnpackScaleNormal(tex2D(_BumpDetailMap, i.uv.xy), 1.0);
half3 N2 = mul(N2_ts, TBN);
// return half4(N2.xyz, 1.0);
N = BlendNormal_Hill12(N1, N2);
// N = BlendNormal_UDN(N1, N2);
// N = BlendNormals_Unity_Native(N1, N2); // jave.lin : 目前看着是: unity native 的效果是最好的
N = lerp(N1, N, _BumpBlending);
return half4(N.xyz, 1.0);
BlendNormal_Hill12
half3 BlendNormal_Hill12(half3 N1, half3 N2)
{
return normalize(N1 * dot(N1, N2) - N2 * N1.z);
}
BlendNormal_UDN
half3 BlendNormal_UDN(half3 N1, half3 N2)
{
return normalize(half3(N1.xy + N2.xy, N1.z));
}
BlendNormals_Unity_Native - 效果目前最好
half3 BlendNormals_Unity_Native(half3 n1, half3 n2)
{
return normalize(half3(n1.xy + n2.xy, n1.z*n2.z));
}
unity standard 里面的 lerp(n1, n2, t)
另外除了上面的方式,其实也可以 lerp 直接过度
unity BRP 中的 standard 相关的 pbr shader 中也有使用到此类方式
Project
Testing_NormalMap_Blending_2023.3.37f1_BRP.rar
提取码:ozgt