Files
Eclipse-Shader/shaders/lib/volumetricClouds.glsl
T

1321 lines
48 KiB
GLSL

#define CIRRUS_LAYER 4
#define ALTOSTRATUS_LAYER 3
#define CUMULONIMBUS_LAYER 2
#define LARGECUMULUS_LAYER 1
#define SMALLCUMULUS_LAYER 0
uniform float thunderStrength;
uniform int entityId;
uniform int worldDay;
uniform int worldTime;
uniform float moonElevation;
uniform float worldTimeSmooth;
#if CLOUD_MOVEMENT_TYPE == 0
float cloud_movement = (worldTimeSmooth + mod(worldDay,100)*24000.0) / 24.0 * Cloud_Speed;
#else
float cloud_movement = frameTimeCounter * Cloud_Speed;
#endif
float lightningFlashTimer = floor(frameTimeCounter * 11.0);
float randomSeed = fract(sin(dot(vec2(lightningFlashTimer), vec2(12.9898,78.233))) * 43758.5453);
float lightningFlash = mix(0.1, 2.5, randomSeed);
#if CUMULONIMBUS > 0
float lightningDuration = 0.75 + CUMULONIMBUS_LIGHTNING_DELAY;
float lightningTimer = floor(frameTimeCounter / lightningDuration);
float timeInLightning = (frameTimeCounter / (lightningDuration) - lightningTimer) * lightningDuration;
float lightningFade = smoothstep(0.6, 0.22, timeInLightning);
#endif
#if CUMULONIMBUS == 1
uniform float cumulonimbusStrength;
#endif
#if defined CUMULONIMBUS_LIGHTNING && CUMULONIMBUS > 0
#ifndef COLORWHEEL
#extension GL_NV_gpu_shader5 : enable
#extension GL_ARB_shader_image_load_store : enable
#extension GL_EXT_shader_image_load_store : enable
#endif
layout (rgba16f) uniform image2D cloudDepthTex;
float lightningStart = mix(20.0, 1.0, smoothstep(0.0, 0.085, timeInLightning));
float lightningMid = smoothstep(0.0, 0.05, timeInLightning) * smoothstep(0.15, 0.066, timeInLightning);
#endif
float rand(float co){
vec2 co2 = vec2(co, co*2.0);
return fract(sin(dot(co2 ,vec2(12.9898,78.233))) * 43758.5453);
}
float getRainDensity(float currentDensity) {
float extraDensity = min(currentDensity + rainStrength * 0.1 + thunderStrength * 0.1, 1.0);
return extraDensity;
}
float densityAtPos(in vec3 pos){
pos /= 18.;
pos.xz *= 0.5;
vec3 p = floor(pos);
vec3 f = fract(pos);
vec2 uv = p.xz + f.xz + p.y * vec2(0.0,193.0);
vec2 coord = uv / 512.0;
//The y channel has an offset to avoid using two textures fetches
vec2 xy = texture2D(noisetex, coord).yx;
return mix(xy.r,xy.g, f.y);
}
// Cirrus code shamelessly "borrowed" from photon shader and edited
vec2 hash2(vec2 p) {
vec3 p3 = fract(vec3(p.xyx) * vec3(.1031, .1030, .0973));
p3 += dot(p3, p3.yzx+33.33);
return fract((p3.xx+p3.yz)*p3.zy);
}
vec2 normalize_hash(vec2 p) {
return normalize(hash2(p) - 0.5);
}
vec2 perlin_gradient(vec2 coord) {
vec2 i = floor(coord);
vec2 f = fract(coord);
vec2 u = f * f * (3.0 - 2.0 * f); // Photon uses quintic interpolation
vec2 du = 30.0 * f * f * (f * (f - 2.0) + 1.0); // This ain't mathematically correct but it looks better ¯\_(ツ)_/¯
vec2 g0 = normalize_hash(i);
vec2 g1 = normalize_hash(i + vec2(1.0, 0.0));
vec2 g2 = normalize_hash(i + vec2(0.0, 1.0));
vec2 g3 = normalize_hash(i + vec2(1.0, 1.0));
float v0 = dot(g0, f);
float v1 = dot(g1, f - vec2(1.0, 0.0));
float v2 = dot(g2, f - vec2(0.0, 1.0));
float v3 = dot(g3, f - vec2(1.0, 1.0));
vec2 omu = 1.0 - u;
return vec2(
((v1 - v0) * omu.y + (v3 - v2) * u.y) * du.x,
((v2 - v0) * omu.x + (v3 - v1) * u.x) * du.y
);
}
vec2 curl2D(vec2 coord) {
vec2 gradient = perlin_gradient(coord);
return vec2(gradient.y, -gradient.x);
}
float getCloudShape(int LayerIndex, int LOD, in vec3 position, float minHeight, float maxHeight){
float coverage = 0.0;
float shape = 0.0;
float largeCloud = 0.0;
float smallCloud = 0.0;
vec3 samplePos = position*vec3(0.25, 0.005, 0.25);
float tallness = maxHeight - minHeight;
float posToMax = maxHeight - position.y;
switch (LayerIndex){
default : { break; }
case CIRRUS_LAYER: {
coverage = SC_parameters.cirrus.x;
vec2 coord = position.zx + 6.0*cloud_movement;
vec2 curl = curl2D(0.00002 * coord) * 0.5
+ curl2D(0.00005 * coord) * 0.25
+ curl2D(0.00018 * coord) * 0.125;
largeCloud = texture2D(noisetex, (position.xz + cloud_movement*2.0)/80000. * CloudLayer3_scale).b;
smallCloud = texture2D(noisetex, (0.000005 / CloudLayer3_scale) * coord).r;
float detail_amplitude = 0.3;
float detail_frequency = 0.00002;
float curl_strength = 1.3;
for (int i = 0; i < 3; ++i) {
float detail = texture2D(noisetex, coord * detail_frequency + curl * curl_strength).r;
smallCloud -= detail * detail_amplitude;
detail_amplitude *= 0.5;
detail_frequency *= 4.0;
curl_strength *= 2.7;
}
smallCloud = abs(largeCloud* -0.4) + smallCloud;
float val = coverage;
shape = min(max(val - smallCloud,0.0)/sqrt(val),1.0);
if (position.y < (-0.0001 * minHeight + 0.8)*minHeight) shape = 0.0;
return shape;
break; }
case ALTOSTRATUS_LAYER: {
coverage = SC_parameters.altostratus.x;
coverage += Rain_coverage * rainStrength;
coverage += Thunder_coverage * thunderStrength;
largeCloud = texture2D(noisetex, (position.xz + cloud_movement)/100000. * CloudLayer2_scale).b;
smallCloud = 1.0 - texture2D(noisetex, ((position.xz - cloud_movement)/7500. - vec2(1.0-largeCloud, -largeCloud)/7.0) * CloudLayer2_scale).b;
smallCloud = largeCloud + smallCloud * 0.4 * clamp(0.9-largeCloud,0.0,1.0);
float val = coverage;
shape = min(max(val - smallCloud,0.0)/sqrt(val),1.0);
shape *= shape;
if (position.y < (-0.0001 * minHeight + 0.8)*minHeight) shape = 0.0;
return shape;
break; }
case LARGECUMULUS_LAYER: {
coverage = SC_parameters.largeCumulus.x;
coverage += Rain_coverage * rainStrength;
coverage += Thunder_coverage * thunderStrength;
largeCloud = texture2D(noisetex, (samplePos.zx + cloud_movement*2.0)/10000.0 * CloudLayer1_scale).b;
smallCloud = texture2D(noisetex, (samplePos.zx - cloud_movement*2.0)/2500.0 * CloudLayer1_scale).b;
smallCloud = abs(largeCloud* -0.7) + smallCloud;
float val = coverage;
shape = min(max(val - smallCloud,0.0)/sqrt(val),1.0);
break; }
case SMALLCUMULUS_LAYER: {
coverage = SC_parameters.smallCumulus.x;
coverage += Rain_coverage * rainStrength;
coverage += Thunder_coverage * thunderStrength;
largeCloud = texture2D(noisetex, (samplePos.xz + cloud_movement)/5000.0 * CloudLayer0_scale).b;
smallCloud = 1.0-texture2D(noisetex, (samplePos.xz - cloud_movement)/500.0 * CloudLayer0_scale).r;
smallCloud = abs(largeCloud-0.6) + smallCloud*smallCloud;
float val = coverage;
shape = min(max(val - smallCloud,0.0)/sqrt(val),1.0);
// shape = abs(largeCloud*2.0 - 1.2)*0.5 - (1.0-smallCloud);
break; }
}
// clamp density of the cloud within its upper/lower bounds
shape = min(min(shape, clamp(posToMax,0,1)), 1.0 - clamp(minHeight - position.y,0,1));
// round out the bottom part slightly
float bottomShape = 1.0-pow(1.0-min(max(position.y-minHeight,0.0) / 25.0, 1.0), 5.0);
// carve out the upper part of clouds. make sure it rounds out at its upper bound
float topShape = min(max(posToMax,0.0) / max(tallness,1.0),1.0);
topShape = min(exp(-0.5 * (1.0-topShape)), 1.0-pow(1.0-topShape,5.0));
shape = max((shape - 1.0) + topShape * bottomShape, 0.0);
/// erosion noise
if(shape > 0.001){
float erodeAmount = 0.5;
// shrink the coverage slightly so it is a similar shape to clouds with erosion. this helps cloud lighting and cloud shadows.
if (LOD < 1) return max(shape - 0.27*erodeAmount,0.0);
samplePos.xz -= cloud_movement/4.0;
// da wind
// if(LayerIndex == SMALLCUMULUS_LAYER)
samplePos.xz += pow(max(position.y - (minHeight+20.0), 0.0) / (max(tallness,1.0)*0.20), 1.5);
float erosion = 0.0;
float omShape = 1.0 - shape;
switch (LayerIndex){
default : { break; }
case SMALLCUMULUS_LAYER: {
erosion += (1.0-densityAtPos(samplePos * CloudLayer0_detail * CloudLayer0_scale / 3.0)) * sqrt(omShape);
float falloff = 1.0 - clamp(posToMax/(CloudLayer0_tallness/CloudLayer0_scale),0.0,1.0);
erosion += abs(densityAtPos(samplePos * CloudLayer0_detail * CloudLayer0_scale) - falloff) * 0.75 * (omShape*omShape) * (1.0-falloff*0.25);
erosion = erosion*erosion*erosion*erosion;
break; }
case LARGECUMULUS_LAYER: {
erosion += (1.0 - densityAtPos(samplePos * CloudLayer1_detail * CloudLayer1_scale / 4.5)) * sqrt(omShape);
float falloff = 1.0 - clamp(posToMax/(CloudLayer1_tallness/CloudLayer1_scale),0.0,1.0);
erosion += abs(densityAtPos(samplePos * CloudLayer1_detail * CloudLayer1_scale) - falloff) * 0.75 * (omShape*omShape) * (1.0-falloff*0.5);
erosion = erosion*erosion*erosion*erosion;
break; }
}
return max(shape - erosion*erodeAmount,0.0);
} else return 0.0;
}
#if CUMULONIMBUS > 0
vec2 getCumulonimbusShape(int LOD, in vec3 position, float minHeight, float maxHeight){
float largeCloud = 0.0;
float smallCloud = 0.0;
vec3 samplePos = position*vec3(1.0, 1.0/48.0, 1.0)/4.0;
float tallness = maxHeight - minHeight;
float posToMax = maxHeight - position.y;
float cumulonimbusScale = 1.0;
//largeCloud = texture2D(noisetex, (samplePos.zx - cloud_movement*6.0) / 17000.0 * cumulonimbusScale * 0.2).b;
//largeCloud = abs(largeCloud* -8.0);
//float val = 2.8 + Rain_coverage * rainStrength;
//float shape = min(max(val - largeCloud,0.0)/sqrt(val),1.0) * smoothstep(5000.0, 10000.0, length(position - cameraPosition));
largeCloud = (max(sin((samplePos.x - cloud_movement*10.0)/2700) * cos((samplePos.z - cloud_movement*10.0)/2700), 0.0));
largeCloud = mix(max(min(largeCloud - 0.25, 0.5)*2.0, 0.0), max(min(largeCloud, 0.25)*4.0, 0.0), thunderStrength);
float shape = largeCloud * smoothstep(5000.0, 10000.0, length(position - cameraPosition));
// return vec2(shape, 1.0);
float isLarge = smoothstep(0.0, 1.0, shape);
// isLarge = 0.0;
float bottomShape = 1.0-pow(1.0-min(max(position.y-minHeight,0.0) / 5.0, 1.0), 5.0);
float smallMaxHeight = (tallness*0.45 + minHeight);
float shape2 = 0.0;
if (position.y < smallMaxHeight) {
float smallTallness = smallMaxHeight - minHeight;
float posToMaxSmall = smallMaxHeight - position.y;
smallCloud = 1-texture2D(noisetex, (samplePos.xz - cloud_movement*4.0) / 1800.0 * cumulonimbusScale * 0.2).r * smoothstep(smallMaxHeight, tallness*0.2+minHeight, position.y);
shape2 = min(max(1.1 - smallCloud,0.0)/sqrt(1.1),1.0) * smoothstep(5000.0, 7000.0, length(position - cameraPosition));
shape2 = min(min(shape2, clamp(smallMaxHeight - position.y,0,1)), 1.0 - clamp(minHeight - position.y,0,1));
float smallTopShape = min(max(posToMaxSmall,0.0) / max(smallTallness,1.0),1.0);
smallTopShape = min(exp(23 * (1.0-smallTopShape)), 1.0-pow(1.0-smallTopShape,9.0));
shape2 = max((shape2 - 1.0) + smallTopShape * bottomShape, 0.0);
}
shape = min(min(shape, clamp(posToMax,0,1)), 1.0 - clamp(minHeight - position.y,0,1));
float topShape = min(max(posToMax,0.0) / max(tallness,1.0),1.0);
topShape = min(exp(-1.0 * (1.0-topShape)), 1.0-pow(1.0-topShape,5.0));
float topShape2 = min(max(posToMax,0.0) / max(tallness,1.0),1.0);
topShape2 = min(exp(-0.1 * (1.0-topShape2)), 1.0-pow(1.0-topShape2,7.0));
shape = max((shape - 1.0) + topShape * bottomShape + (1- topShape2) * bottomShape, 0.0);
shape = pow(shape, 1 + smoothstep(tallness*0.4+minHeight, tallness*0.8+minHeight, position.y));
shape += shape2;
#if CUMULONIMBUS == 1
shape *= pow(cumulonimbusStrength, mix(1.0, 6.0, smoothstep(tallness * 0.75 + minHeight, maxHeight, position.y)));
#endif
if(shape > 0.001){
if (LOD < 1) return vec2(max(shape - 0.27*0.5,0.0), isLarge);
samplePos.xz -= cloud_movement/4.0;
samplePos.xz += pow( max(position.y - (minHeight+20.0), 0.0) / (max(tallness,1.0)*0.20), 1.5);
float omShape = 1.0 - shape;
float erosion = (1.0 - densityAtPos(samplePos * 190 * cumulonimbusScale*0.05)) * sqrt(omShape);
float falloff = 1.0 - clamp((posToMax)/4600.0,0.0,1.0);
erosion += abs(densityAtPos(samplePos * 580 * cumulonimbusScale*0.05) - falloff) * 0.65 * (omShape) * (1.0-falloff*0.5);
erosion = erosion*erosion*erosion*erosion*smoothstep(maxHeight+40.0, tallness*0.5+minHeight, position.y);
return vec2(max(shape - erosion*0.5,0.0), isLarge);
} else return vec2(0.0, isLarge);
}
#endif
#if CUMULONIMBUS > 0 && defined CUMULONIMBUS_LIGHTNING
vec3 getLightningPosition(float minHeight, float maxHeight) {
float angle = rand(lightningTimer) * 6.28318530718;
float rMin = 7000.0;
float rMax = 13000.0;
float radius = sqrt(mix(rMin * rMin, rMax * rMax, rand(lightningTimer + 1)));
vec3 lightningPos = vec3(radius * cos(angle), minHeight + 0.62 * (maxHeight - minHeight), radius * sin(angle));
float shapeAtLightningPos = getCumulonimbusShape(0, lightningPos, minHeight, maxHeight).x;
float moveUp = 0.48;
//if (shapeAtLightningPos < 0.1 && thunderStrength > 0.0) moveUp = mix(0.5, 0.1, thunderStrength);
lightningPos.y = minHeight + (maxHeight - minHeight) * moveUp;
lightningPos.y -= cameraPosition.y;
if (shapeAtLightningPos < 0.1) lightningPos = vec3(0.0);
#ifdef CUSTOM_LIGHTNING_POS
lightningPos = vec3(CUSTOM_LIGHTNING_POS_X, CUSTOM_LIGHTNING_POS_Y, CUSTOM_LIGHTNING_POS_Z) - cameraPosition;
#endif
return lightningPos;
}
#endif
#ifndef LIGHTNINGONLY
float getPlanetShadow(vec3 playerPos, vec3 WsunVec){
float planetShadow = min(max(playerPos.y - (-100.0 + 1.0 / max(WsunVec.y*0.1, 0.0)),0.0) / 100.0, 1.0);
planetShadow = mix(pow(1.0-pow(1.0-planetShadow,2.0),2.0), 1.0, pow(max(WsunVec.y, 0.0),2.0));
return planetShadow;
}
float GetCloudShadow(vec3 playerPos, vec3 sunVector){
#if defined CUSTOM_MOON_ROTATION && LIGHTNING_SHADOWS > 0
#if LIGHTNING_SHADOWS < 2
if (lightningBoltPosition.w > 0.0 && sunElevation < 0.0)
#else
if (lightningBoltPosition.w > 0.0)
#endif
{
return 1.0;
}
#endif
float totalShadow = getPlanetShadow(playerPos, sunVector);
vec3 startPosition = playerPos;
vec3 startOffset = sunVector / abs(sunVector.y);
#ifdef CLOUDS_SHADOWS
float cloudShadows = 0.0;
#ifdef CloudLayer0
startPosition = playerPos + startOffset * max((CloudLayer0_height + 20.0) - playerPos.y, 0.0);
cloudShadows = getCloudShape(SMALLCUMULUS_LAYER, 0, startPosition, CloudLayer0_height, CloudLayer0_height + CloudLayer0_tallness/CloudLayer0_scale)*(getRainDensity(SC_parameters.smallCumulus.y));
#endif
#ifdef CloudLayer1
startPosition = playerPos + startOffset * max((CloudLayer1_height + 30.0) - playerPos.y, 0.0);
cloudShadows += getCloudShape(LARGECUMULUS_LAYER, 0, startPosition, CloudLayer1_height, CloudLayer1_height + CloudLayer1_tallness/CloudLayer1_scale)*(getRainDensity(SC_parameters.largeCumulus.y));
#endif
#ifdef CloudLayer2
startPosition = playerPos + startOffset * max(CloudLayer2_height - playerPos.y, 0.0);
cloudShadows += getCloudShape(ALTOSTRATUS_LAYER, 0, startPosition, CloudLayer2_height, CloudLayer2_height + 5.0)*SC_parameters.altostratus.y * (1.0-abs(sunVector.y));
#endif
#if CUMULONIMBUS > 0
float distanceFactor = clamp(degrees(acos(dot(vec3(0.0, 1.0, 0.0), sunVector))), 45.0, 90.0) - 45.0;
startPosition = playerPos + sunVector * mix(7000, 18000, distanceFactor/45.0);
cloudShadows += getCumulonimbusShape(0, startPosition, 600, 4600 + startPosition.y).x;
#endif
cloudShadows *= CLOUD_SHADOW_STRENGTH;
#if defined CloudLayer0 || defined CloudLayer1 || defined CloudLayer2 || CUMULONIMBUS > 0
totalShadow *= exp((cloudShadows*cloudShadows) * -200.0);
#endif
#endif
return totalShadow;
}
#ifndef CLOUDSHADOWSONLY
uniform sampler2D colortex4;
#if CLOUD_PHASE == 0
// Henyey-Greenstein
float phaseCloud(float x, float g){
float gg = g * g;
return (gg * -0.25 + 0.25) * pow(-2.0 * (g * x) + (gg + 1.0), -1.5) / 3.14;
}
#elif CLOUD_PHASE == 1
// Cornette-Shanks
float phaseCloud(float x, float g){
return (3.0 * (1.0 - g * g) * (1.0 + x * x)) / (25.133 * (2.0 + g * g) * pow(1.0 + g * g - 2.0 * g * x, 1.5));
}
#else
// HG-Draine
float phaseCloud(in float x, in float g)
{
const float a = 0.9;
float gg = g * g;
return ((1 - gg)*(1 + a*x*x))/(4.*(1 + (a*(1 + 2*gg))/3.) * 3.1415926 * pow(1 + gg - 2*g*x,1.5));
}
#endif
float getCloudScattering(
int LayerIndex,
vec3 rayPosition,
vec3 sunVector,
vec3 moonVector,
float dither,
float minHeight,
float maxHeight,
float density
){
int samples = 3;
int LOD = 0;
if(LayerIndex == CUMULONIMBUS_LAYER) samples = 7;
if((LayerIndex == ALTOSTRATUS_LAYER) || (LayerIndex == CIRRUS_LAYER)) samples = 2;
float shadow = 0.0;
vec3 shadowRayPosition = vec3(0.0);
float sunVis = smoothstep(-0.06, 0.01, sunElevation);
sunVis = sunVis * sunVis;
#if defined CAELUM_SUPPORT || !defined CUSTOM_MOON_ROTATION
float moonVis = smoothstep(0.0, 0.075, -moonElevation);
#else
float moonVis = smoothstep(0.0, 0.2, moonVector.y);
#endif
moonVis = moonVis * moonVis;
moonVis *= smoothstep(0.06, -0.06, sunElevation);
float isLarge = 80;
vec3 lightVec = normalize(mix(moonVector, sunVector, smoothstep(-0.06, 0.06, sunElevation)));
for (int i = 0; i < samples; i++){
if((LayerIndex == ALTOSTRATUS_LAYER) || (LayerIndex == CIRRUS_LAYER)){
shadowRayPosition = rayPosition + sunVis * sunVector * (1.0 + i * dither) / (pow(abs(sunVector.y*0.5),3.0) * 0.995 + 0.005) + moonVis * moonVector * (1.0 + i * dither) / (pow(abs(moonVector.y*0.5),3.0) * 0.995 + 0.005);
} else
#if CUMULONIMBUS > 0
if((LayerIndex == LARGECUMULUS_LAYER) || (LayerIndex == SMALLCUMULUS_LAYER))
#endif
{
shadowRayPosition = rayPosition + lightVec * (1.0 + i + dither)*20.0;
}
#if CUMULONIMBUS > 0
else {
shadowRayPosition = rayPosition + lightVec * (1.0 + i + dither)*isLarge;
}
#endif
// float fadeddensity = density * pow(clamp((shadowRayPosition.y - minHeight)/(max(maxHeight-minHeight,1.0)*0.25),0.0,1.0),2.0);
#if CUMULONIMBUS > 0
if(LayerIndex != CUMULONIMBUS_LAYER) {
#endif
shadow += getCloudShape(LayerIndex, LOD, shadowRayPosition, minHeight, maxHeight) * density;
#if CUMULONIMBUS > 0
} else {
shadow += getCumulonimbusShape(LOD, shadowRayPosition, minHeight, maxHeight).x * density;
isLarge *= 1.374;
}
#endif
}
return shadow;
}
vec3 getCloudLighting(
int LayerIndex,
float shape,
float shapeFaded,
float sunShadowMask,
vec3 directLightCol,
vec3 directLightCol2,
float indirectShadowMask,
vec3 indirectLightCol,
vec3 rayPosition,
float backScatterPhase,
vec4 phaseLevels,
float backScatterPhase2,
vec4 phaseLevels2
){
vec3 heightScal = vec3(mix(1.0, 0.5, clamp(rayPosition.y, 0.0, 7000.0)/7000.0));
directLightCol = pow(directLightCol, heightScal);
directLightCol2 = pow(directLightCol2, heightScal);
float beerCoef = -4.0;
float powder = min(exp(beerCoef*exp(beerCoef*shapeFaded)) * 3.5, 1.0);
float backscatter = powder * backScatterPhase;
float forwardscatter = mix(mix(phaseLevels.x, phaseLevels.y, powder), mix(phaseLevels.z, phaseLevels.w, powder), powder);
float backscatter2 = powder * backScatterPhase2;
float forwardscatter2 = mix(mix(phaseLevels2.x, phaseLevels2.y, powder), mix(phaseLevels2.z, phaseLevels2.w, powder), powder);
// backscatter = powder * phaseCloud(-backScatterPhase, 0.25) * 2.0;
// forwardscatter = phaseCloud(backScatterPhase, mix(0.9,0.1,powder));
float expBeer = 6.28 * exp((beerCoef-1.0)*sunShadowMask);
vec3 directScattering = expBeer * directLightCol * (forwardscatter + backscatter);
directScattering += expBeer * directLightCol2 * (forwardscatter2 + backscatter2);
vec3 indirectScattering = indirectLightCol * mix(1.0, exp2(-5.0*shape), indirectShadowMask*indirectShadowMask);
// return indirectScattering;
// return directScattering;
return indirectScattering + directScattering;
}
vec4 raymarchCloud(
int LayerIndex,
int samples,
vec3 rayPosition,
vec3 rayDirection,
float dither,
float minHeight,
float maxHeight,
vec3 sunVector,
vec3 moonVector,
vec3 sunScattering,
vec3 moonScattering,
vec3 skyScattering,
float referenceDistance,
vec3 sampledSkyCol,
inout vec2 cloudPlaneDistance,
float backScatterPhase,
vec4 phaseLevels,
float backScatterPhase2,
vec4 phaseLevels2
){
vec3 color = vec3(0.0);
float totalAbsorbance = 1.0;
#if AURORA_LOCATION > 0
#ifdef LUT
const float mult = 0.375*AURORA_BRIGHTNESS;
#else
const float mult = 0.015*AURORA_BRIGHTNESS;
#endif
const vec3 auroraColor = sin(vec3(AURORA_R, AURORA_G, AURORA_B) + 0.63) * 0.5 + 0.5;
vec3 auroraLighting = mult*auroraColor * auroraAmount * smoothstep(0.0, -0.1, sunVector.y) * smoothstep(0.1, 0.0, moonVector.y);
#endif
// if(LayerIndex == SMALLCUMULUS_LAYER || LayerIndex == LARGECUMULUS_LAYER || LayerIndex == CUMULONIMBUS_LAYER) {
// float planetShadow = getPlanetShadow(rayPosition, sunVector);
// sunScattering *= planetShadow;
// sunMultiScattering *= planetShadow;
// float planetShadow = getPlanetShadow(rayPosition, moonVector);
// moonScattering *= planetShadow;
// moonMultiScattering *= planetShadow;
// }
float distanceFactor = length(rayDirection);
float densityTresholdCheck = 0.0;
if(LayerIndex == SMALLCUMULUS_LAYER) densityTresholdCheck = 0.06;
if(LayerIndex == LARGECUMULUS_LAYER || LayerIndex == CUMULONIMBUS_LAYER) densityTresholdCheck = 0.02;
if((LayerIndex == ALTOSTRATUS_LAYER) || (LayerIndex == CIRRUS_LAYER)) densityTresholdCheck = 0.01;
densityTresholdCheck = mix(1e-5, densityTresholdCheck, dither);
if((LayerIndex == ALTOSTRATUS_LAYER) || (LayerIndex == CIRRUS_LAYER)){
float density = 0.0;
vec3 newPos = rayPosition - cameraPosition;
if(LayerIndex == ALTOSTRATUS_LAYER) {
density = SC_parameters.altostratus.y;
density *= smoothstep(CloudLayer2_distance, CloudLayer2_distance*0.5, length(newPos));
} else {
density = SC_parameters.cirrus.y;
density *= smoothstep(CloudLayer3_distance, CloudLayer3_distance*0.5, length(newPos));
}
if (density == 0.0) return vec4(color, totalAbsorbance);
bool ifAboveOrBelowPlane = max(mix(-1.0, 1.0, clamp(cameraPosition.y - minHeight,0.0,1.0)) * normalize(rayDirection).y,0.0) > 0.0;
// check if the ray staring position is going farther than the reference distance, if yes, dont begin marching. this is to check for intersections with the world.
// check if the camera is above or below the cloud plane, so it doesnt waste work on the opposite hemisphere
#ifndef VL_CLOUDS_DEFERRED
if(length(newPos) > referenceDistance || ifAboveOrBelowPlane) return vec4(color, totalAbsorbance);
#else
if(ifAboveOrBelowPlane) return vec4(color, totalAbsorbance);
#endif
float shape = getCloudShape(LayerIndex, 1, rayPosition, minHeight, maxHeight);
float shapeWithDensity = shape*density;
if(shapeWithDensity > mix(1e-5, 0.06, dither)){
cloudPlaneDistance.x = length(newPos); cloudPlaneDistance.y = 0.0;
}
// check if the pixel has visible clouds before doing work.
if(shapeWithDensity > 1e-5){
// can add the initial cloud shape sample for a free shadow starting step :D
float sunShadowMask = getCloudScattering(LayerIndex, rayPosition, sunVector, moonVector, dither, minHeight, maxHeight, density) * (1.0-abs(WsunVec.y));
float indirectShadowMask = 0.5;
vec3 lighting = getCloudLighting(LayerIndex, shapeWithDensity, shapeWithDensity, sunShadowMask, sunScattering, moonScattering, indirectShadowMask, skyScattering, rayPosition, backScatterPhase, phaseLevels, backScatterPhase2, phaseLevels2);
#if AURORA_LOCATION > 0
lighting += auroraLighting;
#endif
newPos.xz /= max(newPos.y,0.0)*0.0025 + 1.0;
newPos.y = min(newPos.y,0.0);
float distancefog = exp(-0.00025*length(newPos));
vec3 atmosphereHaze = (sampledSkyCol - sampledSkyCol * distancefog);
lighting = lighting * distancefog + atmosphereHaze;
float densityCoeff = exp(-distanceFactor*shapeWithDensity);
color += (lighting - lighting * densityCoeff) * totalAbsorbance;
totalAbsorbance *= densityCoeff;
}
return vec4(color, totalAbsorbance);
}
if(LayerIndex < ALTOSTRATUS_LAYER){
vec3 newPos = rayPosition - cameraPosition;
float densityLarge = getRainDensity(SC_parameters.largeCumulus.y);
float density = 0.0;
if(LayerIndex == SMALLCUMULUS_LAYER) density = getRainDensity(SC_parameters.smallCumulus.y) * smoothstep(CloudLayer0_distance, CloudLayer0_distance*0.5, length(newPos));
if(LayerIndex == LARGECUMULUS_LAYER) density = getRainDensity(SC_parameters.largeCumulus.y) * smoothstep(CloudLayer1_distance, CloudLayer1_distance*0.5, length(newPos));
if(LayerIndex == CUMULONIMBUS_LAYER) density = 0.8;
if (density < 0.01) return vec4(color, totalAbsorbance);
#if AURORA_LOCATION == 0
float skylightOcclusion = 1.0;
#if defined CloudLayer1 && defined CloudLayer0
if(LayerIndex == SMALLCUMULUS_LAYER) {
float upperLayerOcclusion = getCloudShape(LARGECUMULUS_LAYER, 0, rayPosition + vec3(0.0,1.0,0.0) * max((CloudLayer1_height+20) - rayPosition.y,0.0), CloudLayer1_height, CloudLayer1_height+100.0);
skylightOcclusion = mix(mix(0.0,0.2,densityLarge), 1.0, pow(1.0 - upperLayerOcclusion*densityLarge,2));
}
#endif
#endif
vec3 lightningPos = vec3(0.0);
#if CUMULONIMBUS > 0 && defined CUMULONIMBUS_LIGHTNING
if(LayerIndex == CUMULONIMBUS_LAYER || thunderStrength > 0.0){
lightningPos = getLightningPosition(minHeight, maxHeight);
}
#endif
vec3 mainLightVec = normalize(mix(moonVector, sunVector, smoothstep(-0.06, 0.06, sunElevation)));
float tallness = maxHeight - minHeight;
for(int i = 0; i < samples; i++) {
newPos = rayPosition - cameraPosition;
// check if the ray staring position is going farther than the reference distance, if yes, dont begin marching. this is to check for intersections with the world.
#ifndef VL_CLOUDS_DEFERRED
if(length(newPos) > referenceDistance) break;
#endif
float rayHeightInCloud = rayPosition.y - minHeight;
// check if the pixel is in the bounding box before doing work.
if(clamp(rayPosition.y - maxHeight,0.0,1.0) < 1.0 && clamp(rayHeightInCloud,0.0,1.0) > 0.0){
float shape = 0.0;
float isLarge = 1.0;
#if CUMULONIMBUS > 0
if (LayerIndex != CUMULONIMBUS_LAYER) {
#endif
shape = getCloudShape(LayerIndex, 1, rayPosition, minHeight, maxHeight);
#if CUMULONIMBUS > 0
} else {
vec2 cumulonimbusCloud = getCumulonimbusShape(1, rayPosition, minHeight, maxHeight);
shape = cumulonimbusCloud.x;
isLarge = 1.0 + 4.0 * cumulonimbusCloud.y;
}
#endif
float shapeWithDensity = shape*density*isLarge;
float shapeWithDensityFaded = shape*density * pow(clamp((rayHeightInCloud)/(max(tallness,1.0)*0.25),0.0,1.0),2.0);
if(shapeWithDensityFaded > densityTresholdCheck){
cloudPlaneDistance.x = length(newPos); cloudPlaneDistance.y = 0.0;
}
// check if the pixel has visible clouds before doing work.
if(shapeWithDensityFaded > 1e-5){
#if AURORA_LOCATION > 0
float skylightOcclusion = 1.0;
#if defined CloudLayer1 && defined CloudLayer0
if(LayerIndex == SMALLCUMULUS_LAYER) {
float upperLayerOcclusion = getCloudShape(LARGECUMULUS_LAYER, 0, rayPosition + vec3(0.0,1.0,0.0) * max((CloudLayer1_height+20) - rayPosition.y,0.0), CloudLayer1_height, CloudLayer1_height+CloudLayer1_height);
skylightOcclusion = mix(mix(0.0,0.2,densityLarge), 1.0, pow(1.0 - upperLayerOcclusion*densityLarge,5));
}
#endif
#endif
// can add the initial cloud shape sample for a free shadow starting step :D
float indirectShadowMask = 1.0 - min(max(rayHeightInCloud,0.0) / max(tallness,1.0), 1.0);
float sunShadowMask = getCloudScattering(LayerIndex, rayPosition, sunVector, moonVector, dither, minHeight, maxHeight, density);
vec3 shadowStartPos = vec3(0.0);
// do cloud shadows from one layer to another
// large cumulus layer -> small cumulus layer
#if defined CloudLayer0 && defined CloudLayer1
if(LayerIndex == SMALLCUMULUS_LAYER){
shadowStartPos = rayPosition + mainLightVec / abs(mainLightVec.y) * max((CloudLayer1_height + 20.0) - rayPosition.y, 0.0);
sunShadowMask += 3.0 * getCloudShape(LARGECUMULUS_LAYER, 0, shadowStartPos, CloudLayer1_height, CloudLayer1_height+CloudLayer1_tallness)*densityLarge;
}
#endif
// cumulonimbus layer -> other cumulus layers
#if (defined CloudLayer0 || defined CloudLayer1) && CUMULONIMBUS > 0
if(LayerIndex != CUMULONIMBUS_LAYER){
float distanceFactor = clamp(degrees(acos(dot(vec3(0.0, 1.0, 0.0), mainLightVec))), 45.0, 90.0) - 45.0;
shadowStartPos = rayPosition + mainLightVec * 9000.;
sunShadowMask += getCumulonimbusShape(0, shadowStartPos, 600., 4600.+shadowStartPos.y).x * 3.0;
}
#endif
// altostratus layer -> all cumulus layers
#ifdef CloudLayer2
shadowStartPos = rayPosition + mainLightVec / abs(mainLightVec.y) * max(CloudLayer2_height - rayPosition.y, 0.0);
sunShadowMask += getCloudShape(ALTOSTRATUS_LAYER, 0, shadowStartPos, CloudLayer2_height, CloudLayer2_height) * SC_parameters.altostratus.y * (1.0-abs(mainLightVec.y));
#endif
vec3 lighting = getCloudLighting(LayerIndex, shapeWithDensity, shapeWithDensityFaded, sunShadowMask, sunScattering, moonScattering, indirectShadowMask, skyScattering*skylightOcclusion, rayPosition, backScatterPhase, phaseLevels, backScatterPhase2, phaseLevels2);
float lightningIntensity = 0.0;
// normal lightning strikes
float horizontalDist = length((newPos.xz) - lightningBoltPosition.xz);
if (horizontalDist < 7500.0 && lightningBoltPosition.w > 0.0) {
lightningIntensity = exp(-horizontalDist * 0.006) * density * smoothstep(0.0, 0.02, fract(frameTimeCounter)) * lightningFlash;
lighting = mix(lighting, vec3(1.3,1.5,3.0), lightningIntensity);
}
// lightning strikes in cumulonimbus clouds
#if defined CUMULONIMBUS_LIGHTNING && CUMULONIMBUS > 0
if(LayerIndex == CUMULONIMBUS_LAYER || thunderStrength > 0.0){
horizontalDist = length(newPos.xz);
float lightningDist = length((newPos) - lightningPos);
if (lightningDist < 6000.0 && lightningPos != vec3(0.0)) {
lightningIntensity = smoothstep(6000.0, 50.0, lightningDist) * smoothstep(0.15, 1.0, shapeWithDensity) * lightningFlash;
lightningIntensity *= (1.0 - smoothstep(minHeight, 1.05 * maxHeight, rayPosition.y));
lightningIntensity *= smoothstep(17000, 14000, horizontalDist);
lightningIntensity *= lightningFade;
vec3 lightningStrength = vec3(CUSTOM_LIGHTNING_R, 1.05*CUSTOM_LIGHTNING_G, 1.22*CUSTOM_LIGHTNING_B) * CUMULONIMBUS_LIGHTNING_BRIGHTNESS * 0.125;
lighting = mix(lighting, lightningStrength, lightningIntensity);
}
}
#endif
#if AURORA_LOCATION > 0
lighting += auroraLighting * min(max(rayHeightInCloud,0.0) / max(tallness,1.0), 1.0) * skylightOcclusion*skylightOcclusion;
#endif
newPos.xz /= max(newPos.y,0.0)*0.0025 + 1.0;
newPos.y = min(newPos.y,0.0);
float distancefog = exp2(-0.0006*length(newPos));
vec3 atmosphereHaze = (sampledSkyCol - sampledSkyCol * distancefog);
lighting = lighting * distancefog + atmosphereHaze;
float densityCoeff = exp(-distanceFactor*shapeWithDensityFaded);
color += (lighting - lighting * densityCoeff) * totalAbsorbance;
totalAbsorbance *= densityCoeff;
// check if you can see through the cloud on the pixel before doing the next iteration
if (totalAbsorbance < 1e-5) break;
}
}
rayPosition += rayDirection;
}
return vec4(color, totalAbsorbance);
}
}
vec3 getRayOrigin(
vec3 rayStartPos,
vec3 cameraPos,
float dither,
float minHeight,
float maxHeight
){
vec3 cloudDist = vec3(1.0);
cloudDist.xz = mix(vec2(255.0), vec2(5.0), clamp(cameraPos.y - minHeight ,0.0,clamp((maxHeight-15)-cameraPosition.y ,0.0,1.0)));
// allow passing through/above/below the plane without limits
float flip = mix(max(cameraPos.y - maxHeight,0.0), max(minHeight - cameraPos.y,0.0), clamp(rayStartPos.y,0.0,1.0));
// orient the ray to be a flat plane facing up/down
// vec3 position = rayStartPos*dither + cameraPos + (rayStartPos/abs(rayStartPos.y)) * flip;
vec3 position = rayStartPos*dither + cameraPos + (rayStartPos/length(rayStartPos/cloudDist)) * flip;
return position;
}
vec4 GetVolumetricClouds(
vec3 viewPos,
vec2 dither,
vec3 sunVector,
vec3 moonVector,
vec3 directLightCol,
vec3 directLightCol2,
vec3 indirectLightCol,
inout float cloudPlaneDistance,
inout vec2 cloudDistance
){
#ifndef VOLUMETRIC_CLOUDS
return vec4(0.0,0.0,0.0,1.0);
#endif
vec3 color = vec3(0.0);
float totalAbsorbance = 1.0;
vec4 cloudColor = vec4(color, totalAbsorbance);
float cloudheight = CloudLayer0_tallness / CloudLayer0_scale;
float minHeight = CloudLayer0_height;
float maxHeight = cloudheight + minHeight;
#if defined OVERWORLD_SHADER && defined AETHER_FLAG
minHeight = CloudLayer0_height - 350.0;
maxHeight = cloudheight + minHeight;
#endif
float heightRelativeToClouds = clamp(1.0 - max(cameraPosition.y - minHeight,0.0) / 100.0 ,0.0,1.0);
#if defined DISTANT_HORIZONS || defined VOXY
float maxdist = dhVoxyFarPlane - 16.0;
#else
float maxdist = far + 16.0*5.0;
#endif
float lViewPosM = length(viewPos) < maxdist ? length(viewPos) - 1.0 : 100000000.0;
vec4 NormPlayerPos = normalize(gbufferModelViewInverse * vec4(viewPos, 1.0) + vec4(gbufferModelViewInverse[3].xyz,0.0));
// vec3 signedSunVec = sunVector;
vec3 unsignedSunVec = sunVector; //mix(moonVector, sunVector, clamp(float(sunElevation > 1e-5)*2.0-1.0 ,0,1));
// vec3 signedMoonVec = moonVector;
vec3 unsignedMoonVec = moonVector;
float SdotV = dot(unsignedSunVec, NormPlayerPos.xyz);
float SdotV2 = dot(unsignedMoonVec, NormPlayerPos.xyz);
#ifdef SKY_GROUND
NormPlayerPos.y += 0.03 * heightRelativeToClouds;
#endif
float maxSamples = 20.0;
float minSamples = 12.0;
int samples = int(clamp(maxSamples / exp2(abs(NormPlayerPos.y)), minSamples, maxSamples));
// int samples = 30;
///------- setup the ray
// vec3 cloudDist = vec3(1.0); cloudDist.xz = mix(vec2(255.0), vec2(5.0), clamp(maxHeight - cameraPosition.y,0.0,1.0));
vec3 cloudDist = vec3(1.0);
float cloudMix = clamp(smoothstep(minHeight - 400.0, minHeight + 45.0, cameraPosition.y),0.0,clamp(smoothstep(maxHeight + 300.0, maxHeight - 60.0, cameraPosition.y) ,0.0,1.0));
cloudDist.xz = mix(vec2(255.0), vec2(5.5), cloudMix);
// vec3 rayDirection = NormPlayerPos.xyz * (cloudheight/abs(NormPlayerPos.y)/samples);
vec3 rayDirection = NormPlayerPos.xyz * (cloudheight/length(NormPlayerPos.xyz/cloudDist)/samples);
vec3 rayPosition = getRayOrigin(rayDirection, cameraPosition, dither.y, minHeight, maxHeight);
vec3 sampledSkyCol = skyFromTex(normalize(rayPosition-cameraPosition), colortex4)/1200.0 * Sky_Brightness;
#ifdef SKY_GROUND
#if CUMULONIMBUS > 1
heightRelativeToClouds = clamp(1.0 - max(cameraPosition.y - 6000,0.0) / 100.0 ,0.0,1.0);
#endif
sampledSkyCol = mix(sampledSkyCol, indirectLightCol, heightRelativeToClouds);
#endif
// setup for getting distance
vec3 playerPos = mat3(gbufferModelViewInverse) * viewPos;
#if defined DISTANT_HORIZONS || defined VOXY
float maxLength = min(length(playerPos), max(far, dhVoxyRenderDistance))/length(playerPos);
#else
float maxLength = min(length(playerPos), far)/length(playerPos);
#endif
playerPos *= maxLength;
float startDistance = length(playerPos);
float sunVis = smoothstep(-0.06, 0.1, sunElevation);
#if defined CAELUM_SUPPORT || !defined CUSTOM_MOON_ROTATION
float moonVis = smoothstep(0.0, 0.075, -moonElevation);
#else
float moonVis = smoothstep(0.0, 0.2, moonVector.y);
#endif
#if defined EXCLUDE_WRITE_TO_LUT && defined USE_CUSTOM_CLOUD_LIGHTING_COLORS
directLightCol = dot(directLightCol,vec3(0.21, 0.72, 0.07)) * vec3(DIRECTLIGHT_CLOUDS_R,DIRECTLIGHT_CLOUDS_G,DIRECTLIGHT_CLOUDS_B);
directLightCol2 = dot(directLightCol2,vec3(0.21, 0.72, 0.07)) * vec3(DIRECTLIGHT_CLOUDS_R,DIRECTLIGHT_CLOUDS_G,DIRECTLIGHT_CLOUDS_B);
indirectLightCol = dot(indirectLightCol,vec3(0.21, 0.72, 0.07)) * vec3(INDIRECTLIGHT_CLOUDS_R,INDIRECTLIGHT_CLOUDS_G,INDIRECTLIGHT_CLOUDS_B);
#endif
///------- do color stuff outside of the raymarcher loop
// vec3 sunScattering = directLightCol * (phaseCloud(SdotV, 0.85) + phaseCloud(SdotV, 0.75));
// vec3 sunMultiScattering = directLightCol;
// vec3 moonScattering = directLightCol2 * (phaseCloud(SdotV2, 0.85) + phaseCloud(SdotV2, 0.75));
// vec3 moonMultiScattering = directLightCol2;
// the idea is to interpolate between 4 HG function calls with different G parameters
float backScatterPhase = 0.0;
vec4 phaseLevels = vec4(0.0);
if(sunVis > 0.0) {
backScatterPhase = phaseCloud(-SdotV, 0.25) * 2.0;
phaseLevels = vec4(phaseCloud(SdotV, 0.80), phaseCloud(SdotV, 0.55), phaseCloud(SdotV, 0.35), phaseCloud(SdotV, 0.10));
}
float backScatterPhase2 = 0.0;
vec4 phaseLevels2 = vec4(0.0);
if(moonVis > 0.0) {
backScatterPhase2 = phaseCloud(-SdotV2, 0.25) * 2.0;
phaseLevels2 = vec4(phaseCloud(SdotV2, 0.80), phaseCloud(SdotV2, 0.55), phaseCloud(SdotV2, 0.35), phaseCloud(SdotV2, 0.10));
}
// backScatterPhase = SdotV;
vec3 sunScattering = directLightCol * sunVis * sunVis;
vec3 moonScattering = directLightCol2 * moonVis * moonVis * (1.0 - sunVis * sunVis);
vec3 skyScattering = indirectLightCol * (1.0 + sunVis);
// vec3 moonScattering3 = moonScattering * moonVis;
// vec3 moonMultiScattering3 = moonMultiScattering * moonVis;
//
// moonVis = moonVis * moonVis;
// vec3 sunScattering2 = sunScattering * sunVis;
// vec3 sunMultiScattering2 = sunMultiScattering * sunVis;
// vec3 moonScattering2 = moonScattering * moonVis;
// vec3 moonMultiScattering2 = moonMultiScattering * moonVis;
////------- RENDER SMALL CUMULUS CLOUDS
vec4 smallCumulusClouds = cloudColor;
vec2 cloudLayer0_Distance = vec2(startDistance, 1.0);
#ifdef CloudLayer0
float smallCumulusDistance = length(rayPosition - cameraPosition);
smallCumulusClouds = raymarchCloud(SMALLCUMULUS_LAYER, samples, rayPosition, rayDirection, dither.x, minHeight, maxHeight, unsignedSunVec, unsignedMoonVec, sunScattering, moonScattering, skyScattering, lViewPosM, sampledSkyCol, cloudLayer0_Distance, backScatterPhase, phaseLevels, backScatterPhase2, phaseLevels2);
#endif
////------- RENDER LARGE CUMULUS CLOUDS
vec4 largeCumulusClouds = cloudColor;
#ifdef CloudLayer1
vec2 cloudLayer1_Distance = vec2(startDistance, 1.0);
if(smallCumulusClouds.a > 1e-5 || cameraPosition.y > CloudLayer1_height) {
cloudheight = CloudLayer1_tallness;
minHeight = CloudLayer1_height;
maxHeight = cloudheight + minHeight;
cloudMix = clamp(smoothstep(minHeight - 400.0, minHeight + 45.0, cameraPosition.y),0.0,clamp(smoothstep(maxHeight + 300.0, maxHeight - 60.0, cameraPosition.y) ,0.0,1.0));
cloudDist.xz = mix(vec2(255.0), vec2(5.0), cloudMix);
rayDirection = NormPlayerPos.xyz * (cloudheight/length(NormPlayerPos.xyz/cloudDist)/samples);
rayPosition = getRayOrigin(rayDirection, cameraPosition, dither.y, minHeight, maxHeight);
largeCumulusClouds = raymarchCloud(LARGECUMULUS_LAYER, samples, rayPosition, rayDirection, dither.x, minHeight, maxHeight, unsignedSunVec, unsignedMoonVec, sunScattering, moonScattering, skyScattering, lViewPosM, sampledSkyCol, cloudLayer1_Distance, backScatterPhase, phaseLevels, backScatterPhase2, phaseLevels2);
}
float largeCumulusDistance = length(rayPosition - cameraPosition);
#endif
////------- RENDER CUMULONIMBUS CLOUDS
vec4 cumulonimbusClouds = cloudColor;
#if CUMULONIMBUS > 0
vec2 cloudLayer4_Distance = vec2(startDistance, 1.0);
if((smallCumulusClouds.a > 1e-5 && largeCumulusClouds.a > 1e-5) || cameraPosition.y > CloudLayer0_height + CloudLayer0_tallness) {
cloudheight = 4000;
minHeight = 600;
maxHeight = cloudheight + minHeight;
int cumulonimbusSamples = int(1.5*samples);
cloudDist.xz = vec2(8.0);
rayDirection = NormPlayerPos.xyz * (cloudheight/length(NormPlayerPos.xyz/cloudDist)/cumulonimbusSamples);
rayPosition = getRayOrigin(rayDirection, cameraPosition, dither.y, minHeight, maxHeight);
cumulonimbusClouds = raymarchCloud(CUMULONIMBUS_LAYER, cumulonimbusSamples, rayPosition, rayDirection, dither.x, minHeight, maxHeight, unsignedSunVec, unsignedMoonVec, sunScattering, moonScattering, skyScattering, lViewPosM, sampledSkyCol, cloudLayer4_Distance, backScatterPhase, phaseLevels, backScatterPhase2, phaseLevels2);
}
float cumulonimbusDistance = length(rayPosition - cameraPosition);
#endif
// #if defined CAELUM_SUPPORT || !defined CUSTOM_MOON_ROTATION
// moonVis = smoothstep(-0.04, 0.015, -moonElevation);
// #else
// moonVis = smoothstep(0.0, 0.2, moonVector.y);
// #endif
//
// moonScattering2 = moonScattering * moonVis;
// moonMultiScattering2 = moonMultiScattering * moonVis;
////------- RENDER ALTOSTRATUS CLOUDS
vec4 altoStratusClouds = cloudColor;
#ifdef CloudLayer2
vec2 cloudLayer2_Distance = vec2(startDistance, 1.0);
if(smallCumulusClouds.a > 1e-5 && largeCumulusClouds.a > 1e-5 && cumulonimbusClouds.a > 1e-5) {
cloudheight = 5.0;
minHeight = CloudLayer2_height;
maxHeight = cloudheight + minHeight;
cloudDist.xz = mix(vec2(255.0), vec2(5.0), clamp(cameraPosition.y - minHeight,0.0,clamp((maxHeight-15) - cameraPosition.y ,0.0,1.0)));
rayDirection = NormPlayerPos.xyz * (cloudheight/length(NormPlayerPos.xyz/cloudDist));
rayPosition = getRayOrigin(rayDirection, cameraPosition, dither.y, minHeight, maxHeight);
altoStratusClouds = raymarchCloud(ALTOSTRATUS_LAYER, samples, rayPosition, rayDirection, dither.x, minHeight, maxHeight, unsignedSunVec, unsignedMoonVec, sunScattering, moonScattering, skyScattering, lViewPosM, sampledSkyCol, cloudLayer2_Distance, backScatterPhase, phaseLevels, backScatterPhase2, phaseLevels2);
}
#endif
////------- RENDER CIRRUS CLOUDS
vec4 cirrusClouds = cloudColor;
#ifdef CloudLayer3
vec2 cloudLayer3_Distance = vec2(startDistance, 1.0);
if(smallCumulusClouds.a > 1e-5 && largeCumulusClouds.a > 1e-5 && cumulonimbusClouds.a > 1e-5 && altoStratusClouds.a > 1e-5) {
cloudheight = 5.0;
minHeight = CloudLayer3_height;
maxHeight = cloudheight + minHeight;
cloudDist.xz = mix(vec2(255.0), vec2(5.0), clamp(cameraPosition.y - minHeight,0.0,clamp((maxHeight-15) - cameraPosition.y ,0.0,1.0)));
rayDirection = NormPlayerPos.xyz * (cloudheight/length(NormPlayerPos.xyz/cloudDist));
rayPosition = getRayOrigin(rayDirection, cameraPosition, dither.y, minHeight, maxHeight);
cirrusClouds = raymarchCloud(CIRRUS_LAYER, samples, rayPosition, rayDirection, dither.x, minHeight, maxHeight, unsignedSunVec, unsignedMoonVec, sunScattering, moonScattering, skyScattering, lViewPosM, sampledSkyCol, cloudLayer3_Distance, backScatterPhase, phaseLevels, backScatterPhase2, phaseLevels2);
}
#endif
////------- BLEND LAYERS
#if defined CloudLayer0 && defined CloudLayer1
if (cameraPosition.y > CloudLayer1_height) {
vec2 temp = cloudLayer1_Distance;
cloudLayer1_Distance = cloudLayer0_Distance;
cloudLayer0_Distance = temp;
}
#endif
#if CUMULONIMBUS > 0 && !defined CloudLayer3 && !defined CloudLayer0 && !defined CloudLayer1 && !defined CloudLayer2
cloudPlaneDistance = cloudLayer4_Distance.x;
#else
#if defined CloudLayer0
#if defined CloudLayer1
#if defined CloudLayer2
#if defined CloudLayer3
float temp = mix(cloudLayer3_Distance.x, cloudLayer2_Distance.x, cloudLayer3_Distance.y);
temp = mix(cloudLayer1_Distance.x, temp, cloudLayer1_Distance.y);
cloudPlaneDistance = mix(cloudLayer0_Distance.x, temp, cloudLayer0_Distance.y);
#else
float temp = mix(cloudLayer2_Distance.x, cloudLayer1_Distance.x, cloudLayer2_Distance.y);
cloudPlaneDistance = mix(cloudLayer0_Distance.x, temp, cloudLayer0_Distance.y);
#endif
#else
#if defined CloudLayer3
float temp = mix(cloudLayer3_Distance.x, cloudLayer1_Distance.x, cloudLayer3_Distance.y);
cloudPlaneDistance = mix(cloudLayer0_Distance.x, temp, cloudLayer0_Distance.y);
#else
cloudPlaneDistance = mix(cloudLayer0_Distance.x, cloudLayer1_Distance.x, cloudLayer0_Distance.y);
#endif
#endif
#else
#if defined CloudLayer2
#if defined CloudLayer3
float temp = mix(cloudLayer3_Distance.x, cloudLayer2_Distance.x, cloudLayer3_Distance.y);
cloudPlaneDistance = mix(cloudLayer0_Distance.x, temp, cloudLayer0_Distance.y);
#else
float temp = mix(cloudLayer2_Distance.x, cloudLayer0_Distance.x, cloudLayer2_Distance.y);
cloudPlaneDistance = mix(cloudLayer0_Distance.x, temp, cloudLayer0_Distance.y);
#endif
#else
#if defined CloudLayer3
cloudPlaneDistance = mix(cloudLayer0_Distance.x, cloudLayer3_Distance.x, cloudLayer0_Distance.y);
#else
cloudPlaneDistance = cloudLayer0_Distance.x;
#endif
#endif
#endif
#else
#if defined CloudLayer1
#if defined CloudLayer2
#if defined CloudLayer3
float temp = mix(cloudLayer3_Distance.x, cloudLayer2_Distance.x, cloudLayer3_Distance.y);
cloudPlaneDistance = mix(cloudLayer1_Distance.x, temp, cloudLayer1_Distance.y);
#else
float temp = mix(cloudLayer2_Distance.x, cloudLayer1_Distance.x, cloudLayer2_Distance.y);
cloudPlaneDistance = mix(cloudLayer1_Distance.x, temp, cloudLayer1_Distance.y);
#endif
#else
#if defined CloudLayer3
cloudPlaneDistance = mix(cloudLayer1_Distance.x, cloudLayer3_Distance.x, cloudLayer1_Distance.y);
#else
cloudPlaneDistance = cloudLayer1_Distance.x;
#endif
#endif
#else
#if defined CloudLayer2
#if defined CloudLayer3
cloudPlaneDistance = mix(cloudLayer2_Distance.x, cloudLayer3_Distance.x, cloudLayer2_Distance.y);
#else
cloudPlaneDistance = cloudLayer2_Distance.x;
#endif
#else
#if defined CloudLayer3
cloudPlaneDistance = cloudLayer3_Distance.x;
#else
cloudPlaneDistance = 0.0;
#endif
#endif
#endif
#endif
#endif
#if defined CUMULONIMBUS_LIGHTNING && CUMULONIMBUS > 0
#ifdef CloudLayer0
if (smallCumulusClouds.a < 1.0) cloudDistance.r = smallCumulusDistance;
#endif
#ifdef CloudLayer1
if (largeCumulusClouds.a < 1.0) cloudDistance.r = largeCumulusDistance;
#endif
#if defined CloudLayer0 && defined CloudLayer1
if(cameraPosition.y < CloudLayer1_height) {
if (largeCumulusClouds.a < 1.0) cloudDistance.r = largeCumulusDistance;
if (smallCumulusClouds.a < 1.0) cloudDistance.r = smallCumulusDistance;
} else {
if (smallCumulusClouds.a < 1.0) cloudDistance.r = smallCumulusDistance;
if (largeCumulusClouds.a < 1.0) cloudDistance.r = largeCumulusDistance;
}
#endif
if (cumulonimbusClouds.a < 1.0) cloudDistance.g = cumulonimbusDistance;
if(cloudDistance.r > 15000.0) cloudDistance.r = 0.0;
if(cloudDistance.g > 15000.0) cloudDistance.g = 0.0;
#endif
#ifdef CloudLayer3
cloudColor = cirrusClouds;
#endif
#ifdef CloudLayer2
cloudColor.rgb *= altoStratusClouds.a;
cloudColor.rgb += altoStratusClouds.rgb;
cloudColor.a *= altoStratusClouds.a;
#endif
if(cameraPosition.y < CloudLayer0_height + CloudLayer0_tallness) {
#if CUMULONIMBUS > 0
cloudColor.rgb *= cumulonimbusClouds.a;
cloudColor.rgb += cumulonimbusClouds.rgb;
cloudColor.a *= cumulonimbusClouds.a;
#endif
#ifdef CloudLayer1
cloudColor.rgb *= largeCumulusClouds.a;
cloudColor.rgb += largeCumulusClouds.rgb;
cloudColor.a *= largeCumulusClouds.a;
#endif
#ifdef CloudLayer0
cloudColor.rgb *= smallCumulusClouds.a;
cloudColor.rgb += smallCumulusClouds.rgb;
cloudColor.a *= smallCumulusClouds.a;
#endif
} else if(cameraPosition.y < CloudLayer1_height + CloudLayer1_tallness) {
#ifdef CloudLayer0
cloudColor.rgb *= smallCumulusClouds.a;
cloudColor.rgb += smallCumulusClouds.rgb;
cloudColor.a *= smallCumulusClouds.a;
#endif
#if CUMULONIMBUS > 0
cloudColor.rgb *= cumulonimbusClouds.a;
cloudColor.rgb += cumulonimbusClouds.rgb;
cloudColor.a *= cumulonimbusClouds.a;
#endif
#ifdef CloudLayer1
cloudColor.rgb *= largeCumulusClouds.a;
cloudColor.rgb += largeCumulusClouds.rgb;
cloudColor.a *= largeCumulusClouds.a;
#endif
} else {
#ifdef CloudLayer0
cloudColor.rgb *= smallCumulusClouds.a;
cloudColor.rgb += smallCumulusClouds.rgb;
cloudColor.a *= smallCumulusClouds.a;
#endif
#ifdef CloudLayer1
cloudColor.rgb *= largeCumulusClouds.a;
cloudColor.rgb += largeCumulusClouds.rgb;
cloudColor.a *= largeCumulusClouds.a;
#endif
#if CUMULONIMBUS > 0
cloudColor.rgb *= cumulonimbusClouds.a;
cloudColor.rgb += cumulonimbusClouds.rgb;
cloudColor.a *= cumulonimbusClouds.a;
#endif
}
color = cloudColor.rgb;
totalAbsorbance = cloudColor.a;
return vec4(color, totalAbsorbance);
}
#endif
#endif