diff options
| author | yum <yum.food.vr@gmail.com> | 2023-08-09 18:54:17 -0700 |
|---|---|---|
| committer | yum <yum.food.vr@gmail.com> | 2023-08-09 18:54:17 -0700 |
| commit | 3bf013dc3b5479f4fbb458d44801403afe0bb1d2 (patch) | |
| tree | d91ef918797b2036e1005afa1e9b221d293b696f /Shaders/TaSTT_lighting.cginc | |
| parent | 1285caf31578d758c2b52b915eedb17cc12a1826 (diff) | |
Add ray-marched custom chatbox
* Refactor shader code to make development easier. Templates are now
as small as possible.
* Update scaling code. Use Unity scaling instead of a blendshape.
* Check in a fuckton of shader FOSS. Mostly unused.
* Update TaSTT.fbx. Now has 6 faces instead of 2.
Diffstat (limited to 'Shaders/TaSTT_lighting.cginc')
| -rw-r--r-- | Shaders/TaSTT_lighting.cginc | 431 |
1 files changed, 431 insertions, 0 deletions
diff --git a/Shaders/TaSTT_lighting.cginc b/Shaders/TaSTT_lighting.cginc new file mode 100644 index 0000000..10bfd0c --- /dev/null +++ b/Shaders/TaSTT_lighting.cginc @@ -0,0 +1,431 @@ +#ifndef TASTT_LIGHTING +#define TASTT_LIGHTING + +#include "AutoLight.cginc" +#include "UnityPBSLighting.cginc" +#include "ray_march.cginc" +#include "pbr.cginc" +#include "poi.cginc" +#include "stt_generated.cginc" +#include "stt_text.cginc" + +SamplerState linear_repeat_sampler; + +float BG_Enable; +Texture2D BG_BaseColor; +Texture2D BG_NormalMap; +Texture2D BG_Metallic; +Texture2D BG_Smoothness; +float BG_Smoothness_Invert; +float BG_NormalStrength; +float4 BG_BaseColor_ST; +float4 BG_NormalMap_ST; +float4 BG_Metallic_ST; +float4 BG_Smoothness_ST; + +fixed4 Text_Color; +fixed4 Background_Color; +fixed4 Margin_Color; + +float Metallic; +float Smoothness; +float Emissive; + +float Render_Margin; +float Render_Visual_Indicator; +float Margin_Scale; +float Margin_Rounding_Scale; +float Enable_Margin_Effect_Squares; +float Enable_Ray_March; + +float _TaSTT_Indicator_0; +float _TaSTT_Indicator_1; +static const float3 TaSTT_Indicator_Color_0 = HSVtoRGB(float3(0.00, 0.7, 1.0)); +static const float3 TaSTT_Indicator_Color_1 = HSVtoRGB(float3(0.07, 0.7, 1.0)); +static const float3 TaSTT_Indicator_Color_2 = HSVtoRGB(float3(0.30, 0.7, 1.0)); + +fixed4 float3tofixed4(in float3 f3, in float alpha) +{ + return fixed4( + f3.r, + f3.g, + f3.b, + alpha); +} + +void getVertexLightColor(inout v2f i) +{ + #if defined(VERTEXLIGHT_ON) + float3 light_pos = float3(unity_4LightPosX0.x, unity_4LightPosY0.x, + unity_4LightPosZ0.x); + float3 light_vec = light_pos - i.worldPos; + float3 light_dir = normalize(light_vec); + float ndotl = DotClamped(i.normal, light_dir); + // Light fills an expanding sphere with surface area 4 * pi * r^2. + // By conservation of energy, this means that at distance r, light intensity + // is proportional to 1/(r^2). + float attenuation = 1 / (1 + dot(light_vec, light_vec) * unity_4LightAtten0.x); + i.vertexLightColor = unity_LightColor[0].rgb * ndotl * attenuation; + + i.vertexLightColor = Shade4PointLights( + unity_4LightPosX0, unity_4LightPosY0, unity_4LightPosZ0, + unity_LightColor[0].rgb, + unity_LightColor[1].rgb, + unity_LightColor[2].rgb, + unity_LightColor[3].rgb, + unity_4LightAtten0, i.worldPos, i.normal + ); + #endif +} + +v2f vert(appdata v) +{ + v2f o; + o.position = UnityObjectToClipPos(v.position); + o.worldPos = mul(unity_ObjectToWorld, v.position); + o.normal = UnityObjectToWorldNormal(v.normal); + + o.uv.xy = TRANSFORM_TEX(v.uv, BG_BaseColor); + o.uv.zw = 1.0 - v.uv; + getVertexLightColor(o); + + return o; +} + +// dist = sqrt(dx^2 + dy^2) = sqrt(<dx,dy> * <dx,dy>) +bool InRadius2(float2 uv, float2 pos, float radius2) +{ + float2 delta = uv - pos; + return dot(delta, delta) < radius2; +} + +bool InMargin(float2 uv, float2 margin) +{ + bool b0 = uv.x < margin.x; + bool b1 = uv.x > 1 - margin.x; + bool b2 = uv.y < margin.y; + bool b3 = uv.y > 1 - margin.y; + + // De Morgan's law: + // a OR b = !(!a AND !b) + return !(!b0 * !b1 * !b2 * !b3); +} + +bool InSpeechIndicator(float2 uv, float2 margin) +{ + // Margin is uv_margin/2 wide/tall. + // We want a circle whose radius is ~80% of that. + float radius_factor = 0.95; + float radius = margin.x * radius_factor; + // We want this circle to be centered halfway through the margin + // vertically, and at 1.5x the margin width horizontally. + float2 indicator_center = float2(margin.x + radius, margin.y * 0.5); + // Finally, translate it to the top of the board instead of the + // bottom. + indicator_center.y = 1.0 - indicator_center.y; + + return Render_Visual_Indicator && InRadius2(uv, indicator_center, radius * radius); +} + +bool InMarginRounding(float2 uv, float2 margin, float rounding, bool interior) +{ + if (!interior) { + rounding += margin.x; + margin = float2(0, 0); + float err_margin = 0.001; + if (uv.x < err_margin || uv.x > 1.0 - err_margin || + uv.y < err_margin || uv.y > 1.0 - err_margin) { + return true; + } + } + + // This is the center of a circle whose perimeter touches the + // upper left corner of the margin. + float2 c0 = float2(rounding + margin.x, rounding + margin.y); + if (uv.x < c0.x && uv.y < c0.y && uv.x > margin.x && uv.y > margin.y && !InRadius2(uv, c0, rounding * rounding)) { + return true; + } + c0 = float2(rounding + margin.x, 1 - (rounding + margin.y)); + if (uv.x < c0.x && uv.y > c0.y && uv.x > margin.x && uv.y < 1 - margin.y && !InRadius2(uv, c0, rounding * rounding)) { + return true; + } + c0 = float2(1 - (rounding + margin.x), 1 - (rounding + margin.y)); + if (uv.x > c0.x && uv.y > c0.y && uv.x < 1 - margin.x && uv.y < 1 - margin.y && !InRadius2(uv, c0, rounding * rounding)) { + return true; + } + c0 = float2(1 - (rounding + margin.x), rounding + margin.y); + if (uv.x > c0.x && uv.y < c0.y && uv.x < 1 - margin.x && uv.y > margin.y && !InRadius2(uv, c0, rounding * rounding)) { + return true; + } + + return false; +} + +fixed sq_dist(fixed2 p0, fixed2 p1) +{ + fixed2 delta = p1 - p0; + //return abs(delta.x) + abs(delta.y); + return max(abs(delta.x), abs(delta.y)); +} + +fixed4 effect_squares (v2f i) +{ + float2 uv = i.uv.zw; + uv.y *= 2; // Text box has 2:1 aspect ratio + const fixed time = _Time.y; + + fixed theta = PI/4 + sin(time / 4) * 0.1; + fixed2x2 rot = + fixed2x2(cos(theta), -1 * sin(theta), + sin(theta), cos(theta)); + + #define NSQ_X 9.0 + #define NSQ_Y 5.0 + + // Map uv from [0, 1] to [-.5, .5]. + fixed2 p = uv - 0.5; + p *= fixed2(NSQ_X, NSQ_Y); + p = mul(rot, p); + p -= 0.5; + + // See how far we are from the nearest grid point + fixed2 intra_pos = frac(p); + fixed2 intra_center = fixed2(0.5, 0.5); + fixed intra_dist = sq_dist(intra_pos, intra_center); + + fixed st0 = (sin(time) + 1) / 2; + fixed st1 = (sin(time + PI/8) + 1) / 2; + fixed st2 = (sin(time + PI/2) + 1) / 2; + fixed st3 = (sin(time + PI/2 + PI/8) + 1) / 2; + + fixed2 center = fixed2(0, 0); + center = mul(rot, center); + center -= 0.5; + fixed2 rot_lim = fixed2(NSQ_X, NSQ_Y); + rot_lim = mul(rot, rot_lim); + rot_lim -= 0.5; + + float v = 0; + float x = 0; + + if (intra_dist > 0.5 * (0.5 + sin(time * 1.5) * 0.1)) { + v = intra_dist; + } else { + v = 0; + } + + fixed extra_dist = sq_dist(p, center); + fixed check = max(rot_lim.x, rot_lim.y) / 2; + if (extra_dist > check * st0) { + v = 1.0 - v; + } + if (extra_dist > check * st1) { + v = 1.0 - v; + } + if (extra_dist > check * st2) { + v = 1.0 - v; + } + if (extra_dist > check * st3) { + v = 1.0 - v; + } else { + x = 0.50; + } + + fixed3 hsv; + hsv[0] = (v * 0.2 * (1 - x * .8) + 0.55) - x; + hsv[1] = 0.7; + hsv[2] = 0.8; + + fixed3 col = HSVtoRGB(hsv); + + return fixed4(col, 1.0); +} + +fixed4 margin_effect(v2f i) +{ + if (Enable_Margin_Effect_Squares) { + return effect_squares(i); + } else { + return Margin_Color; + } +} + +fixed4 light(v2f i, + Texture2D albedo_map, + Texture2D normal_map, + float normal_str, + Texture2D metallic_map, + Texture2D smoothness_map, + float invert_smoothness, + Texture2D emission_mask, + float3 emission_color, + out float depth) +{ + initNormal(i); + + depth = getWorldSpaceDepth(i.worldPos); + + float2 iddx = ddx(i.uv.x); + float2 iddy = ddy(i.uv.y); + fixed4 albedo = albedo_map.SampleGrad(linear_repeat_sampler, i.uv.xy, + iddx, iddy); + + fixed3 normal = UnpackScaleNormal( + normal_map.SampleGrad(linear_repeat_sampler, i.uv.xy, + iddx, iddy), + normal_str); + // Swap Y and Z + normal = normal.xzy; + + float3 view_dir = normalize(_WorldSpaceCameraPos - i.worldPos); + + float metallic = metallic_map.SampleGrad(linear_repeat_sampler, i.uv.xy, + iddx, iddy); + + float3 specular_tint; + float one_minus_reflectivity; + albedo.rgb = DiffuseAndSpecularFromMetallic( + albedo, metallic, specular_tint, one_minus_reflectivity); + + UnityIndirect indirect_light; + indirect_light.diffuse = 0; + indirect_light.specular = 0; + + float smoothness = smoothness_map.SampleGrad(linear_repeat_sampler, i.uv.xy, + iddx, iddy); + if (invert_smoothness) { + smoothness = 1 - smoothness; + } + + fixed3 emission = emission_mask.SampleGrad(linear_repeat_sampler, i.uv.xy, + iddx, iddy); + + fixed3 pbr = UNITY_BRDF_PBS(albedo, specular_tint, + one_minus_reflectivity, smoothness, + i.normal, view_dir, GetLight(i), GetIndirect(i, view_dir, smoothness)).rgb; + pbr.rgb += emission; + + return fixed4(pbr, albedo.a); +} + +fixed4 light(v2f i, fixed4 unlit, out float depth) +{ + depth = getWorldSpaceDepth(i.worldPos); + + // Get color in spherical harmonics + fixed3 albedo = unlit.rgb; + + float3 view_dir = normalize(_WorldSpaceCameraPos - i.worldPos); + + float3 specular_tint; + float one_minus_reflectivity; + albedo = DiffuseAndSpecularFromMetallic( + albedo, Metallic, specular_tint, one_minus_reflectivity); + + UnityIndirect indirect_light; + indirect_light.diffuse = 0; + indirect_light.specular = 0; + + fixed3 pbr = UNITY_BRDF_PBS(albedo, specular_tint, + one_minus_reflectivity, Smoothness, + i.normal, view_dir, GetLight(i), GetIndirect(i, view_dir, Smoothness)).rgb; + + pbr = lerp(pbr.rgb, unlit.rgb, Emissive); + + return fixed4(pbr, unlit.a); +} + +fixed4 frag(v2f i, out float depth : SV_DepthLessEqual) : SV_Target +{ + float2 uv = i.uv.zw; + depth = -1000.0; + + if (Enable_Ray_March) { + return stt_ray_march(i, depth); + } + + // Fix text orientation + uv.y = 0.5 - uv.y; + uv.x = 1.0 - uv.x; + uv.y *= 2; // Text box has 2:1 aspect ratio + + // Derived from github.com/pema99/shader-knowledge (MIT license). + if (unity_CameraProjection[2][0] != 0.0 || + unity_CameraProjection[2][1] != 0.0) { + uv.x = 1.0 - uv.x; + } + + float2 uv_margin = float2(Margin_Scale, Margin_Scale * 2) / 2; + if (Render_Margin) { + if (Margin_Rounding_Scale > 0.0) { + if (InMarginRounding(uv, uv_margin, Margin_Rounding_Scale, /*interior=*/true)) { + return light(i, margin_effect(i), depth); + } + if (InMarginRounding(uv, uv_margin, Margin_Rounding_Scale, /*interior=*/false)) { + return fixed4(0, 0, 0, 0); + } + } + if (InMargin(uv, uv_margin)) { + if (InSpeechIndicator(uv, uv_margin)) { + if (floor(_TaSTT_Indicator_0) == 1.0) { + // Actively speaking + return light(i, float3tofixed4(TaSTT_Indicator_Color_2, 1.0), depth); + } else if (floor(_TaSTT_Indicator_1) == 1.0) { + // Done speaking, waiting for paging. + return light(i, float3tofixed4(TaSTT_Indicator_Color_1, 1.0), depth); + } else { + // Neither speaking nor paging. + return light(i, float3tofixed4(TaSTT_Indicator_Color_0, 1.0), depth); + } + } + + if (Render_Margin) { + return light(i, margin_effect(i), depth); + } + } + } + + uv_margin *= 4; + float2 uv_with_margin = AddMarginToUV(uv, uv_margin); + + fixed4 text = GetLetter(uv_with_margin); + + if (text.a == 0) { + fixed4 bg; + if (BG_Enable) { + +#if 0 +fixed4 light(inout v2f i, + fixed4 albedo, + float metallic, + float smoothness) +#endif + + const float iddx = ddx(i.uv.x); + const float iddy = ddy(i.uv.y); + fixed4 albedo = BG_BaseColor.SampleGrad(linear_repeat_sampler, i.uv.xy, + iddx, iddy); + float metallic = BG_Metallic.SampleGrad(linear_repeat_sampler, i.uv.xy, + iddx, iddy); + float smoothness = BG_Smoothness.SampleGrad(linear_repeat_sampler, i.uv.xy, + iddx, iddy); + + bg = light(i, + albedo, metallic, smoothness); + } else { + bg = light(i, Background_Color, depth); + } + // Hack: If alpha (text.w) is less than 0.5, don't render it. This + // eliminates outlines around simple emotes with transparent backgrounds. + if (text.w > 0.5) { + // Use emote alpha to mix emote color with background color (compositing). + text.rgb = lerp(bg.rgb, text.rgb, text.w); + bg = light(i, fixed4(text.rgb, 1.0), depth); + } + return bg; + } else { + return light(i, Text_Color, depth); + } +} + +#endif // TASTT_LIGHTING |
