From 6a14a2dd1dd5208c7cb1ea04c49d759484121ce7 Mon Sep 17 00:00:00 2001 From: yum Date: Fri, 14 Apr 2023 03:45:44 -0700 Subject: Fix normal calculation in displacement shader Recompute normals after generating the deformed mesh. Unfortunately, significant anti-aliasing is required to get the generated normals to look smooth in motion. * Enable backface culling. --- Demos/displacement_demo.gif | Bin 4717920 -> 4899631 bytes Shaders/displacement/displacement.shader | 12 +-- Shaders/displacement/displacement_lighting.cginc | 100 ++++++++++++++++++----- Shaders/displacement/pbr.cginc | 1 + 4 files changed, 86 insertions(+), 27 deletions(-) diff --git a/Demos/displacement_demo.gif b/Demos/displacement_demo.gif index 90ba0d0..951b01f 100644 Binary files a/Demos/displacement_demo.gif and b/Demos/displacement_demo.gif differ diff --git a/Shaders/displacement/displacement.shader b/Shaders/displacement/displacement.shader index 6a47853..5c0b517 100644 --- a/Shaders/displacement/displacement.shader +++ b/Shaders/displacement/displacement.shader @@ -4,8 +4,8 @@ Shader "yum_food/displacement" { _BaseColor("Base color", 2D) = "white" {} _Normal("Normal", 2D) = "bump" {} - _Disable_Normal_Texture("Disable normal texture", float) = 0.0 - _Metallic("Metallic", 2D) = "black" {} + _Disable_Normal_Texture("Disable normal texture", float) = 1.0 + _Metallic("Metallic", 2D) = "white" {} _Roughness("Roughness", 2D) = "black" {} _Cubemap("Cubemap", Cube) = "" {} @@ -36,7 +36,7 @@ Shader "yum_food/displacement" Blend SrcAlpha OneMinusSrcAlpha ZWrite On ZTest LEqual - Cull Off + Cull Back CGPROGRAM #pragma target 5.0 @@ -53,7 +53,7 @@ Shader "yum_food/displacement" // 0: no anti-aliasing // 1: sample 4 neighbors (diagonals) // 2: sample 8 neighbors (diagonals + cartesian) - #define OFFSET_AA_LEVEL 2 + #define HEIGHT_AA_LEVEL 2 #include "displacement_lighting.cginc" ENDCG @@ -67,7 +67,7 @@ Shader "yum_food/displacement" Blend One One ZWrite On ZTest LEqual - Cull Off + Cull Back CGPROGRAM #pragma target 5.0 @@ -78,7 +78,7 @@ Shader "yum_food/displacement" #pragma geometry geom #pragma fragment frag - #define OFFSET_AA_LEVEL 2 + #define HEIGHT_AA_LEVEL 2 #include "displacement_lighting.cginc" ENDCG diff --git a/Shaders/displacement/displacement_lighting.cginc b/Shaders/displacement/displacement_lighting.cginc index d0abdb9..3a5e04b 100644 --- a/Shaders/displacement/displacement_lighting.cginc +++ b/Shaders/displacement/displacement_lighting.cginc @@ -80,12 +80,12 @@ v2f vert(appdata v) return o; } -void displace(inout v2f vert) +float getDisplacement(float2 uv) { // Manipulate vertex in world space. float height = 0; - float2 traveling_uv = vert.uv; + float2 traveling_uv = uv; traveling_uv.x += _Time[0] * _Height_Speed_X; traveling_uv.y += _Time[0] * _Height_Speed_Y; traveling_uv = glsl_mod(traveling_uv, 1.0); @@ -93,24 +93,24 @@ void displace(inout v2f vert) const float duv = _Height_AA_Sample_Scale; float z0 = tex2Dlod(_Height, float4(traveling_uv.x, traveling_uv.y, _Height_LOD, 0)); - #if OFFSET_AA_LEVEL >= 1 + #if HEIGHT_AA_LEVEL >= 1 float z1 = tex2Dlod(_Height, float4(traveling_uv.x + duv, traveling_uv.y + duv, _Height_LOD, 0)); float z2 = tex2Dlod(_Height, float4(traveling_uv.x + duv, traveling_uv.y - duv, _Height_LOD, 0)); float z3 = tex2Dlod(_Height, float4(traveling_uv.x - duv, traveling_uv.y - duv, _Height_LOD, 0)); float z4 = tex2Dlod(_Height, float4(traveling_uv.x - duv, traveling_uv.y + duv, _Height_LOD, 0)); #endif - #if OFFSET_AA_LEVEL >= 2 + #if HEIGHT_AA_LEVEL >= 2 float z5 = tex2Dlod(_Height, float4(traveling_uv.x + duv, traveling_uv.y, _Height_LOD, 0)); float z6 = tex2Dlod(_Height, float4(traveling_uv.x - duv, traveling_uv.y, _Height_LOD, 0)); float z7 = tex2Dlod(_Height, float4(traveling_uv.x, traveling_uv.y + duv, _Height_LOD, 0)); float z8 = tex2Dlod(_Height, float4(traveling_uv.x, traveling_uv.y - duv, _Height_LOD, 0)); #endif - #if OFFSET_AA_LEVEL == 0 + #if HEIGHT_AA_LEVEL == 0 height += z0; - #elif OFFSET_AA_LEVEL == 1 + #elif HEIGHT_AA_LEVEL == 1 height += (z0 + z1 + z2 + z3 + z4) / 5.0; - #elif OFFSET_AA_LEVEL == 2 + #elif HEIGHT_AA_LEVEL == 2 height += (z0 + z1 + z2 + z3 + z4 + z5 + z6 + z7 + z8) / 9.0; #endif @@ -118,14 +118,14 @@ void displace(inout v2f vert) height *= _Height_Scale; { - float mask = tex2Dlod(_Height_Mask, float4(vert.uv, _Height_LOD, 0)); + float mask = tex2Dlod(_Height_Mask, float4(uv, _Height_LOD, 0)); mask = pow(mask, _Height_Mask_Exponent); height *= mask; } // 0 at middle, 1 or -1 at edges if (_Center_Out_Speed > 0.0) { - float2 middle_out_uv = vert.uv * 2.0 - 1.0; + float2 middle_out_uv = uv * 2.0 - 1.0; float center_dist2 = length2(middle_out_uv); float ring_radius = fmod(_Time[1] * _Center_Out_Speed, @@ -140,11 +140,46 @@ void displace(inout v2f vert) height *= middle_out_height; } + return height; +} + +void displace(inout v2f vert) +{ + float height = getDisplacement(vert.uv); + vert.worldPos += height * vert.normal; vert.objPos = mul(unity_WorldToObject, float4(vert.worldPos, 1.0)); vert.clipPos = UnityObjectToClipPos(vert.objPos); } +float3 getNormal( + v2f v0, + v2f v1, + v2f v2, + float duv_scale) { + float2 duv_v0v1 = v1.uv - v0.uv; + float2 duv_v0v2 = v2.uv - v0.uv; + + float h0 = getDisplacement(v0.uv); + float h1 = getDisplacement(v0.uv + duv_v0v1 * duv_scale); + float h2 = getDisplacement(v0.uv + duv_v0v2 * duv_scale); + + float dh_v0v1 = h1 - h0; + float dh_v0v2 = h2 - h0; + + float3 dpos_v0v1 = v1.worldPos - v0.worldPos; + float3 dpos_v0v2 = v2.worldPos - v0.worldPos; + + float3 p0 = v0.worldPos + h0 * v0.normal; + float3 p1 = v0.worldPos + dpos_v0v1 * duv_scale + lerp(h0, h1, duv_scale) * v1.normal; + float3 p2 = v0.worldPos + dpos_v0v2 * duv_scale + lerp(h0, h2, duv_scale) * v2.normal; + + float3 tangent = normalize(p1 - p0); + float3 bitangent = normalize(p2 - p0); + float3 new_normal = normalize(cross(tangent, bitangent)); + return new_normal; +} + // maxvertexcount == the number of vertices we create [maxvertexcount(3)] void geom(triangle v2f tri_in[3], @@ -153,17 +188,41 @@ void geom(triangle v2f tri_in[3], { float dx = 0.5; - v2f cur = tri_in[0]; - displace(cur); - tri_out.Append(cur); - - cur = tri_in[1]; - displace(cur); - tri_out.Append(cur); + v2f t0 = tri_in[0]; + v2f t1 = tri_in[1]; + v2f t2 = tri_in[2]; + + displace(t0); + displace(t1); + displace(t2); + + // Math from here: + // http://tonfilm.blogspot.com/2007/01/calculate-normals-in-shader.html + for (int i = 0; i < 3; i++) { + v2f v0 = tri_in[(i + 0) % 3]; + v2f v1 = tri_in[(i + 1) % 3]; + v2f v2 = tri_in[(i + 2) % 3]; + + float3 aa_normal = 0; + aa_normal += getNormal(v0, v1, v2, 0.3); + aa_normal += getNormal(v0, v1, v2, 0.4); + aa_normal += getNormal(v0, v1, v2, 0.5); + aa_normal += getNormal(v0, v1, v2, 0.6); + aa_normal += getNormal(v0, v1, v2, 0.7); + aa_normal = normalize(aa_normal); + + if (i == 0) { + t0.normal = aa_normal; + } else if (i == 1) { + t1.normal = aa_normal; + } else if (i == 2) { + t2.normal = aa_normal; + } + } - cur = tri_in[2]; - displace(cur); - tri_out.Append(cur); + tri_out.Append(t0); + tri_out.Append(t1); + tri_out.Append(t2); tri_out.RestartStrip(); } @@ -185,10 +244,9 @@ float4 effect(inout v2f i, out float depth) if (albedo.a > 0) { depth = getWorldSpaceDepth(i.worldPos); } + return getLitColor(i, albedo, i.worldPos, normal, metallic, 1.0 - roughness, /*custom_cubemap=*/true); - - return 1; } fixed4 frag(v2f i, out float depth : SV_DepthLessEqual) : SV_Target diff --git a/Shaders/displacement/pbr.cginc b/Shaders/displacement/pbr.cginc index e552bcc..690f547 100644 --- a/Shaders/displacement/pbr.cginc +++ b/Shaders/displacement/pbr.cginc @@ -12,6 +12,7 @@ struct v2f float2 uv : TEXCOORD0; float3 normal : TEXCOORD1; float3 worldPos : TEXCOORD2; + float3 objPos : TEXCOORD3; #if defined(VERTEXLIGHT_ON) -- cgit v1.2.3