summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoryum <yum.food.vr@gmail.com>2024-07-09 13:36:56 -0700
committeryum <yum.food.vr@gmail.com>2024-07-09 13:36:56 -0700
commitc9c6cbbe7ad1cd904c1b999d7820a1940f507833 (patch)
tree4d4b12a285cdf608dfe4d17765bab87fabdbed2e
parenta445f2bce613757ae04927c19d36bd473ff22522 (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.cs58
-rw-r--r--feature_macros.cginc2
-rw-r--r--globals.cginc16
-rw-r--r--tooner.shader12
-rw-r--r--tooner_lighting.cginc47
-rw-r--r--tooner_outline_pass.cginc48
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));