[Add] Atmospheric scattering and improved clouds.
This commit is contained in:
parent
19a431ccc8
commit
95134c91d8
@ -4,30 +4,82 @@ out vec4 FragColor;
|
||||
in vec3 TexCoords;
|
||||
|
||||
uniform sampler2D u_CloudTexture;
|
||||
uniform vec3 u_LightDir;
|
||||
uniform vec3 u_SunPos;
|
||||
|
||||
const float PI = 3.14159265359;
|
||||
|
||||
/* Atmosphere parameters. */
|
||||
const float ATMOSPHERE_RADIUS = 6520275.0;
|
||||
const float PLANET_RADIUS = 6371000.0;
|
||||
const float RAYLEIGH_SCALE_HEIGHT = 8000.0;
|
||||
const float MIE_SCALE_HEIGHT = 1200.0;
|
||||
|
||||
/* Scattering coefficients. */
|
||||
const vec3 RAYLEIGH_SCATTERING_COEFF = vec3(0.0000058, 0.0000135, 0.0000331);
|
||||
const vec3 MIE_SCATTERING_COEFF = vec3(0.00002);
|
||||
|
||||
/* Mie scattering parameters. */
|
||||
const float MIE_G = 0.76;
|
||||
|
||||
/* Number of samples for ray marching. */
|
||||
const int SAMPLES = 16;
|
||||
const int LIGHT_SAMPLES = 8;
|
||||
|
||||
float ray_sphere_intersect(vec3 origin, vec3 dir, float radius) {
|
||||
float a = dot(dir, dir);
|
||||
float b = 2.0 * dot(origin, dir);
|
||||
float c = dot(origin, origin) - radius * radius;
|
||||
float d = b * b - 4.0 * a * c;
|
||||
if (d < 0.0) {
|
||||
return -1.0;
|
||||
}
|
||||
return (-b + sqrt(d)) / (2.0 * a);
|
||||
}
|
||||
|
||||
vec3 get_sky_color(vec3 dir) {
|
||||
vec3 eye = vec3(0.0, PLANET_RADIUS + 1.0, 0.0);
|
||||
float dist = ray_sphere_intersect(eye, dir, ATMOSPHERE_RADIUS);
|
||||
vec3 totalRayleigh = vec3(0.0);
|
||||
vec3 totalMie = vec3(0.0);
|
||||
float stepSize = dist / float(SAMPLES);
|
||||
for (int i = 0; i < SAMPLES; i++) {
|
||||
vec3 p = eye + dir * (float(i) + 0.5) * stepSize;
|
||||
float h = length(p) - PLANET_RADIUS;
|
||||
float rayleighDensity = exp(-h / RAYLEIGH_SCALE_HEIGHT);
|
||||
float mieDensity = exp(-h / MIE_SCALE_HEIGHT);
|
||||
float lightDist = ray_sphere_intersect(p, normalize(u_SunPos), ATMOSPHERE_RADIUS);
|
||||
float lightStepSize = lightDist / float(LIGHT_SAMPLES);
|
||||
float opticalDepthLight = 0.0;
|
||||
for (int j = 0; j < LIGHT_SAMPLES; j++) {
|
||||
vec3 lp = p + normalize(u_SunPos) * (float(j) + 0.5) * lightStepSize;
|
||||
float lh = length(lp) - PLANET_RADIUS;
|
||||
opticalDepthLight += exp(-lh / RAYLEIGH_SCALE_HEIGHT) * lightStepSize;
|
||||
}
|
||||
vec3 transmittance = exp(-(opticalDepthLight * RAYLEIGH_SCATTERING_COEFF
|
||||
+ opticalDepthLight * MIE_SCATTERING_COEFF));
|
||||
totalRayleigh += rayleighDensity * stepSize * transmittance;
|
||||
totalMie += mieDensity * stepSize * transmittance;
|
||||
}
|
||||
|
||||
float mu = dot(dir, normalize(u_SunPos));
|
||||
float rayleighPhase = 3.0 / (16.0 * PI) * (1.0 + mu * mu);
|
||||
float miePhase = 3.0 / (8.0 * PI) * ((1.0 - MIE_G * MIE_G)
|
||||
* (1.0 + mu * mu)) / ((2.0 + MIE_G * MIE_G) * pow(1.0 + MIE_G
|
||||
* MIE_G - 2.0 * MIE_G * mu, 1.5));
|
||||
return 20.0 * (totalRayleigh * RAYLEIGH_SCATTERING_COEFF * rayleighPhase
|
||||
+ totalMie * MIE_SCATTERING_COEFF * miePhase);
|
||||
}
|
||||
|
||||
void main() {
|
||||
/* Convert 3D texture coords to 2D using spherical mapping. */
|
||||
float u = atan(TexCoords.z, TexCoords.x) / (2.0 * PI) + 0.5;
|
||||
float v = asin(TexCoords.y) / PI + 0.5;
|
||||
float cloudAlpha = texture(u_CloudTexture, vec2(u,v)).r;
|
||||
|
||||
/* Sample noise value from cloud texture. */
|
||||
float noise = texture(u_CloudTexture, vec2(u, v)).r;
|
||||
vec3 skyColor = get_sky_color(normalize(TexCoords));
|
||||
vec3 cloudColor = mix(skyColor, vec3(1.0), smoothstep(0.5, 0.8, cloudAlpha));
|
||||
|
||||
/* Create soft-edged clouds from the noise. */
|
||||
float cloudAlpha = smoothstep(0.5, 0.8, noise);
|
||||
|
||||
/* Some simple lighting. */
|
||||
vec3 normal = normalize(TexCoords);
|
||||
float diffuse = max(dot(normal, -u_LightDir), 0.0);
|
||||
vec3 cloudColor = vec3(1.0) * (diffuse * 0.5 + 0.5); /* Add some ambient light. */
|
||||
|
||||
/* Fade out clouds at the poles to hide pinching. */
|
||||
float poleFade = 1.0 - abs(TexCoords.y);
|
||||
cloudAlpha *= smoothstep(0.0, 0.2, poleFade);
|
||||
float finalAlpha = smoothstep(0.0, 0.2, poleFade) * smoothstep(0.5, 0.8, cloudAlpha);
|
||||
|
||||
/* Cloud colour is white. it's transparency is determined by alpha. */
|
||||
FragColor = vec4(cloudColor, cloudAlpha);
|
||||
FragColor = vec4(pow(cloudColor, vec3(1.0/2.2)), finalAlpha);
|
||||
}
|
||||
|
||||
@ -1,17 +1,80 @@
|
||||
#version 330 core
|
||||
out vec4 FragColor;
|
||||
|
||||
in vec3 WorldPos;
|
||||
in vec3 v_WorldPos;
|
||||
|
||||
uniform vec3 u_SunPos;
|
||||
|
||||
const float PI = 3.14159265359;
|
||||
|
||||
/* Atmosphere parameters. */
|
||||
const float ATMOSPHERE_RADIUS = 6520275.0;
|
||||
const float PLANET_RADIUS = 6371000.0;
|
||||
const float RAYLEIGH_SCALE_HEIGHT = 8000.0;
|
||||
const float MIE_SCALE_HEIGHT = 1200.0;
|
||||
|
||||
/* Scattering coefficients. */
|
||||
const vec3 RAYLEIGH_SCATTERING_COEFF = vec3(0.0000058, 0.0000135, 0.0000331);
|
||||
const vec3 MIE_SCATTERING_COEFF = vec3(0.00002);
|
||||
|
||||
/* Mie scattering parameters. */
|
||||
const float MIE_G = 0.76;
|
||||
|
||||
/* Number of samples for ray marching. */
|
||||
const int SAMPLES = 16;
|
||||
const int LIGHT_SAMPLES = 8;
|
||||
|
||||
float ray_sphere_intersect(vec3 origin, vec3 dir, float radius) {
|
||||
float a = dot(dir, dir);
|
||||
float b = 2.0 * dot(origin, dir);
|
||||
float c = dot(origin, origin) - radius * radius;
|
||||
float d = b * b - 4.0 * a * c;
|
||||
if (d < 0.0) {
|
||||
return -1.0;
|
||||
}
|
||||
return (-b + sqrt(d)) / (2.0 * a);
|
||||
}
|
||||
|
||||
void main() {
|
||||
/* Define colours for the top and bottom of the sky. */
|
||||
vec3 zenithColor = vec3(0.3, 0.5, 0.8);
|
||||
vec3 horizonColor = vec3(0.7, 0.85, 1.0);
|
||||
vec3 dir = normalize(v_WorldPos);
|
||||
vec3 eye = vec3(0.0, PLANET_RADIUS + 1.0, 0.0);
|
||||
|
||||
float blendFactor = (normalize(WorldPos).y + 1.0) / 2.0;
|
||||
float dist = ray_sphere_intersect(eye, dir, ATMOSPHERE_RADIUS);
|
||||
|
||||
/* Blend the two colours. */
|
||||
vec3 finalColor = mix(horizonColor, zenithColor, blendFactor);
|
||||
vec3 totalRayleigh = vec3(0.0);
|
||||
vec3 totalMie = vec3(0.0);
|
||||
|
||||
FragColor = vec4(finalColor, 1.0);
|
||||
float stepSize = dist / float(SAMPLES);
|
||||
for(int i = 0; i < SAMPLES; i++) {
|
||||
vec3 p = eye + dir * (float(i) + 0.5) * stepSize;
|
||||
float h = length(p) - PLANET_RADIUS;
|
||||
|
||||
float rayleighDensity = exp(-h / RAYLEIGH_SCALE_HEIGHT);
|
||||
float mieDensity = exp(-h / MIE_SCALE_HEIGHT);
|
||||
|
||||
float lightDist = ray_sphere_intersect(p, normalize(u_SunPos), ATMOSPHERE_RADIUS);
|
||||
float lightStepSize = lightDist / float(LIGHT_SAMPLES);
|
||||
float opticalDepthLight = 0.0;
|
||||
for(int j = 0; j < LIGHT_SAMPLES; j++) {
|
||||
vec3 lp = p + normalize(u_SunPos) * (float(j) + 0.5) * lightStepSize;
|
||||
float lh = length(lp) - PLANET_RADIUS;
|
||||
opticalDepthLight += exp(-lh / RAYLEIGH_SCALE_HEIGHT) * lightStepSize;
|
||||
}
|
||||
|
||||
vec3 transmittance = exp(-(opticalDepthLight * RAYLEIGH_SCATTERING_COEFF
|
||||
+ opticalDepthLight * MIE_SCATTERING_COEFF));
|
||||
totalRayleigh += rayleighDensity * stepSize * transmittance;
|
||||
totalMie += mieDensity * stepSize * transmittance;
|
||||
}
|
||||
|
||||
float mu = dot(dir, normalize(u_SunPos));
|
||||
float rayleighPhase = 3.0 / (16.0 * PI) * (1.0 + mu * mu);
|
||||
float miePhase = 3.0 / (8.0 * PI) * ((1.0 - MIE_G * MIE_G)
|
||||
* (1.0 + mu * mu)) / ((2.0 + MIE_G * MIE_G)
|
||||
* pow(1.0 + MIE_G * MIE_G - 2.0 * MIE_G * mu, 1.5));
|
||||
|
||||
vec3 finalColor = 20.0 * (totalRayleigh * RAYLEIGH_SCATTERING_COEFF
|
||||
* rayleighPhase + totalMie * MIE_SCATTERING_COEFF * miePhase);
|
||||
|
||||
FragColor = vec4(pow(finalColor, vec3(1.0/2.2)), 1.0);
|
||||
}
|
||||
|
||||
@ -1,17 +1,13 @@
|
||||
#version 330 core
|
||||
layout (location = 0) in vec3 aPos;
|
||||
|
||||
out vec3 WorldPos;
|
||||
|
||||
uniform mat4 view;
|
||||
uniform mat4 projection;
|
||||
|
||||
void main() {
|
||||
WorldPos = aPos;
|
||||
out vec3 v_WorldPos;
|
||||
|
||||
/* Force depth value to 1.0 after perspective division
|
||||
* to ensure the skybox is always rendered behind everythig else.
|
||||
*/
|
||||
vec4 pos = projection * view * vec4(WorldPos, 1.0);
|
||||
void main() {
|
||||
v_WorldPos = aPos;
|
||||
vec4 pos = projection * view * vec4(aPos, 1.0);
|
||||
gl_Position = pos.xyww;
|
||||
}
|
||||
|
||||
@ -265,6 +265,7 @@ void Renderer::_render_sky(const Camera& camera) {
|
||||
|
||||
_sky_shader.set_mat4("view", view);
|
||||
_sky_shader.set_mat4("projection", _projection);
|
||||
_sky_shader.set_vec3("u_SunPos", {0.0f, 1.0f, 0.0f});
|
||||
|
||||
glBindVertexArray(_sky_vao);
|
||||
glDrawElements(GL_TRIANGLES, _sky_indices_count, GL_UNSIGNED_INT, 0);
|
||||
@ -288,6 +289,7 @@ void Renderer::_render_clouds(const Camera& camera) {
|
||||
_cloud_shader.set_mat4("projection", _projection);
|
||||
_cloud_shader.set_mat4("view", cloud_view);
|
||||
_cloud_shader.set_vec3("u_LightDir", {-0.5f, -1.0f, -0.5f});
|
||||
_cloud_shader.set_vec3("u_SunPos", {0.0f, 1.0f, 0.0f});
|
||||
|
||||
glBindVertexArray(_sky_vao);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user