Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- Shader "Custom/SkyboxProceduralStars"
- {
- Properties {
- [KeywordEnum(None, Simple, High Quality)] _SunDisk ("Sun", Int) = 2
- _SunSize ("Sun Size", Range(0,1)) = 0.04
- _SunSizeConvergence("Sun Size Convergence", Range(1,10)) = 5
- _AtmosphereThickness ("Atmosphere Thickness", Range(0,5)) = 1.0
- _SkyTint ("Sky Tint", Color) = (.5, .5, .5, 1)
- _GroundColor ("Ground", Color) = (.369, .349, .341, 1)
- _Exposure("Exposure", Range(0, 8)) = 1.3
- _StarDensity("Star Density", Range(0, 100)) = 5.0
- _StarBrightness("Star Brightness", Range(0,100)) = 2.0
- _TwinkleSpeed("Twinkle Speed", Range(0, 1.0)) = 0.5
- }
- SubShader {
- Tags { "Queue"="Background" "RenderType"="Background" "PreviewType"="Skybox" }
- Cull Off ZWrite Off
- Pass {
- CGPROGRAM
- #pragma vertex vert
- #pragma fragment frag
- #include "UnityCG.cginc"
- #include "Lighting.cginc"
- #pragma multi_compile _SUNDISK_NONE _SUNDISK_SIMPLE _SUNDISK_HIGH_QUALITY
- uniform half _Exposure; // HDR exposure
- uniform half3 _GroundColor;
- uniform half _SunSize;
- uniform half _SunSizeConvergence;
- uniform half3 _SkyTint;
- uniform half _AtmosphereThickness;
- uniform float _StarDensity;
- uniform float _StarBrightness;
- uniform float _TwinkleSpeed;
- #if defined(UNITY_COLORSPACE_GAMMA)
- #define GAMMA 2
- #define COLOR_2_GAMMA(color) color
- #define COLOR_2_LINEAR(color) color*color
- #define LINEAR_2_OUTPUT(color) sqrt(color)
- #else
- #define GAMMA 2.2
- // HACK: to get gfx-tests in Gamma mode to agree until UNITY_ACTIVE_COLORSPACE_IS_GAMMA is working properly
- #define COLOR_2_GAMMA(color) ((unity_ColorSpaceDouble.r>2.0) ? pow(color,1.0/GAMMA) : color)
- #define COLOR_2_LINEAR(color) color
- #define LINEAR_2_LINEAR(color) color
- #endif
- // RGB wavelengths
- // .35 (.62=158), .43 (.68=174), .525 (.75=190)
- static const float3 kDefaultScatteringWavelength = float3(.65, .57, .475);
- static const float3 kVariableRangeForScatteringWavelength = float3(.15, .15, .15);
- #define OUTER_RADIUS 1.025
- static const float kOuterRadius = OUTER_RADIUS;
- static const float kOuterRadius2 = OUTER_RADIUS*OUTER_RADIUS;
- static const float kInnerRadius = 1.0;
- static const float kInnerRadius2 = 1.0;
- static const float kCameraHeight = 0.0001;
- #define kRAYLEIGH (lerp(0.0, 0.0025, pow(_AtmosphereThickness,2.5))) // Rayleigh constant
- #define kMIE 0.0010 // Mie constant
- #define kSUN_BRIGHTNESS 20.0 // Sun brightness
- #define kMAX_SCATTER 50.0 // Maximum scattering value, to prevent math overflows on Adrenos
- static const half kHDSundiskIntensityFactor = 15.0;
- static const half kSimpleSundiskIntensityFactor = 27.0;
- static const half kSunScale = 400.0 * kSUN_BRIGHTNESS;
- static const float kKmESun = kMIE * kSUN_BRIGHTNESS;
- static const float kKm4PI = kMIE * 4.0 * 3.14159265;
- static const float kScale = 1.0 / (OUTER_RADIUS - 1.0);
- static const float kScaleDepth = 0.25;
- static const float kScaleOverScaleDepth = (1.0 / (OUTER_RADIUS - 1.0)) / 0.25;
- static const float kSamples = 2.0; // THIS IS UNROLLED MANUALLY, DON'T TOUCH
- #define MIE_G (-0.990)
- #define MIE_G2 0.9801
- #define SKY_GROUND_THRESHOLD 0.02
- // fine tuning of performance. You can override defines here if you want some specific setup
- // or keep as is and allow later code to set it according to target api
- // if set vprog will output color in final color space (instead of linear always)
- // 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
- // #define SKYBOX_COLOR_IN_TARGET_COLOR_SPACE 0
- // sun disk rendering:
- // no sun disk - the fastest option
- #define SKYBOX_SUNDISK_NONE 0
- // simplistic sun disk - without mie phase function
- #define SKYBOX_SUNDISK_SIMPLE 1
- // full calculation - uses mie phase function
- #define SKYBOX_SUNDISK_HQ 2
- // uncomment this line and change SKYBOX_SUNDISK_SIMPLE to override material settings
- // #define SKYBOX_SUNDISK SKYBOX_SUNDISK_SIMPLE
- #ifndef SKYBOX_SUNDISK
- #if defined(_SUNDISK_NONE)
- #define SKYBOX_SUNDISK SKYBOX_SUNDISK_NONE
- #elif defined(_SUNDISK_SIMPLE)
- #define SKYBOX_SUNDISK SKYBOX_SUNDISK_SIMPLE
- #else
- #define SKYBOX_SUNDISK SKYBOX_SUNDISK_HQ
- #endif
- #endif
- #ifndef SKYBOX_COLOR_IN_TARGET_COLOR_SPACE
- #if defined(SHADER_API_MOBILE)
- #define SKYBOX_COLOR_IN_TARGET_COLOR_SPACE 1
- #else
- #define SKYBOX_COLOR_IN_TARGET_COLOR_SPACE 0
- #endif
- #endif
- // Calculates the Rayleigh phase function
- half getRayleighPhase(half eyeCos2)
- {
- return 0.75 + 0.75*eyeCos2;
- }
- half getRayleighPhase(half3 light, half3 ray)
- {
- half eyeCos = dot(light, ray);
- return getRayleighPhase(eyeCos * eyeCos);
- }
- struct appdata_t
- {
- float4 vertex : POSITION;
- float2 uv : TEXCOORD0;
- UNITY_VERTEX_INPUT_INSTANCE_ID
- };
- struct v2f
- {
- float4 pos : SV_POSITION;
- float2 uv : TEXCOORD0;
- #if SKYBOX_SUNDISK == SKYBOX_SUNDISK_HQ
- // for HQ sun disk, we need vertex itself to calculate ray-dir per-pixel
- float3 vertex : TEXCOORD4;
- #elif SKYBOX_SUNDISK == SKYBOX_SUNDISK_SIMPLE
- half3 rayDir : TEXCOORD0;
- #else
- // as we dont need sun disk we need just rayDir.y (sky/ground threshold)
- half skyGroundFactor : TEXCOORD0;
- #endif
- // calculate sky colors in vprog
- half3 groundColor : TEXCOORD1;
- half3 skyColor : TEXCOORD2;
- #if SKYBOX_SUNDISK != SKYBOX_SUNDISK_NONE
- half3 sunColor : TEXCOORD3;
- #endif
- UNITY_VERTEX_OUTPUT_STEREO
- };
- float scale(float inCos)
- {
- float x = 1.0 - inCos;
- #if defined(SHADER_API_N3DS)
- // The polynomial expansion here generates too many swizzle instructions for the 3DS vertex assembler
- // Approximate by removing x^1 and x^2
- return 0.25 * exp(-0.00287 + x*x*x*(-6.80 + x*5.25));
- #else
- return 0.25 * exp(-0.00287 + x*(0.459 + x*(3.83 + x*(-6.80 + x*5.25))));
- #endif
- }
- v2f vert (appdata_t v)
- {
- v2f OUT;
- UNITY_SETUP_INSTANCE_ID(v);
- UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
- OUT.pos = UnityObjectToClipPos(v.vertex);
- OUT.uv = v.uv;
- float3 kSkyTintInGammaSpace = COLOR_2_GAMMA(_SkyTint); // convert tint from Linear back to Gamma
- float3 kScatteringWavelength = lerp (
- kDefaultScatteringWavelength-kVariableRangeForScatteringWavelength,
- kDefaultScatteringWavelength+kVariableRangeForScatteringWavelength,
- 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
- float3 kInvWavelength = 1.0 / pow(kScatteringWavelength, 4);
- float kKrESun = kRAYLEIGH * kSUN_BRIGHTNESS;
- float kKr4PI = kRAYLEIGH * 4.0 * 3.14159265;
- float3 cameraPos = float3(0,kInnerRadius + kCameraHeight,0); // The camera's current position
- // Get the ray from the camera to the vertex and its length (which is the far point of the ray passing through the atmosphere)
- float3 eyeRay = normalize(mul((float3x3)unity_ObjectToWorld, v.vertex.xyz));
- float far = 0.0;
- half3 cIn, cOut;
- if(eyeRay.y >= 0.0)
- {
- // Sky
- // Calculate the length of the "atmosphere"
- far = sqrt(kOuterRadius2 + kInnerRadius2 * eyeRay.y * eyeRay.y - kInnerRadius2) - kInnerRadius * eyeRay.y;
- float3 pos = cameraPos + far * eyeRay;
- // Calculate the ray's starting position, then calculate its scattering offset
- float height = kInnerRadius + kCameraHeight;
- float depth = exp(kScaleOverScaleDepth * (-kCameraHeight));
- float startAngle = dot(eyeRay, cameraPos) / height;
- float startOffset = depth*scale(startAngle);
- // Initialize the scattering loop variables
- float sampleLength = far / kSamples;
- float scaledLength = sampleLength * kScale;
- float3 sampleRay = eyeRay * sampleLength;
- float3 samplePoint = cameraPos + sampleRay * 0.5;
- // Now loop through the sample rays
- float3 frontColor = float3(0.0, 0.0, 0.0);
- // Weird workaround: WP8 and desktop FL_9_3 do not like the for loop here
- // (but an almost identical loop is perfectly fine in the ground calculations below)
- // Just unrolling this manually seems to make everything fine again.
- // for(int i=0; i<int(kSamples); i++)
- {
- float height = length(samplePoint);
- float depth = exp(kScaleOverScaleDepth * (kInnerRadius - height));
- float lightAngle = dot(_WorldSpaceLightPos0.xyz, samplePoint) / height;
- float cameraAngle = dot(eyeRay, samplePoint) / height;
- float scatter = (startOffset + depth*(scale(lightAngle) - scale(cameraAngle)));
- float3 attenuate = exp(-clamp(scatter, 0.0, kMAX_SCATTER) * (kInvWavelength * kKr4PI + kKm4PI));
- frontColor += attenuate * (depth * scaledLength);
- samplePoint += sampleRay;
- }
- {
- float height = length(samplePoint);
- float depth = exp(kScaleOverScaleDepth * (kInnerRadius - height));
- float lightAngle = dot(_WorldSpaceLightPos0.xyz, samplePoint) / height;
- float cameraAngle = dot(eyeRay, samplePoint) / height;
- float scatter = (startOffset + depth*(scale(lightAngle) - scale(cameraAngle)));
- float3 attenuate = exp(-clamp(scatter, 0.0, kMAX_SCATTER) * (kInvWavelength * kKr4PI + kKm4PI));
- frontColor += attenuate * (depth * scaledLength);
- samplePoint += sampleRay;
- }
- // Finally, scale the Mie and Rayleigh colors and set up the varying variables for the pixel shader
- cIn = frontColor * (kInvWavelength * kKrESun);
- cOut = frontColor * kKmESun;
- }
- else
- {
- // Ground
- far = (-kCameraHeight) / (min(-0.001, eyeRay.y));
- float3 pos = cameraPos + far * eyeRay;
- // Calculate the ray's starting position, then calculate its scattering offset
- float depth = exp((-kCameraHeight) * (1.0/kScaleDepth));
- float cameraAngle = dot(-eyeRay, pos);
- float lightAngle = dot(_WorldSpaceLightPos0.xyz, pos);
- float cameraScale = scale(cameraAngle);
- float lightScale = scale(lightAngle);
- float cameraOffset = depth*cameraScale;
- float temp = (lightScale + cameraScale);
- // Initialize the scattering loop variables
- float sampleLength = far / kSamples;
- float scaledLength = sampleLength * kScale;
- float3 sampleRay = eyeRay * sampleLength;
- float3 samplePoint = cameraPos + sampleRay * 0.5;
- // Now loop through the sample rays
- float3 frontColor = float3(0.0, 0.0, 0.0);
- float3 attenuate;
- // 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.
- {
- float height = length(samplePoint);
- float depth = exp(kScaleOverScaleDepth * (kInnerRadius - height));
- float scatter = depth*temp - cameraOffset;
- attenuate = exp(-clamp(scatter, 0.0, kMAX_SCATTER) * (kInvWavelength * kKr4PI + kKm4PI));
- frontColor += attenuate * (depth * scaledLength);
- samplePoint += sampleRay;
- }
- cIn = frontColor * (kInvWavelength * kKrESun + kKmESun);
- cOut = clamp(attenuate, 0.0, 1.0);
- }
- #if SKYBOX_SUNDISK == SKYBOX_SUNDISK_HQ
- OUT.vertex = -v.vertex;
- #elif SKYBOX_SUNDISK == SKYBOX_SUNDISK_SIMPLE
- OUT.rayDir = half3(-eyeRay);
- #else
- OUT.skyGroundFactor = -eyeRay.y / SKY_GROUND_THRESHOLD;
- #endif
- // if we want to calculate color in vprog:
- // 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)
- // 2. in case of gamma and SKYBOX_COLOR_IN_TARGET_COLOR_SPACE: do sqrt right away instead of doing that in fshader
- OUT.groundColor = _Exposure * (cIn + COLOR_2_LINEAR(_GroundColor) * cOut);
- OUT.skyColor = _Exposure * (cIn * getRayleighPhase(_WorldSpaceLightPos0.xyz, -eyeRay));
- #if SKYBOX_SUNDISK != SKYBOX_SUNDISK_NONE
- // The sun should have a stable intensity in its course in the sky. Moreover it should match the highlight of a purely specular material.
- // This matching was done using the standard shader BRDF1 on the 5/31/2017
- // Finally we want the sun to be always bright even in LDR thus the normalization of the lightColor for low intensity.
- half lightColorIntensity = clamp(length(_LightColor0.xyz), 0.25, 1);
- #if SKYBOX_SUNDISK == SKYBOX_SUNDISK_SIMPLE
- OUT.sunColor = kSimpleSundiskIntensityFactor * saturate(cOut * kSunScale) * _LightColor0.xyz / lightColorIntensity;
- #else // SKYBOX_SUNDISK_HQ
- OUT.sunColor = kHDSundiskIntensityFactor * saturate(cOut) * _LightColor0.xyz / lightColorIntensity;
- #endif
- #endif
- #if defined(UNITY_COLORSPACE_GAMMA) && SKYBOX_COLOR_IN_TARGET_COLOR_SPACE
- OUT.groundColor = sqrt(OUT.groundColor);
- OUT.skyColor = sqrt(OUT.skyColor);
- #if SKYBOX_SUNDISK != SKYBOX_SUNDISK_NONE
- OUT.sunColor= sqrt(OUT.sunColor);
- #endif
- #endif
- return OUT;
- }
- // Calculates the Mie phase function
- half getMiePhase(half eyeCos, half eyeCos2)
- {
- half temp = 1.0 + MIE_G2 - 2.0 * MIE_G * eyeCos;
- temp = pow(temp, pow(_SunSize,0.65) * 10);
- temp = max(temp,1.0e-4); // prevent division by zero, esp. in half precision
- temp = 1.5 * ((1.0 - MIE_G2) / (2.0 + MIE_G2)) * (1.0 + eyeCos2) / temp;
- #if defined(UNITY_COLORSPACE_GAMMA) && SKYBOX_COLOR_IN_TARGET_COLOR_SPACE
- temp = pow(temp, .454545);
- #endif
- return temp;
- }
- // Calculates the sun shape
- half calcSunAttenuation(half3 lightPos, half3 ray)
- {
- #if SKYBOX_SUNDISK == SKYBOX_SUNDISK_SIMPLE
- half3 delta = lightPos - ray;
- half dist = length(delta);
- half spot = 1.0 - smoothstep(0.0, _SunSize, dist);
- return spot * spot;
- #else // SKYBOX_SUNDISK_HQ
- half focusedEyeCos = pow(saturate(dot(lightPos, ray)), _SunSizeConvergence);
- return getMiePhase(-focusedEyeCos, focusedEyeCos * focusedEyeCos);
- #endif
- }
- float nrand(float2 uv)
- {
- return frac(sin(dot(uv, float2(12.9898, 78.233))) * 43758.5453);
- }
- //
- // Noise Shader Library for Unity - https://github.com/keijiro/NoiseShader
- //
- // Original work (webgl-noise) Copyright (C) 2011 Ashima Arts.
- // Translation and modification was made by Keijiro Takahashi.
- //
- // This shader is based on the webgl-noise GLSL shader. For further details
- // of the original shader, please see the following description from the
- // original source code.
- //
- //
- // Description : Array and textureless GLSL 2D simplex noise function.
- // Author : Ian McEwan, Ashima Arts.
- // Maintainer : ijm
- // Lastmod : 20110822 (ijm)
- // License : Copyright (C) 2011 Ashima Arts. All rights reserved.
- // Distributed under the MIT License. See LICENSE file.
- // https://github.com/ashima/webgl-noise
- //
- float3 mod289(float3 x)
- {
- return x - floor(x / 289.0) * 289.0;
- }
- float2 mod289(float2 x)
- {
- return x - floor(x / 289.0) * 289.0;
- }
- float3 permute(float3 x)
- {
- return mod289((x * 34.0 + 1.0) * x);
- }
- float3 taylorInvSqrt(float3 r)
- {
- return 1.79284291400159 - 0.85373472095314 * r;
- }
- float snoise(float2 v)
- {
- const float4 C = float4( 0.211324865405187, // (3.0-sqrt(3.0))/6.0
- 0.366025403784439, // 0.5*(sqrt(3.0)-1.0)
- -0.577350269189626, // -1.0 + 2.0 * C.x
- 0.024390243902439); // 1.0 / 41.0
- // First corner
- float2 i = floor(v + dot(v, C.yy));
- float2 x0 = v - i + dot(i, C.xx);
- // Other corners
- float2 i1;
- i1.x = step(x0.y, x0.x);
- i1.y = 1.0 - i1.x;
- // x1 = x0 - i1 + 1.0 * C.xx;
- // x2 = x0 - 1.0 + 2.0 * C.xx;
- float2 x1 = x0 + C.xx - i1;
- float2 x2 = x0 + C.zz;
- // Permutations
- i = mod289(i); // Avoid truncation effects in permutation
- float3 p =
- permute(permute(i.y + float3(0.0, i1.y, 1.0))
- + i.x + float3(0.0, i1.x, 1.0));
- float3 m = max(0.5 - float3(dot(x0, x0), dot(x1, x1), dot(x2, x2)), 0.0);
- m = m * m;
- m = m * m;
- // Gradients: 41 points uniformly over a line, mapped onto a diamond.
- // The ring size 17*17 = 289 is close to a multiple of 41 (41*7 = 287)
- float3 x = 2.0 * frac(p * C.www) - 1.0;
- float3 h = abs(x) - 0.5;
- float3 ox = floor(x + 0.5);
- float3 a0 = x - ox;
- // Normalise gradients implicitly by scaling m
- m *= taylorInvSqrt(a0 * a0 + h * h);
- // Compute final noise value at P
- float3 g;
- g.x = a0.x * x0.x + h.x * x0.y;
- g.y = a0.y * x1.x + h.y * x1.y;
- g.z = a0.z * x2.x + h.z * x2.y;
- return 130.0 * dot(m, g);
- }
- half4 frag (v2f IN) : SV_Target
- {
- half3 col = half3(0.0, 0.0, 0.0);
- // if y > 1 [eyeRay.y < -SKY_GROUND_THRESHOLD] - ground
- // if y >= 0 and < 1 [eyeRay.y <= 0 and > -SKY_GROUND_THRESHOLD] - horizon
- // if y < 0 [eyeRay.y > 0] - sky
- #if SKYBOX_SUNDISK == SKYBOX_SUNDISK_HQ
- half3 ray = normalize(mul((float3x3)unity_ObjectToWorld, IN.vertex));
- half y = ray.y / SKY_GROUND_THRESHOLD;
- #elif SKYBOX_SUNDISK == SKYBOX_SUNDISK_SIMPLE
- half3 ray = IN.rayDir.xyz;
- half y = ray.y / SKY_GROUND_THRESHOLD;
- #else
- half y = IN.skyGroundFactor;
- #endif
- // if we did precalculate color in vprog: just do lerp between them
- col = lerp(IN.skyColor, IN.groundColor, saturate(y));
- #if SKYBOX_SUNDISK != SKYBOX_SUNDISK_NONE
- if(y < 0.0)
- {
- col += IN.sunColor * calcSunAttenuation(_WorldSpaceLightPos0.xyz, -ray);
- }
- #endif
- if (y < 0.0) {
- //sky
- float isStar = max(0.0, _StarBrightness * (snoise(IN.uv * 60) * snoise(IN.uv * 100) * snoise(IN.uv * 50) ));
- isStar = pow(isStar, _StarDensity);
- // dunno how to get "time of day" so to speak so just reduce magnitude based
- // on how light the col already is.
- col += max(0.0, 0.05 - col) * isStar * max(0.1, snoise(_Time * _TwinkleSpeed + IN.uv * 60));
- }
- #if defined(UNITY_COLORSPACE_GAMMA) && !SKYBOX_COLOR_IN_TARGET_COLOR_SPACE
- col = LINEAR_2_OUTPUT(col);
- #endif
- return half4(col,1.0);
- }
- ENDCG
- }
- }
- Fallback Off
- //CustomEditor "SkyboxProceduralShaderGUI"
- }
Advertisement
Add Comment
Please, Sign In to add comment