0
\$\begingroup\$

I'm trying to create a chromadepth environment in unity3D. So the camera sees close objects colored as red, and then farthest objects as blue. And everything in between falls down the color spectrum (Red orange yellow green blue)

I've looked into a few things such as z depth or even using fog for this kind of thing. But I'm not sure what the best way to approach this is; or maybe it's already been done before but I couldn't find it in the asset store. I Thanks in advance for your thoughts and time, it is much appreciated.

\$\endgroup\$
2
  • \$\begingroup\$ This talk is from five years ago, but the core ideas it presents about sampling the scene depth texture haven't radically changed since then. It should be enough to get you started. I'm working on some shader examples for a workshop later this month, so I'll post an example when I get to that part of my course prep. \$\endgroup\$
    – DMGregory
    Commented Nov 16, 2016 at 0:55
  • \$\begingroup\$ That would be awesome. Thank you! I will check that out. \$\endgroup\$
    – ibit nosaj
    Commented Nov 16, 2016 at 2:09

2 Answers 2

3
\$\begingroup\$

Here's a simple version which should work both as a fullscreen post effect or as an object in the world, like a magic lens you can look through.

Animation showing a camera panning in & out of a scene with a sliding window rendering the scene's depth in a white-yellow-red-purple spectrum

Pass
{
    CGPROGRAM
    #pragma vertex vert
    #pragma fragment frag
    #pragma target 3.0
    #include "UnityCG.cginc"

    struct appdata
    {
        float4 vertex : POSITION;
    };

    sampler2D _MainTex;
    // Request camera depth buffer as a texture.
    // Incurs extra cost in forward rendering, "just there" in deferred.
    sampler2D _CameraDepthTexture;

    void vert (
        float4 vertex : POSITION,
        out float4 outpos : SV_POSITION)
    {
        outpos = UnityObjectToClipPos(vertex);
    }

    fixed4 frag (UNITY_VPOS_TYPE screenPos : VPOS) : SV_Target
    {
        // Convert pixel coordinates into screen UVs.
        float2 uv = screenPos.xy * (_ScreenParams.zw - 1.0f);
        // Depending on setup/platform, you may need to invert uv.y

        // Sample depth buffer, linearized into the 0...1 range.
        float depth = Linear01Depth(
              UNITY_SAMPLE_DEPTH(tex2D(_CameraDepthTexture, uv)));

        // Compressing the range, so we get more colour 
        // variation close to the camera.
        depth = saturate(2.0f * depth);
        depth = 1.0f - depth;
        depth *= depth;             
        depth = 1.0f - depth;               

        // Use depth value as a lookup into a colour
        // ramp texture of your choosing.
        fixed4 colour = tex2D(_MainTex, depth);

        return colour;
    }
    ENDCG
}

Here I'm using the VPOS semantic to simplify calculating the position of the fragment on the screen. If you need to support shader models below 3 there are other ways to do this, they're just a little messier. ;)

Here's me playing around with this a bit more... Animation of a green sonar wave passing over a dark scene and illuminating it

\$\endgroup\$
4
  • \$\begingroup\$ How do you make it pulse out like that? any chance you could post the code for that? \$\endgroup\$ Commented Nov 17, 2016 at 7:53
  • 1
    \$\begingroup\$ @Justin I made a gradient texture with a sharp bright spot and a long dark tail, with wrap mode set to repeat. Then I subtracted a multiple of _Time.x from my depth value, so the gradient slides over the background periodically. Lastly I took a few extra samples from the surrounding texels in the depth buffer, and used them for a quick edge detection pass to brighten the edges of objects. \$\endgroup\$
    – DMGregory
    Commented Nov 17, 2016 at 13:58
  • \$\begingroup\$ Elegant, I love it. I'm going to save that for a later time when I need a really cool scan effect for the opening of a game level.... Thank you Sir!! If I could upvote again I would!! \$\endgroup\$ Commented Nov 18, 2016 at 1:38
  • \$\begingroup\$ What I did was (For anyone who wants step by step): __ 1Pasted your code into/over the VPOS example script that unity provides on their website. 2Dropped the shader onto a new material 3Created a cube, placed the material on to the cube. 4Made the cube a child of the main camera (under rigidbody firstperson) \$\endgroup\$
    – ibit nosaj
    Commented Nov 27, 2016 at 2:58
0
\$\begingroup\$

I use this in my tile based renderer to show how many lights hit a tile, I also use it for my volume ray casting to get like a heat map effect.

I work with sharpDX but its all done in HLSL so if u tweak it a bit it will work just how u want.

It takes an uint as the distance to camera and another uint as the max range and you can change the colors that match the different ranges.

static const float4 kRadarColors[14] =
{
{ 0, 0.9255, 0.9255, 1 }, // cyan
{ 0, 0.62745, 0.9647, 1 }, // light blue
{ 0, 0, 0.9647, 1 }, // blue
{ 0, 1, 0, 1 }, // bright green
{ 0, 0.7843, 0, 1 }, // green
{ 0, 0.5647, 0, 1 }, // dark green
{ 1, 1, 0, 1 }, // yellow
{ 0.90588, 0.75294, 0, 1 }, // yellow-orange
{ 1, 0.5647, 0, 1 }, // orange
{ 1, 0, 0, 1 }, // bright red
{ 0.8392, 0, 0, 1 }, // red
{ 0.75294, 0, 0, 1 }, // dark red
{ 1, 0, 1, 1 }, // magenta
{ 0.6, 0.3333, 0.7882, 1 }, // purple
};


float4 ConvertDistToColor(uint current, uint max)
{
// black for 0
if (current == 0)
    return float4(0, 0, 0, 1);
// light purple for reaching the max
else if (current == max)
    return float4(0.847, 0.745, 0.921, 1);
// white for going over the max
else if (current > max)
    return float4(1, 1, 1, 1);
// else use weather radar colors
else
{
    // use a log scale to provide more detail when the number of range is smaller

    // want to find the base b such that the logb of max is 14
    // (because we have 14 radar colors)
    float fLogBase = exp2(0.07142857f * log2((float) max));

    // change of base
    // logb(x) = log2(x) / log2(b)
    uint nColorIndex = floor(log2((float) current) / log2(fLogBase));

    return kRadarColors[nColorIndex];
}
}

then use it like this:

return  ConvertDistToColor(asuint(distance(WorldPos, WorldCam) / 255), asuint(farclip / 255));
\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .