191 lines
6.3 KiB
Text
191 lines
6.3 KiB
Text
|
|
Shader "Hidden/Atmosphere"
|
||
|
|
{
|
||
|
|
Properties
|
||
|
|
{
|
||
|
|
_MainTex ("Texture", 2D) = "white" {}
|
||
|
|
}
|
||
|
|
SubShader
|
||
|
|
{
|
||
|
|
// No culling or depth
|
||
|
|
Cull Off ZWrite Off ZTest Always
|
||
|
|
|
||
|
|
Pass
|
||
|
|
{
|
||
|
|
CGPROGRAM
|
||
|
|
#pragma vertex vert
|
||
|
|
#pragma fragment frag
|
||
|
|
|
||
|
|
#include "UnityCG.cginc"
|
||
|
|
#include "../Includes/Math.cginc"
|
||
|
|
//
|
||
|
|
|
||
|
|
struct appdata {
|
||
|
|
float4 vertex : POSITION;
|
||
|
|
float4 uv : TEXCOORD0;
|
||
|
|
};
|
||
|
|
|
||
|
|
struct v2f {
|
||
|
|
float4 pos : SV_POSITION;
|
||
|
|
float2 uv : TEXCOORD0;
|
||
|
|
float3 viewVector : TEXCOORD1;
|
||
|
|
};
|
||
|
|
|
||
|
|
v2f vert (appdata v) {
|
||
|
|
v2f output;
|
||
|
|
output.pos = UnityObjectToClipPos(v.vertex);
|
||
|
|
output.uv = v.uv;
|
||
|
|
// Camera space matches OpenGL convention where cam forward is -z. In unity forward is positive z.
|
||
|
|
// (https://docs.unity3d.com/ScriptReference/Camera-cameraToWorldMatrix.html)
|
||
|
|
float3 viewVector = mul(unity_CameraInvProjection, float4(v.uv.xy * 2 - 1, 0, -1));
|
||
|
|
output.viewVector = mul(unity_CameraToWorld, float4(viewVector,0));
|
||
|
|
return output;
|
||
|
|
}
|
||
|
|
|
||
|
|
float2 squareUV(float2 uv) {
|
||
|
|
float width = _ScreenParams.x;
|
||
|
|
float height =_ScreenParams.y;
|
||
|
|
//float minDim = min(width, height);
|
||
|
|
float scale = 1000;
|
||
|
|
float x = uv.x * width;
|
||
|
|
float y = uv.y * height;
|
||
|
|
return float2 (x/scale, y/scale);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
sampler2D _BlueNoise;
|
||
|
|
sampler2D _MainTex;
|
||
|
|
sampler2D _BakedOpticalDepth;
|
||
|
|
sampler2D _CameraDepthTexture;
|
||
|
|
float4 params;
|
||
|
|
|
||
|
|
float3 dirToSun;
|
||
|
|
|
||
|
|
float3 planetCentre;
|
||
|
|
float atmosphereRadius;
|
||
|
|
float oceanRadius;
|
||
|
|
float planetRadius;
|
||
|
|
|
||
|
|
// Paramaters
|
||
|
|
int numInScatteringPoints;
|
||
|
|
int numOpticalDepthPoints;
|
||
|
|
float intensity;
|
||
|
|
float4 scatteringCoefficients;
|
||
|
|
float ditherStrength;
|
||
|
|
float ditherScale;
|
||
|
|
float densityFalloff;
|
||
|
|
|
||
|
|
|
||
|
|
float densityAtPoint(float3 densitySamplePoint) {
|
||
|
|
float heightAboveSurface = length(densitySamplePoint - planetCentre) - planetRadius;
|
||
|
|
float height01 = heightAboveSurface / (atmosphereRadius - planetRadius);
|
||
|
|
float localDensity = exp(-height01 * densityFalloff) * (1 - height01);
|
||
|
|
return localDensity;
|
||
|
|
}
|
||
|
|
|
||
|
|
float opticalDepth(float3 rayOrigin, float3 rayDir, float rayLength) {
|
||
|
|
float3 densitySamplePoint = rayOrigin;
|
||
|
|
float stepSize = rayLength / (numOpticalDepthPoints - 1);
|
||
|
|
float opticalDepth = 0;
|
||
|
|
|
||
|
|
for (int i = 0; i < numOpticalDepthPoints; i ++) {
|
||
|
|
float localDensity = densityAtPoint(densitySamplePoint);
|
||
|
|
opticalDepth += localDensity * stepSize;
|
||
|
|
densitySamplePoint += rayDir * stepSize;
|
||
|
|
}
|
||
|
|
return opticalDepth;
|
||
|
|
}
|
||
|
|
|
||
|
|
float opticalDepthBaked(float3 rayOrigin, float3 rayDir) {
|
||
|
|
float height = length(rayOrigin - planetCentre) - planetRadius;
|
||
|
|
float height01 = saturate(height / (atmosphereRadius - planetRadius));
|
||
|
|
|
||
|
|
float uvX = 1 - (dot(normalize(rayOrigin - planetCentre), rayDir) * .5 + .5);
|
||
|
|
return tex2Dlod(_BakedOpticalDepth, float4(uvX, height01,0,0));
|
||
|
|
}
|
||
|
|
|
||
|
|
float opticalDepthBaked2(float3 rayOrigin, float3 rayDir, float rayLength) {
|
||
|
|
float3 endPoint = rayOrigin + rayDir * rayLength;
|
||
|
|
float d = dot(rayDir, normalize(rayOrigin-planetCentre));
|
||
|
|
float opticalDepth = 0;
|
||
|
|
|
||
|
|
const float blendStrength = 1.5;
|
||
|
|
float w = saturate(d * blendStrength + .5);
|
||
|
|
|
||
|
|
float d1 = opticalDepthBaked(rayOrigin, rayDir) - opticalDepthBaked(endPoint, rayDir);
|
||
|
|
float d2 = opticalDepthBaked(endPoint, -rayDir) - opticalDepthBaked(rayOrigin, -rayDir);
|
||
|
|
|
||
|
|
opticalDepth = lerp(d2, d1, w);
|
||
|
|
return opticalDepth;
|
||
|
|
}
|
||
|
|
|
||
|
|
float3 calculateLight(float3 rayOrigin, float3 rayDir, float rayLength, float3 originalCol, float2 uv) {
|
||
|
|
float blueNoise = tex2Dlod(_BlueNoise, float4(squareUV(uv) * ditherScale,0,0));
|
||
|
|
blueNoise = (blueNoise - 0.5) * ditherStrength;
|
||
|
|
|
||
|
|
float3 inScatterPoint = rayOrigin;
|
||
|
|
float stepSize = rayLength / (numInScatteringPoints - 1);
|
||
|
|
float3 inScatteredLight = 0;
|
||
|
|
float viewRayOpticalDepth = 0;
|
||
|
|
|
||
|
|
for (int i = 0; i < numInScatteringPoints; i ++) {
|
||
|
|
float sunRayLength = raySphere(planetCentre, atmosphereRadius, inScatterPoint, dirToSun).y;
|
||
|
|
float sunRayOpticalDepth = opticalDepthBaked(inScatterPoint + dirToSun * ditherStrength, dirToSun);
|
||
|
|
float localDensity = densityAtPoint(inScatterPoint);
|
||
|
|
viewRayOpticalDepth = opticalDepthBaked2(rayOrigin, rayDir, stepSize * i);
|
||
|
|
float3 transmittance = exp(-(sunRayOpticalDepth + viewRayOpticalDepth) * scatteringCoefficients);
|
||
|
|
|
||
|
|
inScatteredLight += localDensity * transmittance;
|
||
|
|
inScatterPoint += rayDir * stepSize;
|
||
|
|
}
|
||
|
|
inScatteredLight *= scatteringCoefficients * intensity * stepSize / planetRadius;
|
||
|
|
inScatteredLight += blueNoise * 0.01;
|
||
|
|
|
||
|
|
// Attenuate brightness of original col (i.e light reflected from planet surfaces)
|
||
|
|
// This is a hacky mess, TODO: figure out a proper way to do this
|
||
|
|
const float brightnessAdaptionStrength = 0.15;
|
||
|
|
const float reflectedLightOutScatterStrength = 3;
|
||
|
|
float brightnessAdaption = dot (inScatteredLight,1) * brightnessAdaptionStrength;
|
||
|
|
float brightnessSum = viewRayOpticalDepth * intensity * reflectedLightOutScatterStrength + brightnessAdaption;
|
||
|
|
float reflectedLightStrength = exp(-brightnessSum);
|
||
|
|
float hdrStrength = saturate(dot(originalCol,1)/3-1);
|
||
|
|
reflectedLightStrength = lerp(reflectedLightStrength, 1, hdrStrength);
|
||
|
|
float3 reflectedLight = originalCol * reflectedLightStrength;
|
||
|
|
|
||
|
|
float3 finalCol = reflectedLight + inScatteredLight;
|
||
|
|
|
||
|
|
|
||
|
|
return finalCol;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
float4 frag (v2f i) : SV_Target
|
||
|
|
{
|
||
|
|
float4 originalCol = tex2D(_MainTex, i.uv);
|
||
|
|
float sceneDepthNonLinear = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv);
|
||
|
|
float sceneDepth = LinearEyeDepth(sceneDepthNonLinear) * length(i.viewVector);
|
||
|
|
|
||
|
|
float3 rayOrigin = _WorldSpaceCameraPos;
|
||
|
|
float3 rayDir = normalize(i.viewVector);
|
||
|
|
|
||
|
|
float dstToOcean = raySphere(planetCentre, oceanRadius, rayOrigin, rayDir);
|
||
|
|
float dstToSurface = min(sceneDepth, dstToOcean);
|
||
|
|
|
||
|
|
float2 hitInfo = raySphere(planetCentre, atmosphereRadius, rayOrigin, rayDir);
|
||
|
|
float dstToAtmosphere = hitInfo.x;
|
||
|
|
float dstThroughAtmosphere = min(hitInfo.y, dstToSurface - dstToAtmosphere);
|
||
|
|
|
||
|
|
if (dstThroughAtmosphere > 0) {
|
||
|
|
const float epsilon = 0.0001;
|
||
|
|
float3 pointInAtmosphere = rayOrigin + rayDir * (dstToAtmosphere + epsilon);
|
||
|
|
float3 light = calculateLight(pointInAtmosphere, rayDir, dstThroughAtmosphere - epsilon * 2, originalCol, i.uv);
|
||
|
|
return float4(light, 1);
|
||
|
|
}
|
||
|
|
return originalCol;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
ENDCG
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|