Unity Built-in Sky Box Shader "unity_ColorSpaceDouble"

Hello, everyone. I’m studying the unity built-in skybox shader. In the meantime, I have a question, why do I need to multiply variables unity_ColorSpaceDouble

Shader "Skybox/Cubemap" {
Properties {
    _Tint ("Tint Color", Color) = (.5, .5, .5, .5)
    [Gamma] _Exposure ("Exposure", Range(0, 8)) = 1.0
    _Rotation ("Rotation", Range(0, 360)) = 0
    [NoScaleOffset] _Tex ("Cubemap   (HDR)", Cube) = "grey" {}
}

SubShader {
    Tags { "Queue"="Background" "RenderType"="Background" "PreviewType"="Skybox" }
    Cull Off ZWrite Off

    Pass {

        CGPROGRAM
        #pragma vertex vert
        #pragma fragment frag
        #pragma target 2.0

        #include "UnityCG.cginc"

        samplerCUBE _Tex;
        half4 _Tex_HDR;
        half4 _Tint;
        half _Exposure;
        float _Rotation;

        float3 RotateAroundYInDegrees (float3 vertex, float degrees)
        {
            float alpha = degrees * UNITY_PI / 180.0;
            float sina, cosa;
            sincos(alpha, sina, cosa);
            float2x2 m = float2x2(cosa, -sina, sina, cosa);
            return float3(mul(m, vertex.xz), vertex.y).xzy;
        }

        struct appdata_t {
            float4 vertex : POSITION;
            UNITY_VERTEX_INPUT_INSTANCE_ID
        };

        struct v2f {
            float4 vertex : SV_POSITION;
            float3 texcoord : TEXCOORD0;
            UNITY_VERTEX_OUTPUT_STEREO
        };

        v2f vert (appdata_t v)
        {
            v2f o;
            UNITY_SETUP_INSTANCE_ID(v);
            UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
            float3 rotated = RotateAroundYInDegrees(v.vertex, _Rotation);
            o.vertex = UnityObjectToClipPos(rotated);
            o.texcoord = v.vertex.xyz;
            return o;
        }

        fixed4 frag (v2f i) : SV_Target
        {
            half4 tex = texCUBE (_Tex, i.texcoord);
            half3 c = DecodeHDR (tex, _Tex_HDR);
            [B]c = c * _Tint.rgb * unity_ColorSpaceDouble.rgb;[/B]
            c *= _Exposure;
            return half4(c, 1);
        }
        ENDCG
    }
}

Fallback Off

}

The variable unity_ColorSpaceDouble, What does it mean?

It’s used for color space conversion, I can’t remember if it’s from linear to gamma or the other way around. But it’s an uniform value that can be used as a multiplier. It’s configured somewhere in the UnityCG.cginc.

EDIT: check the source code (I hope this is fresh enough link) Unity-Built-in-Shaders/CGIncludes/UnityCG.cginc at master · TwoTailsGames/Unity-Built-in-Shaders · GitHub

Not exactly.

It’s a value that is perceptually twice as bright as “1.0”. For gamma color space rendering it’s just 2.0 as gamma space rendering is already being done in perceptual color space. For linear color space rendering it’s 4.5947 (2^2.2) because a value of 2.0 in linear color space isn’t twice as bright as 1.0, instead it’s only about 40% brighter. This is because the human eye doesn’t perceive brightness linearly. ~4.6x more photons need to hit your eye for it to be perceived twice as bright.

Using a single multiplier here is an approximation to handle the way many Unity shaders use a tint value that is multiplied by “2”. Using the unity_ColorSpaceDouble instead of a hard coded 2.0 is supposed to make using the same values in gamma space and linear space rendering match more closely. Color values set on a material are automatically gamma corrected if you’re using linear color space rendering, so the default middle grey of (0.5, 0.5, 0.5) ends up as a value of (0.21586, 0.21586, 0.21586) in the shader. That multiplied by 4.5947 gets very close to the ideal of a multiplier of 1.0 at around 0.99183. Wrong, but close enough most people won’t easily notice.

Darker tints will be more obviously wrong as the curve between linear and gamma will be more significantly different than the simple multiply approximation being used.

2 Likes