1 /****************************************************************************** 2 3 @File OGLES3ColourGrading.cpp 4 5 @Title Colour grading 6 7 @Version 8 9 @Copyright Copyright (c) Imagination Technologies Limited. 10 11 @Platform Independent 12 13 @Description Demonstrates how to colour grade your render. 14 15 ******************************************************************************/ 16 #include "PVRShell.h" 17 #include "OGLES3Tools.h" 18 19 /****************************************************************************** 20 Constants 21 ******************************************************************************/ 22 const char* const c_pszMaskTexture = "MaskTexture.pvr"; 23 const char* const c_pszBackgroundTexture = "Background.pvr"; 24 25 // Our colour lookup tables 26 const char* const c_pszLUTs[] = 27 { 28 "identity.pvr", 29 "bw.pvr", 30 "cooler.pvr", 31 "warmer.pvr", 32 "sepia.pvr", 33 "inverted.pvr", 34 "highcontrast.pvr", 35 "bluewhitegradient.pvr" 36 }; 37 38 // Shader source 39 const char* const c_szFragShaderSrcFile = "FragShader.fsh"; 40 const char* const c_szVertShaderSrcFile = "VertShader.vsh"; 41 const char* const c_szSceneFragShaderSrcFile = "SceneFragShader.fsh"; 42 const char* const c_szSceneVertShaderSrcFile = "SceneVertShader.vsh"; 43 const char* const c_szBackgroundFragShaderSrcFile = "BackgroundFragShader.fsh"; 44 45 // POD scene files 46 const char c_szSceneFile[] = "Mask.pod"; 47 48 // Camera constants. Used for making the projection matrix 49 const float CAM_FOV = PVRT_PI / 6; 50 const float CAM_NEAR = 4.0f; 51 const float CAM_FAR = 5000.0f; 52 53 // Index to bind the attributes to vertex shaders 54 const int VERTEX_ARRAY = 0; 55 const int TEXCOORD_ARRAY = 1; 56 const int NORMAL_ARRAY = 2; 57 58 // Look up table enumeration 59 enum ELUTs 60 { 61 eIdentity, 62 eBW, 63 eCooler, 64 eWarmer, 65 eSepia, 66 eInverted, 67 eHighContrast, 68 eBlueWhiteGradient, 69 eLast, 70 71 // The range to cycle through 72 eA = eBW, 73 eB = eBlueWhiteGradient 74 }; 75 76 const char* const c_pszLUTNames[] = 77 { 78 "Identity", 79 "Black and white", 80 "Cooler", 81 "Warmer", 82 "Sepia", 83 "Inverted", 84 "High Contrast", 85 "Blue White Gradient" 86 }; 87 88 /*!**************************************************************************** 89 Class implementing the PVRShell functions. 90 ******************************************************************************/ 91 class OGLES3ColourGrading : public PVRShell 92 { 93 // Print3D object 94 CPVRTPrint3D m_Print3D; 95 96 // Texture handle 97 GLuint m_uiMaskTexture; 98 GLuint m_uiBackgroundTexture; 99 GLuint m_uiLUTs[eLast]; 100 int m_iCurrentLUT; 101 102 // VBO handle 103 GLuint m_ui32FullScreenRectVBO; 104 105 // Stride for vertex data 106 unsigned int m_ui32VertexStride; 107 108 // 3D Model 109 CPVRTModelPOD m_Mask; 110 GLuint* m_puiMaskVBO; 111 GLuint* m_puiMaskIBO; 112 113 GLuint m_ui32BackgroundVBO; 114 115 // Projection and view matrices 116 PVRTMat4 m_mViewProjection; 117 118 // Shaders 119 GLuint m_uiPostVertShader; 120 GLuint m_uiPostFragShader; 121 122 struct 123 { 124 GLuint uiId; 125 } 126 m_PostShaderProgram; 127 128 GLuint m_uiBackgroundFragShader; 129 130 struct 131 { 132 GLuint uiId; 133 } 134 m_BackgroundShaderProgram; 135 136 GLuint m_uiSceneVertShader; 137 GLuint m_uiSceneFragShader; 138 139 struct 140 { 141 GLuint uiId; 142 GLuint uiMVPMatrixLoc; 143 GLuint uiLightDirLoc; 144 GLuint uiMaterialBiasLoc; 145 GLuint uiMaterialScaleLoc; 146 } 147 m_SceneShaderProgram; 148 149 // Render contexts, etc 150 GLint m_i32OriginalFbo; 151 152 // Texture IDs used by the app 153 GLuint m_uiTextureToRenderTo; 154 155 // Handle for our FBO and the depth buffer that it requires 156 GLuint m_uiFBO; 157 158 // Handle for our multi-sampled FBO and the depth buffer that it requires 159 GLuint m_uiFBOMultisampled; 160 GLuint m_uiDepthBufferMultisampled; 161 GLuint m_uiColourBufferMultisampled; 162 163 // Start time 164 unsigned long m_ulStartTime; 165 166 public: 167 // PVRShell functions 168 virtual bool InitApplication(); 169 virtual bool InitView(); 170 virtual bool ReleaseView(); 171 virtual bool QuitApplication(); 172 virtual bool RenderScene(); 173 174 private: 175 bool LoadShaders(CPVRTString& ErrorStr); 176 bool CreateFBO(); 177 void LoadVbos(const bool bRotated); 178 void DrawMesh(const int i32NodeIndex); 179 }; 180 181 182 /*!**************************************************************************** 183 @Function InitApplication 184 @Return bool true if no error occurred 185 @Description Code in InitApplication() will be called by PVRShell once per 186 run, before the rendering context is created. 187 Used to initialize variables that are not dependent on it 188 (e.g. external modules, loading meshes, etc.) 189 If the rendering context is lost, InitApplication() will 190 not be called again. 191 ******************************************************************************/ 192 bool OGLES3ColourGrading::InitApplication() 193 { 194 // Get and set the read path for content files 195 CPVRTResourceFile::SetReadPath((char*)PVRShellGet(prefReadPath)); 196 197 // Get and set the load/release functions for loading external files. 198 // In the majority of cases the PVRShell will return NULL function pointers implying that 199 // nothing special is required to load external files. 200 CPVRTResourceFile::SetLoadReleaseFunctions(PVRShellGet(prefLoadFileFunc), PVRShellGet(prefReleaseFileFunc)); 201 202 // Load the scene 203 if(m_Mask.ReadFromFile(c_szSceneFile) != PVR_SUCCESS) 204 { 205 PVRShellSet(prefExitMessage, "ERROR: Couldn't load the .pod file\n"); 206 return false; 207 } 208 209 // Initialise some variables 210 m_puiMaskVBO = m_puiMaskIBO = 0; 211 m_iCurrentLUT = eA; 212 return true; 213 } 214 215 /*!**************************************************************************** 216 @Function QuitApplication 217 @Return bool true if no error occurred 218 @Description Code in QuitApplication() will be called by PVRShell once per 219 run, just before exiting the program. 220 If the rendering context is lost, QuitApplication() will 221 not be called. 222 ******************************************************************************/ 223 bool OGLES3ColourGrading::QuitApplication() 224 { 225 // Free the memory allocated for the scene 226 m_Mask.Destroy(); 227 228 delete[] m_puiMaskVBO; 229 m_puiMaskVBO = 0; 230 231 delete[] m_puiMaskIBO; 232 m_puiMaskIBO = 0; 233 return true; 234 } 235 236 /*!**************************************************************************** 237 @Function LoadEffects 238 @Output ErrorStr A description of an error, if one occurs. 239 @Return bool true if no error occurred 240 @Description Loads and parses the bundled PFX and generates the various 241 effect objects. 242 ******************************************************************************/ 243 bool OGLES3ColourGrading::LoadShaders(CPVRTString& ErrorStr) 244 { 245 // Load and compile the shaders from files. 246 if(PVRTShaderLoadFromFile(NULL, c_szVertShaderSrcFile, GL_VERTEX_SHADER, GL_SGX_BINARY_IMG, &m_uiPostVertShader, &ErrorStr) != PVR_SUCCESS) 247 { 248 return false; 249 } 250 251 if(PVRTShaderLoadFromFile(NULL, c_szFragShaderSrcFile, GL_FRAGMENT_SHADER, GL_SGX_BINARY_IMG, &m_uiPostFragShader, &ErrorStr) != PVR_SUCCESS) 252 { 253 return false; 254 } 255 256 // Setup and link the shader program 257 const char* aszAttribs[] = { "inVertex", "inTexCoord" }; 258 if(PVRTCreateProgram(&m_PostShaderProgram.uiId, m_uiPostVertShader, m_uiPostFragShader, aszAttribs, 2, &ErrorStr) != PVR_SUCCESS) 259 { 260 return false; 261 } 262 263 // Set the sampler variables to their respective texture unit 264 glUniform1i(glGetUniformLocation(m_PostShaderProgram.uiId, "sTexture"), 0); 265 glUniform1i(glGetUniformLocation(m_PostShaderProgram.uiId, "sColourLUT"), 1); 266 267 // Background shader 268 269 if(PVRTShaderLoadFromFile(NULL, c_szBackgroundFragShaderSrcFile, GL_FRAGMENT_SHADER, GL_SGX_BINARY_IMG, &m_uiBackgroundFragShader, &ErrorStr) != PVR_SUCCESS) 270 { 271 return false; 272 } 273 274 // Set up and link the shader program re-using the vertex shader from the main shader program 275 const char* aszBackgroundAttribs[] = { "inVertex", "inTexCoord" }; 276 if(PVRTCreateProgram(&m_BackgroundShaderProgram.uiId, m_uiPostVertShader, m_uiBackgroundFragShader, aszBackgroundAttribs, 2, &ErrorStr) != PVR_SUCCESS) 277 { 278 return false; 279 } 280 281 // Set the sampler2D variable to the first texture unit 282 glUniform1i(glGetUniformLocation(m_BackgroundShaderProgram.uiId, "sTexture"), 0); 283 284 // Scene shaders - Used for rendering the mask 285 if(PVRTShaderLoadFromFile(NULL, c_szSceneVertShaderSrcFile, GL_VERTEX_SHADER, GL_SGX_BINARY_IMG, &m_uiSceneVertShader, &ErrorStr) != PVR_SUCCESS) 286 { 287 return false; 288 } 289 290 if(PVRTShaderLoadFromFile(NULL, c_szSceneFragShaderSrcFile, GL_FRAGMENT_SHADER, GL_SGX_BINARY_IMG, &m_uiSceneFragShader, &ErrorStr) != PVR_SUCCESS) 291 { 292 return false; 293 } 294 295 // Setup and link the shader program 296 const char* aszSceneAttribs[] = { "inVertex", "inTexCoord", "inNormal" }; 297 if (PVRTCreateProgram(&m_SceneShaderProgram.uiId, m_uiSceneVertShader, m_uiSceneFragShader, aszSceneAttribs, 3, &ErrorStr) != PVR_SUCCESS) 298 { 299 return false; 300 } 301 302 // Set the sampler2D variable to the first texture unit 303 glUniform1i(glGetUniformLocation(m_SceneShaderProgram.uiId, "sTexture"), 0); 304 305 // Store the location of uniforms for later use 306 m_SceneShaderProgram.uiMVPMatrixLoc = glGetUniformLocation(m_SceneShaderProgram.uiId, "MVPMatrix"); 307 m_SceneShaderProgram.uiLightDirLoc = glGetUniformLocation(m_SceneShaderProgram.uiId, "LightDirection"); 308 m_SceneShaderProgram.uiMaterialBiasLoc = glGetUniformLocation(m_SceneShaderProgram.uiId, "MaterialBias"); 309 m_SceneShaderProgram.uiMaterialScaleLoc = glGetUniformLocation(m_SceneShaderProgram.uiId, "MaterialScale"); 310 311 // Set default shader material uniforms 312 float fSpecularConcentration = 0.6f; // a value from 0 to 1 (wider, concentrated) 313 float fSpecularIntensity = 0.3f; // a value from 0 to 1 314 315 // Specular bias 316 glUniform1f(m_SceneShaderProgram.uiMaterialBiasLoc, fSpecularConcentration); 317 // Specular intensity scale 318 glUniform1f(m_SceneShaderProgram.uiMaterialScaleLoc, fSpecularIntensity / (1.0f - fSpecularConcentration)); 319 320 return true; 321 } 322 323 /*!**************************************************************************** 324 @Function LoadVbos 325 @Description Loads the mesh data required for this training course into 326 vertex buffer objects 327 ******************************************************************************/ 328 void OGLES3ColourGrading::LoadVbos(const bool bRotated) 329 { 330 if(!m_puiMaskVBO) 331 m_puiMaskVBO = new GLuint[m_Mask.nNumMesh]; 332 333 if(!m_puiMaskIBO) 334 m_puiMaskIBO = new GLuint[m_Mask.nNumMesh]; 335 336 /* 337 Load vertex data of all meshes in the scene into VBOs 338 339 The meshes have been exported with the "Interleave Vectors" option, 340 so all data is interleaved in the buffer at pMesh->pInterleaved. 341 Interleaving data improves the memory access pattern and cache efficiency, 342 thus it can be read faster by the hardware. 343 */ 344 glGenBuffers(m_Mask.nNumMesh, m_puiMaskVBO); 345 for(unsigned int i = 0; i < m_Mask.nNumMesh; ++i) 346 { 347 // Load vertex data into buffer object 348 SPODMesh& Mesh = m_Mask.pMesh[i]; 349 unsigned int uiSize = Mesh.nNumVertex * Mesh.sVertex.nStride; 350 glBindBuffer(GL_ARRAY_BUFFER, m_puiMaskVBO[i]); 351 glBufferData(GL_ARRAY_BUFFER, uiSize, Mesh.pInterleaved, GL_STATIC_DRAW); 352 353 // Load index data into buffer object if available 354 m_puiMaskIBO[i] = 0; 355 if (Mesh.sFaces.pData) 356 { 357 glGenBuffers(1, &m_puiMaskIBO[i]); 358 uiSize = PVRTModelPODCountIndices(Mesh) * sizeof(GLshort); 359 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_puiMaskIBO[i]); 360 glBufferData(GL_ELEMENT_ARRAY_BUFFER, uiSize, Mesh.sFaces.pData, GL_STATIC_DRAW); 361 } 362 } 363 364 // Create VBO for the fullscreen rect that we'll be rendering our FBO to 365 366 // Interleaved vertex data 367 GLfloat afVertices[] = { 368 // Left quad 369 -1.0f, 1.0f, 0.0f, 1.0f, // Pos 370 0.0f, 1.0f, // UVs 371 372 -1.0f, -1.0f, 0.0f, 1.0f, 373 0.0f, 0.0f, 374 375 0.0f, 1.0f, 0.0f, 1.0f, 376 0.5f, 1.0f, 377 378 0.0f, -1.0f, 0.0f, 1.0f, 379 0.5f, 0.0f, 380 381 1.0f, 1.0f, 0.0f, 1.0f, 382 1.0f, 1.0f, 383 384 1.0f, -1.0f, 0.0f, 1.0f, 385 1.0f, 0.0f, 386 }; 387 388 if(bRotated) // If we're rotated then pre-process the fullscreen rect's geometry to compensate 389 { 390 for(unsigned int i = 0; i < 6; ++i) 391 { 392 float fTmp = afVertices[i * 6 + 1]; 393 afVertices[i * 6 + 1] = afVertices[i * 6]; 394 afVertices[i * 6] = fTmp; 395 396 fTmp = afVertices[i * 6 + 5]; 397 afVertices[i * 6 + 5] = afVertices[i * 6 + 4]; 398 afVertices[i * 6 + 4] = fTmp; 399 } 400 } 401 402 glGenBuffers(1, &m_ui32FullScreenRectVBO); 403 m_ui32VertexStride = 6 * sizeof(GLfloat); // 4 floats for the pos, 2 for the UVs 404 405 // Bind the VBO 406 glBindBuffer(GL_ARRAY_BUFFER, m_ui32FullScreenRectVBO); 407 408 // Set the buffer's data 409 glBufferData(GL_ARRAY_BUFFER, 6 * m_ui32VertexStride, afVertices, GL_STATIC_DRAW); 410 411 // Create the VBO for the background 412 GLfloat afBackgroundVertices[] = { 413 // Left quad 414 -1.0f, 1.0f, 0.0f, 1.0f, // Pos 415 0.0f, 1.0f, // UVs 416 417 -1.0f, -1.0f, 0.0f, 1.0f, 418 0.0f, 0.0f, 419 420 1.0f, 1.0f, 0.0f, 1.0f, 421 1.0f, 1.0f, 422 423 1.0f, -1.0f, 0.0f, 1.0f, 424 1.0f, 0.0f, 425 }; 426 427 if(bRotated) // If we're rotated then pre-process the background geometry 428 { 429 for(unsigned int i = 0; i < 4; ++i) 430 { 431 float fTmp = afBackgroundVertices[i * 6 + 1]; 432 afBackgroundVertices[i * 6 + 1] = -afBackgroundVertices[i * 6]; 433 afBackgroundVertices[i * 6] = -fTmp; 434 } 435 } 436 437 glGenBuffers(1, &m_ui32BackgroundVBO); 438 439 // Bind the VBO 440 glBindBuffer(GL_ARRAY_BUFFER, m_ui32BackgroundVBO); 441 442 // Set the buffer's data 443 glBufferData(GL_ARRAY_BUFFER, 4 * m_ui32VertexStride, afBackgroundVertices, GL_STATIC_DRAW); 444 445 // Unbind our buffers 446 glBindBuffer(GL_ARRAY_BUFFER, 0); 447 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); 448 } 449 450 bool OGLES3ColourGrading::CreateFBO() 451 { 452 GLenum drawBuffers[] = { GL_COLOR_ATTACHMENT0 }; 453 454 // Query the max amount of samples that are supported, we are going to use the max 455 GLint samples; 456 glGetIntegerv(GL_MAX_SAMPLES, &samples); 457 458 // Get the currently bound frame buffer object. On most platforms this just gives 0. 459 glGetIntegerv(GL_FRAMEBUFFER_BINDING, &m_i32OriginalFbo); 460 461 // Create a texture for rendering to 462 glGenTextures(1, &m_uiTextureToRenderTo); 463 glBindTexture(GL_TEXTURE_2D, m_uiTextureToRenderTo); 464 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, PVRShellGet(prefWidth), PVRShellGet(prefHeight), 0, GL_RGB, GL_UNSIGNED_BYTE, 0); 465 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 466 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 467 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 468 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 469 470 // Create the object that will allow us to render to the aforementioned texture 471 glGenFramebuffers(1, &m_uiFBO); 472 glBindFramebuffer(GL_FRAMEBUFFER, m_uiFBO); 473 474 glDrawBuffers(1, drawBuffers); 475 glReadBuffer(GL_COLOR_ATTACHMENT0); 476 477 // Attach the texture to the FBO 478 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_uiTextureToRenderTo, 0); 479 480 // Check that our FBO creation was successful 481 GLuint uStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER); 482 if(uStatus != GL_FRAMEBUFFER_COMPLETE) 483 { 484 PVRShellSet(prefExitMessage, "ERROR: Failed to initialise FBO"); 485 return false; 486 } 487 488 // Create and initialize the multi-sampled FBO. 489 490 // Create the object that will allow us to render to the aforementioned texture 491 glGenFramebuffers(1, &m_uiFBOMultisampled); 492 glBindFramebuffer(GL_FRAMEBUFFER, m_uiFBOMultisampled); 493 494 glDrawBuffers(1, drawBuffers); 495 glReadBuffer(GL_COLOR_ATTACHMENT0); 496 497 // Generate and bind a render buffer which will become a multisampled depth buffer shared between our two FBOs 498 glGenRenderbuffers(1, &m_uiDepthBufferMultisampled); 499 glBindRenderbuffer(GL_RENDERBUFFER, m_uiDepthBufferMultisampled); 500 glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, GL_DEPTH_COMPONENT24, PVRShellGet(prefWidth), PVRShellGet(prefHeight)); 501 502 glGenRenderbuffers(1, &m_uiColourBufferMultisampled); 503 glBindRenderbuffer(GL_RENDERBUFFER, m_uiColourBufferMultisampled); 504 glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, GL_RGB8, PVRShellGet(prefWidth), PVRShellGet(prefHeight)); 505 glBindRenderbuffer(GL_RENDERBUFFER, 0); 506 507 // Attach the multisampled depth buffer we created earlier to our FBO. 508 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_uiDepthBufferMultisampled); 509 510 // Attach the multisampled colour renderbuffer to the FBO 511 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_uiColourBufferMultisampled); 512 513 // Check that our FBO creation was successful 514 uStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER); 515 if(uStatus != GL_FRAMEBUFFER_COMPLETE) 516 { 517 PVRShellSet(prefExitMessage, "ERROR: Failed to initialise multisampled FBO"); 518 return false; 519 } 520 521 // Unbind the frame buffer object so rendering returns back to the backbuffer 522 glBindFramebuffer(GL_FRAMEBUFFER, m_i32OriginalFbo); 523 524 return true; 525 } 526 527 /*!**************************************************************************** 528 @Function InitView 529 @Return bool true if no error occurred 530 @Description Code in InitView() will be called by PVRShell upon 531 initialization or after a change in the rendering context. 532 Used to initialize variables that are dependent on the rendering 533 context (e.g. textures, vertex buffers, etc.) 534 ******************************************************************************/ 535 bool OGLES3ColourGrading::InitView() 536 { 537 // Initialize the textures used by Print3D. 538 bool bRotate = PVRShellGet(prefIsRotated) && PVRShellGet(prefFullScreen); 539 540 if(m_Print3D.SetTextures(0, PVRShellGet(prefWidth), PVRShellGet(prefHeight), bRotate) != PVR_SUCCESS) 541 { 542 PVRShellSet(prefExitMessage, "ERROR: Cannot initialise Print3D\n"); 543 return false; 544 } 545 546 // Create the texture 547 if(PVRTTextureLoadFromPVR(c_pszMaskTexture, &m_uiMaskTexture) != PVR_SUCCESS) 548 { 549 PVRShellSet(prefExitMessage, "ERROR: Failed to load mask texture\n"); 550 return false; 551 } 552 553 if(PVRTTextureLoadFromPVR(c_pszBackgroundTexture, &m_uiBackgroundTexture) != PVR_SUCCESS) 554 { 555 PVRShellSet(prefExitMessage, "ERROR: Failed to load background texture\n"); 556 return false; 557 } 558 559 // Load our 3D texture look up tables 560 for(unsigned int i = 0; i < eLast; ++i) 561 { 562 if(PVRTTextureLoadFromPVR(c_pszLUTs[i], &m_uiLUTs[i]) != PVR_SUCCESS) 563 { 564 PVRShellSet(prefExitMessage, "ERROR: Failed to load a 3D texture\n"); 565 return false; 566 } 567 568 glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 569 glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 570 glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); 571 572 glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 573 glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 574 } 575 576 // Load the effects 577 CPVRTString ErrorStr; 578 if(!LoadShaders(ErrorStr)) 579 { 580 PVRShellSet(prefExitMessage, ErrorStr.c_str()); 581 return false; 582 } 583 584 // Create FBOs 585 if(!CreateFBO()) 586 { 587 PVRShellSet(prefExitMessage, "Failed to create FBO"); 588 return false; 589 } 590 591 // Initialise VBO data 592 LoadVbos(bRotate); 593 594 // Calculate the projection and view matrices 595 float fAspect = PVRShellGet(prefWidth) / (float)PVRShellGet(prefHeight); 596 m_mViewProjection = PVRTMat4::PerspectiveFovRH(CAM_FOV, fAspect, CAM_NEAR, CAM_FAR, PVRTMat4::OGL, bRotate); 597 m_mViewProjection *= PVRTMat4::LookAtRH(PVRTVec3(0.f, 0.f, 150.f), PVRTVec3(0.f), PVRTVec3(0.f, 1.f, 0.f)); 598 599 // Enable backface culling and depth test 600 glEnable(GL_CULL_FACE); 601 glEnable(GL_DEPTH_TEST); 602 603 // Set the clear colour 604 glClearColor(1.0f, 0.0f, 0.0f, 1.0f); 605 606 // Store initial time 607 m_ulStartTime = PVRShellGetTime(); 608 609 return true; 610 } 611 612 /*!**************************************************************************** 613 @Function ReleaseView 614 @Return bool true if no error occurred 615 @Description Code in ReleaseView() will be called by PVRShell when the 616 application quits or before a change in the rendering context. 617 ******************************************************************************/ 618 bool OGLES3ColourGrading::ReleaseView() 619 { 620 // Frees the texture 621 glDeleteTextures(1, &m_uiMaskTexture); 622 glDeleteTextures(1, &m_uiBackgroundTexture); 623 glDeleteTextures(eLast, m_uiLUTs); 624 glDeleteTextures(1, &m_uiTextureToRenderTo); 625 626 // Release Vertex buffer object. 627 glDeleteBuffers(1, &m_ui32FullScreenRectVBO); 628 glDeleteBuffers(1, &m_ui32BackgroundVBO); 629 630 // Release effects 631 glDeleteShader(m_uiPostVertShader); 632 glDeleteShader(m_uiPostFragShader); 633 glDeleteShader(m_uiBackgroundFragShader); 634 glDeleteShader(m_uiSceneVertShader); 635 glDeleteShader(m_uiSceneFragShader); 636 637 glDeleteProgram(m_PostShaderProgram.uiId); 638 glDeleteProgram(m_BackgroundShaderProgram.uiId); 639 glDeleteProgram(m_SceneShaderProgram.uiId); 640 641 // Tidy up the FBOs and renderbuffers 642 643 // Delete frame buffer objects 644 glDeleteFramebuffers(1, &m_uiFBO); 645 glDeleteFramebuffers(1, &m_uiFBOMultisampled); 646 647 // Delete our depth buffer 648 glDeleteRenderbuffers(1, &m_uiDepthBufferMultisampled); 649 glDeleteRenderbuffers(1, &m_uiColourBufferMultisampled); 650 651 // Delete buffer objects 652 glDeleteBuffers(m_Mask.nNumMesh, m_puiMaskVBO); 653 glDeleteBuffers(m_Mask.nNumMesh, m_puiMaskIBO); 654 655 // Release Print3D Textures 656 m_Print3D.ReleaseTextures(); 657 658 return true; 659 } 660 661 /*!**************************************************************************** 662 @Function RenderScene 663 @Return bool true if no error occurred 664 @Description Main rendering loop function of the program. The shell will 665 call this function every frame. 666 eglSwapBuffers() will be performed by PVRShell automatically. 667 PVRShell will also manage important OS events. 668 Will also manage relevant OS events. The user has access to 669 these events through an abstraction layer provided by PVRShell. 670 ******************************************************************************/ 671 bool OGLES3ColourGrading::RenderScene() 672 { 673 // Clears the colour buffer 674 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 675 676 unsigned long ulTime = PVRShellGetTime() - m_ulStartTime; 677 678 // Process input to switch between tone mapping operators 679 if(PVRShellIsKeyPressed(PVRShellKeyNameRIGHT)) 680 { 681 ++m_iCurrentLUT; 682 683 if(m_iCurrentLUT > eB) 684 m_iCurrentLUT = eA; 685 } 686 else if(PVRShellIsKeyPressed(PVRShellKeyNameLEFT)) 687 { 688 --m_iCurrentLUT; 689 690 if(m_iCurrentLUT < eA) 691 m_iCurrentLUT = eB; 692 } 693 694 // Render to our texture 695 { 696 // Bind our FBO 697 glBindFramebuffer(GL_FRAMEBUFFER, m_uiFBOMultisampled); 698 699 // Clear the colour and depth buffer of our FBO surface 700 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 701 702 glDisable(GL_CULL_FACE); 703 glDisable(GL_DEPTH_TEST); 704 705 // Bind the VBO 706 glBindBuffer(GL_ARRAY_BUFFER, m_ui32BackgroundVBO); 707 708 // Use shader program 709 glUseProgram(m_BackgroundShaderProgram.uiId); 710 711 // Enable the vertex attribute arrays 712 glEnableVertexAttribArray(VERTEX_ARRAY); 713 glEnableVertexAttribArray(TEXCOORD_ARRAY); 714 715 // Set the vertex attribute offsets 716 glVertexAttribPointer(VERTEX_ARRAY, 4, GL_FLOAT, GL_FALSE, m_ui32VertexStride, 0); 717 glVertexAttribPointer(TEXCOORD_ARRAY, 2, GL_FLOAT, GL_FALSE, m_ui32VertexStride, (void*) (4 * sizeof(GLfloat))); 718 719 // Bind texture 720 glActiveTexture(GL_TEXTURE0); 721 glBindTexture(GL_TEXTURE_2D, m_uiBackgroundTexture); 722 723 // Draw a screen-aligned quad. 724 725 // Draws a non-indexed triangle array 726 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); 727 728 // Safely disable the vertex attribute arrays 729 glDisableVertexAttribArray(VERTEX_ARRAY); 730 glDisableVertexAttribArray(TEXCOORD_ARRAY); 731 732 glEnable(GL_CULL_FACE); 733 glEnable(GL_DEPTH_TEST); 734 735 // Use shader program 736 glUseProgram(m_SceneShaderProgram.uiId); 737 738 // Rotate the model matrix 739 PVRTMat4 mModel = PVRTMat4::RotationY(ulTime * 0.0015f); 740 741 // Calculate model view projection matrix 742 PVRTMat4 mMVP = m_mViewProjection * mModel; 743 744 // Feeds Projection Model View matrix to the shaders 745 glUniformMatrix4fv(m_SceneShaderProgram.uiMVPMatrixLoc, 1, GL_FALSE, mMVP.ptr()); 746 747 PVRTVec3 vMsLightDir = (PVRTVec3(1, 1, 1) * PVRTMat3(mModel)).normalized(); 748 glUniform3fv(m_SceneShaderProgram.uiLightDirLoc, 1, vMsLightDir.ptr()); 749 750 glBindTexture(GL_TEXTURE_2D, m_uiMaskTexture); 751 752 // Now that the uniforms are set, call another function to actually draw the mesh. 753 DrawMesh(0); 754 755 // Unbind the VBO 756 glBindBuffer(GL_ARRAY_BUFFER, 0); 757 758 // Give the drivers a hint that we don't want the depth and stencil information stored for future use. 759 const GLenum attachments[] = { GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT }; 760 glInvalidateFramebuffer(GL_FRAMEBUFFER, 2, attachments); 761 762 // Blit and resolve the multisampled render buffer to the non-multisampled FBO 763 glBindFramebuffer(GL_READ_FRAMEBUFFER, m_uiFBOMultisampled); 764 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_uiFBO); 765 glBlitFramebuffer(0, 0, PVRShellGet(prefWidth), PVRShellGet(prefHeight), 0, 0, PVRShellGet(prefWidth), PVRShellGet(prefHeight), GL_COLOR_BUFFER_BIT, GL_NEAREST); 766 767 // We are done with rendering to our FBO so switch back to the back buffer. 768 glBindFramebuffer(GL_FRAMEBUFFER, m_i32OriginalFbo); 769 } 770 771 glDisable(GL_CULL_FACE); 772 glDisable(GL_DEPTH_TEST); 773 774 // Use shader program 775 glUseProgram(m_PostShaderProgram.uiId); 776 777 // Bind the VBO 778 glBindBuffer(GL_ARRAY_BUFFER, m_ui32FullScreenRectVBO); 779 780 // Enable the vertex attribute arrays 781 glEnableVertexAttribArray(VERTEX_ARRAY); 782 glEnableVertexAttribArray(TEXCOORD_ARRAY); 783 784 // Set the vertex attribute offsets 785 glVertexAttribPointer(VERTEX_ARRAY, 4, GL_FLOAT, GL_FALSE, m_ui32VertexStride, 0); 786 glVertexAttribPointer(TEXCOORD_ARRAY, 2, GL_FLOAT, GL_FALSE, m_ui32VertexStride, (void*) (4 * sizeof(GLfloat))); 787 788 // Bind texture 789 glActiveTexture(GL_TEXTURE0); 790 glBindTexture(GL_TEXTURE_2D, m_uiTextureToRenderTo); 791 792 glActiveTexture(GL_TEXTURE1); 793 glBindTexture(GL_TEXTURE_3D, m_uiLUTs[m_iCurrentLUT]); 794 795 // Draw a screen-aligned quad. 796 797 // Draw the left-hand side that shows the scene with the colour grading applied 798 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); 799 800 // Draw the right-hande side showing the scene how it looks without 801 glBindTexture(GL_TEXTURE_3D, m_uiLUTs[eIdentity]); 802 glDrawArrays(GL_TRIANGLE_STRIP, 2, 4); 803 804 // Safely disable the vertex attribute arrays 805 glDisableVertexAttribArray(VERTEX_ARRAY); 806 glDisableVertexAttribArray(TEXCOORD_ARRAY); 807 808 // Unbind the VBO 809 glBindBuffer(GL_ARRAY_BUFFER, 0); 810 811 // Render title 812 m_Print3D.DisplayDefaultTitle("Colour grading using 3D textures", c_pszLUTNames[m_iCurrentLUT], ePVRTPrint3DSDKLogo); 813 m_Print3D.Flush(); 814 815 return true; 816 } 817 818 /*!**************************************************************************** 819 @Function DrawMesh 820 @Input i32NodeIndex Node index of the mesh to draw 821 @Description Draws a SPODMesh after the model view matrix has been set and 822 the material prepared. 823 ******************************************************************************/ 824 void OGLES3ColourGrading::DrawMesh(const int i32NodeIndex) 825 { 826 int i32MeshIndex = m_Mask.pNode[i32NodeIndex].nIdx; 827 SPODMesh* pMesh = &m_Mask.pMesh[i32MeshIndex]; 828 829 // bind the VBO for the mesh 830 glBindBuffer(GL_ARRAY_BUFFER, m_puiMaskVBO[i32MeshIndex]); 831 // bind the index buffer, won't hurt if the handle is 0 832 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_puiMaskIBO[i32MeshIndex]); 833 834 // Enable the vertex attribute arrays 835 glEnableVertexAttribArray(VERTEX_ARRAY); 836 glEnableVertexAttribArray(NORMAL_ARRAY); 837 glEnableVertexAttribArray(TEXCOORD_ARRAY); 838 839 // Set the vertex attribute offsets 840 glVertexAttribPointer(VERTEX_ARRAY, 3, GL_FLOAT, GL_FALSE, pMesh->sVertex.nStride, pMesh->sVertex.pData); 841 glVertexAttribPointer(NORMAL_ARRAY, 3, GL_FLOAT, GL_FALSE, pMesh->sNormals.nStride, pMesh->sNormals.pData); 842 glVertexAttribPointer(TEXCOORD_ARRAY, 2, GL_FLOAT, GL_FALSE, pMesh->psUVW[0].nStride, pMesh->psUVW[0].pData); 843 844 // Indexed Triangle list 845 glDrawElements(GL_TRIANGLES, pMesh->nNumFaces*3, GL_UNSIGNED_SHORT, 0); 846 847 // Safely disable the vertex attribute arrays 848 glDisableVertexAttribArray(VERTEX_ARRAY); 849 glDisableVertexAttribArray(NORMAL_ARRAY); 850 glDisableVertexAttribArray(TEXCOORD_ARRAY); 851 852 glBindBuffer(GL_ARRAY_BUFFER, 0); 853 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); 854 } 855 856 /*!**************************************************************************** 857 @Function NewDemo 858 @Return PVRShell* The demo supplied by the user 859 @Description This function must be implemented by the user of the shell. 860 The user should return its PVRShell object defining the 861 behaviour of the application. 862 ******************************************************************************/ 863 PVRShell* NewDemo() 864 { 865 return new OGLES3ColourGrading(); 866 } 867 868 /****************************************************************************** 869 End of file (OGLES3ColourGrading.cpp) 870 ******************************************************************************/ 871 872