diff options
| author | yum <yum.food.vr@gmail.com> | 2024-10-11 18:39:50 -0700 |
|---|---|---|
| committer | yum <yum.food.vr@gmail.com> | 2024-10-11 18:39:50 -0700 |
| commit | 0e39a7ef25e7938b84736519057c06c8d84856cc (patch) | |
| tree | 7af1f8822572aa13747e913fbd8b5ca7d5a82819 | |
| parent | e193c86c869b84dfaaa26465c0e6fb923a27631c (diff) | |
Fog performance optimizations
* Rename albedo cutoff to alpha cutoff
* Add LODs for calculating fog density
* Use 3D noise texture to speed up density calculation
* Eliminate low impact octaves from density calculation
* Add support for multiple emitters
| -rw-r--r-- | Editor/generate_3d_noise.cs | 67 | ||||
| -rw-r--r-- | Editor/tooner.cs | 46 | ||||
| -rw-r--r-- | cnlohr.cginc | 26 | ||||
| -rw-r--r-- | feature_macros.cginc | 2 | ||||
| -rw-r--r-- | fog.cginc | 150 | ||||
| -rw-r--r-- | globals.cginc | 27 | ||||
| -rw-r--r-- | tooner.shader | 29 |
7 files changed, 277 insertions, 70 deletions
diff --git a/Editor/generate_3d_noise.cs b/Editor/generate_3d_noise.cs new file mode 100644 index 0000000..4352d99 --- /dev/null +++ b/Editor/generate_3d_noise.cs @@ -0,0 +1,67 @@ +// !! AI ARTIFACT !! +// This code was originally generated by Claude 3.5 Sonnet. +using UnityEngine; +using UnityEditor; + +public class WhiteNoiseTextureGenerator : EditorWindow +{ + private int textureWidth = 32; + private int textureHeight = 32; + private int textureDepth = 32; + private string textureName = "WhiteNoiseTexture"; + + [MenuItem("Tools/yum_food/White Noise Texture Generator")] + public static void ShowWindow() + { + GetWindow<WhiteNoiseTextureGenerator>("White Noise Texture Generator"); + } + + private void OnGUI() + { + GUILayout.Label("White Noise Texture Generator", EditorStyles.boldLabel); + + textureWidth = EditorGUILayout.IntField("Texture Width", textureWidth); + textureHeight = EditorGUILayout.IntField("Texture Height", textureHeight); + textureDepth = EditorGUILayout.IntField("Texture Depth", textureDepth); + textureName = EditorGUILayout.TextField("Texture Name", textureName); + + if (GUILayout.Button("Generate Texture")) + { + if (textureWidth <= 0 || textureHeight <= 0 || textureDepth <= 0) + { + EditorUtility.DisplayDialog("Error", "Texture dimensions must be greater than zero.", "OK"); + return; + } + + GenerateWhiteNoiseTexture(); + } + } + + private void GenerateWhiteNoiseTexture() + { + Texture3D texture = new Texture3D(textureWidth, textureHeight, textureDepth, TextureFormat.RGBA32, false); + Color[] colors = new Color[textureWidth * textureHeight * textureDepth]; + + for (int z = 0; z < textureDepth; z++) + { + for (int y = 0; y < textureHeight; y++) + { + for (int x = 0; x < textureWidth; x++) + { + int index = x + y * textureWidth + z * textureWidth * textureHeight; + colors[index] = new Color(Random.value, Random.value, Random.value, 1f); + } + } + } + + texture.SetPixels(colors); + texture.Apply(); + + string path = $"Assets/{textureName}.asset"; + AssetDatabase.CreateAsset(texture, path); + AssetDatabase.SaveAssets(); + AssetDatabase.Refresh(); + + EditorUtility.DisplayDialog("Success", $"White noise texture generated and saved at {path}", "OK"); + } +} diff --git a/Editor/tooner.cs b/Editor/tooner.cs index fdcb678..3c0b951 100644 --- a/Editor/tooner.cs +++ b/Editor/tooner.cs @@ -2033,10 +2033,16 @@ public class ToonerGUI : ShaderGUI { FloatProperty(bc, "Noise exponent"); bc = FindProperty("_Gimmick_Fog_00_Normal_Cutoff"); RangeProperty(bc, "Normal cutoff"); - bc = FindProperty("_Gimmick_Fog_00_Albedo_Cutoff"); - RangeProperty(bc, "Albedo cutoff"); + bc = FindProperty("_Gimmick_Fog_00_Alpha_Cutoff"); + RangeProperty(bc, "Alpha cutoff"); bc = FindProperty("_Gimmick_Fog_00_Ray_Origin_Randomization"); RangeProperty(bc, "Ray origin randomization"); + bc = FindProperty("_Gimmick_Fog_00_Lod_Half_Life"); + FloatProperty(bc, "LOD half life"); + bc = FindProperty("_Gimmick_Fog_00_Noise"); + TexturePropertySingleLine( + MakeLabel(bc, "Noise"), + bc); bc = FindProperty("_Gimmick_Fog_00_Emitter_Texture"); TexturePropertySingleLine( @@ -2046,16 +2052,44 @@ public class ToonerGUI : ShaderGUI { if (bc.textureValue) { EditorGUI.indentLevel += 1; - bc = FindProperty("_Gimmick_Fog_00_Emitter_Location"); + bc = FindProperty("_Gimmick_Fog_00_Emitter0_Location"); VectorProperty(bc, "Location (world)"); - bc = FindProperty("_Gimmick_Fog_00_Emitter_Normal"); + bc = FindProperty("_Gimmick_Fog_00_Emitter0_Normal"); VectorProperty(bc, "Normal (world)"); - bc = FindProperty("_Gimmick_Fog_00_Emitter_Scale_X"); + bc = FindProperty("_Gimmick_Fog_00_Emitter0_Scale_X"); FloatProperty(bc, "Scale (x)"); - bc = FindProperty("_Gimmick_Fog_00_Emitter_Scale_Y"); + bc = FindProperty("_Gimmick_Fog_00_Emitter0_Scale_Y"); FloatProperty(bc, "Scale (y)"); + bc = FindProperty("_Gimmick_Fog_00_Emitter_Brightness"); FloatProperty(bc, "Brightness"); + bc = FindProperty("_Gimmick_Fog_00_Emitter_Lod_Half_Life"); + FloatProperty(bc, "LOD half life"); + + for (int i = 0; i < 2; i++) { + bc = FindProperty($"_Gimmick_Fog_00_Emitter{i+1}_Enable_Static"); + enabled = (bc.floatValue != 0.0); + EditorGUI.BeginChangeCheck(); + enabled = Toggle($"Enable emitter {i+1}", enabled); + EditorGUI.EndChangeCheck(); + bc.floatValue = enabled ? 1.0f : 0.0f; + SetKeyword($"_GIMMICK_FOG_00_EMITTER_{i+1}", enabled); + + if (enabled) { + EditorGUI.indentLevel += 1; + + bc = FindProperty($"_Gimmick_Fog_00_Emitter{i+1}_Location"); + VectorProperty(bc, "Location (world)"); + bc = FindProperty($"_Gimmick_Fog_00_Emitter{i+1}_Normal"); + VectorProperty(bc, "Normal (world)"); + bc = FindProperty($"_Gimmick_Fog_00_Emitter{i+1}_Scale_X"); + FloatProperty(bc, "Scale (x)"); + bc = FindProperty($"_Gimmick_Fog_00_Emitter{i+1}_Scale_Y"); + FloatProperty(bc, "Scale (y)"); + + EditorGUI.indentLevel -= 1; + } + } EditorGUI.indentLevel -= 1; } diff --git a/cnlohr.cginc b/cnlohr.cginc index c3163d2..73fb80c 100644 --- a/cnlohr.cginc +++ b/cnlohr.cginc @@ -68,14 +68,13 @@ float GetLinearZFromZDepth_WorksWithMirrors(float zDepthFromMap, float2 screenUV float GetDepthOfWorldPos(float3 worldPos) { float3 full_vec_eye_to_geometry = worldPos - _WorldSpaceCameraPos; - float3 world_dir = normalize(worldPos - _WorldSpaceCameraPos); float4 objPos = mul(unity_WorldToObject, float4(worldPos, 1)); float4 clipPos = UnityObjectToClipPos(objPos); float2 suv = clipPos * float2(0.5, 0.5 * _ProjectionParams.x); float2 screenPos = TransformStereoScreenSpaceTex(suv + 0.5 * clipPos.w, clipPos.w); - float perspective_divide = 1.0 / clipPos.w; + float perspective_divide = rcp(clipPos.w); float perspective_factor = length(full_vec_eye_to_geometry * perspective_divide); float2 screen_uv = screenPos.xy * perspective_divide; float eye_depth_world = @@ -85,4 +84,27 @@ float GetDepthOfWorldPos(float3 worldPos) return eye_depth_world; } +float GetDepthOfWorldPos( + float3 full_vec_eye_to_geometry, + float3 worldPos) +{ + float4 objPos = mul(unity_WorldToObject, float4(worldPos, 1)); + float4 clipPos = UnityObjectToClipPos(objPos); + + float2 suv = clipPos * float2(0.5, 0.5 * _ProjectionParams.x); + bool suv_in_range = + (suv.x >= 0) * (suv.x <= 1) * + (suv.y >= 0) * (suv.y <= 1); + float2 screenPos = TransformStereoScreenSpaceTex(suv + 0.5 * clipPos.w, clipPos.w); + + float perspective_divide = rcp(clipPos.w); + float perspective_factor = length(full_vec_eye_to_geometry * perspective_divide); + float2 screen_uv = screenPos.xy * perspective_divide; + float eye_depth_world = + GetLinearZFromZDepth_WorksWithMirrors( + SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, screen_uv), + screen_uv) * perspective_factor; + return suv_in_range ? eye_depth_world : -1E6; +} + #endif // __CNLOHR_INC diff --git a/feature_macros.cginc b/feature_macros.cginc index 2a68ff5..fa4e2c9 100644 --- a/feature_macros.cginc +++ b/feature_macros.cginc @@ -168,6 +168,8 @@ #pragma shader_feature_local _ SSR_MASK #pragma shader_feature_local _ _GIMMICK_FOG_00 #pragma shader_feature_local _ _GIMMICK_FOG_00_EMITTER_TEXTURE +#pragma shader_feature_local _ _GIMMICK_FOG_00_EMITTER_1 +#pragma shader_feature_local _ _GIMMICK_FOG_00_EMITTER_2 #endif // __FEATURE_MACROS_INC @@ -17,43 +17,77 @@ struct Fog00PBR { float ao; }; -float map(float3 p) { - float density = 0; - float t = _Time[1] * 0.5; +float perlin_noise_3d_tex(float3 p) +{ + float3 sq = floor(p); + float3 sqi = frac(p); + + // 1/256 = 0.00390625 + float r_lo = _Gimmick_Fog_00_Noise.SampleLevel(linear_repeat_s, p.xyz * 0.00390625, 0); + + return r_lo; +} + +float map(float3 p, float lod) { + float3 t = _Time[1] * 0.5; float radius = saturate(_Gimmick_Fog_00_Radius - length(p)); - float tmp; - tmp = perlin_noise_3d(p * _Gimmick_Fog_00_Noise_Scale * 3.1 + t) * radius * 0.5; - density += tmp; - tmp = perlin_noise_3d(p * _Gimmick_Fog_00_Noise_Scale * 1.7 + t) * radius * 0.5; - density *= 0.5; - density += tmp; - tmp = perlin_noise_3d(p * _Gimmick_Fog_00_Noise_Scale * 1.0 + t) * radius * 0.5; - density *= 0.5; - density += tmp; + + float3 pp = p * _Gimmick_Fog_00_Noise_Scale + t; + float density = perlin_noise_3d_tex(pp) * radius * 0.7; density = pow(density, _Gimmick_Fog_00_Noise_Exponent); - // Note: this term annihilates performance by creating large open areas. Long - // avgerage view ray = bad perf! - #if 1 - tmp = perlin_noise_3d(p * _Gimmick_Fog_00_Noise_Scale * 0.167 + t/4) * radius - 0.5; - tmp *= 0.2; - density += tmp; - #endif + // This term creates large open areas + if (lod < 1) { + float tmp = perlin_noise_3d_tex(pp * 0.167 + t/4) * radius - 0.5; + // Aggressively dial down this parameter as density increases. We really + // need to keep paths short when density is high. + float density_performance_fix = rcp(_Gimmick_Fog_00_Density); + density_performance_fix *= density_performance_fix; + tmp *= 0.5 * density_performance_fix; + density += tmp; + } return saturate(density); } -float3 get_normal(float3 p, float map_p) { +float3 get_normal(float3 p, float map_p, float lod) { float3 e = float3(0.001, 0, 0); float center = map_p; // Prevent NaN float e2 = 1E-9; return normalize(float3( - map(p + e.xyz) - center, - map(p + e.yxz) - center, - map(p + e.zyx) - center) + e2); + map(p + e.xyz, lod) - center, + map(p + e.yxz, lod) - center, + map(p + e.zyx, lod) - center) + e2); +} + +void getEmitterData(float3 p, float step_size, + float3 em_loc, float3 em_normal, float em_scale_x, + float em_scale_y, out float3 em_color, out float em_weight, + out float3 p_projected) +{ + // Project onto plane + const float3 p_to_emitter = p - em_loc; + const float2 emitter_scale = float2(em_scale_x, em_scale_y); + const float t = dot(p_to_emitter, em_normal); + float emitter_lod = floor(abs(t) / (_Gimmick_Fog_00_Emitter_Lod_Half_Life * step_size)); + p_projected = p - t * em_normal; + + p_projected -= em_loc; + bool in_range = (abs(p_projected.x) < emitter_scale.x) * (abs(p_projected.y) < emitter_scale.y) * (t > 0); + + float2 emitter_uv = clamp(p_projected.xy, -emitter_scale, emitter_scale) / emitter_scale; + emitter_uv /= 2.0; + emitter_uv += 0.5; + // Go up one LOD every 5 meters + // TODO make this tunable + em_color = _Gimmick_Fog_00_Emitter_Texture.SampleLevel(linear_repeat_s, emitter_uv, emitter_lod); + em_color *= _Gimmick_Fog_00_Emitter_Brightness; + float emitter_dist = in_range ? abs(t) : 1000; + float emitter_falloff = min(1, rcp(pow(emitter_dist, 1.4))); + em_weight = in_range * emitter_falloff; } Fog00PBR getFog00(v2f i) { @@ -108,42 +142,56 @@ Fog00PBR getFog00(v2f i) { _Gimmick_Fog_00_Max_Ray / step_size, world_pos_depth_hit_l / step_size)); step_count *= (1 - no_intersection); +#define FOG_MAX_LOOP 128 + step_count = min(step_count, FOG_MAX_LOOP); float3 normal = i.normal; float ao = 0; for (uint ii = 0; ii < step_count; ii++) { - float3 p = ro + (rd * step_size) * ii; + const float3 p = ro + (rd * step_size) * ii; + const float lod = floor((ii * step_size) / _Gimmick_Fog_00_Lod_Half_Life); - const float col_gray = 0; - const float map_p = map(p); - float4 c = float4(col_gray, col_gray, col_gray, map_p); + const float map_p = map(p, lod); + float4 c = float4(0, 0, 0, map_p); c.a = saturate(c.a * _Gimmick_Fog_00_Density * step_size); #if defined(_GIMMICK_FOG_00_EMITTER_TEXTURE) - // Project onto plane - float3 p_to_emitter = p - _Gimmick_Fog_00_Emitter_Location; - float3 emitter_normal = normalize(_Gimmick_Fog_00_Emitter_Normal); - float2 emitter_scale = float2(_Gimmick_Fog_00_Emitter_Scale_X, _Gimmick_Fog_00_Emitter_Scale_Y); - - float t = dot(p_to_emitter, emitter_normal); - float3 p_projected = p - t * emitter_normal; - - p_projected -= _Gimmick_Fog_00_Emitter_Location; - bool in_range = (abs(p_projected.x) < emitter_scale.x) * (abs(p_projected.y) < emitter_scale.y) * (t > 0); - - float2 emitter_uv = clamp(p_projected.xy, -emitter_scale, emitter_scale) / emitter_scale; - emitter_uv /= 2.0; - emitter_uv += 0.5; - float3 emitter_color = _Gimmick_Fog_00_Emitter_Texture.SampleLevel(linear_repeat_s, emitter_uv, 0); - emitter_color *= _Gimmick_Fog_00_Emitter_Brightness; - float emitter_dist = in_range ? abs(t) : 1000; - // Inverse square is physically accurate, but this looks better. - float emitter_falloff = min(1, rcp(pow(emitter_dist, 1.0))); -#if 1 - c.rgb = lerp(c.rgb, emitter_color, in_range * emitter_falloff); -#else - c.rgb = emitter_color; + { + const float3 em_loc = _Gimmick_Fog_00_Emitter0_Location; + const float3 em_normal = normalize(_Gimmick_Fog_00_Emitter0_Normal); + const float em_scale_x = _Gimmick_Fog_00_Emitter0_Scale_X; + const float em_scale_y = _Gimmick_Fog_00_Emitter0_Scale_Y; + + float3 em_color; + float em_weight; + float3 em_p; + getEmitterData(p, step_size, em_loc, em_normal, em_scale_x, em_scale_y, em_color, em_weight, em_p); +#if defined(_GIMMICK_FOG_00_EMITTER_1) + const float3 em1_loc = _Gimmick_Fog_00_Emitter1_Location; + const float3 em1_normal = normalize(_Gimmick_Fog_00_Emitter1_Normal); + const float em1_scale_x = _Gimmick_Fog_00_Emitter1_Scale_X; + const float em1_scale_y = _Gimmick_Fog_00_Emitter1_Scale_Y; + float3 em1_color; + float em1_weight; + float3 em1_p; + getEmitterData(p, step_size, em1_loc, em1_normal, em1_scale_x, em1_scale_y, em1_color, em1_weight, em1_p); + em_color += em1_color; + em_weight += em1_weight; +#endif +#if defined(_GIMMICK_FOG_00_EMITTER_2) + const float3 em2_loc = _Gimmick_Fog_00_Emitter2_Location; + const float3 em2_normal = normalize(_Gimmick_Fog_00_Emitter2_Normal); + const float em2_scale_x = _Gimmick_Fog_00_Emitter2_Scale_X; + const float em2_scale_y = _Gimmick_Fog_00_Emitter2_Scale_Y; + float3 em2_color; + float em2_weight; + float3 em2_p; + getEmitterData(p, step_size, em2_loc, em2_normal, em2_scale_x, em2_scale_y, em2_color, em2_weight, em2_p); + em_color += em2_color; + em_weight += em2_weight; #endif + c.rgb = lerp(c.rgb, em_color, em_weight); + } #endif acc += c * (1.0 - acc.a); @@ -160,7 +208,7 @@ Fog00PBR getFog00(v2f i) { normal = MY_BLEND_NORMALS(normal, n, n_interp); } #endif - if (acc.a > _Gimmick_Fog_00_Albedo_Cutoff) { + if (acc.a > _Gimmick_Fog_00_Alpha_Cutoff) { acc /= acc.a; break; } diff --git a/globals.cginc b/globals.cginc index e76cc4f..7c76853 100644 --- a/globals.cginc +++ b/globals.cginc @@ -5,6 +5,7 @@ UNITY_DECLARE_DEPTH_TEXTURE(_CameraDepthTexture); +SamplerState point_repeat_s; SamplerState linear_repeat_s; SamplerState linear_clamp_s; SamplerState bilinear_repeat_s; @@ -713,15 +714,31 @@ float _Gimmick_Fog_00_Noise_Scale; float _Gimmick_Fog_00_Noise_Exponent; float _Gimmick_Fog_00_Density; float _Gimmick_Fog_00_Normal_Cutoff; -float _Gimmick_Fog_00_Albedo_Cutoff; +float _Gimmick_Fog_00_Alpha_Cutoff; float _Gimmick_Fog_00_Ray_Origin_Randomization; +float _Gimmick_Fog_00_Lod_Half_Life; +texture3D _Gimmick_Fog_00_Noise; #if defined(_GIMMICK_FOG_00_EMITTER_TEXTURE) texture2D _Gimmick_Fog_00_Emitter_Texture; -float3 _Gimmick_Fog_00_Emitter_Location; -float3 _Gimmick_Fog_00_Emitter_Normal; -float _Gimmick_Fog_00_Emitter_Scale_X; -float _Gimmick_Fog_00_Emitter_Scale_Y; float _Gimmick_Fog_00_Emitter_Brightness; +float _Gimmick_Fog_00_Emitter_Lod_Half_Life; + +float3 _Gimmick_Fog_00_Emitter0_Location; +float3 _Gimmick_Fog_00_Emitter0_Normal; +float _Gimmick_Fog_00_Emitter0_Scale_X; +float _Gimmick_Fog_00_Emitter0_Scale_Y; +#if defined(_GIMMICK_FOG_00_EMITTER_1) +float3 _Gimmick_Fog_00_Emitter1_Location; +float3 _Gimmick_Fog_00_Emitter1_Normal; +float _Gimmick_Fog_00_Emitter1_Scale_X; +float _Gimmick_Fog_00_Emitter1_Scale_Y; +#endif +#if defined(_GIMMICK_FOG_00_EMITTER_2) +float3 _Gimmick_Fog_00_Emitter2_Location; +float3 _Gimmick_Fog_00_Emitter2_Normal; +float _Gimmick_Fog_00_Emitter2_Scale_X; +float _Gimmick_Fog_00_Emitter2_Scale_Y; +#endif #endif #endif diff --git a/tooner.shader b/tooner.shader index d3999e9..72567a2 100644 --- a/tooner.shader +++ b/tooner.shader @@ -619,21 +619,38 @@ Shader "yum_food/tooner" _Mochie_UI_Show("UI show", Float) = 0 _Gimmick_Fog_00_Enable_Static("Enable fog 00", Float) = 0 + _Gimmick_Fog_00_Noise("Noise", 3D) = "black" {} _Gimmick_Fog_00_Max_Ray("Max ray", Float) = 25 _Gimmick_Fog_00_Radius("Radius", Float) = 25 _Gimmick_Fog_00_Step_Size_Factor("Step size (meters)", Float) = 1 _Gimmick_Fog_00_Noise_Scale("Noise scale", Float) = 1 _Gimmick_Fog_00_Noise_Exponent("Noise exponent", Float) = 2.0 - _Gimmick_Fog_00_Density("Density", Range(0,2)) = 1.0 + _Gimmick_Fog_00_Density("Density", Range(0,10)) = 1.0 _Gimmick_Fog_00_Normal_Cutoff("Normal cutoff (alpha)", Range(0,1)) = 0.5 - _Gimmick_Fog_00_Albedo_Cutoff("Albedo cutoff (alpha)", Range(0,1)) = 0.9 + _Gimmick_Fog_00_Alpha_Cutoff("Albedo cutoff (alpha)", Range(0,1)) = 0.9 _Gimmick_Fog_00_Ray_Origin_Randomization("Enable ray origin randomization", Range(0,1)) = 1 + _Gimmick_Fog_00_Lod_Half_Life("fog", Float) = 5 + _Gimmick_Fog_00_Emitter_Texture("Emitter texture", 2D) = "black" {} - _Gimmick_Fog_00_Emitter_Location("fog", Vector) = (0, 0, 0, 0) - _Gimmick_Fog_00_Emitter_Normal("fog", Vector) = (-1, 0, 0, 0) - _Gimmick_Fog_00_Emitter_Scale_X("fog", Float) = 1 - _Gimmick_Fog_00_Emitter_Scale_Y("fog", Float) = 1 _Gimmick_Fog_00_Emitter_Brightness("fog", Float) = 1 + _Gimmick_Fog_00_Emitter_Lod_Half_Life("fog", Float) = 5 + + _Gimmick_Fog_00_Emitter0_Location("fog", Vector) = (0, 0, 0, 0) + _Gimmick_Fog_00_Emitter0_Normal("fog", Vector) = (-1, 0, 0, 0) + _Gimmick_Fog_00_Emitter0_Scale_X("fog", Float) = 1 + _Gimmick_Fog_00_Emitter0_Scale_Y("fog", Float) = 1 + + _Gimmick_Fog_00_Emitter1_Enable_Static("fog", Float) = 0 + _Gimmick_Fog_00_Emitter1_Location("fog", Vector) = (0, 0, 0, 0) + _Gimmick_Fog_00_Emitter1_Normal("fog", Vector) = (-1, 0, 0, 0) + _Gimmick_Fog_00_Emitter1_Scale_X("fog", Float) = 1 + _Gimmick_Fog_00_Emitter1_Scale_Y("fog", Float) = 1 + + _Gimmick_Fog_00_Emitter2_Enable_Static("fog", Float) = 0 + _Gimmick_Fog_00_Emitter2_Location("fog", Vector) = (0, 0, 0, 0) + _Gimmick_Fog_00_Emitter2_Normal("fog", Vector) = (-1, 0, 0, 0) + _Gimmick_Fog_00_Emitter2_Scale_X("fog", Float) = 1 + _Gimmick_Fog_00_Emitter2_Scale_Y("fog", Float) = 1 } SubShader { |
