Customising a 6-sided skybox shader

I’m trying to emulate Unity’s default-skybox ‘sun’, whose position is dependent on the light direction, while using my own 6 textures for the skybox.
I’ve found the following shader which, in my project, visually looks exactly like the built-in ‘Skybox/6 Sided’ shader and which does not render the sun ‘glow’.

  • Shader “Shaders/custom_skybox”
  • {
  • Properties
  • {
  • _Tint( “Tint Color”, Color ) = ( .5, .5, .5, .5 )
  • [Gamma] _Exposure( “Exposure”, Range( 0, 8 ) ) = 1.0
  • _Rotation( “Rotation”, Range( 0, 360 ) ) = 0
  • [NoScaleOffset] _FrontTex( “Front [+Z] (HDR)”, 2D) = “grey” {}
  • [NoScaleOffset] _BackTex( “Back [-Z] (HDR)”, 2D) = “grey” {}
  • [NoScaleOffset] _LeftTex( “Left [+X] (HDR)”, 2D) = “grey” {}
  • [NoScaleOffset] _RightTex( “Right [-X] (HDR)”, 2D) = “grey” {}
  • [NoScaleOffset] _UpTex( “Up [+Y] (HDR)”, 2D) = “grey” {}
  • [NoScaleOffset] _DownTex( “Down [-Y] (HDR)”, 2D) = “grey” {}
  • }
  • SubShader
  • {
  • Tags
  • {
  • “Queue”=“Background”
  • “RenderType”=“Background”
  • “PreviewType”=“Skybox”
  • }
  • Cull Off
  • ZWrite Off
  • //doesn’t change anything, messed with Zero and One in both spots, no fix
  • //Blend SrcAlpha OneMinusSrcAlpha
    • CGINCLUDE
  • #pragma target 5.0
  • #pragma only_renderers d3d11
  • #pragma exclude_renderers gles
  • //-------------------------------------------------------------------------------------------------------------------------------------------------------------
  • #include “UnityCG.cginc”
  • //#include “vr_utils.cginc”
  • //-------------------------------------------------------------------------------------------------------------------------------------------------------------
  • float4 _Tint;
  • float _Exposure;
  • float _Rotation;
    • #define g_vTint _Tint
  • #define g_flExposure _Exposure
  • #define g_flRotation _Rotation
    • //-------------------------------------------------------------------------------------------------------------------------------------------------------------
  • float4 RotateAroundYInDegrees( float4 vPositionOs, float flDegrees )
  • {
  • float flRadians = flDegrees * UNITY_PI / 180.0;
  • float flSin, flCos;
  • sincos( flRadians, flSin, flCos );
  • float2x2 m = float2x2( flCos, -flSin, flSin, flCos );
  • return float4( mul( m, vPositionOs.xz ), vPositionOs.yw ).xzyw;
  • }
    • //-------------------------------------------------------------------------------------------------------------------------------------------------------------
  • struct VS_INPUT
  • {
  • float4 vPositionOs : POSITION;
  • float2 vTexcoord : TEXCOORD0;
  • };
  • struct PS_INPUT
  • {
  • float4 vPositionPs : SV_POSITION;
  • float2 vTexcoord : TEXCOORD0;
  • };
  • struct PS_OUTPUT
  • {
  • float4 vColor : SV_Target0;
  • };
  • //-------------------------------------------------------------------------------------------------------------------------------------------------------------
  • PS_INPUT SkyboxVs( VS_INPUT v )
  • {
  • PS_INPUT o;
  • o.vPositionPs.xyzw = mul( UNITY_MATRIX_MVP, RotateAroundYInDegrees( v.vPositionOs.xyzw, g_flRotation ) );
  • o.vTexcoord.xy = v.vTexcoord.xy;
  • return o;
  • }
  • //-------------------------------------------------------------------------------------------------------------------------------------------------------------
  • PS_OUTPUT SkyboxPs( PS_INPUT i, sampler2D faceSampler, float4 faceSamplerDecode )
  • {
  • float4 vSkyboxTexel = tex2D( faceSampler, i.vTexcoord.xy ).rgba;
  • float3 vSkyboxLinearColor = DecodeHDR( vSkyboxTexel.rgba, faceSamplerDecode.rgba );
  • PS_OUTPUT o;
  • o.vColor.rgb = saturate( vSkyboxLinearColor.rgb * g_vTint.rgb * unity_ColorSpaceDouble.rgb * g_flExposure );
  • o.vColor.a = 1.0;
  • // Dither to fix banding artifacts
  • //o.vColor.rgb += ScreenSpaceDither( i.vPositionPs.xy );
  • return o;
  • }
  • ENDCG
  • Pass
  • {
  • CGPROGRAM
  • #pragma vertex SkyboxVs
  • #pragma fragment MainPs
  • sampler2D _FrontTex;
  • float4 _FrontTex_HDR;
  • PS_OUTPUT MainPs( PS_INPUT i ) { return SkyboxPs( i, _FrontTex, _FrontTex_HDR ); }
  • ENDCG
  • }
  • Pass
  • {
  • CGPROGRAM
  • #pragma vertex SkyboxVs
  • #pragma fragment MainPs
  • sampler2D _BackTex;
  • float4 _BackTex_HDR;
  • PS_OUTPUT MainPs( PS_INPUT i ) { return SkyboxPs( i, _BackTex, _BackTex_HDR ); }
  • ENDCG
  • }
  • Pass
  • {
  • CGPROGRAM
  • #pragma vertex SkyboxVs
  • #pragma fragment MainPs
  • sampler2D _LeftTex;
  • float4 _LeftTex_HDR;
  • PS_OUTPUT MainPs( PS_INPUT i ) { return SkyboxPs( i, _LeftTex, _LeftTex_HDR ); }
  • ENDCG
  • }
  • Pass
  • {
  • CGPROGRAM
  • #pragma vertex SkyboxVs
  • #pragma fragment MainPs
  • sampler2D _RightTex;
  • float4 _RightTex_HDR;
  • PS_OUTPUT MainPs( PS_INPUT i ) { return SkyboxPs( i, _RightTex, _RightTex_HDR ); }
  • ENDCG
  • }
  • Pass
  • {
  • CGPROGRAM
  • #pragma vertex SkyboxVs
  • #pragma fragment MainPs
  • sampler2D _UpTex;
  • float4 _UpTex_HDR;
  • PS_OUTPUT MainPs( PS_INPUT i ) { return SkyboxPs( i, _UpTex, _UpTex_HDR ); }
  • ENDCG
  • }
  • Pass
  • {
  • CGPROGRAM
  • #pragma vertex SkyboxVs
  • #pragma fragment MainPs
  • sampler2D _DownTex;
  • float4 _DownTex_HDR;
  • PS_OUTPUT MainPs( PS_INPUT i ) { return SkyboxPs( i, _DownTex, _DownTex_HDR ); }
  • ENDCG
  • }
  • }
  • }

Now I’d like to implement the ‘glow’ of the sun, dependent on its direction.

First up, I’ll need to pass in a float4 vector _SunDirection, which will be set from the Unity Editor.

Then, I’ll need to dotproduct the normalised _SunDirection with the direction to the current pixel being rendered. A more ‘parallel’ result will mean a bigger boost in brightness.

But I don’t know where to do that in this shader. I have tried using the ‘dot’ and ‘normalize’ commands in the ‘PS_OUTPUT SkyboxPs’ function, but Unity says those commands are not recognised.

Does anyone have any pointers on where is the best place in this shader to make these calculations?

    Shader "Shaders/custom_skybox"
    {
    Properties
    {
    _Tint( "Tint Color", Color ) = ( .5, .5, .5, .5 )
    [Gamma] _Exposure( "Exposure", Range( 0, 8 ) ) = 1.0
    _Rotation( "Rotation", Range( 0, 360 ) ) = 0
    [NoScaleOffset] _FrontTex( "Front [+Z] (HDR)", 2D) = "grey" {}
    [NoScaleOffset] _BackTex( "Back [-Z] (HDR)", 2D) = "grey" {}
    [NoScaleOffset] _LeftTex( "Left [+X] (HDR)", 2D) = "grey" {}
    [NoScaleOffset] _RightTex( "Right [-X] (HDR)", 2D) = "grey" {}
    [NoScaleOffset] _UpTex( "Up [+Y] (HDR)", 2D) = "grey" {}
    [NoScaleOffset] _DownTex( "Down [-Y] (HDR)", 2D) = "grey"
    _SunIntensity ("Sun Intensity", Range(0,4)) = 1
    _SunSize ("Sun Size", Range(0,8)) = 3
    _SunPos ("Sun Direction", Vector) = (0,1,0,0)
{}
    }
    SubShader
    {
    Tags
    {
    "Queue"="Background"
    "RenderType"="Background"
    "PreviewType"="Skybox"
    }
    Cull Off
    ZWrite Off
    //doesn't change anything, messed with Zero and One in both spots, no fix
    //Blend SrcAlpha OneMinusSrcAlpha
    CGINCLUDE
    #pragma target 5.0
    #pragma only_renderers d3d11
    #pragma exclude_renderers gles
    //-------------------------------------------------------------------------------------------------------------------------------------------------------------
    #include "UnityCG.cginc"
    //#include "vr_utils.cginc"
    //-------------------------------------------------------------------------------------------------------------------------------------------------------------
    float4 _Tint;
    float _Exposure;
    float _Rotation;
    float _SunIntensity;
    float _SunSize;
    float4 _SunPos;
    #define g_vTint _Tint
    #define g_flExposure _Exposure
    #define g_flRotation _Rotation
    //-------------------------------------------------------------------------------------------------------------------------------------------------------------
    float4 RotateAroundYInDegrees( float4 vPositionOs, float flDegrees )
    {
    float flRadians = flDegrees * UNITY_PI / 180.0;
    float flSin, flCos;
    sincos( flRadians, flSin, flCos );
    float2x2 m = float2x2( flCos, -flSin, flSin, flCos );
    return float4( mul( m, vPositionOs.xz ), vPositionOs.yw ).xzyw;
    }
    //-------------------------------------------------------------------------------------------------------------------------------------------------------------
    struct VS_INPUT
    {
    float4 vPositionOs : POSITION;
    float2 vTexcoord : TEXCOORD0;
    };
    struct PS_INPUT
    {
    float4 vPositionPs : SV_POSITION;
    float2 vTexcoord : TEXCOORD0;
    float3 viewDir : TEXCOORD1;
    };
    struct PS_OUTPUT
    {
    float4 vColor : SV_Target0;
    };
    //-------------------------------------------------------------------------------------------------------------------------------------------------------------
    PS_INPUT SkyboxVs( VS_INPUT v )
    {
    PS_INPUT o;
    o.vPositionPs.xyzw = mul( UNITY_MATRIX_MVP, RotateAroundYInDegrees( v.vPositionOs.xyzw, g_flRotation ) );
    o.vTexcoord.xy = v.vTexcoord.xy;
    o.viewDir = _WorldSpaceCameraPos.xyz - mul (_Object2World, v.vPositionOs).xyz;
    return o;
    }
    //-------------------------------------------------------------------------------------------------------------------------------------------------------------
    PS_OUTPUT SkyboxPs( PS_INPUT i, sampler2D faceSampler, float4 faceSamplerDecode )
    {
    float4 vSkyboxTexel = tex2D( faceSampler, i.vTexcoord.xy ).rgba;
    float3 vSkyboxLinearColor = DecodeHDR( vSkyboxTexel.rgba, faceSamplerDecode.rgba );
    PS_OUTPUT o;
    float3 sun = pow (dot (normalize (_SunPos), normalize (i.viewDir)), 8 - _SunSize) * _SunIntensity;
    o.vColor.rgb = saturate( (vSkyboxLinearColor.rgb + sun) * g_vTint.rgb * unity_ColorSpaceDouble.rgb * g_flExposure );
    o.vColor.a = 1.0;
    // Dither to fix banding artifacts
    //o.vColor.rgb += ScreenSpaceDither( i.vPositionPs.xy );
    return o;
    }
    ENDCG
    Pass
    {
    CGPROGRAM
    #pragma vertex SkyboxVs
    #pragma fragment MainPs
    sampler2D _FrontTex;
    float4 _FrontTex_HDR;
    PS_OUTPUT MainPs( PS_INPUT i ) { return SkyboxPs( i, _FrontTex, _FrontTex_HDR ); }
    ENDCG
    }
    Pass
    {
    CGPROGRAM
    #pragma vertex SkyboxVs
    #pragma fragment MainPs
    sampler2D _BackTex;
    float4 _BackTex_HDR;
    PS_OUTPUT MainPs( PS_INPUT i ) { return SkyboxPs( i, _BackTex, _BackTex_HDR ); }
    ENDCG
    }
    Pass
    {
    CGPROGRAM
    #pragma vertex SkyboxVs
    #pragma fragment MainPs
    sampler2D _LeftTex;
    float4 _LeftTex_HDR;
    PS_OUTPUT MainPs( PS_INPUT i ) { return SkyboxPs( i, _LeftTex, _LeftTex_HDR ); }
    ENDCG
    }
    Pass
    {
    CGPROGRAM
    #pragma vertex SkyboxVs
    #pragma fragment MainPs
    sampler2D _RightTex;
    float4 _RightTex_HDR;
    PS_OUTPUT MainPs( PS_INPUT i ) { return SkyboxPs( i, _RightTex, _RightTex_HDR ); }
    ENDCG
    }
    Pass
    {
    CGPROGRAM
    #pragma vertex SkyboxVs
    #pragma fragment MainPs
    sampler2D _UpTex;
    float4 _UpTex_HDR;
    PS_OUTPUT MainPs( PS_INPUT i ) { return SkyboxPs( i, _UpTex, _UpTex_HDR ); }
    ENDCG
    }
    Pass
    {
    CGPROGRAM
    #pragma vertex SkyboxVs
    #pragma fragment MainPs
    sampler2D _DownTex;
    float4 _DownTex_HDR;
    PS_OUTPUT MainPs( PS_INPUT i ) { return SkyboxPs( i, _DownTex, _DownTex_HDR ); }
    ENDCG
    }
    }
    }

This should do it, although if you have a directional light I would recommend you use _WorldSpaceLightPos in place of _SunPos just to keep it consistent.

Thanks for the quick reply! I’ve copied that over the old shader, but Unity is complaining that there’s an “invalid subscript ‘vertex’ at line 81 (on d3d11)”
Any idea what that might mean? I’m assuming I can’t access the .vertex property of the v variable at line 80. Not sure why it’s throwing the error at line 81.
But, again, thanks for the reply!

That was entirely my bad, this is not how I would write a shader and as such forgot they used a custom vertex input struct. Answer should be fixed now.

Thanks for the update. It looks like the ‘Shader is not supported on this GPU (none of the subshaders/fallbacks are suitable)’.
I’ve ended up going with another CubeMap shader I found online. Obscurely you can get the view direction, in the frag function. like this:

float3 viewDir = UNITY_MATRIX_IT_MV[2].xyz;

Crazy, but true. I’d like to think there’s a list of all the available methods somewhere. If so, do you happen to know where that list is? I’ve not found it yet.

It may be incompatible due to its fixed shader model 5/DX11 declaration at the start. As for available methods, you can look at the functions that come with CG as well as Unity’s ‘UnityCG.cginc’.

http://http.developer.nvidia.com/CgTutorial/cg_tutorial_appendix_e.html

This is a old thread, but I have the same problem myself… I need the Sun from the default procedural Skybox on my 6 sided Skybox for a space game, but there is no info about this or its viability. I dont know what to do since Im not familiar with shaders.

Did you get this sorted somehow?