1 /*************************************** 2 * Copyright 2011, 2012 GlobWeb contributors. 3 * 4 * This file is part of GlobWeb. 5 * 6 * GlobWeb is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU Lesser General Public License as published by 8 * the Free Software Foundation, version 3 of the License, or 9 * (at your option) any later version. 10 * 11 * GlobWeb is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with GlobWeb. If not, see <http://www.gnu.org/licenses/>. 18 ***************************************/ 19 20 define(['./Utils', './BaseLayer', './Program','./CoordinateSystem'], function(Utils,BaseLayer,Program,CoordinateSystem) { 21 22 /** @name AtmosphereLayer 23 @class 24 A layer to BaseLayer an atmosphere on the globe. 25 @augments RasterLayer 26 @param options Configuration properties for the layer. See {@link BaseLayer} for base properties : 27 <ul> 28 <li>kr : the raylength parameter, default is 0.0025</li> 29 <li>km : the mie parameter, default is 0.0015</li> 30 <li>sunBrightness : the mie parameter, default is 0.0015</li> 31 <li>exposure : the exposure, use for basic high dynamic range, default is 2.0</li> 32 <li>wavelength : the RGB color of the sun, default is [0.650,0.570,0.475]</li> 33 </ul> 34 */ 35 var AtmosphereLayer = function(options) 36 { 37 BaseLayer.prototype.constructor.call( this, options ); 38 if (!this.name) 39 this.name = "Atmosphere"; 40 41 this.kr = (options && options['kr']) || 0.0025; 42 this.km = (options && options['km']) || 0.0015; 43 this.sunBrightness = (options && options['sunBrightness']) || 15.0; 44 this.exposure = (options && options['exposure']) || 2.0; 45 this.wavelength = (options && options['wavelength']) || [0.650,0.570,0.475]; 46 47 // internal properties 48 this._innerRadius = CoordinateSystem.radius; 49 this._outerRadius = this._innerRadius * 1.025; 50 this._skyProgram = null; 51 this._groundProgram = null; 52 this._originalProgram = null; 53 this._isValid = false; 54 } 55 56 /**************************************************************************************************************/ 57 58 Utils.inherits( BaseLayer,AtmosphereLayer ); 59 60 /**************************************************************************************************************/ 61 62 /** 63 Attach the atmosphere layer to the globe 64 */ 65 AtmosphereLayer.prototype._attach = function( g ) 66 { 67 this.globe = g; 68 var renderContext = g.renderContext; 69 70 // Setup program, uniform now that we have the render context 71 72 this._skyFromSpaceProgram = new Program(renderContext); 73 this._skyFromSpaceProgram.loadFromFile( "SkyFromSpaceVert.glsl", "SkyFrag.glsl" ); 74 75 this._skyFromAtmosphereProgram = new Program(renderContext); 76 this._skyFromAtmosphereProgram.loadFromFile( "SkyFromAtmosphereVert.glsl", "SkyFrag.glsl" ); 77 78 this._groundFromSpaceProgram = new Program(renderContext); 79 this._groundFromSpaceProgram.loadFromFile( "GroundFromSpaceVert.glsl", "GroundFrag.glsl" ); 80 81 this._groundFromAtmosphereProgram = new Program(renderContext); 82 this._groundFromAtmosphereProgram.loadFromFile( "GroundFromAtmosphereVert.glsl", "GroundFrag.glsl" ); 83 84 // Check if the atmosphre is valid : all programs must be OK 85 this._isValid = this._skyFromSpaceProgram.glProgram != null 86 && this._skyFromAtmosphereProgram.glProgram != null 87 && this._groundFromSpaceProgram.glProgram != null 88 && this._groundFromAtmosphereProgram.glProgram != null; 89 90 if ( !this._isValid ) 91 return; 92 93 this._skyFromSpaceProgram.apply(); 94 this._initUniforms( this._skyFromSpaceProgram.uniforms ); 95 this._skyFromAtmosphereProgram.apply(); 96 this._initUniforms( this._skyFromAtmosphereProgram.uniforms ); 97 this._groundFromSpaceProgram.apply(); 98 this._initUniforms( this._groundFromSpaceProgram.uniforms ); 99 this._groundFromAtmosphereProgram.apply(); 100 this._initUniforms( this._groundFromAtmosphereProgram.uniforms ); 101 102 // Create the sphere 103 var vertices = []; 104 var indices = []; 105 106 var nbEl = 72; 107 var nbAz = 144; 108 109 // Create the vertices 110 for (var el=-nbEl; el <= nbEl; el++) 111 { 112 var elevation = el * (Math.PI * 0.5) / nbEl; 113 for (var az=-nbAz; az <= nbAz; az++) 114 { 115 var azimuth = az * Math.PI / nbAz; 116 117 var x = this._outerRadius * Math.cos(azimuth) * Math.cos(elevation); 118 var y = this._outerRadius * Math.sin(azimuth) * Math.cos(elevation); 119 var z = this._outerRadius * Math.sin(elevation); 120 121 vertices.push( x ); 122 vertices.push( y ); 123 vertices.push( z ); 124 } 125 } 126 127 // build the sphere triangles 128 for (var el=0; el < 2*nbEl; el++) 129 { 130 for (var az=0; az < 2*nbAz; az++) 131 { 132 indices.push( el * (2*nbAz+1) + az ); 133 indices.push( (el+1) * (2*nbAz+1) + az+1 ); 134 indices.push( el * (2*nbAz+1) + az + 1 ); 135 136 indices.push( (el+1) * (2*nbAz+1) + az+1 ); 137 indices.push( el * (2*nbAz+1) + az ); 138 indices.push( (el+1) * (2*nbAz+1) + az ); 139 } 140 } 141 142 var gl = renderContext.gl; 143 this._vertexBuffer = gl.createBuffer(); 144 gl.bindBuffer(gl.ARRAY_BUFFER, this._vertexBuffer); 145 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); 146 147 this._indexBuffer = gl.createBuffer(); 148 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._indexBuffer); 149 gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW); 150 this._numIndices = indices.length; 151 152 this._originalProgram = g.tileManager.program; 153 154 g.preRenderers.push(this); 155 g.tileManager.addPostRenderer(this); 156 } 157 158 /**************************************************************************************************************/ 159 160 /* 161 Initialize uniforms 162 */ 163 AtmosphereLayer.prototype._initUniforms = function( uniforms ) 164 { 165 var gl = this.globe.renderContext.gl; 166 167 var g = -0.95; // The Mie phase asymmetry factor 168 var scale = 1.0 / ( this.outerRadius - this.innerRadius ); 169 var rayleighScaleDepth = 0.25; 170 var mieScaleDepth = 0.1; 171 172 var lightDir = [1.0,1.0,1.0]; 173 vec3.normalize( lightDir ); 174 175 gl.uniform1f( uniforms["fKrESun"], this.kr * this.sunBrightness ); 176 gl.uniform1f( uniforms["fKmESun"], this.kr * this.sunBrightness ); 177 gl.uniform1f( uniforms["fKr4PI"], this.kr * 4.0 * Math.PI ); 178 gl.uniform1f( uniforms["fKm4PI"], this.km * 4.0 * Math.PI ); 179 gl.uniform1f( uniforms["fExposure"], this.exposure ); 180 181 var wavelength = [ Math.pow( this.wavelength[0], 4.0 ), Math.pow( this.wavelength[1], 4.0 ) ,Math.pow( this.wavelength[2], 4.0 ) ]; 182 gl.uniform3f( uniforms["v3InvWavelength"], 1.0 / wavelength[0], 1.0 / wavelength[1], 1.0 / wavelength[2] ); 183 184 gl.uniform3f( uniforms["v3LightPos"], lightDir[0], lightDir[1], lightDir[2] ); 185 gl.uniform1f( uniforms["fInnerRadius"], this._innerRadius ); 186 gl.uniform1f( uniforms["fInnerRadius2"], this._innerRadius*this._innerRadius ); 187 gl.uniform1f( uniforms["fOuterRadius"], this._outerRadius ); 188 gl.uniform1f( uniforms["fOuterRadius2"], this._outerRadius*this._outerRadius ); 189 gl.uniform1f( uniforms["fScale"], 1.0 / (this._outerRadius - this._innerRadius) ); 190 gl.uniform1f( uniforms["fScaleDepth"], rayleighScaleDepth ); 191 gl.uniform1f( uniforms["fScaleOverScaleDepth"], (1.0 / (this._outerRadius - this._innerRadius)) / rayleighScaleDepth ); 192 gl.uniform1f( uniforms["g"], g ); 193 gl.uniform1f( uniforms["g2"], g*g ); 194 } 195 196 /**************************************************************************************************************/ 197 198 /* 199 Pre-render the atmoshpere 200 */ 201 AtmosphereLayer.prototype.preRender = function() 202 { 203 if ( !this._isValid ) 204 return; 205 206 var tileManager = this.globe.tileManager; 207 if ( !this._visible ) 208 { 209 tileManager.program = this._originalProgram; 210 return; 211 } 212 213 var rc = this.globe.renderContext; 214 var gl = rc.gl; 215 216 // Compute the eye position from the view matrix : the eye position is equals to [0,0,0] * inv(viewMatrix) 217 // Optimized to avoid to compute the view matrix inverse 218 var vm = rc.viewMatrix; 219 var x = vm[12], y = vm[13], z = vm[14]; 220 var eyePos = [ -( vm[0]*x + vm[1]*y + vm[2]*z ), 221 -( vm[4]*x + vm[5]*y + vm[6]*z ), 222 -( vm[8]*x + vm[9]*y + vm[10]*z ) ]; 223 var eyeHeight = vec3.length(eyePos); 224 225 this._skyProgram = eyeHeight < this._outerRadius ? this._skyFromAtmosphereProgram : this._skyFromSpaceProgram; 226 this._groundProgram = eyeHeight < this._outerRadius ? this._groundFromAtmosphereProgram : this._groundFromSpaceProgram; 227 228 this._skyProgram.apply(); 229 230 gl.uniform3f( this._skyProgram.uniforms["v3CameraPos"], eyePos[0], eyePos[1], eyePos[2] ); 231 gl.uniform1f( this._skyProgram.uniforms["fCameraHeight2"], eyeHeight * eyeHeight ); 232 gl.uniform1f( this._skyProgram.uniforms["fCameraHeight"], eyeHeight ); 233 234 this._groundProgram.apply(); 235 236 var earthCenter = [ 0.0, 0.0, 0.0 ]; 237 mat4.multiplyVec3( rc.viewMatrix, earthCenter ); 238 gl.uniform3f( this._groundProgram.uniforms["earthCenter"], earthCenter[0], earthCenter[1], earthCenter[2] ); 239 240 var lightDir = [1.0,1.0,1.0]; 241 vec3.normalize( lightDir ); 242 var x = lightDir[0], y = lightDir[1], z = lightDir[2]; 243 var mat = rc.viewMatrix; 244 lightDir[0] = mat[0]*x + mat[4]*y + mat[8]*z; 245 lightDir[1] = mat[1]*x + mat[5]*y + mat[9]*z; 246 lightDir[2] = mat[2]*x + mat[6]*y + mat[10]*z ; 247 gl.uniform3f( this._groundProgram.uniforms["lightDir"], lightDir[0], lightDir[1], lightDir[2] ); 248 249 gl.uniform3f( this._groundProgram.uniforms["v3CameraPos"], eyePos[0], eyePos[1], eyePos[2] ); 250 gl.uniform1f( this._groundProgram.uniforms["fCameraHeight2"], eyeHeight * eyeHeight ); 251 gl.uniform1f( this._groundProgram.uniforms["fCameraHeight"], eyeHeight ); 252 253 tileManager.program = this._groundProgram; 254 255 rc.far = 2; 256 } 257 258 /**************************************************************************************************************/ 259 260 /* 261 Render the atmosphere 262 */ 263 AtmosphereLayer.prototype.render = function() 264 { 265 if ( !this._isValid || ! this._visible ) 266 return; 267 268 var rc = this.globe.renderContext; 269 var gl = rc.gl; 270 271 gl.enable(gl.CULL_FACE); 272 273 this._skyProgram.apply(); 274 275 gl.uniformMatrix4fv(this._skyProgram.uniforms["projectionMatrix"], false, rc.projectionMatrix); 276 gl.uniformMatrix4fv(this._skyProgram.uniforms["viewMatrix"], false, rc.viewMatrix); 277 278 gl.bindBuffer(gl.ARRAY_BUFFER, this._vertexBuffer); 279 gl.vertexAttribPointer(this._skyProgram.attributes['vertex'], 3, gl.FLOAT, false, 0, 0); 280 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._indexBuffer); 281 gl.drawElements(gl.TRIANGLES, this._numIndices, gl.UNSIGNED_SHORT, 0); 282 283 gl.disable(gl.CULL_FACE); 284 } 285 286 /**************************************************************************************************************/ 287 288 return AtmosphereLayer; 289 290 }); 291 292