Search Unity

  1. Calling all beginners! Join the FPS Beginners Mods Challenge until December 13.
    Dismiss Notice
  2. It's Cyber Week at the Asset Store!
    Dismiss Notice

Unity 5 Beta 13 Procedural Skybox Shader

Discussion in 'Unity 5 Pre-order Beta' started by alexandre-fiset, Nov 14, 2014.

  1. alexandre-fiset

    alexandre-fiset

    Joined:
    Mar 19, 2012
    Posts:
    405
    Hi there,

    Does anybody know how can we change the color of the sky with the new procedural Skybox Shader?

    The new scattering effect is cool, but now we are stuck with a plain blue sky. I looked at the shader code, but could not find any color value in it other than "front color" which is set in a for loop I don't understand.

    What if we want to create a new planet that has a red sky? Is it possible with with the new shader? The one from Beta 9 supported custom colors.
     
  2. Luckymouse

    Luckymouse

    Joined:
    Jan 31, 2010
    Posts:
    481
    you can modify the wavelengths value to change the sky color

    Code (CSharp):
    1.         // RGB wavelengths
    2.         #define WR 0.68
    3.         #define WG 0.55
    4.         #define WB 0.44
     
  3. Todd-Wasson

    Todd-Wasson

    Joined:
    Aug 7, 2014
    Posts:
    1,065
    Where do you put the defines? The 13 sky is a lot darker than the 12 sky. I'd like to lighten it up to look closer to 12.
     
  4. the_motionblur

    the_motionblur

    Joined:
    Mar 4, 2008
    Posts:
    1,736
    I've noticed too that Beta13 changed the procedural sky material. The new one feels rather limited, unfortunately.

    You can still change the material for the whole scene, though. Under the lighting window go to the Scene Tab and there from environment lighting select gradient. This is more like the previous sky material but directly inside the lighting window.
     
  5. Todd-Wasson

    Todd-Wasson

    Joined:
    Aug 7, 2014
    Posts:
    1,065
    Thanks, I saw that earlier. That doesn't affect the brightness of the actual sky though. 12 was a really brilliant, bright sky which looked great to me. Now it's just too dark up high, almost like twilight.
     
  6. the_motionblur

    the_motionblur

    Joined:
    Mar 4, 2008
    Posts:
    1,736
    The actuall brightness of the sky is set below with the "Ambient Intensity" slider.
    It's also a good idea to have a directional light source in the scene which sets the sun on the sky.
     
  7. Todd-Wasson

    Todd-Wasson

    Joined:
    Aug 7, 2014
    Posts:
    1,065
    All those settings do is change the lighting of the scenery, they don't change the blue sky color at all that I can see.
     
  8. the_motionblur

    the_motionblur

    Joined:
    Mar 4, 2008
    Posts:
    1,736
    It seems you are correct. Please file a bug report if you think this is the wrong behaviour.
    I agree that this is not optimal at the moment.
     
  9. AcidArrow

    AcidArrow

    Joined:
    May 20, 2010
    Posts:
    6,115
    Download the built-in shaders, find the shader Skybox-Procedural. Copy it in your project, edit it and change the name (in the shader, not the file itself, but you can do that as well if you want) and remove procedural/ in front (not sure why, but it wouldn't show up in the list if I left it).

    Make a new material, apply your edited shader to it, set that material as a skybox.

    Now in the shader file, you can tweak all the defines (including the ones Luckymouse told) and put values that you like.

    There are interesting too:

    #define kRAYLEIGH 0.0025 // Rayleigh constant
    #define kMIE 0.0010 // Mie constant
    #define kSUN_BRIGHTNESS 20.0 // Sun brightness


    I think the new procedural is pretty great. The implementation is kinda basic, but it looks ok, you can edit the shader to improve it, and with the new skybox model, the gradient is done per vertex, which means it's a lot faster than a per pixel skybox.
     
    Todd-Wasson and the_motionblur like this.
  10. Reanimate_L

    Reanimate_L

    Joined:
    Oct 10, 2009
    Posts:
    2,382
    This might help
    Code (CSharp):
    1. Shader "Skybox/ProceduralUpgrade" {
    2. Properties {
    3.     _HdrExposure("HDR Exposure", Float) = 1.3
    4.     _GroundColor ("Ground", Color) = (.369, .349, .341, 1)
    5.     _RL("Rayleigh", Float) = 0.0025
    6.     _MIE ("MIE", Float) = 0.0010
    7.     _SUN("Sun brightness", Float) = 20.0
    8.  
    9. }
    10.  
    11. SubShader {
    12.     Tags { "Queue"="Background" "RenderType"="Background" "PreviewType"="Skybox" }
    13.     Cull Off ZWrite Off
    14.  
    15.     Pass {
    16.        
    17.         CGPROGRAM
    18.         #pragma vertex vert
    19.         #pragma fragment frag
    20.  
    21.         #include "UnityCG.cginc"
    22.         #include "Lighting.cginc"
    23.  
    24.  
    25.         uniform half _HdrExposure,_RL,_MIE,_SUN;        // HDR exposure
    26.         uniform half3 _GroundColor;
    27.  
    28.  
    29.         // RGB wavelengths
    30.         #define WR 0.65
    31.         #define WG 0.57
    32.         #define WB 0.475
    33.         static const float3 kInvWavelength = float3(1.0 / (WR*WR*WR*WR), 1.0 / (WG*WG*WG*WG), 1.0 / (WB*WB*WB*WB));
    34.         #define OUTER_RADIUS 1.025
    35.         static const float kOuterRadius = OUTER_RADIUS;
    36.         static const float kOuterRadius2 = OUTER_RADIUS*OUTER_RADIUS;
    37.         static const float kInnerRadius = 1.0;
    38.         static const float kInnerRadius2 = 1.0;
    39.  
    40.         static const float kCameraHeight = 0.0001;
    41.  
    42.         //#define kRAYLEIGH 0.0025        // Rayleigh constant
    43.         //#define kMIE 0.0010              // Mie constant
    44.         //#define kSUN_BRIGHTNESS 20.0     // Sun brightness
    45.         #define kRAYLEIGH _RL        // Rayleigh constant
    46.         #define kMIE _MIE             // Mie constant
    47.         #define kSUN_BRIGHTNESS _SUN     // Sun brightness
    48.  
    49.         static const float kKrESun = kRAYLEIGH * kSUN_BRIGHTNESS;
    50.         static const float kKmESun = kMIE * kSUN_BRIGHTNESS;
    51.         static const float kKr4PI = kRAYLEIGH * 4.0 * 3.14159265;
    52.         static const float kKm4PI = kMIE * 4.0 * 3.14159265;
    53.         static const float kScale = 1.0 / (OUTER_RADIUS - 1.0);
    54.         static const float kScaleDepth = 0.25;
    55.         static const float kScaleOverScaleDepth = (1.0 / (OUTER_RADIUS - 1.0)) / 0.25;
    56.         static const float kSamples = 2.0; // THIS IS UNROLLED MANUALLY, DON'T TOUCH
    57.  
    58.         #define MIE_G (-0.990)
    59.         #define MIE_G2 0.9801
    60.  
    61.  
    62.         struct appdata_t {
    63.             float4 vertex : POSITION;
    64.         };
    65.  
    66.         struct v2f {
    67.                 float4 pos : SV_POSITION;
    68.                 half3 rayDir : TEXCOORD0;    // Vector for incoming ray, normalized ( == -eyeRay )
    69.                 half3 cIn : TEXCOORD1;         // In-scatter coefficient
    70.                 half3 cOut : TEXCOORD2;        // Out-scatter coefficient
    71.            };
    72.  
    73.         float scale(float inCos)
    74.         {
    75.             float x = 1.0 - inCos;
    76.             return 0.25 * exp(-0.00287 + x*(0.459 + x*(3.83 + x*(-6.80 + x*5.25))));
    77.         }
    78.  
    79.         v2f vert (appdata_t v)
    80.         {
    81.             v2f OUT;
    82.             OUT.pos = mul(UNITY_MATRIX_MVP, v.vertex);
    83.  
    84.             float3 cameraPos = float3(0,kInnerRadius + kCameraHeight,0);     // The camera's current position
    85.        
    86.             // Get the ray from the camera to the vertex and its length (which is the far point of the ray passing through the atmosphere)
    87.             float3 eyeRay = normalize(mul((float3x3)_Object2World, v.vertex.xyz));
    88.  
    89.             OUT.rayDir = half3(-eyeRay);
    90.  
    91.             float far = 0.0;
    92.             if(eyeRay.y >= 0.0)
    93.             {
    94.                 // Sky
    95.                 // Calculate the length of the "atmosphere"
    96.                 far = sqrt(kOuterRadius2 + kInnerRadius2 * eyeRay.y * eyeRay.y - kInnerRadius2) - kInnerRadius * eyeRay.y;
    97.  
    98.                 float3 pos = cameraPos + far * eyeRay;
    99.                
    100.                 // Calculate the ray's starting position, then calculate its scattering offset
    101.                 float height = kInnerRadius + kCameraHeight;
    102.                 float depth = exp(kScaleOverScaleDepth * (-kCameraHeight));
    103.                 float startAngle = dot(eyeRay, cameraPos) / height;
    104.                 float startOffset = depth*scale(startAngle);
    105.                
    106.            
    107.                 // Initialize the scattering loop variables
    108.                 float sampleLength = far / kSamples;
    109.                 float scaledLength = sampleLength * kScale;
    110.                 float3 sampleRay = eyeRay * sampleLength;
    111.                 float3 samplePoint = cameraPos + sampleRay * 0.5;
    112.  
    113.                 // Now loop through the sample rays
    114.                 float3 frontColor = float3(0.0, 0.0, 0.0);
    115.                 // WTF BBQ: WP8 and desktop FL_9_1 do not like the for loop here
    116.                 // (but an almost identical loop is perfectly fine in the ground calculations below)
    117.                 // Just unrolling this manually seems to make everything fine again.
    118. //                for(int i=0; i<int(kSamples); i++)
    119.                 {
    120.                     float height = length(samplePoint);
    121.                     float depth = exp(kScaleOverScaleDepth * (kInnerRadius - height));
    122.                     float lightAngle = dot(_WorldSpaceLightPos0.xyz, samplePoint) / height;
    123.                     float cameraAngle = dot(eyeRay, samplePoint) / height;
    124.                     float scatter = (startOffset + depth*(scale(lightAngle) - scale(cameraAngle)));
    125.                     float3 attenuate = exp(-scatter * (kInvWavelength * kKr4PI + kKm4PI));
    126.  
    127.                     frontColor += attenuate * (depth * scaledLength);
    128.                     samplePoint += sampleRay;
    129.                 }
    130.                 {
    131.                     float height = length(samplePoint);
    132.                     float depth = exp(kScaleOverScaleDepth * (kInnerRadius - height));
    133.                     float lightAngle = dot(_WorldSpaceLightPos0.xyz, samplePoint) / height;
    134.                     float cameraAngle = dot(eyeRay, samplePoint) / height;
    135.                     float scatter = (startOffset + depth*(scale(lightAngle) - scale(cameraAngle)));
    136.                     float3 attenuate = exp(-scatter * (kInvWavelength * kKr4PI + kKm4PI));
    137.  
    138.                     frontColor += attenuate * (depth * scaledLength);
    139.                     samplePoint += sampleRay;
    140.                 }
    141.  
    142.  
    143.  
    144.                 // Finally, scale the Mie and Rayleigh colors and set up the varying variables for the pixel shader
    145.                 OUT.cIn.xyz = frontColor * (kInvWavelength * kKrESun);
    146.                 OUT.cOut = frontColor * kKmESun;
    147.             }
    148.             else
    149.             {
    150.                 // Ground
    151.                 far = (-kCameraHeight) / (min(-0.00001, eyeRay.y));
    152.  
    153.                 float3 pos = cameraPos + far * eyeRay;
    154.  
    155.                 // Calculate the ray's starting position, then calculate its scattering offset
    156.                 float depth = exp((-kCameraHeight) * (1.0/kScaleDepth));
    157.                 float cameraAngle = dot(-eyeRay, pos);
    158.                 float lightAngle = dot(_WorldSpaceLightPos0.xyz, pos);
    159.                 float cameraScale = scale(cameraAngle);
    160.                 float lightScale = scale(lightAngle);
    161.                 float cameraOffset = depth*cameraScale;
    162.                 float temp = (lightScale + cameraScale);
    163.                
    164.                 // Initialize the scattering loop variables
    165.                 float sampleLength = far / kSamples;
    166.                 float scaledLength = sampleLength * kScale;
    167.                 float3 sampleRay = eyeRay * sampleLength;
    168.                 float3 samplePoint = cameraPos + sampleRay * 0.5;
    169.                
    170.                 // Now loop through the sample rays
    171.                 float3 frontColor = float3(0.0, 0.0, 0.0);
    172.                 float3 attenuate;
    173.                 for(int i=0; i<int(kSamples); i++)
    174.                 {
    175.                     float height = length(samplePoint);
    176.                     float depth = exp(kScaleOverScaleDepth * (kInnerRadius - height));
    177.                     float scatter = depth*temp - cameraOffset;
    178.                     attenuate = exp(-scatter * (kInvWavelength * kKr4PI + kKm4PI));
    179.                     frontColor += attenuate * (depth * scaledLength);
    180.                     samplePoint += sampleRay;
    181.                 }
    182.            
    183.                 OUT.cIn.xyz = frontColor * (kInvWavelength * kKrESun + kKmESun);
    184.                 OUT.cOut.xyz = clamp(attenuate, 0.0, 1.0);
    185.             }
    186.  
    187.  
    188.             return OUT;
    189.  
    190.         }
    191.  
    192.  
    193.         // Calculates the Mie phase function
    194.         half getMiePhase(half eyeCos, half eyeCos2)
    195.         {
    196.             half temp = 1.0 + MIE_G2 - 2.0 * MIE_G * eyeCos;
    197.             // A somewhat rough approx for :
    198.             // temp = pow(temp, 1.5);
    199.             temp = smoothstep(0.0, 0.01, temp) * temp;
    200.             temp = max(temp,1.0e-4); // prevent division by zero, esp. in half precision
    201.             return 1.5 * ((1.0 - MIE_G2) / (2.0 + MIE_G2)) * (1.0 + eyeCos2) / temp;
    202.         }
    203.  
    204.         // Calculates the Rayleigh phase function
    205.         half getRayleighPhase(half eyeCos2)
    206.         {
    207.             return 0.75 + 0.75*eyeCos2;
    208.         }
    209.  
    210.         half4 frag (v2f IN) : SV_Target
    211.         {
    212.             half3 col;
    213.             if(IN.rayDir.y < 0.0)
    214.             {
    215.                 half eyeCos = dot(_WorldSpaceLightPos0.xyz, normalize(IN.rayDir.xyz));
    216.                 half eyeCos2 = eyeCos * eyeCos;
    217.                 col = getRayleighPhase(eyeCos2) * IN.cIn.xyz + getMiePhase(eyeCos, eyeCos2) * IN.cOut * _LightColor0.xyz;
    218.             }
    219.             else
    220.             {
    221.                 col = IN.cIn.xyz + _GroundColor * IN.cOut;
    222.             }
    223.             //Adjust color from HDR
    224.             col *= _HdrExposure;
    225.             return half4(col,1.0);
    226.  
    227.         }
    228.         ENDCG
    229.     }
    230. }    
    231.  
    232.  
    233. Fallback Off
    234.  
    235. }
    236.  
     
    Todd-Wasson likes this.
  11. Todd-Wasson

    Todd-Wasson

    Joined:
    Aug 7, 2014
    Posts:
    1,065
    Thanks a lot, guys. I'll give that a try when I get a chance.
     
  12. Stephen-Lavelle

    Stephen-Lavelle

    Joined:
    Apr 17, 2013
    Posts:
    41
    FWIW, much preferred the procedural skybox shader when all the colours (sky/land/horizon/sun) were easily definable - for game jam settings it makes it significantly easier to throw in a random sky. It's gone from being wonderful to being...I guess yeah, not so wonderful - for me it's less flexible now than just using an old-style preset skybox and colouring the material. (I used the colourable one for several short projects in the time it was included)
     
  13. Todd-Wasson

    Todd-Wasson

    Joined:
    Aug 7, 2014
    Posts:
    1,065
    Thanks for the help, guys. One question:

    I got to this part, but am hung up on the last step: Set that material as a skybox. How do I that exactly? I have created the material and have set the shader to "Skybox" which I've created as instructed, but don't see how to set that material as a skybox. If I fiddle with the HDR exposure setting I can see it change in the inspector, but it's not actually going over to the game itself. It's like it's using the default skybox anyway and ignoring this new one, something like that.
     
  14. Todd-Wasson

    Todd-Wasson

    Joined:
    Aug 7, 2014
    Posts:
    1,065
    Oh, nevermind, I just had to drag it onto the sky in the preview window. Thanks again for the help, guys. Much appreciated. :)
     
  15. alexandre-fiset

    alexandre-fiset

    Joined:
    Mar 19, 2012
    Posts:
    405
    For those interested: It seems like Beta 15 added support for a Tint color as well as an Atmospheric density value to the procedural Skybox. That was fast.
     
    Stephen-Lavelle and AcidArrow like this.
  16. the_motionblur

    the_motionblur

    Joined:
    Mar 4, 2008
    Posts:
    1,736
    Beta 16 will apparently have more control again.
    One of my bugtracker tickets got an update with the note from QA team that b16 will have this resolved.

    Thank you, Unity. :)
     
    Stephen-Lavelle likes this.
  17. Stephen-Lavelle

    Stephen-Lavelle

    Joined:
    Apr 17, 2013
    Posts:
    41
    yaaaay - gonna wait for the stable release, but yaaaay