From ba2bc1a43c2fedd69abe8bfd9411f7b0653153c7 Mon Sep 17 00:00:00 2001 From: Evert Prants Date: Tue, 31 Mar 2020 14:17:22 +0300 Subject: [PATCH] Atmosphere shader (from space only) --- assets/shaders/atmosphere.fs | 26 +++++- assets/shaders/atmosphere.vs | 74 ++++++++++++++- src/engine/components/planet/atmosphere.js | 45 +++++++-- src/engine/components/planet/index.js | 2 +- src/engine/environment.js | 2 +- src/engine/mesh/geometry/sphere.js | 103 ++++++++++++++------- src/index.js | 14 +-- 7 files changed, 210 insertions(+), 56 deletions(-) diff --git a/assets/shaders/atmosphere.fs b/assets/shaders/atmosphere.fs index dce5f74..2c56618 100644 --- a/assets/shaders/atmosphere.fs +++ b/assets/shaders/atmosphere.fs @@ -1,5 +1,27 @@ precision mediump float; -void main() { - gl_FragColor = vec4(0.0, 0.0, 1.0, 1.0); +uniform vec3 v3LightPosition; +uniform float g; +uniform float g2; +varying vec3 v3Direction; +varying vec3 c0; +varying vec3 c1; + +// Calculates the Mie phase function +float getMiePhase(float fCos, float fCos2, float g, float g2){ + return 1.5 * ((1.0 - g2) / (2.0 + g2)) * (1.0 + fCos2) / pow(1.0 + g2 - 2.0 * g * fCos, 1.5); +} + +// Calculates the Rayleigh phase function +float getRayleighPhase(float fCos2) { + return 0.75 + 0.75 * fCos2; +} + +void main (void) { + float fCos = dot(v3LightPosition, v3Direction) / length(v3Direction); + float fCos2 = fCos * fCos; + vec3 color = getRayleighPhase(fCos2) * c0 + + getMiePhase(fCos, fCos2, g, g2) * c1; + gl_FragColor = vec4(color, 1.0); + gl_FragColor.a = gl_FragColor.b; } diff --git a/assets/shaders/atmosphere.vs b/assets/shaders/atmosphere.vs index 5a5aea9..9276554 100644 --- a/assets/shaders/atmosphere.vs +++ b/assets/shaders/atmosphere.vs @@ -1,12 +1,80 @@ precision mediump float; attribute vec3 aVertexPosition; -attribute vec2 aTexCoords; + +uniform vec3 v3CameraPosition; // The camera position +uniform vec3 v3LightPosition; // The direction vector to the light source +uniform vec3 v3InvWavelength; // 1 / pow(wavelength, 4) for the red, green, and blue channels +uniform float fCameraHeight; // The camera's current height +uniform float fCameraHeight2; // fCameraHeight^2 +uniform float fOuterRadius; // The outer (atmosphere) radius +uniform float fOuterRadius2; // fOuterRadius^2 +uniform float fInnerRadius; // The inner (planetary) radius +uniform float fInnerRadius2; // fInnerRadius^2 +uniform float fKrESun; // Kr * ESun +uniform float fKmESun; // Km * ESun +uniform float fKr4PI; // Kr * 4 * PI +uniform float fKm4PI; // Km * 4 * PI +uniform float fScale; // 1 / (fOuterRadius - fInnerRadius) +uniform float fScaleDepth; // The scale depth (i.e. the altitude at which the atmosphere's average density is found) +uniform float fScaleOverScaleDepth; // fScale / fScaleDepth + +const int nSamples = 3; +const float fSamples = 3.0; + +varying vec3 v3Direction; +varying vec3 c0; +varying vec3 c1; uniform mat4 uModelMatrix; uniform mat4 uViewMatrix; uniform mat4 uProjectionMatrix; -void main() { - gl_Position = uProjectionMatrix * uViewMatrix * uModelMatrix * vec4(aVertexPosition,1); +float scale(float fCos) +{ + float x = 1.0 - fCos; + return fScaleDepth * exp(-0.00287 + x*(0.459 + x*(3.83 + x*(-6.80 + x*5.25)))); +} +void main(void) +{ + // Get the ray from the camera to the vertex and its length (which is the far point of the ray passing through the atmosphere) + vec3 v3Ray = aVertexPosition - v3CameraPosition; + float fFar = length(v3Ray); + v3Ray /= fFar; + // Calculate the closest intersection of the ray with the outer atmosphere (which is the near point of the ray passing through the atmosphere) + float B = 2.0 * dot(v3CameraPosition, v3Ray); + float C = fCameraHeight2 - fOuterRadius2; + float fDet = max(0.0, B*B - 4.0 * C); + float fNear = 0.5 * (-B - sqrt(fDet)); + // Calculate the ray's starting position, then calculate its scattering offset + vec3 v3Start = v3CameraPosition + v3Ray * fNear; + fFar -= fNear; + float fStartAngle = dot(v3Ray, v3Start) / fOuterRadius; + float fStartDepth = exp(-1.0 / fScaleDepth); + float fStartOffset = fStartDepth * scale(fStartAngle); + //c0 = vec3(1.0, 0, 0) * fStartAngle; + // Initialize the scattering loop variables + float fSampleLength = fFar / fSamples; + float fScaledLength = fSampleLength * fScale; + vec3 v3SampleRay = v3Ray * fSampleLength; + vec3 v3SamplePoint = v3Start + v3SampleRay * 0.5; + //gl_FrontColor = vec4(0.0, 0.0, 0.0, 0.0); + // Now loop through the sample rays + vec3 v3FrontColor = vec3(0.0, 0.0, 0.0); + for(int i=0; i 0) indices.push(a, b, d) + if (iy !== heightSegments - 1 || thetaEnd < Math.PI) indices.push(b, c, d) } } - for (let r = 0, pointer = 0; r < rings - 1; r++) { - for (let s = 0; s < sectors - 1; s++) { - const topLeft = r * sectors + s - const topRight = topLeft + 1 - const bottomLeft = (r + 1) * sectors + s - const bottomRight = bottomLeft + 1 - indices[pointer++] = bottomLeft - indices[pointer++] = topLeft - indices[pointer++] = topRight - indices[pointer++] = topRight - indices[pointer++] = bottomRight - indices[pointer++] = bottomLeft - } - } - - return Mesh.construct(Screen.gl, vertices, indices, textureCoords, normals) + return Mesh.construct(Screen.gl, vertices, indices, uvs, normals) } } diff --git a/src/index.js b/src/index.js index e41d0ac..bcd1796 100644 --- a/src/index.js +++ b/src/index.js @@ -29,7 +29,7 @@ async function pipeline () { const entity = await loadMesh(game.gl, 'test') const terrainShader = await game.shaders.createShaderFromFiles(game.gl, 'terrain', false) const skyboxShader = await game.shaders.createShaderFromFiles(game.gl, 'skybox', false) - // let atmosShader = await game.shaders.createShaderFromFiles(game.gl, 'atmosphere', false) + const atmosShader = await game.shaders.createShaderFromFiles(game.gl, 'atmosphere', false) entity.setRotation([0.0, 0.0, -90.0]) @@ -96,7 +96,7 @@ async function pipeline () { // Planet test const planet = new CubePlanet([0.0, 0.0, 0.0], new PlanetGenerator(16, 1000, hmap)) - // let atmosphere = new Atmosphere([0.0, 0.0, 0.0], 1000, 1025, 1, [0.0, 0.0, 1.0]) + const atmosphere = new Atmosphere([0.0, 0.0, 0.0], 1000, 1025, [0.650, 0.570, 0.475]) // Update function for camera and terrain let fpsTimer = 0 @@ -157,8 +157,8 @@ async function pipeline () { game.prepare() // Draw the skybox - skyboxShader.use(gl) - skybox.draw(gl, skyboxShader, cam) + //skyboxShader.use(gl) + //skybox.draw(gl, skyboxShader, cam) // Use terrain shader terrainShader.use(gl) @@ -176,9 +176,9 @@ async function pipeline () { material.apply(gl, terrainShader) planet.draw(gl, terrainShader) - // atmosShader.use(gl) - // cam.draw(gl, atmosShader) - // atmosphere.draw(gl, atmosShader, cam, env.sun, true) + atmosShader.use(gl) + cam.draw(gl, atmosShader) + atmosphere.draw(gl, atmosShader, cam, env.sun, true) } // Render function for the triangle