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

735 lines
27 KiB
GLSL

#define ALTOSTRATUS_LAYER 2
#define LARGECUMULUS_LAYER 1
#define SMALLCUMULUS_LAYER 0
float curvatureoffset = 0.04;
uniform vec2 windDirection;
// uniform float animation;
#if CLOUD_ANIMATION_MODE == 0
uniform int worldDay;
uniform int worldTime;
// uniform float worldTimeAnimation;
float cloud_movement = (worldTime + mod(worldDay,100)*24000.0) / 24.0 * Cloud_Speed;
#elif CLOUD_ANIMATION_MODE == 1
// uniform float frameTimeCounter;
float cloud_movement = frameTimeCounter * Cloud_Speed;
#endif
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 = texture(noisetex, coord).yx;
return mix(xy.r,xy.g, f.y);
}
vec3 getPlanetAbsorb(in vec3 worldPos, in vec3 sunVector, sampler2D colortex){
float position = clamp((worldPos.y - (FAKE_PLANET_START_HEIGHT + 1.0/abs(sunVector.y*0.1)))*256.0 / FAKE_PLANET_GRADIENT_LENGTH,0.0,257.0);
// float position = clamp((worldPos.y + 60.0)*256.0 / 1500.0,0.0,257.0);
vec3 skyAbsorb = texture(colortex, vec2(16.5, position)*texelSize).rgb / 2400.0;
#ifdef ReflectedFog
return skyAbsorb*2400.0/150.0 * 2.5;
#else
return skyAbsorb;
#endif
}
float getCloudShape(int LayerIndex, int LOD, in vec3 position, float minHeight, float maxHeight){
vec3 samplePos = position*vec3(0.25, 0.005, 0.25);
float coverage = 0.0;
float shape = 0.0;
float largeCloud = 0.0;
float smallCloud = 0.0;
switch (LayerIndex){
default : { break; }
case SMALLCUMULUS_LAYER: {
coverage = parameters.smallCumulus.x;
vec2 animatedPos = samplePos.xz + windDirection/4.0 * Cloud_Speed;
largeCloud = texture(noisetex, animatedPos/5000.0 * CloudLayer0_scale).b;
smallCloud = 1.0-texture(noisetex,animatedPos/500.0 * CloudLayer0_scale).r;
smallCloud = abs(largeCloud-0.6) + smallCloud*smallCloud;
shape = min(max(coverage - smallCloud,0.0)/(1e-6+sqrt(coverage)),1.0) ;
break; }
case LARGECUMULUS_LAYER: {
coverage = parameters.largeCumulus.x;
vec2 animatedPos = samplePos.zx + windDirection* Cloud_Speed;
largeCloud = texture(noisetex, animatedPos/10000.0 * CloudLayer1_scale).b;
smallCloud = texture(noisetex, animatedPos/2500.0 * CloudLayer1_scale).b;
smallCloud = abs(largeCloud* -0.7) + smallCloud;
shape = min(max(coverage - smallCloud,0.0)/(1e-6+sqrt(coverage)),1.0) ;
break; }
case ALTOSTRATUS_LAYER: {
coverage = parameters.altostratus.x;
vec2 animatedPos = samplePos.xz + windDirection * 10.0 * Cloud_Speed;
largeCloud = texture(noisetex, animatedPos/100000. * CloudLayer2_scale).b;
smallCloud = 1.0 - texture(noisetex, (animatedPos/7500. - vec2(1.0-largeCloud, -largeCloud)/5.0) * CloudLayer2_scale).b;
smallCloud = largeCloud + smallCloud * 0.4 * clamp(1.5-largeCloud,0.0,1.0);
shape = min(max(coverage - smallCloud,0.0) / (1e-6+sqrt(coverage)),1.0);
shape *= shape;
break; }
}
if(LayerIndex == ALTOSTRATUS_LAYER) return shape;
// clamp density of the cloud within its upper/lower bounds
shape = min(min(shape, clamp(maxHeight - position.y,0,1)), 1.0 - clamp(minHeight - position.y,0,1));
// carve out the upper part of clouds. make sure it rounds out at its upper bound
float topShape = min(max(maxHeight-position.y,0.0) / max(maxHeight-minHeight,1.0),1.0);
topShape = min(exp(-0.5 * (1.0-topShape)), 1.0-pow(1.0-topShape,5.0));
// 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);
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);
float erosion = 0.0;
switch (LayerIndex){
default : { break; }
case SMALLCUMULUS_LAYER: {
samplePos.xz += windDirection/2.0* Cloud_Speed;
samplePos.xz += (-vec2(-windDirection.y, windDirection.x)/1500.0)*pow( max(position.y - (minHeight+20.0), 0.0) / (max(maxHeight-minHeight,1.0)*0.20), 1.5);
erosion += (1.0-densityAtPos(samplePos * 200.0 * CloudLayer0_scale)) * sqrt(1.0-shape);
float falloff = 1.0 - clamp((maxHeight - position.y)/100.0,0.0,1.0);
erosion += abs(densityAtPos(samplePos * 600.0 * CloudLayer0_scale) - falloff) * 0.75 * (1.0-shape) * (1.0-falloff*0.25);
erosion = erosion*erosion*erosion*erosion;
break; }
case LARGECUMULUS_LAYER: {
samplePos.zx += windDirection*1.74* Cloud_Speed;
samplePos.zx += (-vec2(-windDirection.y, windDirection.x)/1500.0)*pow( abs(position.y - (minHeight+20.0)) / (max(maxHeight-minHeight,1.0)*0.20), 1.75);
erosion += (1.0 - densityAtPos(samplePos * 70.0 * CloudLayer1_scale)) * sqrt(1.0-shape);
float falloff = 1.0 - clamp((maxHeight - position.y)/200.0,0.0,1.0);
erosion += abs(densityAtPos(samplePos * 250.0 * CloudLayer1_scale) - falloff) * 0.75 * (1.0-shape) * (1.0-falloff*0.5);
erosion = erosion*erosion*erosion*erosion;
break; }
}
return max(shape - erosion*erodeAmount,0.0);
} else return 0.0;
}
float getPlanetShadow(vec3 playerPos, vec3 WsunVec){
#ifdef FAKE_PLANET
return 1.0;
#endif
float planetShadow = min(max(playerPos.y - (FAKE_PLANET_START_HEIGHT + 1.0 / abs(WsunVec.y*0.1)),0.0) / 100.0, 1.0);
planetShadow = mix(pow(1.0-pow(1.0-planetShadow,2.0),2.0), 1.0, pow(abs(WsunVec.y),2.0));
return planetShadow;
}
float GetCloudShadow(vec3 playerPos, vec3 sunVector){
float totalShadow = getPlanetShadow(playerPos, sunVector);
vec3 startPosition = playerPos;
vec3 startOffset = sunVector / abs(sunVector.y);
#if defined OVERWORLD_SHADER && defined AETHER_FLAG
float layer0Height = CloudLayer0_height - 350.0;
float layer1Height = CloudLayer1_height - 350.0;
float layer2Height = CloudLayer2_height - 350.0;
#else
float layer0Height = CloudLayer0_height;
float layer1Height = CloudLayer1_height;
float layer2Height = CloudLayer2_height;
#endif
#if CLOUD_SHADOW_AMOUNT > 0
float cloudShadows = 0.0;
#ifdef CloudLayer0
startPosition = playerPos + startOffset * max((layer0Height + 20.0) - playerPos.y, 0.0);
cloudShadows = getCloudShape(SMALLCUMULUS_LAYER, 0, startPosition, layer0Height, CloudLayer0_tallness / CloudLayer0_scale + layer0Height)*parameters.smallCumulus.y;
#endif
#ifdef CloudLayer1
startPosition = playerPos + startOffset * max((layer1Height + 20.0) - playerPos.y, 0.0);
cloudShadows += getCloudShape(LARGECUMULUS_LAYER, 0, startPosition, layer1Height, CloudLayer1_tallness / CloudLayer1_scale + layer1Height)*parameters.largeCumulus.y;
#endif
#ifdef CloudLayer2
startPosition = playerPos + startOffset * max(layer2Height - playerPos.y, 0.0);
cloudShadows += getCloudShape(ALTOSTRATUS_LAYER, 0, startPosition, layer2Height, layer2Height)*parameters.altostratus.y * (1.0-abs(WsunVec.y));
#endif
cloudShadows *= float(CLOUD_SHADOW_AMOUNT)/100.0;
#if defined CloudLayer0 || defined CloudLayer1 || defined CloudLayer2
totalShadow *= exp((cloudShadows*cloudShadows) * -200.0);
#endif
#endif
return totalShadow;
}
#ifndef CLOUDSHADOWSONLY
uniform sampler2D colortex4;
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;
}
float getCloudScattering(
int LayerIndex,
vec3 rayPosition,
vec3 sunVector,
float dither,
float minHeight,
float maxHeight,
float density
){
int samples = 3;
int LOD = 0;
// crazy mode
// samples = 10;
// LOD = 1;
if(LayerIndex == ALTOSTRATUS_LAYER) samples = 2;
float shadow = 0.0;
vec3 shadowRayPosition = vec3(0.0);
for (int i = 0; i < samples; i++){
if(LayerIndex == ALTOSTRATUS_LAYER){
// shadowRayPosition = rayPosition + sunVector * (1.0 + i * dither) / (pow(abs(sunVector.y*0.5),3.0) * 0.995 + 0.005);
shadowRayPosition = rayPosition + sunVector * (0.25 + i * dither) * 200.0;
}else{
// shadowRayPosition = rayPosition + sunVector * (1.0 + i + dither)*20.0;
shadowRayPosition = rayPosition + sunVector * (0.05 + i + dither)*20.0;
}
// float fadeddensity = density * pow(clamp((shadowRayPosition.y - minHeight)/(max(maxHeight-minHeight,1.0)*0.25),0.0,1.0),2.0);
shadow += getCloudShape(LayerIndex, LOD, shadowRayPosition, minHeight, maxHeight) * density;
}
return shadow;
}
vec3 getCloudLighting(
float shape,
float shapeFaded,
float sunShadowMask,
vec3 directLightCol,
float indirectShadowMask,
vec3 indirectLightCol
,float backScatterPhase
,vec4 phaseLevels
){
float beerCoef = -4.0;
float powder = min(exp(beerCoef*exp(beerCoef*shapeFaded)) * 3.5, 1);
float backscatter = powder * backScatterPhase;
float forwardscatter = mix(mix(phaseLevels.x, phaseLevels.y, powder), mix(phaseLevels.z, phaseLevels.w, powder), powder);
// backscatter = powder * phaseCloud(-backScatterPhase, 0.25) * 2.0;
// forwardscatter = phaseCloud(backScatterPhase, mix(0.9,0.1,powder));
vec3 directScattering = 6.28 * directLightCol * exp((beerCoef-1.0)*sunShadowMask) * (forwardscatter + backscatter);
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 sunScattering,
vec3 skyScattering,
float referenceDistance,
vec3 sampledSkyCol,
inout vec2 cloudPlaneDistance
,float backScatterPhase
,vec4 phaseLevels
){
vec3 color = vec3(0.0);
float totalAbsorbance = 1.0;
float planetShadow = getPlanetShadow(rayPosition, sunVector);
sunScattering *= planetShadow;
#ifdef FAKE_PLANET
sunScattering = getPlanetAbsorb(rayPosition, WsunVec, colortex4);
#endif
float distanceFactor = length(rayDirection);
float densityTresholdCheck = 0.0;
if(LayerIndex == SMALLCUMULUS_LAYER) densityTresholdCheck = 0.06;
if(LayerIndex == LARGECUMULUS_LAYER) densityTresholdCheck = 0.02;
if(LayerIndex == ALTOSTRATUS_LAYER) densityTresholdCheck = 0.01;
densityTresholdCheck = mix(1e-5, densityTresholdCheck, dither);
if(LayerIndex == ALTOSTRATUS_LAYER){
float density = parameters.altostratus.y;
bool ifAboveOrBelowPlane = max(mix(-1.0, 1.0, clamp(cameraPosition.y - minHeight,0.0,1.0)) * normalize(rayDirection).y + 0.0001,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(rayPosition - cameraPosition) > 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.y > 0.5){
cloudPlaneDistance.x = length(rayPosition - cameraPosition); 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, dither, minHeight, maxHeight, density) * (1.0-abs(WsunVec.y));
float indirectShadowMask = 0.5;
vec3 lighting = getCloudLighting(shapeWithDensity, shapeWithDensity, sunShadowMask, sunScattering, indirectShadowMask, skyScattering, backScatterPhase, phaseLevels);
vec3 newPos = rayPosition - cameraPosition;
#ifdef AERIAL_PERSPECTIVE_TEST
float skydensity = exp(-0.00035*length(newPos));
float ifAboveOrBelowPlane = mix(-1.0, 1.0, clamp(cameraPosition.y - minHeight,0.0,1.0)) ;
vec3 samplesky = skyFromTex(clamp(normalize(vec3(newPos.x,ifAboveOrBelowPlane*newPos.y,newPos.z)) - vec3(0,curvatureoffset - 0.005,0),-1,1) , colortex4).rgb/1200.0;
lighting = lighting * skydensity + (samplesky - samplesky * skydensity);
#else
newPos.xz /= max(newPos.y,0.0)*0.0025 + 1.0;
newPos.y = min(newPos.y,0.0);
float distancefog = exp(-(0.00035 + rainStrength * 0.0015) * length(newPos));
vec3 atmosphereHaze = (sampledSkyCol - sampledSkyCol * distancefog);
lighting = lighting * distancefog + atmosphereHaze;
#endif
float densityCoeff = exp(-distanceFactor*shapeWithDensity);
color += (lighting - lighting * densityCoeff) * totalAbsorbance;
totalAbsorbance *= densityCoeff;
}
return vec4(color, totalAbsorbance);
}
if(LayerIndex < ALTOSTRATUS_LAYER){
float density = parameters.smallCumulus.y;
if(LayerIndex == LARGECUMULUS_LAYER) density = parameters.largeCumulus.y;
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,parameters.largeCumulus.y), 1.0, pow(1.0 - upperLayerOcclusion*parameters.largeCumulus.y,2));
}
#endif
for(int i = 0; i < samples; i++) {
// 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(rayPosition - cameraPosition) > referenceDistance) break;
#endif
// check if the pixel is in the bounding box before doing work.
if(clamp(rayPosition.y - maxHeight,0.0,1.0) < 1.0 && clamp(rayPosition.y - minHeight,0.0,1.0) > 0.0){
float shape = getCloudShape(LayerIndex, 1, rayPosition, minHeight, maxHeight);
float shapeWithDensity = shape*density;
float shapeWithDensityFaded = shape*density * pow(clamp((rayPosition.y - minHeight)/(max(maxHeight-minHeight,1.0)*0.25),0.0,1.0),2.0);
if(shapeWithDensityFaded > densityTresholdCheck && cloudPlaneDistance.y > 0.5){
cloudPlaneDistance.x = length(rayPosition - cameraPosition); cloudPlaneDistance.y = 0.0;
}
// check if the pixel has visible clouds before doing work.
if(shapeWithDensityFaded > 1e-5){
// can add the initial cloud shape sample for a free shadow starting step :D
float indirectShadowMask = 1.0 - min(max(rayPosition.y - minHeight,0.0) / max(maxHeight-minHeight,1.0), 1.0);
float sunShadowMask = getCloudScattering(LayerIndex, rayPosition, sunVector, dither, minHeight, maxHeight, density);
// do cloud shadows from one layer to another
// large cumulus layer -> small cumulus layer
#if defined CloudLayer0 && defined CloudLayer1
if(LayerIndex == SMALLCUMULUS_LAYER){
vec3 shadowStartPos = rayPosition + sunVector / abs(sunVector.y) * max((CloudLayer1_height + 20.0) - rayPosition.y, 0.0);
sunShadowMask += 3.0 * getCloudShape(LARGECUMULUS_LAYER, 0, shadowStartPos, CloudLayer1_height, CloudLayer1_height+100.0)*parameters.largeCumulus.y;
}
#endif
// altostratus layer -> all cumulus layers
#if defined CloudLayer2
vec3 shadowStartPos = rayPosition + sunVector / abs(sunVector.y) * max(CloudLayer2_height - rayPosition.y, 0.0);
sunShadowMask += getCloudShape(ALTOSTRATUS_LAYER, 0, shadowStartPos, CloudLayer2_height, CloudLayer2_height) * parameters.altostratus.y * (1.0-abs(sunVector.y));
#endif
vec3 lighting = getCloudLighting(shapeWithDensity, shapeWithDensityFaded, sunShadowMask, sunScattering, indirectShadowMask, skyScattering * skylightOcclusion, backScatterPhase, phaseLevels);
#if defined LIGHTNING_FLASH && defined LIGHTNINGFLASH_VL
lighting += createLightningPointLight(rayPosition - cameraPosition, lightningBoltPosition.xyz, shapeWithDensity, indirectShadowMask);
#endif
vec3 newPos = rayPosition - cameraPosition;
#ifdef AERIAL_PERSPECTIVE_TEST
float skydensity = exp(-0.00035*length(newPos));
float ifAboveOrBelowPlane = mix(-1.0, 1.0, clamp(cameraPosition.y - minHeight,0.0,1.0)) ;
vec3 samplesky = skyFromTex(clamp(normalize(vec3(newPos.x,ifAboveOrBelowPlane*newPos.y,newPos.z)) - vec3(0,curvatureoffset - 0.005,0),-1,1) , colortex4).rgb/1200.0;
lighting = lighting * skydensity + (samplesky - samplesky * skydensity);
#else
newPos.xz /= max(newPos.y,0.0)*0.0025 + 1.0;
newPos.y = min(newPos.y,0.0);
float distancefog = exp(-(0.00035 + rainStrength * 0.0015) * length(newPos));
vec3 atmosphereHaze = (sampledSkyCol - sampledSkyCol * distancefog);
lighting = lighting * distancefog + atmosphereHaze;
#endif
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
,int samples
){
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/cloudDist)) * flip;
vec3 position = rayStartPos*dither + cameraPos + (rayStartPos/length(rayStartPos/cloudDist)) * flip;
return position;
}
vec4 getCorrectBlendOrder(int viewIndex, vec4 smallCumulusColor,vec4 largeCumulusColor,vec4 altostratusColor){
// swap order of blending based on where the camera position is relative to the cloud plane altitude.
// viewIndex = 0 is below small cumulus layer
// viewIndex = 1 is above large cumulus layer
// viewIndex = 2 is above altostratus layer
vec4 blendedCloudsColor = vec4(0.0,0.0,0.0,1.0);
switch (viewIndex){
default : { break; }
case 0: {
#ifdef CloudLayer2
blendedCloudsColor = altostratusColor;
#endif
#ifdef CloudLayer1
blendedCloudsColor.rgb = blendedCloudsColor.rgb * largeCumulusColor.a + largeCumulusColor.rgb;
blendedCloudsColor.a *= largeCumulusColor.a;
#endif
#ifdef CloudLayer0
blendedCloudsColor.rgb = blendedCloudsColor.rgb * smallCumulusColor.a + smallCumulusColor.rgb;
blendedCloudsColor.a *= smallCumulusColor.a;
#endif
break; }
case 1: {
#ifdef CloudLayer2
blendedCloudsColor = altostratusColor;
#endif
#ifdef CloudLayer0
blendedCloudsColor.rgb = blendedCloudsColor.rgb * smallCumulusColor.a + smallCumulusColor.rgb;
blendedCloudsColor.a *= smallCumulusColor.a;
#endif
#ifdef CloudLayer1
blendedCloudsColor.rgb = blendedCloudsColor.rgb * largeCumulusColor.a + largeCumulusColor.rgb;
blendedCloudsColor.a *= largeCumulusColor.a;
#endif
break; }
case 2: {
#ifdef CloudLayer0
blendedCloudsColor = smallCumulusColor;
#endif
#ifdef CloudLayer1
blendedCloudsColor.rgb = blendedCloudsColor.rgb * largeCumulusColor.a + largeCumulusColor.rgb;
blendedCloudsColor.a *= largeCumulusColor.a;
#endif
#ifdef CloudLayer2
blendedCloudsColor.rgb = blendedCloudsColor.rgb * altostratusColor.a + altostratusColor.rgb;
blendedCloudsColor.a *= altostratusColor.a;
#endif
break; }
}
return blendedCloudsColor;
}
vec4 GetVolumetricClouds(
vec3 viewPos,
vec2 dither,
vec3 sunVector,
vec3 directLightCol,
vec3 indirectLightCol,
inout float cloudPlaneDistance
,in vec4 phaseLevels
,in float backScatterPhase
){
#if !(defined CloudLayer0 || defined CloudLayer1 || defined CloudLayer2)
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
int layerViewIndex = 0;
#if defined CloudLayer1
if(CloudLayer1_height < cameraPosition.y) layerViewIndex = 1;
#endif
#if defined CloudLayer2
if(CloudLayer2_height < cameraPosition.y) layerViewIndex = 2;
#endif
float heightRelativeToClouds = clamp(1.0 - max(cameraPosition.y - minHeight,0.0) / 100.0 ,0.0,1.0);
#ifdef USING_LOD_MOD
float maxdist = LOD_RENDERDISTANCE;
#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 unignedSunVec = sunVector;// * (float(sunElevation > 1e-5)*2.0-1.0);
float SdotV = dot(unignedSunVec, NormPlayerPos.xyz);
// #ifdef SKY_GROUND
NormPlayerPos.y += curvatureoffset;
// #endif
float maxSamples = 15.0;
float minSamples = 10.0;
int samples = int(clamp(maxSamples / sqrt(exp2(NormPlayerPos.y)), 1.0, minSamples));
// samples = 200;
///------- setup the ray
vec3 cloudDist = vec3(1.0);
cloudDist.xz = mix(vec2(255.0), vec2(5.0), clamp(cameraPosition.y - minHeight,0.0,clamp((maxHeight-5) - cameraPosition.y ,0.0,1.0)));
vec3 rayDirection = NormPlayerPos.xyz * (cloudheight/length(NormPlayerPos.xyz/cloudDist)/samples);
vec3 rayPosition = getRayOrigin(rayDirection, cameraPosition, dither.y, minHeight, maxHeight,samples);
#ifdef SKY_GROUND
vec3 sampledSkyCol = mix(skyFromTex(normalize(rayPosition - cameraPosition), colortex4)/1200.0 * Sky_Brightness, indirectLightCol, 1.0);
#else
vec3 sampledSkyCol = skyFromTex(normalize(rayPosition - cameraPosition), colortex4)/1200.0 * Sky_Brightness;
#endif
// setup for getting distance
vec3 playerPos = mat3(gbufferModelViewInverse) * viewPos;
#ifdef USING_LOD_MOD
float maxLength = min(length(playerPos), max(far, LOD_RENDERDISTANCE))/length(playerPos);
#else
float maxLength = min(length(playerPos), far)/length(playerPos);
#endif
playerPos *= maxLength;
float startDistance = length(playerPos);
#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);
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
// the idea is to interpolate between 4 HG function calls with different G parameters
// float backScatterPhase = phaseCloud(-SdotV, 0.25) * 2.0;
// vec4 phaseLevels = vec4(phaseCloud(SdotV, 0.80), phaseCloud(SdotV, 0.55), phaseCloud(SdotV, 0.35), phaseCloud(SdotV, 0.10));
// backScatterPhase = SdotV;
vec3 sunScattering = directLightCol;
vec3 skyScattering = indirectLightCol * (1.0 + pow(1.0-pow(1.0-clamp(sunVector.y,0.0,1.0),5.0),5.0));
bool occlusionCheck = true;
////------- RENDER SMALL CUMULUS CLOUDS
vec4 smallCumulusClouds = cloudColor;
vec2 cloudLayer0_Distance = vec2(startDistance, 1.0);
#ifdef CloudLayer0
smallCumulusClouds = raymarchCloud(SMALLCUMULUS_LAYER, samples, rayPosition, rayDirection, dither.x, minHeight, maxHeight, unignedSunVec, sunScattering, skyScattering, lViewPosM, sampledSkyCol, cloudLayer0_Distance, backScatterPhase, phaseLevels);
#endif
////------- RENDER LARGE CUMULUS CLOUDS
vec4 largeCumulusClouds = cloudColor;
#ifdef CloudLayer1
cloudheight = CloudLayer1_tallness/CloudLayer1_scale;
minHeight = CloudLayer1_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)/samples);
rayPosition = getRayOrigin(rayDirection, cameraPosition, dither.y, minHeight, maxHeight,samples);
vec2 cloudLayer1_Distance = vec2(startDistance, 1.0);
occlusionCheck = layerViewIndex < 1 ? smallCumulusClouds.a > 1e-5 : true;
if(occlusionCheck) largeCumulusClouds = raymarchCloud(LARGECUMULUS_LAYER, samples, rayPosition, rayDirection, dither.x, minHeight, maxHeight, unignedSunVec, sunScattering, skyScattering, lViewPosM, sampledSkyCol, cloudLayer1_Distance, backScatterPhase, phaseLevels);
#endif
////------- RENDER ALTOSTRATUS CLOUDS
vec4 altoStratusClouds = cloudColor;
#ifdef CloudLayer2
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,1);
vec2 cloudLayer2_Distance = vec2(startDistance, 1.0);
occlusionCheck = layerViewIndex < 2 ? (smallCumulusClouds.a > 1e-5 || largeCumulusClouds.a > 1e-5) : true;
if(occlusionCheck) altoStratusClouds = raymarchCloud(ALTOSTRATUS_LAYER, samples, rayPosition, rayDirection, dither.x, minHeight, maxHeight, unignedSunVec, sunScattering, skyScattering, lViewPosM, sampledSkyCol, cloudLayer2_Distance, backScatterPhase, phaseLevels);
#endif
////------- BLEND LAYERS
// this is for an intersection check for VL fog, so that fog cannot march beyond a cloud.
#if defined CloudLayer0 && defined CloudLayer1 && defined CloudLayer2
cloudPlaneDistance = mix(cloudLayer1_Distance.x, cloudLayer2_Distance.x, cloudLayer1_Distance.y);
cloudPlaneDistance = mix(cloudLayer0_Distance.x, cloudPlaneDistance, cloudLayer0_Distance.y);
#endif
#if defined CloudLayer0 && !defined CloudLayer1 && !defined CloudLayer2
cloudPlaneDistance = cloudLayer0_Distance.x;
#endif
#if defined CloudLayer0 && defined CloudLayer1 && !defined CloudLayer2
cloudPlaneDistance = mix(cloudLayer0_Distance.x, cloudLayer1_Distance.x, cloudLayer0_Distance.y);
#endif
#if !defined CloudLayer0 && defined CloudLayer1 && defined CloudLayer2
cloudPlaneDistance = mix(cloudLayer2_Distance.x, cloudLayer1_Distance.x, cloudLayer2_Distance.y);
#endif
#if defined CloudLayer0 && !defined CloudLayer1 && !defined CloudLayer2
cloudPlaneDistance = cloudLayer0_Distance.x;
#endif
#if defined CloudLayer0 && !defined CloudLayer1 && defined CloudLayer2
cloudPlaneDistance = cloudLayer0_Distance.x;
#endif
#if !defined CloudLayer0 && defined CloudLayer1 && !defined CloudLayer2
cloudPlaneDistance = cloudLayer1_Distance.x;
#endif
#if !defined CloudLayer0 && !defined CloudLayer1 && defined CloudLayer2
cloudPlaneDistance = cloudLayer2_Distance.x;
#endif
vec4 blendedCloudColor = getCorrectBlendOrder(layerViewIndex, smallCumulusClouds, largeCumulusClouds, altoStratusClouds);
color = blendedCloudColor.rgb;
totalAbsorbance = blendedCloudColor.a;
// return vec4(vec3(cloudPlaneDistance/1500.0), 0.0);
return vec4(color, totalAbsorbance);
}
#endif