6
\$\begingroup\$

I'm very new to Unity and at this point I don't even really know what questions to ask to move forward with what I want to do.

My end goal is the ability to switch between a standard color view, a White Hot thermal camera kind of view, and a Black Hot thermal view. If I can figure out how to do White Hot, I imagine it'll be trivial to do Black Hot as well.

Here is a real-world image of the effect I'm trying to come up with:

A white hot thermal image

My guess right now is that I could achieve this with 3 different cameras. One for each view mode.

I figure I could create an object with a full color material and have a child object with a different material to show to a camera that only shows White Hot objects. I could filter on tags so only the main object or one of it's child objects is visible at a time but this would require 3 times as many objects in my game. I would hope there's a way to show a Color camera one material but show a WhiteHot camera a different material but I don't see any way to do that.

I found a forum thread about thermal vision but I don't know anything about shaders and the provided sample project won't run in the web player and doesn't gracefully upgrade to my version of Unity.

Where is square one when it comes to having different "camera modes" like this?

EDIT: A brief look into shaders has shown semi-fruitful. I've created the following ultra-simple shader:

Shader "Custom/WhiteHotUnlit"
{
    Properties
    {
        _Temp("Temperature", Float) = 0.0
    }
    SubShader
    {
        Tags { "RenderType" = "Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            float _Temp;

            struct vertInput {
                float4 pos : POSITION;
            };
            struct vertOutput {
                float4 pos : SV_POSITION;
            };

            vertOutput vert(vertInput input) {
                vertOutput o;
                o.pos = mul(UNITY_MATRIX_MVP, input.pos);
                return o;
            }

            half4 frag(vertOutput output) : COLOR {
                return half4(1.0 * _Temp, 1.0 * _Temp, 1.0 * _Temp, 1.0);
            }
            ENDCG
        }
    }
}

This allows me to use the shader on an object and set that object's Temperature and it'll show as a shade of gray depending on that temp. This also lets me set it on a per-object basis. So far so good! However, I can't seem to figure out how to have two shaders on an object. I want my Color Camera to display all objects using the Standard shader and when I switch to the WhiteHot Camera then it should render everything with the WhiteHot shader WITH THE TEMP VALUE ASSIGNED TO THAT OBJECT. I set up the second camera with:

WhiteHot.SetReplacementShader(whShader, null);

And nothing gets drawn when I'm using that camera. I also can't seem to give one object two mesh renderers. I guess that makes sense, but I was hoping to do one renderer per camera. I feel like I'm getting much closer! But I'm not there quite yet...

EDIT 2: Since Vertex and Fragment shaders seem to override and remove shadows (please correct me if I'm wrong, PLEASE) then I've decided to try a surface shader. I've switched to a one camera, one shader approach.

Here's my surface shader right now:

Shader "Custom/WhiteHotLit" {
    Properties {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _Glossiness ("Smoothness", Range(0,1)) = 0.0
        _Metallic ("Metallic", Range(0,1)) = 0.0

        //_Mode ("Mode", Int) = 0
        _Temp ("Temperature", Range(0, 1)) = 0.0
    }
    SubShader {
        Tags { "RenderType"="Opaque" }
        LOD 200


        CGPROGRAM
        // Physically based Standard lighting model, and enable shadows on all light types
        #pragma surface surf Standard fullforwardshadows

        // Use shader model 3.0 target, to get nicer looking lighting
        #pragma target 3.0

        sampler2D _MainTex;

        struct Input {
            float2 uv_MainTex;
        };

        half _Glossiness;
        half _Metallic;
        fixed4 _Color;
        uniform int _Mode = 0;
        float _Temp;

        void surf(Input IN, inout SurfaceOutputStandard o) {
            switch (_Mode) {
            case 1:
                // Here I don't want shadows or lighting
                o.Albedo = fixed3(_Temp, _Temp, _Temp);
                break;
            case 2:
                // Here I don't want shadows or lighting
                o.Albedo = fixed3(1.0 - _Temp, 1.0 - _Temp, 1.0 - _Temp);
                break;
            default:
                // Here I want shadows and lighting
                fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
                o.Albedo = c.rgb;
                o.Metallic = _Metallic;
                o.Smoothness = _Glossiness;
                o.Alpha = c.a;
                break;
            }
        }
        ENDCG
    } 
    FallBack "Diffuse"
}

Now I can make a call to Shader.SetGlobalInt('_Mode', <int>); and change it to 0 (Full Color), 1 (White Hot), or 2 (Black Hot). However, just like Vertex/Fragment Shaders seem to always exclude shadows, this Surface Shader always includes them. I'd like to find a way that for Mode's 1 and 2 I can turn off shadows.

I could turn off all the lights and adjust the ambient light but that sounds like a duct tape kind of solution to this. Could a multi-pass shader do what I want? On one pass could it be a surface shader and on another pass could it be a vertex/fragment shader?

\$\endgroup\$
  • 2
    \$\begingroup\$ You may be able to do this with shader replacement without any extra objects in your scene. You can store the thermal amounts in the alpha channel of the main texture for each object, or in an extra texture unused by the visible spectrum material. The alternate shader then uses that channel/texture instead of the normal diffuse/albedo. \$\endgroup\$ – DMGregory Oct 20 '15 at 16:58
  • \$\begingroup\$ I've gotten over the issue about where to store temperature and displaying the objects using it's proper colors or the colors inspired by the temp. Now I'm trying to figure out how to either 1) Conditionally remove shadows using a surface shader or 2) Conditionally add shadows using a vertex/fragment shader. \$\endgroup\$ – Corey Ogburn Oct 20 '15 at 18:00
  • \$\begingroup\$ I haven't tried it yet. Can tags be changed at runtime? \$\endgroup\$ – Corey Ogburn Oct 20 '15 at 18:06
  • \$\begingroup\$ I spoke too soon again. Deleted my old comment recommending the ForceNoShadowCasting tag. You can override subshader tags at runtime. \$\endgroup\$ – DMGregory Oct 20 '15 at 18:11
  • \$\begingroup\$ Looks like I'd have to do that on a per Material basis. Is there a global way to set that tag? \$\endgroup\$ – Corey Ogburn Oct 20 '15 at 18:57
1
\$\begingroup\$

Unfortunately, I think the right way to implement a realistic thermal vision would be difficult to execute in Unity, as it would require utilization of some experimental stuff. Ideally, you would add a temperature texture property to the standard deferred shader, then add a new 'temperature' buffer pass to the deferred rendering pipeline using that texture property, then your camera could then grab that pass and render it at the flick of a switch.

Alternatively, you could use a post-process effect to achieve a similar result. Here you'll write a shader similar to a ramp shader (see ToonRamp shader here), which maps the final color value of your image to a 'thermal' value. Work-pipeline wise this would be super easy to execute, but it may be difficult to achieve the desired look, because you won't have access to some hidden 'temperature' property.

As for your current implementation, it could work by writing a custom lighting function and adding your switch statement in there to switch between lighting styles, but in general, you want to avoid branching code in shaders because it significantly impacts performance. A much more efficient solution (and frankly, easier to implement on a complex project) is Unity's shader replacement feature. So, you'll want to use 3 separate shaders, one for normal, white-hot, and black hot, then flip between them. Keep in mind, though, that this will force forward-rendering onto your project, because for now, unity has its lighting system for deferred rendering hidden in a black box.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.