diff options
| author | yum <yum.food.vr@gmail.com> | 2024-07-09 13:36:56 -0700 |
|---|---|---|
| committer | yum <yum.food.vr@gmail.com> | 2024-07-09 13:36:56 -0700 |
| commit | c9c6cbbe7ad1cd904c1b999d7820a1940f507833 (patch) | |
| tree | 4d4b12a285cdf608dfe4d17765bab87fabdbed2e | |
| parent | a445f2bce613757ae04927c19d36bd473ff22522 (diff) | |
Add matcap and vertex location quantization
matcap quantization simply quantizes matcap colors to a configurable #
of equal-sized steps.
vertex location quantization snaps each vertex location to a quantized
location in object space. You can control the direction relative to the
mesh normal that this quantization occurs. So if you have skintight
clothing, set base direction to -1 and clothing direction to 1. Then
skin will quantize inwards, and clothing will quantize outwards. Areas
of high curvature (s.a. nips) may still cause problems, so I also added
a mask feature.
Finally, fix an issue where exploding the mesh causes outlines to appear
at the world origin.
| -rw-r--r-- | Editor/tooner.cs | 58 | ||||
| -rw-r--r-- | feature_macros.cginc | 2 | ||||
| -rw-r--r-- | globals.cginc | 16 | ||||
| -rw-r--r-- | tooner.shader | 12 | ||||
| -rw-r--r-- | tooner_lighting.cginc | 47 | ||||
| -rw-r--r-- | tooner_outline_pass.cginc | 48 |
6 files changed, 144 insertions, 39 deletions
diff --git a/Editor/tooner.cs b/Editor/tooner.cs index ecad810..0e6b6e3 100644 --- a/Editor/tooner.cs +++ b/Editor/tooner.cs @@ -322,7 +322,11 @@ public class ToonerGUI : ShaderGUI { editor.FloatProperty( bc, "Emission strength"); - EditorGUI.indentLevel -= 1; + + bc = FindProperty($"_Matcap{i}Quantization"); + editor.FloatProperty( + bc, + "Quantization"); bc = FindProperty($"_Matcap{i}Distortion0"); enabled = bc.floatValue > 1E-6; @@ -331,6 +335,8 @@ public class ToonerGUI : ShaderGUI { EditorGUI.EndChangeCheck(); bc.floatValue = enabled ? 1.0f : 0.0f; SetKeyword($"_MATCAP{i}_DISTORTION0", enabled); + + EditorGUI.indentLevel -= 1; } } @@ -401,12 +407,12 @@ public class ToonerGUI : ShaderGUI { bc = FindProperty($"_Rim_Lighting{i}_Emission"); editor.FloatProperty( bc, - "Rim lighting emission"); + "Emission"); bc = FindProperty($"_Rim_Lighting{i}_Quantization"); editor.FloatProperty( bc, - "Rim lighting quantization"); + "Quantization"); bc = FindProperty($"_Rim_Lighting{i}_Glitter_Enabled"); enabled = bc.floatValue > 1E-6; @@ -788,7 +794,8 @@ public class ToonerGUI : ShaderGUI { } } - void DoGimmicks() { + void DoGimmickFlatColor() + { MaterialProperty bc; bc = FindProperty("_Gimmick_Flat_Color_Enable_Static"); bool enabled = (bc.floatValue != 0.0); @@ -803,7 +810,6 @@ public class ToonerGUI : ShaderGUI { } EditorGUI.indentLevel += 1; - EditorGUI.indentLevel -= 1; bc = FindProperty("_Gimmick_Flat_Color_Enable_Dynamic"); enabled = (bc.floatValue != 0.0); @@ -816,6 +822,48 @@ public class ToonerGUI : ShaderGUI { editor.ColorProperty(bc, "Color"); bc = FindProperty("_Gimmick_Flat_Color_Emission"); editor.ColorProperty(bc, "Emission"); + + EditorGUI.indentLevel -= 1; + } + + void DoGimmickQuantizeLocation() { + MaterialProperty bc; + bc = FindProperty("_Gimmick_Quantize_Location_Enable_Static"); + bool enabled = (bc.floatValue != 0.0); + EditorGUI.BeginChangeCheck(); + enabled = EditorGUILayout.Toggle("Quantize location", enabled); + EditorGUI.EndChangeCheck(); + bc.floatValue = enabled ? 1.0f : 0.0f; + SetKeyword("_GIMMICK_QUANTIZE_LOCATION", enabled); + + if (!enabled) { + return; + } + + EditorGUI.indentLevel += 1; + + bc = FindProperty("_Gimmick_Quantize_Location_Enable_Dynamic"); + enabled = (bc.floatValue != 0.0); + EditorGUI.BeginChangeCheck(); + enabled = EditorGUILayout.Toggle("Enable (runtime switch)", enabled); + EditorGUI.EndChangeCheck(); + bc.floatValue = enabled ? 1.0f : 0.0f; + + bc = FindProperty("_Gimmick_Quantize_Location_Precision"); + editor.FloatProperty(bc, "Precision"); + bc = FindProperty("_Gimmick_Quantize_Location_Direction"); + editor.FloatProperty(bc, "Direction"); + bc = FindProperty("_Gimmick_Quantize_Location_Mask"); + editor.TexturePropertySingleLine( + MakeLabel(bc, "Mask"), + bc); + + EditorGUI.indentLevel -= 1; + } + + void DoGimmicks() { + DoGimmickFlatColor(); + DoGimmickQuantizeLocation(); } enum RenderingMode { diff --git a/feature_macros.cginc b/feature_macros.cginc index 885ffad..73353c2 100644 --- a/feature_macros.cginc +++ b/feature_macros.cginc @@ -85,6 +85,8 @@ #pragma shader_feature_local _ _MATCAP1_DISTORTION0 #pragma shader_feature_local _ _AMBIENT_OCCLUSION #pragma shader_feature_local _ _GIMMICK_FLAT_COLOR +#pragma shader_feature_local _ _GIMMICK_QUANTIZE_LOCATION +#pragma shader_feature_local _ _GIMMICK_VERTEX_NORMAL_SLIDE #endif // __FEATURE_MACROS_INC diff --git a/globals.cginc b/globals.cginc index 9a60bf8..cc5f170 100644 --- a/globals.cginc +++ b/globals.cginc @@ -195,6 +195,7 @@ texture2D _Matcap0; texture2D _Matcap0_Mask; float _Matcap0_Mask_Invert; float _Matcap0Str; +float _Matcap0Quantization; float _Matcap0Mode; float _Matcap0Emission; #endif @@ -205,6 +206,7 @@ texture2D _Matcap1; texture2D _Matcap1_Mask; float _Matcap1_Mask_Invert; float _Matcap1Str; +float _Matcap1Quantization; float _Matcap1Mode; float _Matcap1Emission; #endif @@ -295,5 +297,19 @@ float4 _Gimmick_Flat_Color_Color; float3 _Gimmick_Flat_Color_Emission; #endif +#if defined(_GIMMICK_QUANTIZE_LOCATION) +float _Gimmick_Quantize_Location_Enable_Static; +float _Gimmick_Quantize_Location_Enable_Dynamic; +float _Gimmick_Quantize_Location_Precision; +float _Gimmick_Quantize_Location_Direction; +texture2D _Gimmick_Quantize_Location_Mask; +#endif + +#if defined(_GIMMICK_VERTEX_NORMAL_SLIDE) +float _Gimmick_Vertex_Normal_Slide_Enable_Static; +float _Gimmick_Vertex_Normal_Slide_Enable_Dynamic; +float _Gimmick_Vertex_Normal_Slide_Distance; +#endif + #endif diff --git a/tooner.shader b/tooner.shader index 5a0fea4..487bf5e 100644 --- a/tooner.shader +++ b/tooner.shader @@ -151,6 +151,7 @@ Shader "yum_food/tooner" _Matcap0Mode("Matcap mode", Float) = 0 _Matcap0Str("Matcap strength", Float) = 1 _Matcap0Emission("Matcap emission", Float) = 0 + _Matcap0Quantization("Matcap quantization", Float) = -1 _Matcap0Distortion0("Matcap distortion0", Float) = 0 _Matcap1("Matcap", 2D) = "black" {} @@ -159,6 +160,7 @@ Shader "yum_food/tooner" _Matcap1Mode("Matcap mode", Float) = 0 _Matcap1Str("Matcap strength", Float) = 1 _Matcap1Emission("Matcap emission", Float) = 0 + _Matcap1Quantization("Matcap quantization", Float) = -1 _Matcap1Distortion0("Matcap distortion0", Float) = 0 _Rim_Lighting0_Enabled("Enable rim lighting", Float) = 0 @@ -234,6 +236,16 @@ Shader "yum_food/tooner" _Gimmick_Flat_Color_Enable_Dynamic("Enable flat color gimmick", Float) = 0.0 _Gimmick_Flat_Color_Color("Flat color gimmick color", Color) = (0, 0, 0, 1) _Gimmick_Flat_Color_Emission("Flat color gimmick emission", Color) = (0, 0, 0, 1) + + _Gimmick_Quantize_Location_Enable_Static("Enable quantize location gimmick", Float) = 0.0 + _Gimmick_Quantize_Location_Enable_Dynamic("Enable quantize location gimmick", Float) = 0.0 + _Gimmick_Quantize_Location_Precision("Enable quantize location precision", Float) = 100.0 + _Gimmick_Quantize_Location_Direction("Enable quantize location direction", Float) = 1.0 + _Gimmick_Quantize_Location_Mask("Enable quantize location mask", 2D) = "white" {} + + _Gimmick_Vertex_Normal_Slide_Enable_Static("Enable vertex normal slide", Float) = 0.0 + _Gimmick_Vertex_Normal_Slide_Enable_Dynamic("Enable vertex normal slide", Float) = 0.0 + _Gimmick_Vertex_Normal_Slide_Distance("Vertex normal slide distance", Float) = 0.01 } SubShader { diff --git a/tooner_lighting.cginc b/tooner_lighting.cginc index 2eb6a47..abeef2a 100644 --- a/tooner_lighting.cginc +++ b/tooner_lighting.cginc @@ -63,6 +63,19 @@ v2f vert(appdata v) UNITY_TRANSFER_INSTANCE_ID(v, o); UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); +#if defined(_GIMMICK_QUANTIZE_LOCATION) + if (_Gimmick_Quantize_Location_Enable_Dynamic) { + float q = _Gimmick_Quantize_Location_Precision; + float3 v_new0 = floor(v.vertex * q) / q; + float3 d = v_new0 - v.vertex; + float3 v_new1 = v.vertex - d; + bool flip_dir = (sign(dot(d, v.normal)) != sign(_Gimmick_Quantize_Location_Direction)); + float3 v_q = lerp(v_new0, v_new1, flip_dir); + float mask = _Gimmick_Quantize_Location_Mask.SampleLevel(linear_repeat_s, v.uv0.xy, /*lod=*/0); + v.vertex.xyz = lerp(v.vertex.xyz, v_q, mask); + } +#endif + o.vertex = UnityObjectToClipPos(v.vertex); o.worldPos = mul(unity_ObjectToWorld, v.vertex); o.objPos = v.vertex; @@ -590,28 +603,44 @@ void applyOverlayNormal(inout float3 raw_normal, PbrOverlay ov, v2f i, float idd // Use UVs to smoothly blend between fully detailed normals when close up and // flat normals when far away. If we don't do this, then we see moire effects // on e.g. striped normal maps. - raw_normal_2 = UnpackScaleNormal(_PBR_Overlay0_NormalTex.SampleGrad(linear_repeat_s, UV_SCOFF(i.uv, _PBR_Overlay0_NormalTex_ST), iddx, iddy), _PBR_Overlay0_Tex_NormalStr * ov.ov0_mask); + raw_normal_2 = UnpackScaleNormal(_PBR_Overlay0_NormalTex.SampleGrad(linear_repeat_s, + UV_SCOFF(i.uv, _PBR_Overlay0_NormalTex_ST), + iddx * _PBR_Overlay0_NormalTex_ST.x, + iddy * _PBR_Overlay0_NormalTex_ST.y), + _PBR_Overlay0_Tex_NormalStr * ov.ov0_mask); raw_normal = BlendNormals( raw_normal, raw_normal_2); #endif // _PBR_OVERLAY0 && _PBR_OVERLAY0_NORMAL_MAP #if defined(_PBR_OVERLAY1) && defined(_PBR_OVERLAY1_NORMAL_MAP) - raw_normal_2 = UnpackScaleNormal(_PBR_Overlay1_NormalTex.SampleGrad(linear_repeat_s, UV_SCOFF(i.uv, _PBR_Overlay1_NormalTex_ST), iddx, iddy), _PBR_Overlay1_Tex_NormalStr * ov.ov1_mask); + raw_normal_2 = UnpackScaleNormal(_PBR_Overlay1_NormalTex.SampleGrad(linear_repeat_s, + UV_SCOFF(i.uv, _PBR_Overlay1_NormalTex_ST), + iddx * _PBR_Overlay1_NormalTex_ST.x, + iddy * _PBR_Overlay1_NormalTex_ST.y), + _PBR_Overlay1_Tex_NormalStr * ov.ov0_mask); raw_normal = BlendNormals( raw_normal, raw_normal_2); #endif // _PBR_OVERLAY1 && _PBR_OVERLAY1_NORMAL_MAP #if defined(_PBR_OVERLAY2) && defined(_PBR_OVERLAY2_NORMAL_MAP) - raw_normal_2 = UnpackScaleNormal(_PBR_Overlay2_NormalTex.SampleGrad(linear_repeat_s, UV_SCOFF(i.uv, _PBR_Overlay2_NormalTex_ST), iddx, iddy), _PBR_Overlay2_Tex_NormalStr * ov.ov2_mask); + raw_normal_2 = UnpackScaleNormal(_PBR_Overlay2_NormalTex.SampleGrad(linear_repeat_s, + UV_SCOFF(i.uv, _PBR_Overlay2_NormalTex_ST), + iddx * _PBR_Overlay2_NormalTex_ST.x, + iddy * _PBR_Overlay2_NormalTex_ST.y), + _PBR_Overlay2_Tex_NormalStr * ov.ov0_mask); raw_normal = BlendNormals( raw_normal, raw_normal_2); #endif // _PBR_OVERLAY2 && _PBR_OVERLAY2_NORMAL_MAP #if defined(_PBR_OVERLAY3) && defined(_PBR_OVERLAY3_NORMAL_MAP) - raw_normal_2 = UnpackScaleNormal(_PBR_Overlay3_NormalTex.SampleGrad(linear_repeat_s, UV_SCOFF(i.uv, _PBR_Overlay3_NormalTex_ST), iddx, iddy), _PBR_Overlay3_Tex_NormalStr * ov.ov3_mask); + raw_normal_2 = UnpackScaleNormal(_PBR_Overlay3_NormalTex.SampleGrad(linear_repeat_s, + UV_SCOFF(i.uv, _PBR_Overlay3_NormalTex_ST), + iddx * _PBR_Overlay3_NormalTex_ST.x, + iddy * _PBR_Overlay3_NormalTex_ST.y), + _PBR_Overlay3_Tex_NormalStr * ov.ov0_mask); raw_normal = BlendNormals( raw_normal, @@ -754,6 +783,11 @@ float4 effect(inout v2f i) #endif float3 matcap = _Matcap0.SampleGrad(linear_repeat_s, matcap_uv, iddx, iddy) * _Matcap0Str; + float q = _Matcap0Quantization; + if (q > 0) { + matcap = ceil(matcap * q) / q; + } + #if defined(_MATCAP0_MASK) float4 matcap_mask_raw = _Matcap0_Mask.SampleGrad(linear_repeat_s, i.uv.xy, iddx, iddy); float matcap_mask = matcap_mask_raw.r; @@ -802,6 +836,11 @@ float4 effect(inout v2f i) #endif float3 matcap = _Matcap1.SampleGrad(linear_repeat_s, matcap_uv, iddx, iddy) * _Matcap1Str; + float q = _Matcap1Quantization; + if (q > 0) { + matcap = ceil(matcap * q) / q; + } + #if defined(_MATCAP1_MASK) float4 matcap_mask_raw = _Matcap1_Mask.SampleGrad(linear_repeat_s, i.uv.xy, iddx, iddy); float matcap_mask = matcap_mask_raw.r; diff --git a/tooner_outline_pass.cginc b/tooner_outline_pass.cginc index c972b58..9050811 100644 --- a/tooner_outline_pass.cginc +++ b/tooner_outline_pass.cginc @@ -25,17 +25,17 @@ struct tess_factors { v2f vert(appdata v) { - float3 objPos = v.vertex; + float4 objPos = v.vertex; float4 clipPos = UnityObjectToClipPos(v.vertex); float3 clipNormal = mul((float3x3) UNITY_MATRIX_MVP, v.normal); - float3 worldPos = mul(unity_ObjectToWorld, objPos); + float4 worldPos = mul(unity_ObjectToWorld, objPos); float3 worldNormal = UnityObjectToWorldNormal(v.normal); #if defined(_OUTLINES) float outline_mask = _Outline_Mask.SampleLevel(linear_repeat_s, v.uv0.xy, /*lod=*/1); outline_mask = _Outline_Mask_Invert > 1E-6 ? 1 - outline_mask : outline_mask; - worldPos += worldNormal * _Outline_Width * outline_mask * _Outline_Width_Multiplier; + worldPos.xyz += worldNormal * _Outline_Width * outline_mask * _Outline_Width_Multiplier; objPos = mul(unity_WorldToObject, worldPos); clipPos = UnityObjectToClipPos(objPos); @@ -161,10 +161,6 @@ void geom(triangle v2f tri_in[3], uint pid: SV_PrimitiveID, inout TriangleStream<v2f> tri_out) { -#if !defined(_OUTLINES) - return; -#endif - v2f v0 = tri_in[0]; v2f v1 = tri_in[1]; v2f v2 = tri_in[2]; @@ -173,9 +169,10 @@ void geom(triangle v2f tri_in[3], float3 v1_objPos; float3 v2_objPos; +#if defined(_EXPLODE) float3 n = normalize(cross(v1.worldPos - v0.worldPos, v2.worldPos - v0.worldPos)); float3 avg_pos; -#if defined(_EXPLODE) + float3 n0 = v0.normal; float3 n1 = v1.normal; float3 n2 = v2.normal; @@ -184,15 +181,13 @@ void geom(triangle v2f tri_in[3], phase = smoothstep(0, 1, phase); phase *= phase; phase *= 4; + const float pid_rand = rand((int) pid); if (phase > 1E-6) { - const float pid_rand = rand((int) pid); - float3 axis = normalize(float3( rand((int) ((v0.uv.x + v0.uv.y) * 1E9)) * 2 - 1, rand((int) ((v1.uv.x + v1.uv.y) * 1E9)) * 2 - 1, rand((int) ((v2.uv.x + v2.uv.y) * 1E9)) * 2 - 1)); - float3 np = BlendNormals(n, axis * phase); v0.worldPos += np * phase * pid_rand; @@ -218,29 +213,20 @@ void geom(triangle v2f tri_in[3], v1.worldPos -= avg_pos; v2.worldPos -= avg_pos; - float r = rand((int) pid); - float theta = phase * 3.14159 * 4 + phase * (sin(_Time[1] * (1 + pid_rand) / 2.0 + r) + cos(_Time[1] * (1 + pid_rand) / 6.1 + r) * 2) * pid_rand * 2; + float theta = phase * 3.14159 * 4 + phase * (sin(_Time[1] * (1 + pid_rand) / 2.0 + pid_rand) + cos(_Time[1] * (1 + pid_rand) / 6.1 + pid_rand) * 2) * pid_rand * 2; float4 quat = get_quaternion(axis, theta); v0.worldPos = rotate_vector(v0.worldPos, quat); v1.worldPos = rotate_vector(v1.worldPos, quat); v2.worldPos = rotate_vector(v2.worldPos, quat); - //v0.worldPos *= 1.1; - //v1.worldPos *= 1.1; - //v2.worldPos *= 1.1; - v0.worldPos += avg_pos; v1.worldPos += avg_pos; v2.worldPos += avg_pos; - float3 nn = normalize(cross(v1.worldPos - v0.worldPos, v2.worldPos - v0.worldPos)); - v0.worldPos -= nn * .0005; - v1.worldPos -= nn * .0005; - v2.worldPos -= nn * .0005; - - v0.normal = nn; - v1.normal = nn; - v2.normal = nn; + n = normalize(cross(v1.worldPos - v0.worldPos, v2.worldPos - v0.worldPos)); + v0.normal = n; + v1.normal = n; + v2.normal = n; // Omit geometry that's too close when exploded. /* @@ -249,16 +235,18 @@ void geom(triangle v2f tri_in[3], } */ - // Apply transformed worldPos to other coordinate systems. v0_objPos = mul(unity_WorldToObject, float4(v0.worldPos, 1)); v1_objPos = mul(unity_WorldToObject, float4(v1.worldPos, 1)); v2_objPos = mul(unity_WorldToObject, float4(v2.worldPos, 1)); - v0.vertex = UnityObjectToClipPos(v0_objPos); - v1.vertex = UnityObjectToClipPos(v1_objPos); - v2.vertex = UnityObjectToClipPos(v2_objPos); + // Apply transformed worldPos to other coordinate systems. + if (_Explode_Phase > 1E-6) { + v0.vertex = UnityObjectToClipPos(v0_objPos); + v1.vertex = UnityObjectToClipPos(v1_objPos); + v2.vertex = UnityObjectToClipPos(v2_objPos); + } } -#endif +#endif // __EXPLODE #if defined(_SCROLL) { float3 n = normalize(cross(v1.worldPos - v0.worldPos, v2.worldPos - v0.worldPos)); |
