March 03, 2018 - hot metal shader, lighting, look-dev

A Plethora of Screen Caps

The past couple weeks I've been obsessively recording screencaps of every update I'm making with the lightsaber game. I think what happens is I capture footage in the hopes of posting on this blog but after reviewing the footage I almost always think "It would be so much more impressive if "... and I find a way to improve the experience. This goes on for days and days and it's happening as I type. Right now I'm thinking how much cooler it would be if when small chunks of cut up metal disappear they poof out of existence with a spark or little explosion. I'm also thinking of an algorithm to make L or C-shaped cuts. But ultimately what might be a huge improvement to me will look like the same shit to someone else ;)

Here's the latest update. I think it's in a good place mainly because I'm finally seeing frame rates approaching 90fps and I'm really happy with the look of the hot metal and the blade animation, realtime reflections, and dynamic lighting:


Leading up to the recording of this video I made a number of updates including:

-on/off switching with blade animation plus a light source with animated intensity
-darker lighting - just trying to show off the glowy goodness
-blade throwing technique - I let someone playtest and he was throwing the saber like this.
-removed reflections from the floor because of the weird reflection probe glitch. I'm mostly interested in the reflections on the saber hilt (which has a reflection probe parented to it). For other reflective objects I've discovered the box projection option which seems to help.
- major updates to the cut metal shader

Shader discussion

Instead of an overall hue shift I'm modulating the "heat" signal which is a grayscale map with a little wave multiplied into it that gets multiplied down over time.
Then I'm shifting the hue, saturation, and value based on that heat signal to produce a more realistic cooling effect.  What really sells it is the way the three channels follow different curves i.e. the hue shifts relatively fast compared to the value which waits until the very end to fade out intensity. see math for tweening link because I used that stuff all over the place.

Here is some of the inspiration I used for the hot metal shader:

hot metal colors reference:
https://en.wikipedia.org/wiki/Incandescence




The "SaberCut" Shader

This is the current shader that I'm using for the hot metal. To use this you'll need to set HeatStart to the current Time.time value otherwise it will just start out hot and then fade out after hitting play. I don't know if this is an efficient way to handle the cooling of the metal. I'm not sure if it will be less expensive if I drive the cooling via a c-sharp script. I'll have to test that.

Also, find a grunge map and put it in the HeatMap field if you want it to look more interesting.

Shader "Shimmy/saberCut" {
    
    Properties {
        _Color ("Color", Color) = (1,1,0,1)
        _MainTex ("Texture", 2D) = "white" {}
        _Specular ("Specular", 2D) = "black" {}

        _HeatInterval ("HeatInterval", Float) = 15
        _HeatStart ("HeatStart", Float) = 0

        _HeatMap ("Heat Map", 2D) = "white" {}
        _HeatMapMin ("HeatMapMin", Range (0,1))=0.1
        _HeatEmissionBoost ("HeatEmissionBoost", Float) = 3

    }
    
   SubShader {
        Tags {
            "Queue" = "Geometry"
        }
        ZWrite On
        CGPROGRAM
            #pragma surface surf StandardSpecular halfasview//approxview //vertex:vert addshadow

            sampler2D _MainTex;
            sampler2D _HeatMap;
            sampler2D _Specular;

            struct Input {
                float2 uv_MainTex;
                float2 uv_HeatMap;
                float2 uv_Specular;
            };

            fixed4 _Color;
            fixed4 _HeatStartColor;
            fixed4 _HeatEndColor;
            fixed _HeatMapMin;
            fixed _HeatEmissionBoost;
            half _HeatInterval;
            half _HeatStart;

        float3 hsv_to_rgb(float3 HSV)
        {
            float3 RGB = HSV.z;

            float var_h = HSV.x * 6;
            float var_i = floor(var_h);   // Or ... var_i = floor( var_h )
            float var_1 = HSV.z * (1.0 - HSV.y);
            float var_2 = HSV.z * (1.0 - HSV.y * (var_h-var_i));
            float var_3 = HSV.z * (1.0 - HSV.y * (1-(var_h-var_i)));
            if      (var_i == 0) { RGB = float3(HSV.z, var_3, var_1); }
            else if (var_i == 1) { RGB = float3(var_2, HSV.z, var_1); }
            else if (var_i == 2) { RGB = float3(var_1, HSV.z, var_3); }
            else if (var_i == 3) { RGB = float3(var_1, var_2, HSV.z); }
            else if (var_i == 4) { RGB = float3(var_3, var_1, HSV.z); }
            else                 { RGB = float3(HSV.z, var_1, var_2); }

            return (RGB);
        }

            void surf (Input IN, inout SurfaceOutputStandardSpecular o) {
                fixed4 c = tex2D (_MainTex, IN.uv_MainTex);
                fixed4 h = tex2D (_HeatMap, IN.uv_HeatMap);
                c *= h;
                half timeElapsed = _Time[1] - _HeatStart;
                fixed heatRatio = (_HeatInterval - timeElapsed) / _HeatInterval;
                o.Albedo = (c.rgb * _Color);

                half sineTime = 0;
                if (heatRatio>0){
                    sineTime = (sin(_Time[3]*4)+1) / 2 * .33 + .67;
                } else {
                    sineTime = 1;
                }

                half sineWaveUV = sin((IN.uv_MainTex.x + IN.uv_MainTex.y) * 3.14 * 10) * .3 + .7;
                half3 hsv = (0,0,0);
                //fixed values fall between -2 and +2

                fixed3 e = (0,0,0);
                heatRatio *= sineWaveUV;
                heatRatio *= h.x * (1-_HeatMapMin) + _HeatMapMin;//IN.uv_HeatMap.x;

                if (timeElapsed < _HeatInterval){
                    hsv = half3(heatRatio * .18 - .02, (1-pow(1-heatRatio,2)) * .05 + .95,min( heatRatio*heatRatio * 7,6));

                    //hsv.x *= sineTime * (pow(sineWaveUV, 2) *  .95 + .05);//(sineTime + pow(sineWaveUV, 2)) / 2;
                    e = half3(hsv_to_rgb(hsv)); //hsv looks much better with half precision compared to fixed. 
                }

                if (heatRatio <= 0){
                    o.Emission = (0,0,0);

                } else {  //this stuff should be done in the fragment shader maybe
                    o.Emission = e;
                }
                o.Emission *= _HeatEmissionBoost;
                o.Specular = tex2D (_Specular, IN.uv_Specular);
            }
        ENDCG

    } 
    Fallback "Standard"
}






Here are 10 sequential videos I captured to show off the latest and greatest updates:


Shader Tweaks


Screenshot Saturday


Animating blade and throwing technique


Bloom, more shader/light adjustments


Updated unity, shader optimization attempt, some lighting optimization


Lighting / shader tweaks


New cap card means much sharper images and a potentially faster frame rate:


Build and run version for Screenshot Saturday


Reflection probes tweaks

Comments

Popular Posts