Search Unity

Skybox without bloom

Discussion in 'Image Effects' started by hjohnsen, Jan 15, 2019.

  1. hjohnsen

    hjohnsen

    Joined:
    Feb 4, 2018
    Posts:
    67
    Hi,

    I am using a bloom post processing effect on my camera but I want that the skybox is not affected by the bloom effect.
    Does anybody know of a shader or asset that could do that ? It should be easy to do since the only thing to add is a test on the zbuffer (but I'm really bad with shaders).

    ++
     
  2. ifurkend

    ifurkend

    Joined:
    Sep 4, 2012
    Posts:
    350
    If you mean excluding the skybox from all Post-Processing effects, there is no easy way to do this. The "second skybox only camera" trick won't work.

    If you need only the HDR bloom effect to exclude the procedural skybox, it is just a matter of clamping the final color value in the skybox shader and set the bloom threshold with the same cap value. Here is the modified code of the built-in procedural skybox shader, I just added the _GlowCap:

    Code (CSharp):
    1. // Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt)
    2.  
    3. Shader "Skybox/Procedural Clamp Intensity" {
    4. Properties {
    5.     [KeywordEnum(None, Simple, High Quality)] _SunDisk ("Sun", Int) = 2
    6.     _SunSize ("Sun Size", Range(0,1)) = 0.04
    7.     _SunSizeConvergence("Sun Size Convergence", Range(1,10)) = 5
    8.  
    9.     _AtmosphereThickness ("Atmosphere Thickness", Range(0,5)) = 1.0
    10.     _SkyTint ("Sky Tint", Color) = (.5, .5, .5, 1)
    11.     _GroundColor ("Ground", Color) = (.369, .349, .341, 1)
    12.  
    13.     _Exposure("Exposure", Range(0, 8)) = 1.3
    14.  
    15.     _GlowCap("Intensity Cap", Range(0,3)) = 1
    16. }
    17.  
    18. SubShader {
    19.     Tags { "Queue"="Background" "RenderType"="Background" "PreviewType"="Skybox" }
    20.     Cull Off ZWrite Off
    21.  
    22.     Pass {
    23.  
    24.         CGPROGRAM
    25.         #pragma vertex vert
    26.         #pragma fragment frag
    27.  
    28.         #include "UnityCG.cginc"
    29.         #include "Lighting.cginc"
    30.  
    31.         #pragma multi_compile _SUNDISK_NONE _SUNDISK_SIMPLE _SUNDISK_HIGH_QUALITY
    32.  
    33.         uniform half _Exposure;     // HDR exposure
    34.         uniform half3 _GroundColor;
    35.         uniform half _SunSize;
    36.         uniform half _SunSizeConvergence;
    37.         uniform half3 _SkyTint;
    38.         uniform half _AtmosphereThickness;
    39.         uniform half _GlowCap;
    40.  
    41.     #if defined(UNITY_COLORSPACE_GAMMA)
    42.         #define GAMMA 2
    43.         #define COLOR_2_GAMMA(color) color
    44.         #define COLOR_2_LINEAR(color) color*color
    45.         #define LINEAR_2_OUTPUT(color) sqrt(color)
    46.     #else
    47.         #define GAMMA 2.2
    48.         // HACK: to get gfx-tests in Gamma mode to agree until UNITY_ACTIVE_COLORSPACE_IS_GAMMA is working properly
    49.         #define COLOR_2_GAMMA(color) ((unity_ColorSpaceDouble.r>2.0) ? pow(color,1.0/GAMMA) : color)
    50.         #define COLOR_2_LINEAR(color) color
    51.         #define LINEAR_2_LINEAR(color) color
    52.     #endif
    53.  
    54.         // RGB wavelengths
    55.         // .35 (.62=158), .43 (.68=174), .525 (.75=190)
    56.         static const float3 kDefaultScatteringWavelength = float3(.65, .57, .475);
    57.         static const float3 kVariableRangeForScatteringWavelength = float3(.15, .15, .15);
    58.  
    59.         #define OUTER_RADIUS 1.025
    60.         static const float kOuterRadius = OUTER_RADIUS;
    61.         static const float kOuterRadius2 = OUTER_RADIUS*OUTER_RADIUS;
    62.         static const float kInnerRadius = 1.0;
    63.         static const float kInnerRadius2 = 1.0;
    64.  
    65.         static const float kCameraHeight = 0.0001;
    66.  
    67.         #define kRAYLEIGH (lerp(0.0, 0.0025, pow(_AtmosphereThickness,2.5)))      // Rayleigh constant
    68.         #define kMIE 0.0010             // Mie constant
    69.         #define kSUN_BRIGHTNESS 20.0    // Sun brightness
    70.  
    71.         #define kMAX_SCATTER 50.0 // Maximum scattering value, to prevent math overflows on Adrenos
    72.  
    73.         static const half kHDSundiskIntensityFactor = 15.0;
    74.         static const half kSimpleSundiskIntensityFactor = 27.0;
    75.  
    76.         static const half kSunScale = 400.0 * kSUN_BRIGHTNESS;
    77.         static const float kKmESun = kMIE * kSUN_BRIGHTNESS;
    78.         static const float kKm4PI = kMIE * 4.0 * 3.14159265;
    79.         static const float kScale = 1.0 / (OUTER_RADIUS - 1.0);
    80.         static const float kScaleDepth = 0.25;
    81.         static const float kScaleOverScaleDepth = (1.0 / (OUTER_RADIUS - 1.0)) / 0.25;
    82.         static const float kSamples = 2.0; // THIS IS UNROLLED MANUALLY, DON'T TOUCH
    83.  
    84.         #define MIE_G (-0.990)
    85.         #define MIE_G2 0.9801
    86.  
    87.         #define SKY_GROUND_THRESHOLD 0.02
    88.  
    89.         // fine tuning of performance. You can override defines here if you want some specific setup
    90.         // or keep as is and allow later code to set it according to target api
    91.  
    92.         // if set vprog will output color in final color space (instead of linear always)
    93.         // in case of rendering in gamma mode that means that we will do lerps in gamma mode too, so there will be tiny difference around horizon
    94.         // #define SKYBOX_COLOR_IN_TARGET_COLOR_SPACE 0
    95.  
    96.         // sun disk rendering:
    97.         // no sun disk - the fastest option
    98.         #define SKYBOX_SUNDISK_NONE 0
    99.         // simplistic sun disk - without mie phase function
    100.         #define SKYBOX_SUNDISK_SIMPLE 1
    101.         // full calculation - uses mie phase function
    102.         #define SKYBOX_SUNDISK_HQ 2
    103.  
    104.         // uncomment this line and change SKYBOX_SUNDISK_SIMPLE to override material settings
    105.         // #define SKYBOX_SUNDISK SKYBOX_SUNDISK_SIMPLE
    106.  
    107.     #ifndef SKYBOX_SUNDISK
    108.         #if defined(_SUNDISK_NONE)
    109.             #define SKYBOX_SUNDISK SKYBOX_SUNDISK_NONE
    110.         #elif defined(_SUNDISK_SIMPLE)
    111.             #define SKYBOX_SUNDISK SKYBOX_SUNDISK_SIMPLE
    112.         #else
    113.             #define SKYBOX_SUNDISK SKYBOX_SUNDISK_HQ
    114.         #endif
    115.     #endif
    116.  
    117.     #ifndef SKYBOX_COLOR_IN_TARGET_COLOR_SPACE
    118.         #if defined(SHADER_API_MOBILE)
    119.             #define SKYBOX_COLOR_IN_TARGET_COLOR_SPACE 1
    120.         #else
    121.             #define SKYBOX_COLOR_IN_TARGET_COLOR_SPACE 0
    122.         #endif
    123.     #endif
    124.  
    125.         // Calculates the Rayleigh phase function
    126.         half getRayleighPhase(half eyeCos2)
    127.         {
    128.             return 0.75 + 0.75*eyeCos2;
    129.         }
    130.         half getRayleighPhase(half3 light, half3 ray)
    131.         {
    132.             half eyeCos = dot(light, ray);
    133.             return getRayleighPhase(eyeCos * eyeCos);
    134.         }
    135.  
    136.  
    137.         struct appdata_t
    138.         {
    139.             float4 vertex : POSITION;
    140.             UNITY_VERTEX_INPUT_INSTANCE_ID
    141.         };
    142.  
    143.         struct v2f
    144.         {
    145.             float4  pos             : SV_POSITION;
    146.  
    147.         #if SKYBOX_SUNDISK == SKYBOX_SUNDISK_HQ
    148.             // for HQ sun disk, we need vertex itself to calculate ray-dir per-pixel
    149.             float3  vertex          : TEXCOORD0;
    150.         #elif SKYBOX_SUNDISK == SKYBOX_SUNDISK_SIMPLE
    151.             half3   rayDir          : TEXCOORD0;
    152.         #else
    153.             // as we dont need sun disk we need just rayDir.y (sky/ground threshold)
    154.             half    skyGroundFactor : TEXCOORD0;
    155.         #endif
    156.  
    157.             // calculate sky colors in vprog
    158.             half3   groundColor     : TEXCOORD1;
    159.             half3   skyColor        : TEXCOORD2;
    160.  
    161.         #if SKYBOX_SUNDISK != SKYBOX_SUNDISK_NONE
    162.             half3   sunColor        : TEXCOORD3;
    163.         #endif
    164.  
    165.             UNITY_VERTEX_OUTPUT_STEREO
    166.         };
    167.  
    168.  
    169.         float scale(float inCos)
    170.         {
    171.             float x = 1.0 - inCos;
    172.         #if defined(SHADER_API_N3DS)
    173.             // The polynomial expansion here generates too many swizzle instructions for the 3DS vertex assembler
    174.             // Approximate by removing x^1 and x^2
    175.             return 0.25 * exp(-0.00287 + x*x*x*(-6.80 + x*5.25));
    176.         #else
    177.             return 0.25 * exp(-0.00287 + x*(0.459 + x*(3.83 + x*(-6.80 + x*5.25))));
    178.         #endif
    179.         }
    180.  
    181.         v2f vert (appdata_t v)
    182.         {
    183.             v2f OUT;
    184.             UNITY_SETUP_INSTANCE_ID(v);
    185.             UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
    186.             OUT.pos = UnityObjectToClipPos(v.vertex);
    187.  
    188.             float3 kSkyTintInGammaSpace = COLOR_2_GAMMA(_SkyTint); // convert tint from Linear back to Gamma
    189.             float3 kScatteringWavelength = lerp (
    190.                 kDefaultScatteringWavelength-kVariableRangeForScatteringWavelength,
    191.                 kDefaultScatteringWavelength+kVariableRangeForScatteringWavelength,
    192.                 half3(1,1,1) - kSkyTintInGammaSpace); // using Tint in sRGB gamma allows for more visually linear interpolation and to keep (.5) at (128, gray in sRGB) point
    193.             float3 kInvWavelength = 1.0 / pow(kScatteringWavelength, 4);
    194.  
    195.             float kKrESun = kRAYLEIGH * kSUN_BRIGHTNESS;
    196.             float kKr4PI = kRAYLEIGH * 4.0 * 3.14159265;
    197.  
    198.             float3 cameraPos = float3(0,kInnerRadius + kCameraHeight,0);    // The camera's current position
    199.  
    200.             // Get the ray from the camera to the vertex and its length (which is the far point of the ray passing through the atmosphere)
    201.             float3 eyeRay = normalize(mul((float3x3)unity_ObjectToWorld, v.vertex.xyz));
    202.  
    203.             float far = 0.0;
    204.             half3 cIn, cOut;
    205.  
    206.             if(eyeRay.y >= 0.0)
    207.             {
    208.                 // Sky
    209.                 // Calculate the length of the "atmosphere"
    210.                 far = sqrt(kOuterRadius2 + kInnerRadius2 * eyeRay.y * eyeRay.y - kInnerRadius2) - kInnerRadius * eyeRay.y;
    211.  
    212.                 float3 pos = cameraPos + far * eyeRay;
    213.  
    214.                 // Calculate the ray's starting position, then calculate its scattering offset
    215.                 float height = kInnerRadius + kCameraHeight;
    216.                 float depth = exp(kScaleOverScaleDepth * (-kCameraHeight));
    217.                 float startAngle = dot(eyeRay, cameraPos) / height;
    218.                 float startOffset = depth*scale(startAngle);
    219.  
    220.  
    221.                 // Initialize the scattering loop variables
    222.                 float sampleLength = far / kSamples;
    223.                 float scaledLength = sampleLength * kScale;
    224.                 float3 sampleRay = eyeRay * sampleLength;
    225.                 float3 samplePoint = cameraPos + sampleRay * 0.5;
    226.  
    227.                 // Now loop through the sample rays
    228.                 float3 frontColor = float3(0.0, 0.0, 0.0);
    229.                 // Weird workaround: WP8 and desktop FL_9_3 do not like the for loop here
    230.                 // (but an almost identical loop is perfectly fine in the ground calculations below)
    231.                 // Just unrolling this manually seems to make everything fine again.
    232. //              for(int i=0; i<int(kSamples); i++)
    233.                 {
    234.                     float height = length(samplePoint);
    235.                     float depth = exp(kScaleOverScaleDepth * (kInnerRadius - height));
    236.                     float lightAngle = dot(_WorldSpaceLightPos0.xyz, samplePoint) / height;
    237.                     float cameraAngle = dot(eyeRay, samplePoint) / height;
    238.                     float scatter = (startOffset + depth*(scale(lightAngle) - scale(cameraAngle)));
    239.                     float3 attenuate = exp(-clamp(scatter, 0.0, kMAX_SCATTER) * (kInvWavelength * kKr4PI + kKm4PI));
    240.  
    241.                     frontColor += attenuate * (depth * scaledLength);
    242.                     samplePoint += sampleRay;
    243.                 }
    244.                 {
    245.                     float height = length(samplePoint);
    246.                     float depth = exp(kScaleOverScaleDepth * (kInnerRadius - height));
    247.                     float lightAngle = dot(_WorldSpaceLightPos0.xyz, samplePoint) / height;
    248.                     float cameraAngle = dot(eyeRay, samplePoint) / height;
    249.                     float scatter = (startOffset + depth*(scale(lightAngle) - scale(cameraAngle)));
    250.                     float3 attenuate = exp(-clamp(scatter, 0.0, kMAX_SCATTER) * (kInvWavelength * kKr4PI + kKm4PI));
    251.  
    252.                     frontColor += attenuate * (depth * scaledLength);
    253.                     samplePoint += sampleRay;
    254.                 }
    255.  
    256.  
    257.  
    258.                 // Finally, scale the Mie and Rayleigh colors and set up the varying variables for the pixel shader
    259.                 cIn = frontColor * (kInvWavelength * kKrESun);
    260.                 cOut = frontColor * kKmESun;
    261.             }
    262.             else
    263.             {
    264.                 // Ground
    265.                 far = (-kCameraHeight) / (min(-0.001, eyeRay.y));
    266.  
    267.                 float3 pos = cameraPos + far * eyeRay;
    268.  
    269.                 // Calculate the ray's starting position, then calculate its scattering offset
    270.                 float depth = exp((-kCameraHeight) * (1.0/kScaleDepth));
    271.                 float cameraAngle = dot(-eyeRay, pos);
    272.                 float lightAngle = dot(_WorldSpaceLightPos0.xyz, pos);
    273.                 float cameraScale = scale(cameraAngle);
    274.                 float lightScale = scale(lightAngle);
    275.                 float cameraOffset = depth*cameraScale;
    276.                 float temp = (lightScale + cameraScale);
    277.  
    278.                 // Initialize the scattering loop variables
    279.                 float sampleLength = far / kSamples;
    280.                 float scaledLength = sampleLength * kScale;
    281.                 float3 sampleRay = eyeRay * sampleLength;
    282.                 float3 samplePoint = cameraPos + sampleRay * 0.5;
    283.  
    284.                 // Now loop through the sample rays
    285.                 float3 frontColor = float3(0.0, 0.0, 0.0);
    286.                 float3 attenuate;
    287. //              for(int i=0; i<int(kSamples); i++) // Loop removed because we kept hitting SM2.0 temp variable limits. Doesn't affect the image too much.
    288.                 {
    289.                     float height = length(samplePoint);
    290.                     float depth = exp(kScaleOverScaleDepth * (kInnerRadius - height));
    291.                     float scatter = depth*temp - cameraOffset;
    292.                     attenuate = exp(-clamp(scatter, 0.0, kMAX_SCATTER) * (kInvWavelength * kKr4PI + kKm4PI));
    293.                     frontColor += attenuate * (depth * scaledLength);
    294.                     samplePoint += sampleRay;
    295.                 }
    296.  
    297.                 cIn = frontColor * (kInvWavelength * kKrESun + kKmESun);
    298.                 cOut = clamp(attenuate, 0.0, 1.0);
    299.             }
    300.  
    301.         #if SKYBOX_SUNDISK == SKYBOX_SUNDISK_HQ
    302.             OUT.vertex          = -v.vertex;
    303.         #elif SKYBOX_SUNDISK == SKYBOX_SUNDISK_SIMPLE
    304.             OUT.rayDir          = half3(-eyeRay);
    305.         #else
    306.             OUT.skyGroundFactor = -eyeRay.y / SKY_GROUND_THRESHOLD;
    307.         #endif
    308.  
    309.             // if we want to calculate color in vprog:
    310.             // 1. in case of linear: multiply by _Exposure in here (even in case of lerp it will be common multiplier, so we can skip mul in fshader)
    311.             // 2. in case of gamma and SKYBOX_COLOR_IN_TARGET_COLOR_SPACE: do sqrt right away instead of doing that in fshader
    312.  
    313.             OUT.groundColor = _Exposure * (cIn + COLOR_2_LINEAR(_GroundColor) * cOut);
    314.             OUT.skyColor    = _Exposure * (cIn * getRayleighPhase(_WorldSpaceLightPos0.xyz, -eyeRay));
    315.  
    316.         #if SKYBOX_SUNDISK != SKYBOX_SUNDISK_NONE
    317.             // The sun should have a stable intensity in its course in the sky. Moreover it should match the highlight of a purely specular material.
    318.             // This matching was done using the standard shader BRDF1 on the 5/31/2017
    319.             // Finally we want the sun to be always bright even in LDR thus the normalization of the lightColor for low intensity.
    320.             half lightColorIntensity = clamp(length(_LightColor0.xyz), 0.25, 1);
    321.             #if SKYBOX_SUNDISK == SKYBOX_SUNDISK_SIMPLE
    322.                 OUT.sunColor    = kSimpleSundiskIntensityFactor * saturate(cOut * kSunScale) * _LightColor0.xyz / lightColorIntensity;
    323.             #else // SKYBOX_SUNDISK_HQ
    324.                 OUT.sunColor    = kHDSundiskIntensityFactor * saturate(cOut) * _LightColor0.xyz / lightColorIntensity;
    325.             #endif
    326.  
    327.         #endif
    328.  
    329.         #if defined(UNITY_COLORSPACE_GAMMA) && SKYBOX_COLOR_IN_TARGET_COLOR_SPACE
    330.             OUT.groundColor = sqrt(OUT.groundColor);
    331.             OUT.skyColor    = sqrt(OUT.skyColor);
    332.             #if SKYBOX_SUNDISK != SKYBOX_SUNDISK_NONE
    333.                 OUT.sunColor= sqrt(OUT.sunColor);
    334.             #endif
    335.         #endif
    336.  
    337.             return OUT;
    338.         }
    339.  
    340.  
    341.         // Calculates the Mie phase function
    342.         half getMiePhase(half eyeCos, half eyeCos2)
    343.         {
    344.             half temp = 1.0 + MIE_G2 - 2.0 * MIE_G * eyeCos;
    345.             temp = pow(temp, pow(_SunSize,0.65) * 10);
    346.             temp = max(temp,1.0e-4); // prevent division by zero, esp. in half precision
    347.             temp = 1.5 * ((1.0 - MIE_G2) / (2.0 + MIE_G2)) * (1.0 + eyeCos2) / temp;
    348.             #if defined(UNITY_COLORSPACE_GAMMA) && SKYBOX_COLOR_IN_TARGET_COLOR_SPACE
    349.                 temp = pow(temp, .454545);
    350.             #endif
    351.             return temp;
    352.         }
    353.  
    354.         // Calculates the sun shape
    355.         half calcSunAttenuation(half3 lightPos, half3 ray)
    356.         {
    357.         #if SKYBOX_SUNDISK == SKYBOX_SUNDISK_SIMPLE
    358.             half3 delta = lightPos - ray;
    359.             half dist = length(delta);
    360.             half spot = 1.0 - smoothstep(0.0, _SunSize, dist);
    361.             return spot * spot;
    362.         #else // SKYBOX_SUNDISK_HQ
    363.             half focusedEyeCos = pow(saturate(dot(lightPos, ray)), _SunSizeConvergence);
    364.             return getMiePhase(-focusedEyeCos, focusedEyeCos * focusedEyeCos);
    365.         #endif
    366.         }
    367.  
    368.         half4 frag (v2f IN) : SV_Target
    369.         {
    370.             half3 col = half3(0.0, 0.0, 0.0);
    371.  
    372.         // if y > 1 [eyeRay.y < -SKY_GROUND_THRESHOLD] - ground
    373.         // if y >= 0 and < 1 [eyeRay.y <= 0 and > -SKY_GROUND_THRESHOLD] - horizon
    374.         // if y < 0 [eyeRay.y > 0] - sky
    375.         #if SKYBOX_SUNDISK == SKYBOX_SUNDISK_HQ
    376.             half3 ray = normalize(mul((float3x3)unity_ObjectToWorld, IN.vertex));
    377.             half y = ray.y / SKY_GROUND_THRESHOLD;
    378.         #elif SKYBOX_SUNDISK == SKYBOX_SUNDISK_SIMPLE
    379.             half3 ray = IN.rayDir.xyz;
    380.             half y = ray.y / SKY_GROUND_THRESHOLD;
    381.         #else
    382.             half y = IN.skyGroundFactor;
    383.         #endif
    384.  
    385.             // if we did precalculate color in vprog: just do lerp between them
    386.             col = lerp(IN.skyColor, IN.groundColor, saturate(y));
    387.  
    388.         #if SKYBOX_SUNDISK != SKYBOX_SUNDISK_NONE
    389.             if(y < 0.0)
    390.             {
    391.                 col += IN.sunColor * calcSunAttenuation(_WorldSpaceLightPos0.xyz, -ray);
    392.             }
    393.         #endif
    394.  
    395.         #if defined(UNITY_COLORSPACE_GAMMA) && !SKYBOX_COLOR_IN_TARGET_COLOR_SPACE
    396.             col = LINEAR_2_OUTPUT(col);
    397.         #endif
    398.          
    399.             col = min(col,_GlowCap);
    400.  
    401.             return half4(col,1.0);
    402.  
    403.         }
    404.         ENDCG
    405.     }
    406. }
    407.  
    408.  
    409. Fallback Off
    410. CustomEditor "SkyboxProceduralShaderGUI"
    411. }
     
  3. hjohnsen

    hjohnsen

    Joined:
    Feb 4, 2018
    Posts:
    67
    Thank you a lot ifurkend, this is a good idea that I will try.

    ++
     
  4. Sneer1

    Sneer1

    Joined:
    Nov 10, 2014
    Posts:
    2
    A bit late, but in the Post-process Layer / Defered Fog is an option to exclude the skybox.