├── .gitignore ├── assets └── shaders │ ├── upscale.vert │ ├── deprecated │ ├── atmo.vert │ ├── pree.vert │ ├── pree.frag │ └── atmo.frag │ ├── tex.frag │ ├── tex.vert │ ├── upscale.frag │ ├── sky.vert │ └── sky.frag ├── LICENSE ├── Makefile ├── README.md ├── gen_noise.cpp └── main.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | *.jpg 2 | *.jpeg 3 | *.bmp 4 | *.tga 5 | include/ 6 | gen_noise 7 | main 8 | 9 | *~ 10 | *.swp 11 | *.png 12 | -------------------------------------------------------------------------------- /assets/shaders/upscale.vert: -------------------------------------------------------------------------------- 1 | #version 330 core 2 | layout (location = 0) in vec2 position; 3 | 4 | void main() 5 | { 6 | gl_Position = vec4(position, 0.0, 1.0); 7 | } 8 | -------------------------------------------------------------------------------- /assets/shaders/deprecated/atmo.vert: -------------------------------------------------------------------------------- 1 | #version 330 core 2 | layout (location = 0) in vec2 position; 3 | 4 | void main() 5 | { 6 | gl_Position = vec4(position, 0.0, 1.0); 7 | } 8 | -------------------------------------------------------------------------------- /assets/shaders/tex.frag: -------------------------------------------------------------------------------- 1 | #version 330 core 2 | in vec2 TexCoords; 3 | 4 | uniform sampler2D fbo; 5 | 6 | out vec4 color; 7 | 8 | void main() 9 | { 10 | color = texture(fbo, TexCoords, 0.0); 11 | } 12 | -------------------------------------------------------------------------------- /assets/shaders/tex.vert: -------------------------------------------------------------------------------- 1 | #version 330 core 2 | layout (location = 0) in vec2 position; 3 | 4 | out vec2 TexCoords; 5 | 6 | void main() 7 | { 8 | gl_Position = vec4(position, 0.0, 1.0); 9 | TexCoords = position*0.5+0.5; 10 | } 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Clay John 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | #OBJS specifies which files to compile as part of the project 2 | OBJS = main.cpp #include/FastNoise/FastNoise.cpp include/TileableVolumeNoise/TileableVolumeNoise.cpp 3 | 4 | #CC specifies which compiler we're using 5 | CC = g++ 6 | 7 | #INCLUDE_PATHS specifies the additional include paths we'll need 8 | INCLUDE_PATHS = -I -Iinclude -I/usr/local/include#-Iinclude/TileableVolumeNoise/glm#-IC:\glew\include -IC:\SOIL\include 9 | 10 | #LIBRARY_PATHS specifies the additional library paths we'll need 11 | LIBRARY_PATHS = -L/usr/local/lib#-LC:\GLFW\lib #-LC:\glew\lib -LC:\SOIL\lib 12 | 13 | #COMPILER_FLAGS specifies the additional compilation options we're using 14 | COMPILER_FLAGS = -std=c++11 15 | 16 | #LINKER_FLAGS specifies the libraries we're linking against 17 | LINKER_FLAGS = -lXi -lGLEW -lGLU -lm -lGL -lm -lpthread -ldl -ldrm -lXdamage -lX11-xcb -lxcb-glx -lxcb-dri2 -lglfw3 -lrt -lm -ldl -lXrandr -lXinerama -lXxf86vm -lXext -lXcursor -lXrender -lXfixes -lX11 -lpthread -lxcb -lXau -lXdmcp 18 | #-lGLEW -lglfw3 -lGL -lm -lXrandr -lXi -lX11 -lXxf86vm -lpthread #-lGL -lglfw3#-lglew32 -lmingw32 -lSOIL -lopengl32 -lglfw3 -lglu32 -lgdi32 19 | #OBJ_NAME specifies the name of our exectuable 20 | OBJ_NAME = main 21 | 22 | #This is the target that compiles our executable 23 | all : $(OBJS) 24 | $(CC) $(OBJS) $(INCLUDE_PATHS) $(LIBRARY_PATHS) $(COMPILER_FLAGS) $(LINKER_FLAGS) -o $(OBJ_NAME) 25 | -------------------------------------------------------------------------------- /assets/shaders/upscale.frag: -------------------------------------------------------------------------------- 1 | #version 330 core 2 | 3 | uniform sampler2D buff; 4 | uniform sampler2D pong; 5 | uniform int check; 6 | uniform mat4 MVPM; 7 | uniform mat4 LFMVPM; 8 | uniform vec2 resolution; 9 | uniform float downscale; 10 | uniform float aspect; 11 | 12 | out vec4 color; 13 | 14 | 15 | int check_pos(vec2 x, float size) { 16 | return int(mod(floor(x.x), size) + mod(floor(x.y), size)*size); 17 | } 18 | 19 | void main() 20 | { 21 | vec2 shift = vec2(floor(float(check)/downscale), mod(float(check), downscale)); 22 | 23 | vec2 uv = floor(gl_FragCoord.xy/downscale); 24 | uv = uv/(resolution/downscale); 25 | 26 | vec4 col = vec4(0.0); 27 | if (check_pos(gl_FragCoord.xy, downscale)!=check){ 28 | //reprojection from http://john-chapman-graphics.blogspot.ca/2013/01/what-is-motion-blur-motion-pictures-are.html 29 | //look into running all this on cpu 30 | //discard; 31 | vec2 uvd = gl_FragCoord.xy/resolution-vec2(0.5); 32 | uvd *= 2.0; 33 | vec4 uvdir = (vec4(uvd, 1.0, 1.0)); 34 | mat4 invmat = inverse(MVPM); 35 | vec4 worldPos = (inverse((MVPM))*uvdir); 36 | vec4 current = worldPos; 37 | vec4 previous = LFMVPM * current; 38 | previous.xyz /= previous.w; 39 | previous.xy = previous.xy * 0.5 + 0.5; 40 | vec2 blurVec = previous.xy - uv.xy; 41 | vec2 lookup = uv.xy+blurVec; 42 | float mip = 0.0; 43 | if (lookup.x<0.0||lookup.x>1.0||lookup.y<0.0||lookup.y>1.0) { 44 | col = texture(buff, uv.xy); 45 | } else { 46 | uv = gl_FragCoord.xy/resolution; 47 | col = texture(pong, lookup); 48 | } 49 | } else { 50 | col = texture(buff, uv.xy); 51 | } 52 | color.xyz = col.xyz; 53 | color.a = 1.0; 54 | } 55 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Realtime Clouds 2 | Experiment with generating clouds in real time on low end computer 3 | 4 | ## About 5 | This is an experiment in recreating procedural clouds that are similar in quality to the ones in Horizon Zero Dawn 6 | The core algorithm is very similar to what is described in the various articles on the clouds in Horizen Zero Dawn 7 | 8 | As of the writing of this the program runs between ~15-50ms/frame on my chromebook (2.4-GHz Intel Bay Trail-M dual-core N2830) 9 | And on my desktop computer it runs ~0.5-2ms/frame (Nvidia GTX 1050) 10 | 11 | This project is far from finished, it will still undergo various optimizations and tweaks as well as added functionality 12 | The goal is to make this into something that can be used in other projects 13 | 14 | ### Screenshots! (as of 07/12/17) 15 | ![dec7-1](https://user-images.githubusercontent.com/16521339/33801879-362f844a-dd1e-11e7-87fb-5b13e2aefc24.png) 16 | ![dec7-2](https://user-images.githubusercontent.com/16521339/33801880-38cf507c-dd1e-11e7-9d5c-ef3ca9967a9f.png) 17 | ![dec7-3](https://user-images.githubusercontent.com/16521339/33801881-3a97b8b8-dd1e-11e7-9297-3b609dc0d42d.png) 18 | 19 | 20 | ## Resources 21 | I used many different resources while working on this, and I will use many more as it improves 22 | 23 | #### Horizon Zero Dawn Related 24 | [The Real Time Volumetric Cloudscapes of Horizon Zero Dawn](https://www.guerrilla-games.com/read/the-real-time-volumetric-cloudscapes-of-horizon-zero-dawn) 25 | 26 | [Gpu Pro 7 Article](https://www.crcpress.com/GPU-Pro-7-Advanced-Rendering-Techniques/Engel/p/book/9781498742535) 27 | 28 | [Nubis: Authoring Real-Time Volumetric Cloudscapes with the Decima Engine](https://www.guerrilla-games.com/read/nubis-authoring-real-time-volumetric-cloudscapes-with-the-decima-engine) 29 | 30 | 31 | #### Atmospheric Scattering 32 | Shadertoy - [Atmosphere System Test](https://www.shadertoy.com/view/XtBXDz) - valentingalea 33 | 34 | #### General 35 | Production volume rendering: [Book](https://www.amazon.ca/Production-Rendering-Implementation-Magnus-Wrenninge/dp/156881724X), [2011 Course](http://magnuswrenninge.com/productionvolumerendering), [2017 Course](https://graphics.pixar.com/library/ProductionVolumeRendering/) 36 | 37 | [Physically Based Sky, Atmosphere and Cloud Rendering in Frostbite](https://www.ea.com/frostbite/news/physically-based-sky-atmosphere-and-cloud-rendering) 38 | -------------------------------------------------------------------------------- /assets/shaders/deprecated/pree.vert: -------------------------------------------------------------------------------- 1 | #version 330 core 2 | layout (location = 0) in vec2 position; 3 | 4 | uniform vec3 sunPosition; 5 | uniform float rayleigh; 6 | uniform float turbidity; 7 | uniform float mieCoefficient; 8 | 9 | out vec3 vWorldPosition; 10 | out vec3 vSunDirection; 11 | out float vSunfade; 12 | out vec3 vBetaR; 13 | out vec3 vBetaM; 14 | out float vSunE; 15 | 16 | const vec3 up = vec3( 0.0, 1.0, 0.0 ); 17 | 18 | // constants for atmospheric scattering 19 | const float e = 2.71828182845904523536028747135266249775724709369995957; 20 | const float pi = 3.141592653589793238462643383279502884197169; 21 | 22 | // wavelength of used primaries, according to preetham 23 | const vec3 lambda = vec3( 680E-9, 550E-9, 450E-9 ); 24 | // this pre-calcuation replaces older TotalRayleigh(vec3 lambda) function: 25 | // (8.0 * pow(pi, 3.0) * pow(pow(n, 2.0) - 1.0, 2.0) * (6.0 + 3.0 * pn)) / (3.0 * N * pow(lambda, vec3(4.0)) * (6.0 - 7.0 * pn)) 26 | const vec3 totalRayleigh = vec3( 5.804542996261093E-6, 1.3562911419845635E-5, 3.0265902468824876E-5 ); 27 | 28 | // mie stuff 29 | // K coefficient for the primaries 30 | const float v = 4.0; 31 | const vec3 K = vec3( 0.686, 0.678, 0.666 ); 32 | // MieConst = pi * pow( ( 2.0 * pi ) / lambda, vec3( v - 2.0 ) ) * K 33 | const vec3 MieConst = vec3( 1.8399918514433978E14, 2.7798023919660528E14, 4.0790479543861094E14 ); 34 | 35 | // earth shadow hack 36 | // cutoffAngle = pi / 1.95; 37 | const float cutoffAngle = 1.6110731556870734; 38 | const float steepness = 1.5; 39 | const float EE = 1000.0; 40 | 41 | float sunIntensity( float zenithAngleCos ) { 42 | zenithAngleCos = clamp( zenithAngleCos, -1.0, 1.0 ); 43 | return EE * max( 0.0, 1.0 - pow( e, -( ( cutoffAngle - acos( zenithAngleCos ) ) / steepness ) ) ); 44 | } 45 | 46 | vec3 totalMie( float T ) { 47 | float c = ( 0.2 * T ) * 10E-18; 48 | return 0.434 * c * MieConst; 49 | } 50 | 51 | void main() { 52 | 53 | vec4 worldPosition = vec4( position, -1.0, 1.0 ); 54 | vWorldPosition = worldPosition.xyz; 55 | 56 | gl_Position = vec4( position, -1.0, 1.0 ); 57 | 58 | vSunDirection = normalize( sunPosition ); 59 | 60 | vSunE = sunIntensity( dot( vSunDirection, up ) ); 61 | 62 | vSunfade = 1.0 - clamp( 1.0 - exp( ( sunPosition.y / 450000.0 ) ), 0.0, 1.0 ); 63 | 64 | float rayleighCoefficient = rayleigh - ( 1.0 * ( 1.0 - vSunfade ) ); 65 | 66 | // extinction (absorbtion + out scattering) 67 | // rayleigh coefficients 68 | vBetaR = totalRayleigh * rayleighCoefficient; 69 | 70 | // mie coefficients 71 | vBetaM = totalMie( turbidity ) * mieCoefficient; 72 | 73 | } 74 | 75 | -------------------------------------------------------------------------------- /assets/shaders/deprecated/pree.frag: -------------------------------------------------------------------------------- 1 | #version 330 core 2 | 3 | out vec4 Color; 4 | 5 | in vec3 vSunDirection; 6 | in float vSunfade; 7 | in vec3 vBetaR; 8 | in vec3 vBetaM; 9 | in float vSunE; 10 | 11 | 12 | uniform float luminance; 13 | uniform float mieDirectionalG; 14 | uniform mat4 MVPM; 15 | 16 | const vec3 cameraPos = vec3( 0.0, 0.0, 0.0 ); 17 | 18 | // constants for atmospheric scattering 19 | const float pi = 3.141592653589793238462643383279502884197169; 20 | 21 | const float n = 1.0003; // refractive index of air 22 | const float N = 2.545E25; // number of molecules per unit volume for air at 23 | // 288.15K and 1013mb (sea level -45 celsius) 24 | 25 | // optical length at zenith for molecules 26 | const float rayleighZenithLength = 8.4E3; 27 | const float mieZenithLength = 1.25E3; 28 | const vec3 up = vec3( 0.0, 1.0, 0.0 ); 29 | // 66 arc seconds -> degrees, and the cosine of that 30 | const float sunAngularDiameterCos = 0.999956676946448443553574619906976478926848692873900859324; 31 | 32 | // 3.0 / ( 16.0 * pi ) 33 | const float THREE_OVER_SIXTEENPI = 0.05968310365946075; 34 | // 1.0 / ( 4.0 * pi ) 35 | const float ONE_OVER_FOURPI = 0.07957747154594767; 36 | 37 | float rayleighPhase( float cosTheta ) { 38 | return THREE_OVER_SIXTEENPI * ( 1.0 + pow( cosTheta, 2.0 ) ); 39 | } 40 | 41 | float hgPhase( float cosTheta, float g ) { 42 | float g2 = pow( g, 2.0 ); 43 | float inv = 1.0 / pow( 1.0 - 2.0 * g * cosTheta + g2, 1.5 ); 44 | return ONE_OVER_FOURPI * ( ( 1.0 - g2 ) * inv ); 45 | } 46 | 47 | // Filmic ToneMapping http://filmicgames.com/archives/75 48 | const float A = 0.15; 49 | const float B = 0.50; 50 | const float C = 0.10; 51 | const float D = 0.20; 52 | const float E = 0.02; 53 | const float F = 0.30; 54 | 55 | const float whiteScale = 1.0748724675633854; // 1.0 / Uncharted2Tonemap(1000.0) 56 | 57 | vec3 Uncharted2Tonemap( vec3 x ) { 58 | return ( ( x * ( A * x + C * B ) + D * E ) / ( x * ( A * x + B ) + D * F ) ) - E / F; 59 | } 60 | 61 | 62 | void main() { 63 | 64 | vec2 puv = (gl_FragCoord.xy)/(512.0); 65 | puv = puv-vec2(0.5); 66 | puv *= 2.0; 67 | vec4 uvdir = (vec4(puv.xy, 1.0, 1.0)); 68 | vec4 worldPos = (inverse((MVPM))*uvdir); 69 | vec3 dir = normalize(worldPos.xyz/worldPos.w); 70 | vec3 vWorldPosition = dir; 71 | 72 | 73 | 74 | // optical length 75 | // cutoff angle at 90 to avoid singularity in next formula. 76 | float zenithAngle = acos( max( 0.0, dot( up, normalize( vWorldPosition - cameraPos ) ) ) ); 77 | float inverse = 1.0 / ( cos( zenithAngle ) + 0.15 * pow( 93.885 - ( ( zenithAngle * 180.0 ) / pi ), -1.253 ) ); 78 | float sR = rayleighZenithLength * inverse; 79 | float sM = mieZenithLength * inverse; 80 | 81 | // combined extinction factor 82 | vec3 Fex = exp( -( vBetaR * sR + vBetaM * sM ) ); 83 | 84 | // in scattering 85 | float cosTheta = dot( normalize( vWorldPosition - cameraPos ), vSunDirection ); 86 | 87 | float rPhase = rayleighPhase( cosTheta * 0.5 + 0.5 ); 88 | vec3 betaRTheta = vBetaR * rPhase; 89 | 90 | float mPhase = hgPhase( cosTheta, mieDirectionalG ); 91 | vec3 betaMTheta = vBetaM * mPhase; 92 | 93 | vec3 Lin = pow( vSunE * ( ( betaRTheta + betaMTheta ) / ( vBetaR + vBetaM ) ) * ( 1.0 - Fex ), vec3( 1.5 ) ); 94 | Lin *= mix( vec3( 1.0 ), pow( vSunE * ( ( betaRTheta + betaMTheta ) / ( vBetaR + vBetaM ) ) * Fex, vec3( 1.0 / 2.0 ) ), clamp( pow( 1.0 - dot( up, vSunDirection ), 5.0 ), 0.0, 1.0 ) ); 95 | 96 | // nightsky 97 | vec3 direction = normalize( vWorldPosition - cameraPos ); 98 | float theta = acos( direction.y ); // elevation --> y-axis, [-pi/2, pi/2] 99 | float phi = atan( direction.z, direction.x ); // azimuth --> x-axis [-pi/2, pi/2] 100 | vec2 uv = vec2( phi, theta ) / vec2( 2.0 * pi, pi ) + vec2( 0.5, 0.0 ); 101 | vec3 L0 = vec3( 0.1 ) * Fex; 102 | 103 | // composition + solar disc 104 | float sundisk = smoothstep( sunAngularDiameterCos, sunAngularDiameterCos + 0.00002, cosTheta ); 105 | L0 += ( vSunE * 19000.0 * Fex ) * sundisk; 106 | 107 | vec3 texColor = ( Lin + L0 ) * 0.04 + vec3( 0.0, 0.0003, 0.00075 ); 108 | 109 | vec3 curr = Uncharted2Tonemap( ( log2( 2.0 / pow( luminance, 4.0 ) ) ) * texColor ); 110 | vec3 color = curr * whiteScale; 111 | 112 | vec3 retColor = pow( color, vec3( 1.0 / ( 1.2 + ( 1.2 * vSunfade ) ) ) ); 113 | 114 | Color = vec4( retColor, 1.0 ); 115 | } 116 | -------------------------------------------------------------------------------- /gen_noise.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define STB_IMAGE_WRITE_IMPLEMENTATION 6 | 7 | #include "include/glm/glm.hpp" 8 | #include "include/TileableVolumeNoise/TileableVolumeNoise.h" 9 | #include "include/stb_image_write.h" 10 | 11 | //TODO make a repeating 2d cirrus noise texture 12 | //TODO Clean this up for github 13 | //TODO Make a repeating curl noise implementation 14 | 15 | /* 16 | * g++ include/TileableVolumeNoise/tileableVolumeNoise.cpp gen_noise.cpp -o gen_noise -std=c++11 17 | */ 18 | 19 | // the remap function used in the shaders as described in Gpu Pro 7. It must match when using pre packed textures 20 | float remap(float originalValue, float originalMin, float originalMax, float newMin, float newMax) 21 | { 22 | return newMin + (((originalValue - originalMin) / (originalMax - originalMin)) * (newMax - newMin)); 23 | } 24 | 25 | float smoothstep(float edge0, float edge1, float x) { 26 | float t = std::min(std::max((x - edge0) / (edge1 - edge0), 0.0f), 1.0f); 27 | return t * t * (3.0 - 2.0 * t); 28 | } 29 | 30 | int main() { 31 | //initialize perlin noise arrays for textures 32 | std::cout<<"Generating Perlin Noise for LUT's"< worleyNoise4 is just noise due to sampling frequency=texel freque. So only take into account 2 frequenciM 79 | worlNoiseArray[i] = char(cellFBM0*255); 80 | worlNoiseArray[i+1] = char(cellFBM1*255); 81 | worlNoiseArray[i+2] = char(cellFBM2*255); 82 | } 83 | stbi_write_bmp("assets/worlnoise.bmp", 32*32, 32, 3, worlNoiseArray); 84 | delete worlNoiseArray; 85 | */ 86 | /* 87 | std::cout<<"Generating Perlin-Worley Noise 128x128x128 RGBA"< worleyNoise5 is just noise due to sampling frequency=texel frequency. So only take into account 2 frequencies for FBM 119 | 120 | 121 | perlWorlNoiseArray[i] = char(PerlWorlNoise*255); 122 | perlWorlNoiseArray[i+1] = char(worleyFBM0*255); 123 | perlWorlNoiseArray[i+2] = char(worleyFBM1*255); 124 | perlWorlNoiseArray[i+3] = char(worleyFBM2*255); 125 | } 126 | stbi_write_tga("assets/perlworlnoise.tga", 128*128, 128, 4, perlWorlNoiseArray); 127 | delete perlWorlNoiseArray; 128 | */ 129 | 130 | return 0; 131 | } 132 | -------------------------------------------------------------------------------- /assets/shaders/deprecated/atmo.frag: -------------------------------------------------------------------------------- 1 | #version 330 core 2 | 3 | uniform vec2 resolution; 4 | uniform float time; 5 | uniform mat4 MVPM; 6 | 7 | out vec4 color; 8 | 9 | /* 10 | Atmospheric scattering based off of: 11 | */ 12 | 13 | #define PI 3.14159265359 14 | #define BIAS 1e-4 // small offset to avoid self-intersections 15 | 16 | // scattering coefficients at sea level (m) 17 | const vec3 betaR = vec3(5.5e-6, 13.0e-6, 22.4e-6); // Rayleigh 18 | const vec3 betaM = vec3(21e-6); // Mie 19 | 20 | // scale height (m) 21 | // thickness of the atmosphere if its density were uniform 22 | const float hR = 7994.0; // Rayleigh 23 | const float hM = 1200.0; // Mie 24 | 25 | const float earth_radius = 6360e3; // (m) 26 | const float atmosphere_radius = 6420e3; // (m) 27 | 28 | vec3 sun_dir = vec3(0, 1, 0); 29 | const float sun_power = 30.0; 30 | 31 | 32 | const int num_samples = 16; 33 | const int num_samples_light = 8; 34 | 35 | 36 | 37 | struct ray_t { 38 | vec3 origin; 39 | vec3 direction; 40 | }; 41 | 42 | struct sphere_t { 43 | vec3 origin; 44 | float radius; 45 | }; 46 | 47 | const sphere_t atmosphere = sphere_t(vec3(0, 0, 0), atmosphere_radius); 48 | 49 | 50 | mat3 rotate_around_x(const in float angle_degrees) 51 | { 52 | float angle = radians(angle_degrees); 53 | float _sin = sin(angle); 54 | float _cos = cos(angle); 55 | return mat3(1, 0, 0, 0, _cos, -_sin, 0, _sin, _cos); 56 | } 57 | 58 | bool isect_sphere(const in ray_t ray, const in sphere_t sphere, inout float t0, inout float t1) 59 | { 60 | vec3 rc = sphere.origin - ray.origin; 61 | float radius2 = sphere.radius * sphere.radius; 62 | float tca = dot(rc, ray.direction); 63 | float d2 = dot(rc, rc) - tca * tca; 64 | if (d2 > radius2) return false; 65 | float thc = sqrt(radius2 - d2); 66 | t0 = tca - thc; 67 | t1 = tca + thc; 68 | 69 | return true; 70 | } 71 | 72 | float rayleigh_phase_func(float mu) 73 | { 74 | return 75 | 3. * (1. + mu*mu) 76 | / //------------------------ 77 | (16. * PI); 78 | } 79 | 80 | // Henyey-Greenstein phase function factor [-1, 1] 81 | // represents the average cosine of the scattered directions 82 | // 0 is isotropic scattering 83 | // > 1 is forward scattering, < 1 is backwards 84 | float henyey_greenstein_phase_func(float mu) 85 | { 86 | const float g = 0.76; 87 | return 88 | (1. - g*g) 89 | / //--------------------------------------------- 90 | ((4. + PI) * pow(1. + g*g - 2.*g*mu, 1.5)); 91 | } 92 | 93 | bool get_sun_light( 94 | const in ray_t ray, 95 | inout float optical_depthR, 96 | inout float optical_depthM 97 | ){ 98 | float t0 = 0.0; 99 | float t1 = 0.0; 100 | isect_sphere(ray, atmosphere, t0, t1); 101 | 102 | float march_pos = 0.; 103 | float march_step = t1 / float(num_samples_light); 104 | 105 | for (int i = 0; i < num_samples_light; i++) { 106 | vec3 s = 107 | ray.origin + 108 | ray.direction * (march_pos + 0.5 * march_step); 109 | float height = length(s) - earth_radius; 110 | if (height < 0.) 111 | return false; 112 | 113 | optical_depthR += exp(-height / hR) * march_step; 114 | optical_depthM += exp(-height / hM) * march_step; 115 | 116 | march_pos += march_step; 117 | } 118 | 119 | return true; 120 | } 121 | 122 | vec3 get_incident_light(const in ray_t ray) 123 | { 124 | // "pierce" the atmosphere with the viewing ray 125 | float t0 = 0.0; 126 | float t1 = 0.0; 127 | if (!isect_sphere( 128 | ray, atmosphere, t0, t1)) { 129 | return vec3(0); 130 | } 131 | 132 | float march_step = t1 / float(num_samples); 133 | 134 | // cosine of angle between view and light directions 135 | float mu = dot(ray.direction, sun_dir); 136 | 137 | // Rayleigh and Mie phase functions 138 | // A black box indicating how light is interacting with the material 139 | // Similar to BRDF except 140 | // * it usually considers a single angle 141 | // (the phase angle between 2 directions) 142 | // * integrates to 1 over the entire sphere of directions 143 | float phaseR = rayleigh_phase_func(mu); 144 | float phaseM = 145 | henyey_greenstein_phase_func(mu); 146 | 147 | // optical depth (or "average density") 148 | // represents the accumulated extinction coefficients 149 | // along the path, multiplied by the length of that path 150 | float optical_depthR = 0.; 151 | float optical_depthM = 0.; 152 | 153 | vec3 sumR = vec3(0); 154 | vec3 sumM = vec3(0); 155 | float march_pos = 0.; 156 | 157 | for (int i = 0; i < num_samples; i++) { 158 | vec3 s = 159 | ray.origin + 160 | ray.direction * (march_pos + 0.5 * march_step); 161 | float height = length(s) - earth_radius; 162 | 163 | // integrate the height scale 164 | float hr = exp(-height / hR) * march_step; 165 | float hm = exp(-height / hM) * march_step; 166 | optical_depthR += hr; 167 | optical_depthM += hm; 168 | 169 | // gather the sunlight 170 | ray_t light_ray = ray_t(s, sun_dir); 171 | float optical_depth_lightR = 0.; 172 | float optical_depth_lightM = 0.; 173 | bool overground = get_sun_light( 174 | light_ray, 175 | optical_depth_lightR, 176 | optical_depth_lightM); 177 | 178 | if (overground) { 179 | vec3 tau = 180 | betaR * (optical_depthR + optical_depth_lightR) + 181 | betaM * 1.1 * (optical_depthM + optical_depth_lightM); 182 | vec3 attenuation = exp(-tau); 183 | 184 | sumR += hr * attenuation; 185 | sumM += hm * attenuation; 186 | } 187 | 188 | march_pos += march_step; 189 | } 190 | 191 | return 192 | sun_power * 193 | (sumR * phaseR * betaR + 194 | sumM * phaseM * betaM); 195 | } 196 | 197 | vec3 U2Tone(vec3 x) { 198 | const float A = 0.15; 199 | const float B = 0.50; 200 | const float C = 0.10; 201 | const float D = 0.20; 202 | const float E = 0.02; 203 | const float F = 0.30; 204 | 205 | return ((x*(A*x+C*B)+D*E)/(x*(A*x+B)+D*F))-E/F; 206 | } 207 | 208 | void main() 209 | { 210 | vec2 aspect_ratio = vec2(resolution.x / resolution.y, 1); 211 | vec2 point_ndc = gl_FragCoord.xy / resolution.xy; 212 | vec3 point_cam = vec3((2.0 * point_ndc - 1.0) * aspect_ratio, 1.0); 213 | 214 | vec4 worldPos = (inverse((MVPM))*vec4(point_cam, 1.0)); 215 | worldPos.xyz /= worldPos.w; 216 | point_cam = normalize(worldPos.xyz); 217 | 218 | vec3 col = vec3(0.4); 219 | if (point_cam.y>-0.05) { 220 | // sun 221 | mat3 rot = rotate_around_x(-abs(sin(time / 20.)) * 90.); 222 | sun_dir *= rot; 223 | 224 | ray_t ray = ray_t(vec3(0.0, earth_radius+1.0, 0.0), point_cam); 225 | col = get_incident_light(ray); 226 | } 227 | col = U2Tone(col); 228 | col /= U2Tone(vec3(2.5)); 229 | col = sqrt(col); 230 | color = vec4(col, 1); 231 | } 232 | -------------------------------------------------------------------------------- /assets/shaders/sky.vert: -------------------------------------------------------------------------------- 1 | #version 330 core 2 | layout (location = 0) in vec2 position; 3 | 4 | uniform vec3 sunPosition; 5 | const float rayleigh = 2.0; 6 | const float turbidity = 10.0; 7 | const float mieCoefficient = 0.005; 8 | 9 | out vec3 vSunDirection; 10 | out float vSunfade; 11 | out vec3 vBetaR; 12 | out vec3 vBetaM; 13 | out float vSunE; 14 | out vec3 vSunColor; 15 | out vec3 vAmbient; 16 | 17 | 18 | /* 19 | Atmospheric scattering based off of: https://www.shadertoy.com/view/XtBXDz 20 | Author: valentingalea 21 | */ 22 | 23 | #define PI 3.14159265359 24 | 25 | // scattering coefficients at sea level (m) 26 | const vec3 betaR = vec3(5.5e-6, 13.0e-6, 22.4e-6); // Rayleigh 27 | const vec3 betaM = vec3(21e-6); // Mie 28 | 29 | // scale height (m) 30 | // thickness of the atmosphere if its density were uniform 31 | const float hR = 7994.0; // Rayleigh 32 | const float hM = 1200.0; // Mie 33 | 34 | const float earth_radius = 6360e3; // (m) 35 | const float atmosphere_radius = 6420e3; // (m) 36 | 37 | const float sun_power = 30.0; 38 | 39 | //These numbers can be lowered to acheive faster speed, but they are good enough here 40 | const int num_samples = 16; 41 | const int num_samples_light = 8; 42 | 43 | struct ray_t { 44 | vec3 origin; 45 | vec3 direction; 46 | }; 47 | 48 | bool isect_sphere(const in ray_t ray, inout float t0, inout float t1) 49 | { 50 | vec3 rc = vec3(0.0) - ray.origin; 51 | float radius2 = atmosphere_radius * atmosphere_radius; 52 | float tca = dot(rc, ray.direction); 53 | float d2 = dot(rc, rc) - tca * tca; 54 | if (d2 > radius2) return false; 55 | float thc = sqrt(radius2 - d2); 56 | t0 = tca - thc; 57 | t1 = tca + thc; 58 | 59 | return true; 60 | } 61 | 62 | float rayleigh_phase_func(float mu) 63 | { 64 | return 65 | 3. * (1. + mu*mu) 66 | / //------------------------ 67 | (16. * PI); 68 | } 69 | 70 | float HG(float costheta, float g) { 71 | const float k = 0.0795774715459; 72 | return k*(1.0-g*g)/(pow(1.0+g*g-2.0*g*costheta, 1.5)); 73 | } 74 | 75 | bool get_sun_light( 76 | const in ray_t ray, 77 | inout float optical_depthR, 78 | inout float optical_depthM 79 | ){ 80 | float t0 = 0.0; 81 | float t1 = 0.0; 82 | isect_sphere(ray, t0, t1); 83 | 84 | float march_pos = 0.; 85 | float march_step = t1 / float(num_samples_light); 86 | 87 | for (int i = 0; i < num_samples_light; i++) { 88 | vec3 s = 89 | ray.origin + 90 | ray.direction * (march_pos + 0.5 * march_step); 91 | float height = length(s) - earth_radius; 92 | if (height < 0.0) 93 | return false; 94 | 95 | optical_depthR += exp(-height / hR) * march_step; 96 | optical_depthM += exp(-height / hM) * march_step; 97 | 98 | march_pos += march_step; 99 | } 100 | 101 | return true; 102 | } 103 | 104 | vec3 get_incident_light(const in ray_t ray) 105 | { 106 | // "pierce" the atmosphere with the viewing ray 107 | float t0 = 0.0; 108 | float t1 = 0.0; 109 | if (!isect_sphere( 110 | ray, t0, t1)) { 111 | return vec3(0); 112 | } 113 | 114 | float march_step = t1 / float(num_samples); 115 | 116 | vec3 sun_dir = normalize(sunPosition); 117 | // cosine of angle between view and light directions 118 | float mu = dot(ray.direction, sun_dir); 119 | 120 | // Rayleigh and Mie phase functions 121 | float phaseR = rayleigh_phase_func(mu); 122 | float phaseM = HG(mu, 0.96);//0.76 more proper but this looks nice 123 | 124 | // optical depth (or "average density") 125 | // represents the accumulated extinction coefficients 126 | // along the path, multiplied by the length of that path 127 | float optical_depthR = 0.; 128 | float optical_depthM = 0.; 129 | 130 | vec3 sumR = vec3(0); 131 | vec3 sumM = vec3(0); 132 | float march_pos = 0.; 133 | 134 | for (int i = 0; i < num_samples; i++) { 135 | vec3 s = 136 | ray.origin + 137 | ray.direction * (march_pos + 0.5 * march_step); 138 | float height = length(s) - earth_radius; 139 | 140 | // integrate the height scale 141 | float hr = exp(-height / hR) * march_step; 142 | float hm = exp(-height / hM) * march_step; 143 | optical_depthR += hr; 144 | optical_depthM += hm; 145 | 146 | // gather the sunlight 147 | ray_t light_ray = ray_t(s, sun_dir); 148 | float optical_depth_lightR = 0.; 149 | float optical_depth_lightM = 0.; 150 | bool overground = get_sun_light( 151 | light_ray, 152 | optical_depth_lightR, 153 | optical_depth_lightM); 154 | 155 | if (overground) { 156 | vec3 tau = 157 | betaR * (optical_depthR + optical_depth_lightR) + 158 | betaM * 1.1 * (optical_depthM + optical_depth_lightM); 159 | vec3 attenuation = exp(-tau); 160 | 161 | sumR += hr * attenuation; 162 | sumM += hm * attenuation; 163 | } 164 | 165 | march_pos += march_step; 166 | } 167 | 168 | return 169 | sun_power * 170 | (sumR * phaseR * betaR + 171 | sumM * phaseM * betaM); 172 | } 173 | 174 | /* 175 | Mostly Preetham model stuff here 176 | */ 177 | 178 | const vec3 up = vec3( 0.0, 1.0, 0.0 ); 179 | 180 | // constants for atmospheric scattering 181 | const float e = 2.71828182845904523536028747135266249775724709369995957; 182 | const float pi = 3.141592653589793238462643383279502884197169; 183 | 184 | // wavelength of used primaries, according to preetham 185 | const vec3 lambda = vec3( 680E-9, 550E-9, 450E-9 ); 186 | // this pre-calcuation replaces older TotalRayleigh(vec3 lambda) function: 187 | // (8.0 * pow(pi, 3.0) * pow(pow(n, 2.0) - 1.0, 2.0) * (6.0 + 3.0 * pn)) / (3.0 * N * pow(lambda, vec3(4.0)) * (6.0 - 7.0 * pn)) 188 | const vec3 totalRayleigh = vec3( 5.804542996261093E-6, 1.3562911419845635E-5, 3.0265902468824876E-5 ); 189 | 190 | // mie stuff 191 | // K coefficient for the primaries 192 | const float v = 4.0; 193 | const vec3 K = vec3( 0.686, 0.678, 0.666 ); 194 | // MieConst = pi * pow( ( 2.0 * pi ) / lambda, vec3( v - 2.0 ) ) * K 195 | const vec3 MieConst = vec3( 1.8399918514433978E14, 2.7798023919660528E14, 4.0790479543861094E14 ); 196 | 197 | // earth shadow hack 198 | // cutoffAngle = pi / 1.95; 199 | const float cutoffAngle = 1.6110731556870734; 200 | const float steepness = 1.5; 201 | const float EE = 1000.0; 202 | 203 | float sunIntensity( float zenithAngleCos ) { 204 | zenithAngleCos = clamp( zenithAngleCos, -1.0, 1.0 ); 205 | return EE * max( 0.0, 1.0 - pow( e, -( ( cutoffAngle - acos( zenithAngleCos ) ) / steepness ) ) ); 206 | } 207 | 208 | vec3 totalMie( float T ) { 209 | float c = ( 0.2 * T ) * 10E-18; 210 | return 0.434 * c * MieConst; 211 | } 212 | 213 | void main() { 214 | gl_Position = vec4( position, -1.0, 1.0 ); 215 | 216 | vSunDirection = normalize( sunPosition ); 217 | 218 | vSunE = sunIntensity( dot( vSunDirection, up ) ); 219 | 220 | vSunfade = 1.0 - clamp( 1.0 - exp( ( sunPosition.y / 450000.0 ) ), 0.0, 1.0 ); 221 | 222 | float rayleighCoefficient = rayleigh - ( 1.0 * ( 1.0 - vSunfade ) ); 223 | // extinction (absorbtion + out scattering) 224 | // rayleigh coefficients 225 | vBetaR = totalRayleigh * rayleighCoefficient; 226 | // mie coefficients 227 | vBetaM = totalMie( turbidity ) * mieCoefficient; 228 | 229 | ray_t ray = ray_t(vec3(0.0, earth_radius+1.0, 0.0), normalize(vSunDirection+vec3(0.01, 0.01, 0.0))); 230 | vSunColor = get_incident_light(ray); 231 | 232 | ray = ray_t(vec3(0.0, earth_radius+1.0, 0.0), normalize(vec3(0.4, 0.1, 0.0))); 233 | vAmbient = get_incident_light(ray); 234 | } 235 | 236 | -------------------------------------------------------------------------------- /assets/shaders/sky.frag: -------------------------------------------------------------------------------- 1 | #version 330 core 2 | //TODO update cloud shaping 3 | //TODO fix up weather texture 4 | //TODO add more flexibility to parameters 5 | // rain/coverage 6 | // earth radius 7 | // sky color //maybe LUT // or just vec3 for coefficients 8 | //TODO add 2d cloud layer on top 9 | 10 | uniform sampler3D perlworl; 11 | uniform sampler3D worl; 12 | uniform sampler2D curl; 13 | uniform sampler2D weather; 14 | 15 | uniform int check; 16 | uniform mat4 MVPM; 17 | uniform float aspect; 18 | uniform float time; 19 | uniform vec2 resolution; 20 | uniform float downscale; 21 | 22 | //preetham variables 23 | in vec3 vSunDirection; 24 | in float vSunfade; 25 | in vec3 vBetaR; 26 | in vec3 vBetaM; 27 | in float vSunE; 28 | in vec3 vAmbient; 29 | in vec3 vSunColor; 30 | 31 | out vec4 color; 32 | 33 | //I like the look of the small sky, but could be tweaked however 34 | const float g_radius = 200000.0; //ground radius 35 | const float sky_b_radius = 201000.0;//bottom of cloud layer 36 | const float sky_t_radius = 202300.0;//top of cloud layer 37 | const float c_radius = 6008400.0; //2d noise layer 38 | 39 | const float cwhiteScale = 1.1575370919881305;//precomputed 1/U2Tone(40) 40 | 41 | /* 42 | SHARED FUNCTIONS 43 | */ 44 | 45 | vec3 U2Tone(const vec3 x) { 46 | const float A = 0.15; 47 | const float B = 0.50; 48 | const float C = 0.10; 49 | const float D = 0.20; 50 | const float E = 0.02; 51 | const float F = 0.30; 52 | 53 | return ((x*(A*x+C*B)+D*E)/(x*(A*x+B)+D*F))-E/F; 54 | } 55 | 56 | 57 | float HG(float costheta, float g) { 58 | const float k = 0.0795774715459; 59 | return k*(1.0-g*g)/(pow(1.0+g*g-2.0*g*costheta, 1.5)); 60 | } 61 | 62 | 63 | /* 64 | stars from a different project 65 | */ 66 | 67 | uniform vec3 moonpos = vec3(0.3, 0.3, 0.0); 68 | 69 | 70 | float SimplexPolkaDot3D( vec3 P, float density ) // the maximal dimness of a dot ( 0.0->1.0 0.0 = all dots bright, 1.0 = maximum variation ) 71 | { 72 | // calculate the simplex vector and index math 73 | vec3 Pi; 74 | vec3 Pi_1; 75 | vec3 Pi_2; 76 | vec4 v1234_x; 77 | vec4 v1234_y; 78 | vec4 v1234_z; 79 | 80 | vec3 Pn = P; 81 | // simplex math constants 82 | float SKEWFACTOR = 1.0/3.0; 83 | float UNSKEWFACTOR = 1.0/6.0; 84 | float SIMPLEX_CORNER_POS = 0.5; 85 | float SIMPLEX_PYRAMID_HEIGHT = 0.70710678118654752440084436210485; // sqrt( 0.5 ) height of simplex pyramid. 86 | 87 | Pn *= SIMPLEX_PYRAMID_HEIGHT; // scale space so we can have an approx feature size of 1.0 ( optional ) 88 | 89 | // Find the vectors to the corners of our simplex pyramid 90 | Pi = floor( Pn + vec3(dot( Pn, vec3( SKEWFACTOR) ) )); 91 | vec3 x0 = Pn - Pi + vec3(dot(Pi, vec3( UNSKEWFACTOR ) )); 92 | vec3 g = step(x0.yzx, x0.xyz); 93 | vec3 l = vec3(1.0) - g; 94 | Pi_1 = min( g.xyz, l.zxy ); 95 | Pi_2 = max( g.xyz, l.zxy ); 96 | vec3 x1 = x0 - Pi_1 + vec3(UNSKEWFACTOR); 97 | vec3 x2 = x0 - Pi_2 + vec3(SKEWFACTOR); 98 | vec3 x3 = x0 - vec3(SIMPLEX_CORNER_POS); 99 | 100 | // pack them into a parallel-friendly arrangement 101 | v1234_x = vec4( x0.x, x1.x, x2.x, x3.x ); 102 | v1234_y = vec4( x0.y, x1.y, x2.y, x3.y ); 103 | v1234_z = vec4( x0.z, x1.z, x2.z, x3.z ); 104 | 105 | vec3 gridcell = Pi; 106 | vec3 v1_mask = Pi_1; 107 | vec3 v2_mask = Pi_2; 108 | 109 | vec2 OFFSET = vec2( 50.0, 161.0 ); 110 | float DOMAIN = 69.0; 111 | float SOMELARGEFLOAT = 6351.29681; 112 | float ZINC = 487.500388; 113 | 114 | // truncate the domain 115 | gridcell.xyz = gridcell.xyz - floor(gridcell.xyz * ( 1.0 / DOMAIN )) * DOMAIN; 116 | vec3 gridcell_inc1 = step( gridcell, vec3( DOMAIN - 1.5 ) ) * ( gridcell + vec3(1.0) ); 117 | 118 | // compute x*x*y*y for the 4 corners 119 | vec4 Pp = vec4( gridcell.xy, gridcell_inc1.xy ) + vec4(OFFSET.xy, OFFSET.xy); 120 | Pp *= Pp; 121 | vec4 V1xy_V2xy = mix( vec4(Pp.xy, Pp.xy), vec4(Pp.zw, Pp.zw), vec4( v1_mask.xy, v2_mask.xy ) ); // apply mask for v1 and v2 122 | Pp = vec4( Pp.x, V1xy_V2xy.x, V1xy_V2xy.z, Pp.z ) * vec4( Pp.y, V1xy_V2xy.y, V1xy_V2xy.w, Pp.w ); 123 | 124 | vec2 V1z_V2z = vec2(gridcell_inc1.z); 125 | if (v1_mask.z <0.5) { 126 | V1z_V2z.x = gridcell.z; 127 | } 128 | if (v2_mask.z <0.5) { 129 | V1z_V2z.y = gridcell.z; 130 | } 131 | vec4 temp = vec4(SOMELARGEFLOAT) + vec4( gridcell.z, V1z_V2z.x, V1z_V2z.y, gridcell_inc1.z ) * ZINC; 132 | vec4 mod_vals = vec4(1.0) / ( temp ); 133 | 134 | // compute the final hash 135 | vec4 hash = fract( Pp * mod_vals ); 136 | 137 | 138 | // apply user controls 139 | float INV_SIMPLEX_TRI_HALF_EDGELEN = 2.3094010767585030580365951220078; // scale to a 0.0->1.0 range. 2.0 / sqrt( 0.75 ) 140 | float radius = INV_SIMPLEX_TRI_HALF_EDGELEN;///(1.15-density); 141 | v1234_x *= radius; 142 | v1234_y *= radius; 143 | v1234_z *= radius; 144 | 145 | // return a smooth falloff from the closest point. ( we use a f(x)=(1.0-x*x)^3 falloff ) 146 | vec4 point_distance = max( vec4( 0.0 ), vec4(1.0) - ( v1234_x*v1234_x + v1234_y*v1234_y + v1234_z*v1234_z ) ); 147 | point_distance = point_distance*point_distance*point_distance; 148 | vec4 b = (vec4(density)-hash)*(1.0/density); 149 | b = max(vec4(0.0), b); 150 | b = min(vec4(1.0), b); 151 | b = pow(b, vec4(1.0/density)); 152 | return dot(b, point_distance); 153 | } 154 | 155 | vec3 stars(vec3 ndir) { 156 | 157 | vec3 COLOR = vec3(0.0); 158 | float star = SimplexPolkaDot3D(ndir*100.0, 0.15)+SimplexPolkaDot3D(ndir*150.0, 0.25)*0.7; 159 | vec3 col = vec3(0.05, 0.07, 0.1); 160 | COLOR.rgb = col+max(0.0, (star-smoothstep(0.2, 0.95, 0.5-0.5*ndir.y))); 161 | COLOR.rgb += vec3(0.05, 0.07, 0.1)*(1.0-smoothstep(-0.1, 0.45, ndir.y)); 162 | //need to add a little bit of atmospheric effects, both for when the moon is high 163 | //and for the end when color comes into the sky 164 | //For moon halo 165 | //COLOR.rgb += smoothstep(0.9, 1.0, dot(ndir, normalize(moonpos))); 166 | //float d = length(ndir-normalize(moonpos)); 167 | //COLOR.rgb += 0.8*exp(-4.0*d)*vec3(1.1, 1.0, 0.8); 168 | //COLOR.rgb += 0.2*exp(-2.0*d); 169 | 170 | return COLOR; 171 | } 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | /* 188 | //This implementation of the preetham model is a modified: https://github.com/mrdoob/three.js/blob/master/examples/js/objects/Sky.js 189 | //written by: zz85 / https://github.com/zz85 190 | */ 191 | 192 | const float mieDirectionalG = 0.8; 193 | 194 | // constants for atmospheric scattering 195 | const float pi = 3.141592653589793238462643383279502884197169; 196 | 197 | // optical length at zenith for molecules 198 | const float rayleighZenithLength = 8.4E3; 199 | const float mieZenithLength = 1.25E3; 200 | const vec3 up = vec3( 0.0, 1.0, 0.0 ); 201 | // 66 arc seconds -> degrees, and the cosine of that 202 | const float sunAngularDiameterCos = 0.999956676946448443553574619906976478926848692873900859324; 203 | 204 | // 3.0 / ( 16.0 * pi ) 205 | const float THREE_OVER_SIXTEENPI = 0.05968310365946075; 206 | 207 | const float whiteScale = 1.0748724675633854; // 1.0 / Uncharted2Tonemap(1000.0) 208 | 209 | vec3 preetham(const vec3 vWorldPosition) { 210 | // optical length 211 | // cutoff angle at 90 to avoid singularity in next formula. 212 | float zenithAngle = acos( max( 0.0, dot( up, normalize( vWorldPosition ) ) ) ); 213 | float inv = 1.0 / ( cos( zenithAngle ) + 0.15 * pow( 93.885 - ( ( zenithAngle * 180.0 ) / pi ), -1.253 ) ); 214 | float sR = rayleighZenithLength * inv; 215 | float sM = mieZenithLength * inv; 216 | 217 | // combined extinction factor 218 | vec3 Fex = exp( -( vBetaR * sR + vBetaM * sM ) ); 219 | 220 | // in scattering 221 | float cosTheta = dot( normalize( vWorldPosition ), vSunDirection ); 222 | 223 | float rPhase = THREE_OVER_SIXTEENPI * ( 1.0 + pow( cosTheta*0.5+0.5, 2.0 ) ); 224 | vec3 betaRTheta = vBetaR * rPhase; 225 | 226 | float mPhase = HG( cosTheta, mieDirectionalG ); 227 | vec3 betaMTheta = vBetaM * mPhase; 228 | 229 | vec3 Lin = pow( vSunE * ( ( betaRTheta + betaMTheta ) / ( vBetaR + vBetaM ) ) * ( 1.0 - Fex ), vec3( 1.5 ) ); 230 | Lin *= mix( vec3( 1.0 ), pow( vSunE * ( ( betaRTheta + betaMTheta ) / ( vBetaR + vBetaM ) ) * Fex, vec3( 1.0 / 2.0 ) ), clamp( pow( 1.0 - dot( up, vSunDirection ), 5.0 ), 0.0, 1.0 ) ); 231 | 232 | vec3 L0 = vec3( 0.5 ) * Fex; 233 | 234 | // composition + solar disc 235 | float sundisk = smoothstep( sunAngularDiameterCos, sunAngularDiameterCos + 0.00002, cosTheta ); 236 | L0 += ( vSunE * 19000.0 * Fex ) * sundisk; 237 | 238 | vec3 texColor = ( Lin + L0 ) * 0.04 + vec3( 0.0, 0.0003, 0.00075 ); 239 | 240 | vec3 curr = U2Tone( texColor ); 241 | vec3 color = curr * whiteScale; 242 | 243 | vec3 retColor = pow( color, vec3( 1.0 / ( 1.2 + ( 1.2 * vSunfade ) ) ) ); 244 | 245 | return retColor; 246 | } 247 | /* 248 | =============================================================== 249 | end of atmospheric scattering 250 | */ 251 | 252 | const vec3 RANDOM_VECTORS[6] = vec3[6] 253 | ( 254 | vec3( 0.38051305f, 0.92453449f, -0.02111345f), 255 | vec3(-0.50625799f, -0.03590792f, -0.86163418f), 256 | vec3(-0.32509218f, -0.94557439f, 0.01428793f), 257 | vec3( 0.09026238f, -0.27376545f, 0.95755165f), 258 | vec3( 0.28128598f, 0.42443639f, -0.86065785f), 259 | vec3(-0.16852403f, 0.14748697f, 0.97460106f) 260 | ); 261 | 262 | // fractional value for sample position in the cloud layer 263 | float GetHeightFractionForPoint(float inPosition) 264 | { // get global fractional position in cloud zone 265 | float height_fraction = (inPosition - sky_b_radius) / (sky_t_radius - sky_b_radius); 266 | return clamp(height_fraction, 0.0, 1.0); 267 | } 268 | 269 | vec4 mixGradients(const float cloudType){ 270 | 271 | const vec4 STRATUS_GRADIENT = vec4(0.02f, 0.05f, 0.09f, 0.11f); 272 | const vec4 STRATOCUMULUS_GRADIENT = vec4(0.02f, 0.2f, 0.48f, 0.625f); 273 | const vec4 CUMULUS_GRADIENT = vec4(0.01f, 0.0625f, 0.78f, 1.0f); // these fractions would need to be altered if cumulonimbus are added to the same pass 274 | float stratus = 1.0f - clamp(cloudType * 2.0f, 0.0, 1.0); 275 | float stratocumulus = 1.0f - abs(cloudType - 0.5f) * 2.0f; 276 | float cumulus = clamp(cloudType - 0.5f, 0.0, 1.0) * 2.0f; 277 | return STRATUS_GRADIENT * stratus + STRATOCUMULUS_GRADIENT * stratocumulus + CUMULUS_GRADIENT * cumulus; 278 | } 279 | 280 | float densityHeightGradient(const float heightFrac, const float cloudType) { 281 | vec4 cloudGradient = mixGradients(cloudType); 282 | return smoothstep(cloudGradient.x, cloudGradient.y, heightFrac) - smoothstep(cloudGradient.z, cloudGradient.w, heightFrac); 283 | } 284 | 285 | float intersectSphere(const vec3 pos, const vec3 dir, const float r) { 286 | float a = dot(dir, dir); 287 | float b = 2.0 * dot(dir, pos); 288 | float c = dot(pos, pos) - (r * r); 289 | float d = sqrt((b*b) - 4.0*a*c); 290 | float p = -b - d; 291 | float p2 = -b + d; 292 | return max(p, p2)/(2.0*a); 293 | } 294 | 295 | // Utility function that maps a value from one range to another. 296 | float remap(const float originalValue, const float originalMin, const float originalMax, const float newMin, const float newMax) 297 | { 298 | return newMin + (((originalValue - originalMin) / (originalMax - originalMin)) * (newMax - newMin)); 299 | } 300 | 301 | float density(vec3 p, vec3 weather,const bool hq,const float LOD) { 302 | p.x += time*20.0; 303 | float height_fraction = GetHeightFractionForPoint(length(p)); 304 | vec4 n = textureLod(perlworl, p*0.0003, LOD); 305 | float fbm = n.g*0.625+n.b*0.25+n.a*0.125; 306 | float g = densityHeightGradient(height_fraction, 0.5); 307 | float base_cloud = remap(n.r, -(1.0-fbm), 1.0, 0.0, 1.0); 308 | float cloud_coverage = smoothstep(0.6, 1.3, weather.x); 309 | base_cloud = remap(base_cloud*g, 1.0-cloud_coverage, 1.0, 0.0, 1.0); 310 | base_cloud *= cloud_coverage; 311 | if (hq) { 312 | vec2 whisp = texture(curl, p.xy*0.0003).xy; 313 | p.xy += whisp*400.0*(1.0-height_fraction); 314 | vec3 hn = texture(worl, p*0.004, LOD-2.0).xyz; 315 | float hfbm = hn.r*0.625+hn.g*0.25+hn.b*0.125; 316 | hfbm = mix(hfbm, 1.0-hfbm, clamp(height_fraction*3.0, 0.0, 1.0)); 317 | base_cloud = remap(base_cloud, hfbm*0.2, 1.0, 0.0, 1.0); 318 | } 319 | return clamp(base_cloud, 0.0, 1.0); 320 | } 321 | 322 | vec4 march(const vec3 pos, const vec3 end, vec3 dir, const int depth) { 323 | float T = 1.0; 324 | float alpha = 0.0; 325 | vec3 p = pos; 326 | float ss = length(dir); 327 | const float t_dist = sky_t_radius-sky_b_radius; 328 | float lss = t_dist/float(depth); 329 | vec3 ldir = vSunDirection*ss; 330 | vec3 L = vec3(0.0); 331 | int count=0; 332 | float t = 1.0; 333 | float costheta = dot(normalize(ldir), normalize(dir)); 334 | float phase = max(max(HG(costheta, 0.6), HG(costheta, (0.99-1.3*normalize(ldir).y))), HG(costheta, -0.3)); 335 | for (int i=0;i0.0) { //calculate lighting, but only when we are in a non-zero density point 350 | for (int j=0;j<6;j++) { 351 | lp += (ldir+(RANDOM_VECTORS[j]*float(j+1))*lss); 352 | vec3 lweather = texture(weather, lp.xz*weather_scale).xyz; 353 | lt = density(lp, lweather, false, float(j)); 354 | cd += lt; 355 | ncd += (lt * (1.0-(cd*(1.0/(lss*6.0))))); 356 | } 357 | lp += ldir*12.0; 358 | vec3 lweather = texture(weather, lp.xz*weather_scale).xyz; 359 | lt = density(lp, lweather, false, 5.0); 360 | cd += lt; 361 | ncd += (lt * (1.0-(cd*(1.0/(lss*18.0))))); 362 | 363 | float beers = max(exp(-ld*ncd*lss), exp(-ld*0.25*ncd*lss)*0.7); 364 | float powshug = 1.0-exp(-ld*ncd*lss*2.0); 365 | 366 | vec3 ambient = 5.0*vAmbient*mix(0.15, 1.0, height_fraction); 367 | vec3 sunC = pow(vSunColor, vec3(0.75)); 368 | L += (ambient+sunC*beers*powshug*2.0*phase)*(t)*T*ss; 369 | alpha += (1.0-dt)*(1.0-alpha); 370 | } 371 | } 372 | return vec4(L, alpha); 373 | } 374 | 375 | 376 | void main() 377 | { 378 | vec2 shift = vec2(floor(float(check)/downscale), mod(float(check), downscale)); 379 | //shift = vec2(0.0); 380 | vec2 uv = (gl_FragCoord.xy*downscale+shift.yx)/(resolution); 381 | uv = uv-vec2(0.5); 382 | uv *= 2.0; 383 | uv.x *= aspect; 384 | vec4 uvdir = (vec4(uv.xy, 1.0, 1.0)); 385 | vec4 worldPos = (inverse((MVPM))*uvdir); 386 | vec3 dir = normalize(worldPos.xyz/worldPos.w); 387 | 388 | vec4 col = vec4(0.0); 389 | if (dir.y>0.0) { 390 | 391 | vec3 camPos = vec3(0.0, g_radius, 0.0); 392 | vec3 start = camPos+dir*intersectSphere(camPos, dir, sky_b_radius); 393 | vec3 end = camPos+dir*intersectSphere(camPos, dir, sky_t_radius); 394 | const float t_dist = sky_t_radius-sky_b_radius; 395 | float shelldist = (length(end-start)); 396 | vec4 volume; 397 | float steps = (mix(96.0, 54.0, dot(dir, vec3(0.0, 1.0, 0.0)))); 398 | float dmod = smoothstep(0.0, 1.0, (shelldist/t_dist)/14.0); 399 | float s_dist = mix(t_dist, t_dist*4.0, dmod)/(steps); 400 | vec3 raystep = dir*s_dist; 401 | volume = march(start, end, raystep, int(steps)); 402 | volume.xyz = U2Tone(volume.xyz)*cwhiteScale; 403 | volume.xyz = sqrt(volume.xyz); 404 | vec3 background = vec3(0.0); 405 | if (volume.a<0.99) { 406 | background = preetham(dir); 407 | background = max(background, stars(dir)); 408 | } 409 | 410 | col = vec4(background*(1.0-volume.a)+volume.xyz*volume.a, 1.0); 411 | if (volume.a>1.0) {col = vec4(1.0, 0.0, 0.0, 1.0);} 412 | } else { 413 | col = vec4(vec3(0.4), 1.0); 414 | } 415 | color = col; 416 | } 417 | 418 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | //TODO Change the weather map, all channels should be allowed to reach 0 at certain points 2 | //change the raymarch, do only weather/density samples at first then switch to noise samples 3 | 4 | #include 5 | #include 6 | 7 | // GLEW 8 | #define GLEW_STATIC 9 | #include 10 | 11 | // GLFW 12 | #include 13 | 14 | // Other includes 15 | #include "include/shader.h" 16 | #include "include/camera.h" 17 | 18 | // glm 19 | #include "glm/glm.hpp" 20 | #include "glm/gtc/matrix_transform.hpp" 21 | #include "glm/gtc/type_ptr.hpp" 22 | #define STB_IMAGE_IMPLEMENTATION 23 | #include "include/stb_image.h" 24 | 25 | 26 | void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode); 27 | void scroll_callback(GLFWwindow* window, double xoffset, double yoffset); 28 | void mouse_callback(GLFWwindow* window, double xpos, double ypos); 29 | void Do_Movement(); 30 | 31 | // Camera 32 | Camera camera(glm::vec3(0.0f, 0.0f, 3.0f)); 33 | bool keys[1024]; 34 | GLfloat lastX = 400, lastY = 300; 35 | bool firstMouse = true; 36 | glm::mat4 MVPM; 37 | glm::mat4 LFMVPM; 38 | 39 | //measuring time 40 | GLfloat deltaTime = 0.0f; 41 | GLfloat lastFrame = 0.0f; 42 | GLuint frames = 0; 43 | GLfloat timePassed = 0.0f; 44 | GLfloat startTime = 0.0f; 45 | 46 | // Window dimensions 47 | const GLuint WIDTH = 512, HEIGHT = 512; 48 | const GLuint downscale = 4; //4 is best//any more and the gains dont make up for the lag 49 | GLuint downscalesq = downscale*downscale; 50 | GLfloat ASPECT = float(WIDTH)/float(HEIGHT); 51 | 52 | // The MAIN function, from here we start the application and run the game loop 53 | int main() 54 | { 55 | glfwInit(); 56 | glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); 57 | glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); 58 | glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); 59 | glfwWindowHint(GLFW_RESIZABLE, GL_FALSE); 60 | 61 | GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "Realtime Clouds", NULL, NULL); 62 | glfwMakeContextCurrent(window); 63 | 64 | glfwSetCursorPosCallback(window, mouse_callback); 65 | glfwSetScrollCallback(window, scroll_callback); 66 | glfwSetKeyCallback(window, key_callback); 67 | 68 | glfwSetWindowPos(window, 200, 17);//so you can see frame rate 69 | glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); 70 | glfwSwapInterval(0);//turn off vsync 71 | 72 | //GLEW init 73 | glewExperimental = GL_TRUE; 74 | glewInit(); 75 | 76 | //not sure this is necessary? 77 | glViewport(0, 0, WIDTH, HEIGHT); 78 | 79 | //Shader class built on the one in learnopengl.com 80 | Shader ourShader("sky.vert", "sky.frag"); 81 | Shader postShader("tex.vert", "tex.frag"); 82 | Shader upscaleShader("upscale.vert", "upscale.frag"); 83 | 84 | GLfloat vertices[] = { 85 | -1.0f, -1.0f, 86 | -1.0f, 3.0f, 87 | 3.0f, -1.0f, 88 | }; 89 | 90 | GLuint VBO, VAO; 91 | glGenVertexArrays(1, &VAO); 92 | glGenBuffers(1, &VBO); 93 | glBindVertexArray(VAO); 94 | 95 | glBindBuffer(GL_ARRAY_BUFFER, VBO); 96 | glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); 97 | 98 | glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), (GLvoid*)0); 99 | glEnableVertexAttribArray(0); 100 | 101 | 102 | //our main full size framebuffer 103 | GLuint fbo, fbotex; 104 | 105 | glGenFramebuffers(1, &fbo); 106 | glGenTextures(1, &fbotex); 107 | glBindTexture(GL_TEXTURE_2D, fbotex); 108 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, WIDTH, HEIGHT, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); 109 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 110 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 111 | glBindTexture(GL_TEXTURE_2D, 0); 112 | 113 | glBindFramebuffer(GL_FRAMEBUFFER, fbo); 114 | glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbotex, 0); 115 | 116 | 117 | //our secondary full size framebuffer for copying and reading from the main framebuffer 118 | GLuint copyfbo, copyfbotex; 119 | 120 | glGenFramebuffers(1, ©fbo); 121 | glGenTextures(1, ©fbotex); 122 | glBindTexture(GL_TEXTURE_2D, copyfbotex); 123 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, WIDTH, HEIGHT, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); 124 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 125 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 126 | glBindTexture(GL_TEXTURE_2D, 0); 127 | 128 | glBindFramebuffer(GL_FRAMEBUFFER, copyfbo); 129 | glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, copyfbotex, 0); 130 | 131 | 132 | //our downscaled buffer that we actually render to 133 | GLuint subbuffer, subbuffertex; 134 | 135 | glGenFramebuffers(1, &subbuffer); 136 | glGenTextures(1, &subbuffertex); 137 | glBindTexture(GL_TEXTURE_2D, subbuffertex); 138 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, WIDTH/downscale, HEIGHT/downscale, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); 139 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 140 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 141 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 142 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 143 | glBindTexture(GL_TEXTURE_2D, 0); 144 | 145 | glBindFramebuffer(GL_FRAMEBUFFER, subbuffer); 146 | glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, subbuffertex, 0); 147 | 148 | glBindFramebuffer(GL_FRAMEBUFFER, 0); 149 | 150 | //setup noise textures 151 | GLuint curltex, worltex, perlworltex, weathertex; 152 | 153 | //stbi_set_flip_vertically_on_load(true); 154 | int x, y, n; 155 | unsigned char *curlNoiseArray = stbi_load("assets/curlnoise.png", &x, &y, &n, 0); 156 | 157 | glGenTextures(1, &curltex); 158 | glBindTexture(GL_TEXTURE_2D, curltex); 159 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 128, 128, 0, GL_RGB, GL_UNSIGNED_BYTE, curlNoiseArray); 160 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 161 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 162 | glBindTexture(GL_TEXTURE_2D, 0); 163 | stbi_image_free(curlNoiseArray); 164 | 165 | unsigned char *weatherNoiseArray = stbi_load("assets/weather.bmp", &x, &y, &n, 0); 166 | 167 | glGenTextures(1, &weathertex); 168 | glBindTexture(GL_TEXTURE_2D, weathertex); 169 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 512, 512, 0, GL_RGB, GL_UNSIGNED_BYTE, weatherNoiseArray); 170 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 171 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 172 | glBindTexture(GL_TEXTURE_2D, 0); 173 | stbi_image_free(weatherNoiseArray); 174 | 175 | unsigned char *worlNoiseArray = stbi_load("assets/worlnoise.bmp", &x, &y, &n, 0); 176 | glGenTextures(1, &worltex); 177 | glBindTexture(GL_TEXTURE_3D, worltex); 178 | glTexImage3D(GL_TEXTURE_3D, 0, GL_RGB, 32, 32, 32, 0, GL_RGB, GL_UNSIGNED_BYTE, worlNoiseArray); 179 | glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); 180 | glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 181 | glGenerateMipmap(GL_TEXTURE_3D); 182 | glBindTexture(GL_TEXTURE_3D, 0); 183 | stbi_image_free(worlNoiseArray); 184 | 185 | unsigned char *perlWorlNoiseArray = stbi_load("assets/perlworlnoise.tga", &x, &y, &n, 4); 186 | 187 | glGenTextures(1, &perlworltex); 188 | glBindTexture(GL_TEXTURE_3D, perlworltex); 189 | glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA, 128, 128, 128, 0, GL_RGBA, GL_UNSIGNED_BYTE, perlWorlNoiseArray); 190 | glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); 191 | glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 192 | glGenerateMipmap(GL_TEXTURE_3D); 193 | glBindTexture(GL_TEXTURE_3D, 0); 194 | stbi_image_free(perlWorlNoiseArray); 195 | 196 | 197 | //set up Model-View-Projection matrix 198 | //this way you only update when camera moves 199 | glm::mat4 view; 200 | view = camera.GetViewMatrix(); 201 | glm::mat4 projection; 202 | projection = glm::perspective(glm::radians(camera.Zoom), (float)WIDTH/(float)HEIGHT, 0.1f, 1000.0f); 203 | MVPM = projection * view ; 204 | 205 | //setup shader uniform info 206 | ourShader.Use(); 207 | GLuint uniformMatrix = glGetUniformLocation(ourShader.Program, "MVPM"); 208 | GLuint aspectUniform = glGetUniformLocation(ourShader.Program, "aspect"); 209 | GLuint checku = glGetUniformLocation(ourShader.Program, "check"); 210 | GLuint timeu = glGetUniformLocation(ourShader.Program, "time"); 211 | GLuint resolutionu = glGetUniformLocation(ourShader.Program, "resolution"); 212 | GLuint downscaleu = glGetUniformLocation(ourShader.Program, "downscale"); 213 | GLuint perlworluniform = glGetUniformLocation(ourShader.Program, "perlworl"); 214 | GLuint worluniform = glGetUniformLocation(ourShader.Program, "worl"); 215 | GLuint curluniform = glGetUniformLocation(ourShader.Program, "curl"); 216 | GLuint weatheruniform = glGetUniformLocation(ourShader.Program, "weather"); 217 | GLuint psunPosition = glGetUniformLocation(ourShader.Program, "sunPosition");//for preetham 218 | 219 | upscaleShader.Use(); 220 | GLuint upuniformMatrix = glGetUniformLocation(upscaleShader.Program, "MVPM"); 221 | GLuint upLFuniformMatrix = glGetUniformLocation(upscaleShader.Program, "LFMVPM"); 222 | GLuint upcheck = glGetUniformLocation(upscaleShader.Program, "check"); 223 | GLuint upresolution = glGetUniformLocation(upscaleShader.Program, "resolution"); 224 | GLuint updownscale = glGetUniformLocation(upscaleShader.Program, "downscale"); 225 | GLuint upaspect = glGetUniformLocation(upscaleShader.Program, "aspect"); 226 | GLuint buffuniform = glGetUniformLocation(upscaleShader.Program, "buff"); 227 | GLuint ponguniform = glGetUniformLocation(upscaleShader.Program, "pong"); 228 | 229 | 230 | int check = 0;//used for checkerboarding in the upscale shader 231 | while (!glfwWindowShouldClose(window)) 232 | { 233 | //This block measures frame time in ms or fps 234 | GLfloat currentFrame = glfwGetTime(); 235 | deltaTime = currentFrame - lastFrame; 236 | timePassed = currentFrame; 237 | if (timePassed - startTime > 0.25 && frames > 10) { 238 | //frame rate 239 | //std::cout<= 0 && key < 1024) 429 | { 430 | if(action == GLFW_PRESS) 431 | keys[key] = true; 432 | else if(action == GLFW_RELEASE) 433 | keys[key] = false; 434 | } 435 | } 436 | --------------------------------------------------------------------------------