Firstborn/Assets/RPG Creation Kit/Scripts/Shaders/Custom-ProceduralCubed.shader

439 lines
21 KiB
Plaintext
Raw Normal View History

Shader "Skybox/Custom-ProceduralCubed" {
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
_CTint("Cubemap Tint Color", Color) = (.5, .5, .5, .5)
[Gamma] _CExposure("Cubemap Exposure", Range(0, 8)) = 1.0
_CRotation("Cubemap Rotation", Range(0, 360)) = 0
[NoScaleOffset] _CTex("Cubemap (HDR)", Cube) = "grey" {}
}
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"
samplerCUBE _CTex;
half4 _CTex_HDR;
half4 _CTint;
half _CExposure;
float _CRotation;
#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;
#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;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct v2f
{
float4 pos : SV_POSITION;
#if SKYBOX_SUNDISK == SKYBOX_SUNDISK_HQ
// for HQ sun disk, we need vertex itself to calculate ray-dir per-pixel
float3 vertex : TEXCOORD0;
#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
float3 texcoord : TEXCOORD4;
UNITY_VERTEX_OUTPUT_STEREO
};
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;
}
float scale(float inCos)
{
float x = 1.0 - inCos;
return 0.25 * exp(-0.00287 + x * (0.459 + x * (3.83 + x * (-6.80 + x * 5.25))));
}
v2f vert(appdata_t v)
{
v2f OUT;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
OUT.pos = UnityObjectToClipPos(v.vertex);
float3 rotated = RotateAroundYInDegrees(v.vertex, _CRotation);
//OUT.pos = UnityObjectToClipPos(rotated);
OUT.texcoord = rotated.xyz;
//float3 rotated = RotateAroundYInDegrees(v.vertex, _CRotation);
//float4 vertexrotated = UnityObjectToClipPos(rotated);
//OUT.texcoord = vertexrotated.xyz;
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
}
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));
half4 tex = texCUBE(_CTex, IN.texcoord);
half3 c = DecodeHDR(tex, _CTex_HDR);
col += (c * _CTint.rgb * unity_ColorSpaceDouble.rgb) * _CExposure;
#if SKYBOX_SUNDISK != SKYBOX_SUNDISK_NONE
if (y < 0.0)
{
col += IN.sunColor * calcSunAttenuation(_WorldSpaceLightPos0.xyz, -ray);
}
#endif
#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
}