1 module dash.graphics.adapters.gl; 2 import dash.core, dash.components, dash.graphics; 3 import dash.utility.output, dash.utility.math; 4 5 import derelict.opengl3.gl3; 6 7 import std.algorithm, std.array; 8 9 abstract class OpenGL : Adapter 10 { 11 private: 12 uint _deferredFrameBuffer; 13 uint _diffuseRenderTexture; //Alpha channel stores Specular map average 14 uint _normalRenderTexture; //Alpha channel stores nothing important 15 uint _depthRenderTexture; 16 17 public: 18 /// FBO for deferred render textures 19 mixin( Property!_deferredFrameBuffer ); 20 /// Texture storing the Diffuse colors and Specular Intensity 21 mixin( Property!_diffuseRenderTexture ); 22 /// Texture storing the Sphermapped Normal XY and the Object ID in Z 23 mixin( Property!_normalRenderTexture ); 24 /// Texture storing the depth 25 mixin( Property!_depthRenderTexture ); 26 27 /** 28 * Initializes the FBO and Textures for deferred rendering 29 */ 30 override void initializeDeferredRendering() 31 { 32 //http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-14-render-to-texture/ 33 34 //Create the frame buffer, which will contain the textures to render to 35 deferredFrameBuffer = 0; 36 glGenFramebuffers( 1, &_deferredFrameBuffer ); 37 glBindFramebuffer( GL_FRAMEBUFFER, _deferredFrameBuffer ); 38 39 //Generate our 3 textures 40 glGenTextures( 1, &_diffuseRenderTexture ); 41 glGenTextures( 1, &_normalRenderTexture ); 42 glGenTextures( 1, &_depthRenderTexture ); 43 44 // Initialize render textures 45 resizeDefferedRenderBuffer(); 46 47 //And finally set all of these to our frameBuffer 48 glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _diffuseRenderTexture, 0 ); 49 glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, _normalRenderTexture, 0 ); 50 glFramebufferTexture2D( GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, _depthRenderTexture, 0 ); 51 52 GLenum[ 2 ] DrawBuffers = [ GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 ]; 53 glDrawBuffers( 2, DrawBuffers.ptr ); 54 55 auto status = glCheckFramebufferStatus( GL_FRAMEBUFFER ); 56 if( status != GL_FRAMEBUFFER_COMPLETE ) 57 { 58 string mapFramebufferError( int code ) 59 { 60 switch( code ) 61 { 62 case(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT): return "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT"; 63 case(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT): return "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT"; 64 case(GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER): return "GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER"; 65 case(GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER): return "GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER"; 66 case(GL_FRAMEBUFFER_UNSUPPORTED): return "GL_FRAMEBUFFER_UNSUPPORTED"; 67 case(GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE): return "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE"; 68 case(GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS): return "GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS"; 69 default: return "UNKNOWN"; 70 } 71 } 72 73 fatalf( "Deffered rendering Frame Buffer was not initialized correctly. Error: %s", mapFramebufferError(status) ); 74 assert(false); 75 } 76 } 77 78 /** 79 * Resizes the deffered rendering buffer. 80 */ 81 final void resizeDefferedRenderBuffer() 82 { 83 //For each texture, we bind it to our active texture, and set the format and filtering 84 glBindTexture( GL_TEXTURE_2D, _diffuseRenderTexture ); 85 glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, null ); 86 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); 87 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); 88 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); 89 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); 90 91 glBindTexture( GL_TEXTURE_2D, _normalRenderTexture ); 92 glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA16F, width, height, 0, GL_RGBA, GL_FLOAT, null ); 93 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); 94 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); 95 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); 96 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); 97 98 glBindTexture( GL_TEXTURE_2D, _depthRenderTexture ); 99 glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32, width, height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, null ); 100 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); 101 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); 102 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); 103 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); 104 } 105 106 /** 107 * Read from the depth buffer at the given point. 108 */ 109 override float getDepthAtScreenPoint( vec2ui point ) 110 { 111 float depth; 112 glBindFramebuffer( GL_FRAMEBUFFER, deferredFrameBuffer ); 113 glReadBuffer( GL_DEPTH_ATTACHMENT ); 114 glReadPixels( point.x, point.y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth); 115 glBindFramebuffer( GL_FRAMEBUFFER, 0 ); 116 return depth; 117 } 118 119 /** 120 * Read from the depth buffer at the given point. 121 */ 122 override uint getObjectIDAtScreenPoint( vec2ui point ) 123 { 124 float fId; 125 glBindFramebuffer( GL_FRAMEBUFFER, deferredFrameBuffer ); 126 glReadBuffer( GL_COLOR_ATTACHMENT1 ); 127 glReadPixels( point.x, point.y, 1, 1, GL_ALPHA, GL_FLOAT, &fId ); 128 glBindFramebuffer( GL_FRAMEBUFFER, 0 ); 129 return cast(uint)fId; 130 } 131 132 /** 133 * Currently the entire rendering pass for the active Scene. TODO: Refactor the name 134 */ 135 override void endDraw() 136 { 137 if( !DGame.instance.activeScene ) 138 { 139 warning( "No active scene." ); 140 return; 141 } 142 143 auto scene = DGame.instance.activeScene; 144 145 if( !scene.camera ) 146 { 147 warning( "No camera on active scene." ); 148 return; 149 } 150 151 auto lights = scene.objects 152 .filter!(obj => obj.stateFlags.drawLight && obj.light) 153 .map!(obj => obj.light); 154 155 auto getLightsByType( Type )() 156 { 157 return lights 158 .filter!(obj => typeid(obj) == typeid(Type)) 159 .map!(obj => cast(Type)obj); 160 } 161 162 auto ambientLights = getLightsByType!AmbientLight; 163 auto directionalLights = getLightsByType!DirectionalLight; 164 auto pointLights = getLightsByType!PointLight; 165 auto spotLights = getLightsByType!SpotLight; 166 167 mat4f projection = scene.camera.perspectiveMatrix; 168 mat4f invProj = scene.camera.inversePerspectiveMatrix; 169 170 scene.root.transform.updateMatrix(); 171 172 /** 173 * Pass for all objects with Meshes 174 */ 175 void geometryPass() 176 { 177 foreach( obj; scene.objects ) 178 { 179 if( obj.mesh && obj.stateFlags.drawMesh ) 180 { 181 mat4f worldView = scene.camera.viewMatrix * obj.transform.matrix; 182 mat4f worldViewProj = projection * worldView; 183 184 if( !( obj.mesh.boundingBox in Frustum( worldViewProj ) ) ) 185 { 186 // If we can't see an object, don't draw it. 187 continue; 188 } 189 190 // set the shader 191 Shader shader = obj.mesh.animated 192 ? Shaders.animatedGeometry 193 : Shaders.geometry; 194 195 glUseProgram( shader.programID ); 196 glBindVertexArray( obj.mesh.glVertexArray ); 197 198 shader.bindUniformMatrix4fv( shader.WorldView, worldView ); 199 shader.bindUniformMatrix4fv( shader.WorldViewProjection, worldViewProj ); 200 shader.bindUniform1ui( shader.ObjectId, obj.id ); 201 202 if( obj.mesh.animated ) 203 shader.bindUniformMatrix4fvArray( shader.Bones, obj.animation.currBoneTransforms ); 204 205 shader.bindMaterial( obj.material ); 206 207 glDrawElements( GL_TRIANGLES, obj.mesh.numVertices, GL_UNSIGNED_INT, null ); 208 209 glBindVertexArray(0); 210 } 211 } 212 } 213 214 /** 215 * Calculate shadow maps for lights in the scene. 216 */ 217 void shadowPass() 218 { 219 foreach( light; directionalLights ) 220 { 221 if( light.castShadows ) 222 { 223 glBindFramebuffer( GL_FRAMEBUFFER, light.shadowMapFrameBuffer ); 224 glClear( GL_DEPTH_BUFFER_BIT ); 225 glViewport( 0, 0, light.shadowMapSize, light.shadowMapSize ); 226 227 // determine the world space volume for all objects 228 box3f frustum; 229 foreach( object; scene.objects ) 230 { 231 if( object.mesh && object.stateFlags.drawMesh ) 232 { 233 frustum.expandInPlace( (object.transform.matrix * vec4f(object.mesh.boundingBox.min, 1.0f)).xyz ); 234 frustum.expandInPlace( (object.transform.matrix * vec4f(object.mesh.boundingBox.max, 1.0f)).xyz ); 235 } 236 } 237 238 light.calculateProjView( frustum ); 239 240 foreach( object; scene.objects ) 241 { 242 if( object.mesh && object.stateFlags.drawMesh ) 243 { 244 // set the shader 245 Shader shader = object.mesh.animated 246 ? Shaders.animatedShadowMap 247 : Shaders.shadowMap; 248 249 glUseProgram( shader.programID ); 250 glBindVertexArray( object.mesh.glVertexArray ); 251 252 shader.bindUniformMatrix4fv( shader.WorldViewProjection, 253 light.projView * object.transform.matrix); 254 255 if( object.mesh.animated ) 256 shader.bindUniformMatrix4fvArray( shader.Bones, object.animation.currBoneTransforms ); 257 258 glDrawElements( GL_TRIANGLES, object.mesh.numVertices, GL_UNSIGNED_INT, null ); 259 260 glBindVertexArray(0); 261 } 262 } 263 glBindFramebuffer( GL_FRAMEBUFFER, 0 ); 264 } 265 } 266 267 foreach( light; pointLights ){} 268 269 foreach( light; spotLights ){} 270 } 271 272 /** 273 * Pass for all objects with lights 274 */ 275 void lightPass() 276 { 277 /** 278 * Binds the g-buffer textures for sampling. 279 */ 280 void bindGeometryOutputs( Shader shader ) 281 { 282 // diffuse 283 glUniform1i( shader.DiffuseTexture, 0 ); 284 glActiveTexture( GL_TEXTURE0 ); 285 glBindTexture( GL_TEXTURE_2D, _diffuseRenderTexture ); 286 287 // normal 288 glUniform1i( shader.NormalTexture, 1 ); 289 glActiveTexture( GL_TEXTURE1 ); 290 glBindTexture( GL_TEXTURE_2D, _normalRenderTexture ); 291 292 // depth 293 glUniform1i( shader.DepthTexture, 2 ); 294 glActiveTexture( GL_TEXTURE2 ); 295 glBindTexture( GL_TEXTURE_2D, _depthRenderTexture ); 296 } 297 298 // Ambient Light 299 if( !ambientLights.empty ) 300 { 301 auto shader = Shaders.ambientLight; 302 glUseProgram( shader.programID ); 303 304 bindGeometryOutputs( shader ); 305 306 shader.bindAmbientLight( ambientLights.front ); 307 308 // bind the window mesh for ambient lights 309 glBindVertexArray( Assets.unitSquare.glVertexArray ); 310 glDrawElements( GL_TRIANGLES, Assets.unitSquare.numVertices, GL_UNSIGNED_INT, null ); 311 312 ambientLights.popFront; 313 314 if( !ambientLights.empty ) 315 { 316 warning( "Only one ambient light per scene is utilized." ); 317 } 318 } 319 320 // Directional Lights 321 if( !directionalLights.empty ) 322 { 323 auto shader = Shaders.directionalLight; 324 glUseProgram( shader.programID ); 325 326 bindGeometryOutputs( shader ); 327 328 // bind inverseProj for rebuilding world positions from pixel locations 329 shader.bindUniformMatrix4fv( shader.InverseProjection, invProj ); 330 shader.bindUniform2f( shader.ProjectionConstants, scene.camera.projectionConstants ); 331 332 // bind the window mesh for directional lights 333 glBindVertexArray( Assets.unitSquare.glVertexArray ); 334 335 // bind and draw directional lights 336 foreach( light; directionalLights ) 337 { 338 glUniform1i( shader.ShadowMap, 3 ); 339 glActiveTexture( GL_TEXTURE3 ); 340 glBindTexture( GL_TEXTURE_2D, light.shadowMapTexture ); 341 342 shader.bindUniformMatrix4fv( shader.LightProjectionView, light.projView ); 343 shader.bindUniformMatrix4fv( shader.CameraView, scene.camera.viewMatrix); 344 shader.bindDirectionalLight( light, scene.camera.viewMatrix ); 345 346 glDrawElements( GL_TRIANGLES, Assets.unitSquare.numVertices, GL_UNSIGNED_INT, null ); 347 } 348 } 349 350 // Point Lights 351 if( !pointLights.empty ) 352 { 353 auto shader = Shaders.pointLight; 354 glUseProgram( shader.programID ); 355 356 bindGeometryOutputs( shader ); 357 358 // bind WorldView for creating the View rays for reconstruction position 359 shader.bindUniform2f( shader.ProjectionConstants, scene.camera.projectionConstants ); 360 361 // bind the mesh for point lights 362 glBindVertexArray( Assets.unitSquare.glVertexArray ); 363 364 // bind and draw point lights 365 foreach( light; pointLights ) 366 { 367 // logInfo(light.owner.name); 368 shader.bindUniformMatrix4fv( shader.WorldView, 369 scene.camera.viewMatrix * light.getTransform() ); 370 shader.bindUniformMatrix4fv( shader.WorldViewProjection, 371 projection * scene.camera.viewMatrix * light.getTransform() ); 372 shader.bindPointLight( light, scene.camera.viewMatrix ); 373 glDrawElements( GL_TRIANGLES, Assets.unitSquare.numVertices, GL_UNSIGNED_INT, null ); 374 } 375 } 376 377 // Spot Lights 378 if( !spotLights.empty ) 379 { 380 // TODO 381 } 382 } 383 384 /** 385 * Draw the UI 386 */ 387 void uiPass() 388 { 389 Shader shader = Shaders.userInterface; 390 glUseProgram( shader.programID ); 391 glBindVertexArray( Assets.unitSquare.glVertexArray ); 392 393 auto ui = scene.ui; 394 shader.bindUniformMatrix4fv( shader.WorldProj, 395 scene.camera.orthogonalMatrix * ui.scaleMat ); 396 shader.bindUI( ui ); 397 glDrawElements( GL_TRIANGLES, Assets.unitSquare.numVertices, GL_UNSIGNED_INT, null ); 398 399 glBindVertexArray(0); 400 } 401 402 glBindFramebuffer( GL_FRAMEBUFFER, _deferredFrameBuffer ); 403 // must be called before glClear to clear the depth buffer, otherwise depth buffer won't be cleared 404 glDepthMask( GL_TRUE ); 405 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); 406 glEnable( GL_DEPTH_TEST ); 407 glDisable( GL_BLEND ); 408 409 geometryPass(); 410 411 //glCullFace( GL_FRONT ); 412 413 shadowPass(); 414 415 //glCullFace( GL_BACK ); 416 glViewport( 0, 0, Graphics.width, Graphics.height ); 417 418 // settings for light pass 419 glDepthMask( GL_FALSE ); 420 glDisable( GL_DEPTH_TEST ); 421 glEnable( GL_BLEND ); 422 glBlendFunc( GL_ONE, GL_ONE ); 423 424 //This line switches back to the default framebuffer 425 glBindFramebuffer( GL_FRAMEBUFFER, 0 ); 426 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); 427 428 lightPass(); 429 430 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); 431 432 //ui is currently broken on Linux 433 //TODO: Repair the UI on Linux systems 434 version(linux){} 435 else 436 uiPass(); 437 438 // put it on the screen 439 swapBuffers(); 440 441 // clean up 442 glBindVertexArray(0); 443 glUseProgram(0); 444 } 445 }