1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
| Shader "Test/Better Bump Mapping" { Properties { _Color ("Color Tint", Color) = (1, 1, 1, 1) _MainTex ("Main Tex", 2D) = "white" {} _BumpMap ("Normal Map", 2D) = "bump" {} _BumpScale ("Bump Scale", Float) = 1.0 _Specular ("Specular", Color) = (1, 1, 1, 1) _Gloss ("Gloss", Range(8.0, 256)) = 20 [Space(20)] [Header(Parallax Mapping)] [KeywordEnum(No, Parallax, Steep, Occlusion, Relief)] _ParallaxModel ("Parallax Model", Float) = 0 _HeightMap ("Height Map", 2D) = "white" {} _Scale("Scale(For Parallax)", Range(-0.05, 0.05)) = 0.01 _MaxSteps("Step(For Steep/Relief)", Range(1,20)) = 8 } SubShader { Pass { Tags { "LightMode"="ForwardBase" } CGPROGRAM
#pragma vertex vert #pragma fragment frag #include "Lighting.cginc" #pragma multi_compile _PARALLAXMODEL_NO _PARALLAXMODEL_PARALLAX _PARALLAXMODEL_STEEP _PARALLAXMODEL_OCCLUSION _PARALLAXMODEL_RELIEF
fixed4 _Color; sampler2D _MainTex; float4 _MainTex_ST; sampler2D _BumpMap; // float4 _BumpMap_ST; float _BumpScale; sampler2D _HeightMap; // float4 _HeightMap_ST; fixed4 _Specular; float _Gloss; float _Scale; int _MaxSteps;
struct a2v { float4 vertex : POSITION; float3 normal : NORMAL; float4 tangent : TANGENT; float4 texcoord : TEXCOORD0; }; struct v2f { float4 pos : SV_POSITION; float2 uv : TEXCOORD0; fixed3 lightDir : TEXCOORD1; float3 viewDir : TEXCOORD2; };
v2f vert(a2v v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
float3 binormal = cross(normalize(v.normal), normalize(v.tangent.xyz)) * v.tangent.w; float3x3 rotation = float3x3(v.tangent.xyz, binormal, v.normal);
o.lightDir = mul(rotation, ObjSpaceLightDir(v.vertex)).xyz; o.viewDir = mul(rotation, ObjSpaceViewDir(v.vertex)).xyz;
return o; }
fixed4 frag(v2f i) : SV_Target { fixed3 tangentLightDir = normalize(i.lightDir); fixed3 tangentViewDir = normalize(i.viewDir);
float2 uv = i.uv; // --- 视差偏移计算 --- #if defined(_PARALLAXMODEL_PARALLAX) float height = 1 - tex2D(_HeightMap, uv).r; float2 offset = height * tangentViewDir.xy * _Scale / max(tangentViewDir.z, 0.01); uv += offset; #elif defined(_PARALLAXMODEL_STEEP) // 陡峭视差(固定层数) float stepSize = 1.0 / _MaxSteps; float dx = ddx(i.uv); float dy = ddy(i.uv);
float2 deltaUV = tangentViewDir.xy * _Scale / (tangentViewDir.z * _MaxSteps); // 每步偏移量 float curHeight = 1.0-tex2Dgrad(_HeightMap, uv, dx, dy).r; float rayHeight = 0.0; // 从顶部开始 for (int j = 0; j < _MaxSteps; j++) { rayHeight = stepSize * (j + 1.0); uv += deltaUV; // 沿视线向里移动 curHeight = 1.0-tex2Dgrad(_HeightMap, uv, dx, dy).r; if (rayHeight > curHeight) break; // 穿过表面 } #elif defined(_PARALLAXMODEL_OCCLUSION) float stepSize = 1.0 / _MaxSteps; float dx = ddx(i.uv); float dy = ddy(i.uv);
float2 deltaUV = tangentViewDir.xy * _Scale / (tangentViewDir.z * _MaxSteps); // 每步偏移量 float2 preUV = uv; float curHeight = 1.0-tex2Dgrad(_HeightMap, uv, dx, dy).r; float rayHeight = 0.0; // 从顶部开始 for (int j = 0; j < _MaxSteps; j++) { preUV = uv; rayHeight = stepSize * (j + 1.0); uv += deltaUV; // 沿视线向里移动 curHeight = 1.0-tex2Dgrad(_HeightMap, uv, dx, dy).r; if (rayHeight > curHeight) break; // 穿过表面 }
float preHeight = 1.0 - tex2Dgrad(_HeightMap, preUV, dx, dy).r; float prevDiff = preHeight - rayHeight + stepSize; // 射线在前一层高出表面的距离 float curDiff = rayHeight - curHeight; // 射线在当前层低于表面的距离 float weight = prevDiff / (prevDiff + curDiff); // [0,1] 之间的比例
uv = lerp(preUV, uv, weight); #elif defined(_PARALLAXMODEL_RELIEF) // 陡峭视差步进:找到交点所在区间 float stepSize = 1.0 / _MaxSteps; float dx = ddx(i.uv); float dy = ddy(i.uv);
float2 deltaUV = tangentViewDir.xy * _Scale / (tangentViewDir.z * _MaxSteps); // 每步偏移量 float2 uv0 = uv; // 前一层 UV float2 uv1 = uv; // 当前层 UV(初始化为相同,步进后更新) float rayDepth = 0.0; // 当前射线深度(从表面顶部开始) float prevSurfaceDepth = 1.0 - tex2Dgrad(_HeightMap, uv, dx, dy).r; // 前一层表面深度 float curSurfaceDepth = prevSurfaceDepth;
// 步进搜索,找到射线首次穿过表面的位置 for (int j = 0; j < _MaxSteps; j++) { rayDepth += stepSize; // 射线进入下一层 uv1 += deltaUV; // UV 移动到下一层 curSurfaceDepth = 1.0 - tex2Dgrad(_HeightMap, uv1, dx, dy).r; // 当前层表面深度
if (rayDepth > curSurfaceDepth) { break; } uv0 = uv1; prevSurfaceDepth = curSurfaceDepth; }
// 此时: // uv0 对应射线深度 rayDepth - stepSize(前一层) // uv1 对应射线深度 rayDepth(当前层) // prevSurfaceDepth 是前一层表面深度(应 ≤ 前一层射线深度) // curSurfaceDepth 是当前层表面深度(应 < 当前射线深度)
// 二分搜索(8次迭代足够) float t0 = rayDepth - stepSize; // 前一层射线深度 float t1 = rayDepth; // 当前层射线深度 float2 uvMid; float midDepth;
[unroll] //不生成跳转指令,直接把循环体复制 8 次。 for (int k = 0; k < 8; k++) { float tMid = (t0 + t1) * 0.5; // UV 在两点间线性插值(因为 UV 与深度成线性关系) uvMid = lerp(uv0, uv1, (tMid - t0) / (t1 - t0)); midDepth = 1.0 - tex2Dgrad(_HeightMap, uvMid, dx, dy).r; // 采样中点表面深度
if (midDepth > tMid) { // 表面深度大于射线深度,交点在上半区 t0 = tMid; uv0 = uvMid; } else { // 表面深度小于等于射线深度,交点在下半区 t1 = tMid; uv1 = uvMid; } }
// 最终使用中点 UV 作为交点 uv = lerp(uv0, uv1, 0.5); // 或直接使用 uvMid(最后一次循环已更新) #else // _PARALLAXMODEL_NO:什么都不做 #endif // 从法线纹理中获取纹素 fixed4 packedNormal = tex2D(_BumpMap, uv); fixed3 tangentNormal; // 设置纹理为“法线映射”,使用内置函数 tangentNormal = UnpackNormal(packedNormal); tangentNormal.xy *= _BumpScale; tangentNormal.z = sqrt(1.0 - saturate(dot(tangentNormal.xy, tangentNormal.xy)));
fixed3 albedo = tex2D(_MainTex, uv).rgb * _Color.rgb; fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo; fixed3 diffuse = _LightColor0.rgb * albedo.rgb * saturate(dot(tangentNormal, tangentLightDir));
fixed3 halfDir = normalize(tangentLightDir + tangentViewDir); fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(tangentNormal, halfDir)), _Gloss); fixed3 color = ambient + diffuse + specular; // color = fixed3(height, height, height);
return fixed4(color, 1.0); } ENDCG } } FallBack "Specular" }
|