| /****************************************************************************** |
| |
| @File OGLES3ColourGrading.cpp |
| |
| @Title Colour grading |
| |
| @Version |
| |
| @Copyright Copyright (c) Imagination Technologies Limited. |
| |
| @Platform Independent |
| |
| @Description Demonstrates how to colour grade your render. |
| |
| ******************************************************************************/ |
| #include "PVRShell.h" |
| #include "OGLES3Tools.h" |
| |
| /****************************************************************************** |
| Constants |
| ******************************************************************************/ |
| const char* const c_pszMaskTexture = "MaskTexture.pvr"; |
| const char* const c_pszBackgroundTexture = "Background.pvr"; |
| |
| // Our colour lookup tables |
| const char* const c_pszLUTs[] = |
| { |
| "identity.pvr", |
| "bw.pvr", |
| "cooler.pvr", |
| "warmer.pvr", |
| "sepia.pvr", |
| "inverted.pvr", |
| "highcontrast.pvr", |
| "bluewhitegradient.pvr" |
| }; |
| |
| // Shader source |
| const char* const c_szFragShaderSrcFile = "FragShader.fsh"; |
| const char* const c_szVertShaderSrcFile = "VertShader.vsh"; |
| const char* const c_szSceneFragShaderSrcFile = "SceneFragShader.fsh"; |
| const char* const c_szSceneVertShaderSrcFile = "SceneVertShader.vsh"; |
| const char* const c_szBackgroundFragShaderSrcFile = "BackgroundFragShader.fsh"; |
| |
| // POD scene files |
| const char c_szSceneFile[] = "Mask.pod"; |
| |
| // Camera constants. Used for making the projection matrix |
| const float CAM_FOV = PVRT_PI / 6; |
| const float CAM_NEAR = 4.0f; |
| const float CAM_FAR = 5000.0f; |
| |
| // Index to bind the attributes to vertex shaders |
| const int VERTEX_ARRAY = 0; |
| const int TEXCOORD_ARRAY = 1; |
| const int NORMAL_ARRAY = 2; |
| |
| // Look up table enumeration |
| enum ELUTs |
| { |
| eIdentity, |
| eBW, |
| eCooler, |
| eWarmer, |
| eSepia, |
| eInverted, |
| eHighContrast, |
| eBlueWhiteGradient, |
| eLast, |
| |
| // The range to cycle through |
| eA = eBW, |
| eB = eBlueWhiteGradient |
| }; |
| |
| const char* const c_pszLUTNames[] = |
| { |
| "Identity", |
| "Black and white", |
| "Cooler", |
| "Warmer", |
| "Sepia", |
| "Inverted", |
| "High Contrast", |
| "Blue White Gradient" |
| }; |
| |
| /*!**************************************************************************** |
| Class implementing the PVRShell functions. |
| ******************************************************************************/ |
| class OGLES3ColourGrading : public PVRShell |
| { |
| // Print3D object |
| CPVRTPrint3D m_Print3D; |
| |
| // Texture handle |
| GLuint m_uiMaskTexture; |
| GLuint m_uiBackgroundTexture; |
| GLuint m_uiLUTs[eLast]; |
| int m_iCurrentLUT; |
| |
| // VBO handle |
| GLuint m_ui32FullScreenRectVBO; |
| |
| // Stride for vertex data |
| unsigned int m_ui32VertexStride; |
| |
| // 3D Model |
| CPVRTModelPOD m_Mask; |
| GLuint* m_puiMaskVBO; |
| GLuint* m_puiMaskIBO; |
| |
| GLuint m_ui32BackgroundVBO; |
| |
| // Projection and view matrices |
| PVRTMat4 m_mViewProjection; |
| |
| // Shaders |
| GLuint m_uiPostVertShader; |
| GLuint m_uiPostFragShader; |
| |
| struct |
| { |
| GLuint uiId; |
| } |
| m_PostShaderProgram; |
| |
| GLuint m_uiBackgroundFragShader; |
| |
| struct |
| { |
| GLuint uiId; |
| } |
| m_BackgroundShaderProgram; |
| |
| GLuint m_uiSceneVertShader; |
| GLuint m_uiSceneFragShader; |
| |
| struct |
| { |
| GLuint uiId; |
| GLuint uiMVPMatrixLoc; |
| GLuint uiLightDirLoc; |
| GLuint uiMaterialBiasLoc; |
| GLuint uiMaterialScaleLoc; |
| } |
| m_SceneShaderProgram; |
| |
| // Render contexts, etc |
| GLint m_i32OriginalFbo; |
| |
| // Texture IDs used by the app |
| GLuint m_uiTextureToRenderTo; |
| |
| // Handle for our FBO and the depth buffer that it requires |
| GLuint m_uiFBO; |
| |
| // Handle for our multi-sampled FBO and the depth buffer that it requires |
| GLuint m_uiFBOMultisampled; |
| GLuint m_uiDepthBufferMultisampled; |
| GLuint m_uiColourBufferMultisampled; |
| |
| // Start time |
| unsigned long m_ulStartTime; |
| |
| public: |
| // PVRShell functions |
| virtual bool InitApplication(); |
| virtual bool InitView(); |
| virtual bool ReleaseView(); |
| virtual bool QuitApplication(); |
| virtual bool RenderScene(); |
| |
| private: |
| bool LoadShaders(CPVRTString& ErrorStr); |
| bool CreateFBO(); |
| void LoadVbos(const bool bRotated); |
| void DrawMesh(const int i32NodeIndex); |
| }; |
| |
| |
| /*!**************************************************************************** |
| @Function InitApplication |
| @Return bool true if no error occurred |
| @Description Code in InitApplication() will be called by PVRShell once per |
| run, before the rendering context is created. |
| Used to initialize variables that are not dependent on it |
| (e.g. external modules, loading meshes, etc.) |
| If the rendering context is lost, InitApplication() will |
| not be called again. |
| ******************************************************************************/ |
| bool OGLES3ColourGrading::InitApplication() |
| { |
| // Get and set the read path for content files |
| CPVRTResourceFile::SetReadPath((char*)PVRShellGet(prefReadPath)); |
| |
| // Get and set the load/release functions for loading external files. |
| // In the majority of cases the PVRShell will return NULL function pointers implying that |
| // nothing special is required to load external files. |
| CPVRTResourceFile::SetLoadReleaseFunctions(PVRShellGet(prefLoadFileFunc), PVRShellGet(prefReleaseFileFunc)); |
| |
| // Load the scene |
| if(m_Mask.ReadFromFile(c_szSceneFile) != PVR_SUCCESS) |
| { |
| PVRShellSet(prefExitMessage, "ERROR: Couldn't load the .pod file\n"); |
| return false; |
| } |
| |
| // Initialise some variables |
| m_puiMaskVBO = m_puiMaskIBO = 0; |
| m_iCurrentLUT = eA; |
| return true; |
| } |
| |
| /*!**************************************************************************** |
| @Function QuitApplication |
| @Return bool true if no error occurred |
| @Description Code in QuitApplication() will be called by PVRShell once per |
| run, just before exiting the program. |
| If the rendering context is lost, QuitApplication() will |
| not be called. |
| ******************************************************************************/ |
| bool OGLES3ColourGrading::QuitApplication() |
| { |
| // Free the memory allocated for the scene |
| m_Mask.Destroy(); |
| |
| delete[] m_puiMaskVBO; |
| m_puiMaskVBO = 0; |
| |
| delete[] m_puiMaskIBO; |
| m_puiMaskIBO = 0; |
| return true; |
| } |
| |
| /*!**************************************************************************** |
| @Function LoadEffects |
| @Output ErrorStr A description of an error, if one occurs. |
| @Return bool true if no error occurred |
| @Description Loads and parses the bundled PFX and generates the various |
| effect objects. |
| ******************************************************************************/ |
| bool OGLES3ColourGrading::LoadShaders(CPVRTString& ErrorStr) |
| { |
| // Load and compile the shaders from files. |
| if(PVRTShaderLoadFromFile(NULL, c_szVertShaderSrcFile, GL_VERTEX_SHADER, GL_SGX_BINARY_IMG, &m_uiPostVertShader, &ErrorStr) != PVR_SUCCESS) |
| { |
| return false; |
| } |
| |
| if(PVRTShaderLoadFromFile(NULL, c_szFragShaderSrcFile, GL_FRAGMENT_SHADER, GL_SGX_BINARY_IMG, &m_uiPostFragShader, &ErrorStr) != PVR_SUCCESS) |
| { |
| return false; |
| } |
| |
| // Setup and link the shader program |
| const char* aszAttribs[] = { "inVertex", "inTexCoord" }; |
| if(PVRTCreateProgram(&m_PostShaderProgram.uiId, m_uiPostVertShader, m_uiPostFragShader, aszAttribs, 2, &ErrorStr) != PVR_SUCCESS) |
| { |
| return false; |
| } |
| |
| // Set the sampler variables to their respective texture unit |
| glUniform1i(glGetUniformLocation(m_PostShaderProgram.uiId, "sTexture"), 0); |
| glUniform1i(glGetUniformLocation(m_PostShaderProgram.uiId, "sColourLUT"), 1); |
| |
| // Background shader |
| |
| if(PVRTShaderLoadFromFile(NULL, c_szBackgroundFragShaderSrcFile, GL_FRAGMENT_SHADER, GL_SGX_BINARY_IMG, &m_uiBackgroundFragShader, &ErrorStr) != PVR_SUCCESS) |
| { |
| return false; |
| } |
| |
| // Set up and link the shader program re-using the vertex shader from the main shader program |
| const char* aszBackgroundAttribs[] = { "inVertex", "inTexCoord" }; |
| if(PVRTCreateProgram(&m_BackgroundShaderProgram.uiId, m_uiPostVertShader, m_uiBackgroundFragShader, aszBackgroundAttribs, 2, &ErrorStr) != PVR_SUCCESS) |
| { |
| return false; |
| } |
| |
| // Set the sampler2D variable to the first texture unit |
| glUniform1i(glGetUniformLocation(m_BackgroundShaderProgram.uiId, "sTexture"), 0); |
| |
| // Scene shaders - Used for rendering the mask |
| if(PVRTShaderLoadFromFile(NULL, c_szSceneVertShaderSrcFile, GL_VERTEX_SHADER, GL_SGX_BINARY_IMG, &m_uiSceneVertShader, &ErrorStr) != PVR_SUCCESS) |
| { |
| return false; |
| } |
| |
| if(PVRTShaderLoadFromFile(NULL, c_szSceneFragShaderSrcFile, GL_FRAGMENT_SHADER, GL_SGX_BINARY_IMG, &m_uiSceneFragShader, &ErrorStr) != PVR_SUCCESS) |
| { |
| return false; |
| } |
| |
| // Setup and link the shader program |
| const char* aszSceneAttribs[] = { "inVertex", "inTexCoord", "inNormal" }; |
| if (PVRTCreateProgram(&m_SceneShaderProgram.uiId, m_uiSceneVertShader, m_uiSceneFragShader, aszSceneAttribs, 3, &ErrorStr) != PVR_SUCCESS) |
| { |
| return false; |
| } |
| |
| // Set the sampler2D variable to the first texture unit |
| glUniform1i(glGetUniformLocation(m_SceneShaderProgram.uiId, "sTexture"), 0); |
| |
| // Store the location of uniforms for later use |
| m_SceneShaderProgram.uiMVPMatrixLoc = glGetUniformLocation(m_SceneShaderProgram.uiId, "MVPMatrix"); |
| m_SceneShaderProgram.uiLightDirLoc = glGetUniformLocation(m_SceneShaderProgram.uiId, "LightDirection"); |
| m_SceneShaderProgram.uiMaterialBiasLoc = glGetUniformLocation(m_SceneShaderProgram.uiId, "MaterialBias"); |
| m_SceneShaderProgram.uiMaterialScaleLoc = glGetUniformLocation(m_SceneShaderProgram.uiId, "MaterialScale"); |
| |
| // Set default shader material uniforms |
| float fSpecularConcentration = 0.6f; // a value from 0 to 1 (wider, concentrated) |
| float fSpecularIntensity = 0.3f; // a value from 0 to 1 |
| |
| // Specular bias |
| glUniform1f(m_SceneShaderProgram.uiMaterialBiasLoc, fSpecularConcentration); |
| // Specular intensity scale |
| glUniform1f(m_SceneShaderProgram.uiMaterialScaleLoc, fSpecularIntensity / (1.0f - fSpecularConcentration)); |
| |
| return true; |
| } |
| |
| /*!**************************************************************************** |
| @Function LoadVbos |
| @Description Loads the mesh data required for this training course into |
| vertex buffer objects |
| ******************************************************************************/ |
| void OGLES3ColourGrading::LoadVbos(const bool bRotated) |
| { |
| if(!m_puiMaskVBO) |
| m_puiMaskVBO = new GLuint[m_Mask.nNumMesh]; |
| |
| if(!m_puiMaskIBO) |
| m_puiMaskIBO = new GLuint[m_Mask.nNumMesh]; |
| |
| /* |
| Load vertex data of all meshes in the scene into VBOs |
| |
| The meshes have been exported with the "Interleave Vectors" option, |
| so all data is interleaved in the buffer at pMesh->pInterleaved. |
| Interleaving data improves the memory access pattern and cache efficiency, |
| thus it can be read faster by the hardware. |
| */ |
| glGenBuffers(m_Mask.nNumMesh, m_puiMaskVBO); |
| for(unsigned int i = 0; i < m_Mask.nNumMesh; ++i) |
| { |
| // Load vertex data into buffer object |
| SPODMesh& Mesh = m_Mask.pMesh[i]; |
| unsigned int uiSize = Mesh.nNumVertex * Mesh.sVertex.nStride; |
| glBindBuffer(GL_ARRAY_BUFFER, m_puiMaskVBO[i]); |
| glBufferData(GL_ARRAY_BUFFER, uiSize, Mesh.pInterleaved, GL_STATIC_DRAW); |
| |
| // Load index data into buffer object if available |
| m_puiMaskIBO[i] = 0; |
| if (Mesh.sFaces.pData) |
| { |
| glGenBuffers(1, &m_puiMaskIBO[i]); |
| uiSize = PVRTModelPODCountIndices(Mesh) * sizeof(GLshort); |
| glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_puiMaskIBO[i]); |
| glBufferData(GL_ELEMENT_ARRAY_BUFFER, uiSize, Mesh.sFaces.pData, GL_STATIC_DRAW); |
| } |
| } |
| |
| // Create VBO for the fullscreen rect that we'll be rendering our FBO to |
| |
| // Interleaved vertex data |
| GLfloat afVertices[] = { |
| // Left quad |
| -1.0f, 1.0f, 0.0f, 1.0f, // Pos |
| 0.0f, 1.0f, // UVs |
| |
| -1.0f, -1.0f, 0.0f, 1.0f, |
| 0.0f, 0.0f, |
| |
| 0.0f, 1.0f, 0.0f, 1.0f, |
| 0.5f, 1.0f, |
| |
| 0.0f, -1.0f, 0.0f, 1.0f, |
| 0.5f, 0.0f, |
| |
| 1.0f, 1.0f, 0.0f, 1.0f, |
| 1.0f, 1.0f, |
| |
| 1.0f, -1.0f, 0.0f, 1.0f, |
| 1.0f, 0.0f, |
| }; |
| |
| if(bRotated) // If we're rotated then pre-process the fullscreen rect's geometry to compensate |
| { |
| for(unsigned int i = 0; i < 6; ++i) |
| { |
| float fTmp = afVertices[i * 6 + 1]; |
| afVertices[i * 6 + 1] = afVertices[i * 6]; |
| afVertices[i * 6] = fTmp; |
| |
| fTmp = afVertices[i * 6 + 5]; |
| afVertices[i * 6 + 5] = afVertices[i * 6 + 4]; |
| afVertices[i * 6 + 4] = fTmp; |
| } |
| } |
| |
| glGenBuffers(1, &m_ui32FullScreenRectVBO); |
| m_ui32VertexStride = 6 * sizeof(GLfloat); // 4 floats for the pos, 2 for the UVs |
| |
| // Bind the VBO |
| glBindBuffer(GL_ARRAY_BUFFER, m_ui32FullScreenRectVBO); |
| |
| // Set the buffer's data |
| glBufferData(GL_ARRAY_BUFFER, 6 * m_ui32VertexStride, afVertices, GL_STATIC_DRAW); |
| |
| // Create the VBO for the background |
| GLfloat afBackgroundVertices[] = { |
| // Left quad |
| -1.0f, 1.0f, 0.0f, 1.0f, // Pos |
| 0.0f, 1.0f, // UVs |
| |
| -1.0f, -1.0f, 0.0f, 1.0f, |
| 0.0f, 0.0f, |
| |
| 1.0f, 1.0f, 0.0f, 1.0f, |
| 1.0f, 1.0f, |
| |
| 1.0f, -1.0f, 0.0f, 1.0f, |
| 1.0f, 0.0f, |
| }; |
| |
| if(bRotated) // If we're rotated then pre-process the background geometry |
| { |
| for(unsigned int i = 0; i < 4; ++i) |
| { |
| float fTmp = afBackgroundVertices[i * 6 + 1]; |
| afBackgroundVertices[i * 6 + 1] = -afBackgroundVertices[i * 6]; |
| afBackgroundVertices[i * 6] = -fTmp; |
| } |
| } |
| |
| glGenBuffers(1, &m_ui32BackgroundVBO); |
| |
| // Bind the VBO |
| glBindBuffer(GL_ARRAY_BUFFER, m_ui32BackgroundVBO); |
| |
| // Set the buffer's data |
| glBufferData(GL_ARRAY_BUFFER, 4 * m_ui32VertexStride, afBackgroundVertices, GL_STATIC_DRAW); |
| |
| // Unbind our buffers |
| glBindBuffer(GL_ARRAY_BUFFER, 0); |
| glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); |
| } |
| |
| bool OGLES3ColourGrading::CreateFBO() |
| { |
| GLenum drawBuffers[] = { GL_COLOR_ATTACHMENT0 }; |
| |
| // Query the max amount of samples that are supported, we are going to use the max |
| GLint samples; |
| glGetIntegerv(GL_MAX_SAMPLES, &samples); |
| |
| // Get the currently bound frame buffer object. On most platforms this just gives 0. |
| glGetIntegerv(GL_FRAMEBUFFER_BINDING, &m_i32OriginalFbo); |
| |
| // Create a texture for rendering to |
| glGenTextures(1, &m_uiTextureToRenderTo); |
| glBindTexture(GL_TEXTURE_2D, m_uiTextureToRenderTo); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, PVRShellGet(prefWidth), PVRShellGet(prefHeight), 0, GL_RGB, GL_UNSIGNED_BYTE, 0); |
| glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
| glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
| glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
| glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
| |
| // Create the object that will allow us to render to the aforementioned texture |
| glGenFramebuffers(1, &m_uiFBO); |
| glBindFramebuffer(GL_FRAMEBUFFER, m_uiFBO); |
| |
| glDrawBuffers(1, drawBuffers); |
| glReadBuffer(GL_COLOR_ATTACHMENT0); |
| |
| // Attach the texture to the FBO |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_uiTextureToRenderTo, 0); |
| |
| // Check that our FBO creation was successful |
| GLuint uStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER); |
| if(uStatus != GL_FRAMEBUFFER_COMPLETE) |
| { |
| PVRShellSet(prefExitMessage, "ERROR: Failed to initialise FBO"); |
| return false; |
| } |
| |
| // Create and initialize the multi-sampled FBO. |
| |
| // Create the object that will allow us to render to the aforementioned texture |
| glGenFramebuffers(1, &m_uiFBOMultisampled); |
| glBindFramebuffer(GL_FRAMEBUFFER, m_uiFBOMultisampled); |
| |
| glDrawBuffers(1, drawBuffers); |
| glReadBuffer(GL_COLOR_ATTACHMENT0); |
| |
| // Generate and bind a render buffer which will become a multisampled depth buffer shared between our two FBOs |
| glGenRenderbuffers(1, &m_uiDepthBufferMultisampled); |
| glBindRenderbuffer(GL_RENDERBUFFER, m_uiDepthBufferMultisampled); |
| glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, GL_DEPTH_COMPONENT24, PVRShellGet(prefWidth), PVRShellGet(prefHeight)); |
| |
| glGenRenderbuffers(1, &m_uiColourBufferMultisampled); |
| glBindRenderbuffer(GL_RENDERBUFFER, m_uiColourBufferMultisampled); |
| glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, GL_RGB8, PVRShellGet(prefWidth), PVRShellGet(prefHeight)); |
| glBindRenderbuffer(GL_RENDERBUFFER, 0); |
| |
| // Attach the multisampled depth buffer we created earlier to our FBO. |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_uiDepthBufferMultisampled); |
| |
| // Attach the multisampled colour renderbuffer to the FBO |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_uiColourBufferMultisampled); |
| |
| // Check that our FBO creation was successful |
| uStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER); |
| if(uStatus != GL_FRAMEBUFFER_COMPLETE) |
| { |
| PVRShellSet(prefExitMessage, "ERROR: Failed to initialise multisampled FBO"); |
| return false; |
| } |
| |
| // Unbind the frame buffer object so rendering returns back to the backbuffer |
| glBindFramebuffer(GL_FRAMEBUFFER, m_i32OriginalFbo); |
| |
| return true; |
| } |
| |
| /*!**************************************************************************** |
| @Function InitView |
| @Return bool true if no error occurred |
| @Description Code in InitView() will be called by PVRShell upon |
| initialization or after a change in the rendering context. |
| Used to initialize variables that are dependent on the rendering |
| context (e.g. textures, vertex buffers, etc.) |
| ******************************************************************************/ |
| bool OGLES3ColourGrading::InitView() |
| { |
| // Initialize the textures used by Print3D. |
| bool bRotate = PVRShellGet(prefIsRotated) && PVRShellGet(prefFullScreen); |
| |
| if(m_Print3D.SetTextures(0, PVRShellGet(prefWidth), PVRShellGet(prefHeight), bRotate) != PVR_SUCCESS) |
| { |
| PVRShellSet(prefExitMessage, "ERROR: Cannot initialise Print3D\n"); |
| return false; |
| } |
| |
| // Create the texture |
| if(PVRTTextureLoadFromPVR(c_pszMaskTexture, &m_uiMaskTexture) != PVR_SUCCESS) |
| { |
| PVRShellSet(prefExitMessage, "ERROR: Failed to load mask texture\n"); |
| return false; |
| } |
| |
| if(PVRTTextureLoadFromPVR(c_pszBackgroundTexture, &m_uiBackgroundTexture) != PVR_SUCCESS) |
| { |
| PVRShellSet(prefExitMessage, "ERROR: Failed to load background texture\n"); |
| return false; |
| } |
| |
| // Load our 3D texture look up tables |
| for(unsigned int i = 0; i < eLast; ++i) |
| { |
| if(PVRTTextureLoadFromPVR(c_pszLUTs[i], &m_uiLUTs[i]) != PVR_SUCCESS) |
| { |
| PVRShellSet(prefExitMessage, "ERROR: Failed to load a 3D texture\n"); |
| return false; |
| } |
| |
| glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
| glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
| glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); |
| |
| glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
| glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
| } |
| |
| // Load the effects |
| CPVRTString ErrorStr; |
| if(!LoadShaders(ErrorStr)) |
| { |
| PVRShellSet(prefExitMessage, ErrorStr.c_str()); |
| return false; |
| } |
| |
| // Create FBOs |
| if(!CreateFBO()) |
| { |
| PVRShellSet(prefExitMessage, "Failed to create FBO"); |
| return false; |
| } |
| |
| // Initialise VBO data |
| LoadVbos(bRotate); |
| |
| // Calculate the projection and view matrices |
| float fAspect = PVRShellGet(prefWidth) / (float)PVRShellGet(prefHeight); |
| m_mViewProjection = PVRTMat4::PerspectiveFovRH(CAM_FOV, fAspect, CAM_NEAR, CAM_FAR, PVRTMat4::OGL, bRotate); |
| m_mViewProjection *= PVRTMat4::LookAtRH(PVRTVec3(0.f, 0.f, 150.f), PVRTVec3(0.f), PVRTVec3(0.f, 1.f, 0.f)); |
| |
| // Enable backface culling and depth test |
| glEnable(GL_CULL_FACE); |
| glEnable(GL_DEPTH_TEST); |
| |
| // Set the clear colour |
| glClearColor(1.0f, 0.0f, 0.0f, 1.0f); |
| |
| // Store initial time |
| m_ulStartTime = PVRShellGetTime(); |
| |
| return true; |
| } |
| |
| /*!**************************************************************************** |
| @Function ReleaseView |
| @Return bool true if no error occurred |
| @Description Code in ReleaseView() will be called by PVRShell when the |
| application quits or before a change in the rendering context. |
| ******************************************************************************/ |
| bool OGLES3ColourGrading::ReleaseView() |
| { |
| // Frees the texture |
| glDeleteTextures(1, &m_uiMaskTexture); |
| glDeleteTextures(1, &m_uiBackgroundTexture); |
| glDeleteTextures(eLast, m_uiLUTs); |
| glDeleteTextures(1, &m_uiTextureToRenderTo); |
| |
| // Release Vertex buffer object. |
| glDeleteBuffers(1, &m_ui32FullScreenRectVBO); |
| glDeleteBuffers(1, &m_ui32BackgroundVBO); |
| |
| // Release effects |
| glDeleteShader(m_uiPostVertShader); |
| glDeleteShader(m_uiPostFragShader); |
| glDeleteShader(m_uiBackgroundFragShader); |
| glDeleteShader(m_uiSceneVertShader); |
| glDeleteShader(m_uiSceneFragShader); |
| |
| glDeleteProgram(m_PostShaderProgram.uiId); |
| glDeleteProgram(m_BackgroundShaderProgram.uiId); |
| glDeleteProgram(m_SceneShaderProgram.uiId); |
| |
| // Tidy up the FBOs and renderbuffers |
| |
| // Delete frame buffer objects |
| glDeleteFramebuffers(1, &m_uiFBO); |
| glDeleteFramebuffers(1, &m_uiFBOMultisampled); |
| |
| // Delete our depth buffer |
| glDeleteRenderbuffers(1, &m_uiDepthBufferMultisampled); |
| glDeleteRenderbuffers(1, &m_uiColourBufferMultisampled); |
| |
| // Delete buffer objects |
| glDeleteBuffers(m_Mask.nNumMesh, m_puiMaskVBO); |
| glDeleteBuffers(m_Mask.nNumMesh, m_puiMaskIBO); |
| |
| // Release Print3D Textures |
| m_Print3D.ReleaseTextures(); |
| |
| return true; |
| } |
| |
| /*!**************************************************************************** |
| @Function RenderScene |
| @Return bool true if no error occurred |
| @Description Main rendering loop function of the program. The shell will |
| call this function every frame. |
| eglSwapBuffers() will be performed by PVRShell automatically. |
| PVRShell will also manage important OS events. |
| Will also manage relevant OS events. The user has access to |
| these events through an abstraction layer provided by PVRShell. |
| ******************************************************************************/ |
| bool OGLES3ColourGrading::RenderScene() |
| { |
| // Clears the colour buffer |
| glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); |
| |
| unsigned long ulTime = PVRShellGetTime() - m_ulStartTime; |
| |
| // Process input to switch between tone mapping operators |
| if(PVRShellIsKeyPressed(PVRShellKeyNameRIGHT)) |
| { |
| ++m_iCurrentLUT; |
| |
| if(m_iCurrentLUT > eB) |
| m_iCurrentLUT = eA; |
| } |
| else if(PVRShellIsKeyPressed(PVRShellKeyNameLEFT)) |
| { |
| --m_iCurrentLUT; |
| |
| if(m_iCurrentLUT < eA) |
| m_iCurrentLUT = eB; |
| } |
| |
| // Render to our texture |
| { |
| // Bind our FBO |
| glBindFramebuffer(GL_FRAMEBUFFER, m_uiFBOMultisampled); |
| |
| // Clear the colour and depth buffer of our FBO surface |
| glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); |
| |
| glDisable(GL_CULL_FACE); |
| glDisable(GL_DEPTH_TEST); |
| |
| // Bind the VBO |
| glBindBuffer(GL_ARRAY_BUFFER, m_ui32BackgroundVBO); |
| |
| // Use shader program |
| glUseProgram(m_BackgroundShaderProgram.uiId); |
| |
| // Enable the vertex attribute arrays |
| glEnableVertexAttribArray(VERTEX_ARRAY); |
| glEnableVertexAttribArray(TEXCOORD_ARRAY); |
| |
| // Set the vertex attribute offsets |
| glVertexAttribPointer(VERTEX_ARRAY, 4, GL_FLOAT, GL_FALSE, m_ui32VertexStride, 0); |
| glVertexAttribPointer(TEXCOORD_ARRAY, 2, GL_FLOAT, GL_FALSE, m_ui32VertexStride, (void*) (4 * sizeof(GLfloat))); |
| |
| // Bind texture |
| glActiveTexture(GL_TEXTURE0); |
| glBindTexture(GL_TEXTURE_2D, m_uiBackgroundTexture); |
| |
| // Draw a screen-aligned quad. |
| |
| // Draws a non-indexed triangle array |
| glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); |
| |
| // Safely disable the vertex attribute arrays |
| glDisableVertexAttribArray(VERTEX_ARRAY); |
| glDisableVertexAttribArray(TEXCOORD_ARRAY); |
| |
| glEnable(GL_CULL_FACE); |
| glEnable(GL_DEPTH_TEST); |
| |
| // Use shader program |
| glUseProgram(m_SceneShaderProgram.uiId); |
| |
| // Rotate the model matrix |
| PVRTMat4 mModel = PVRTMat4::RotationY(ulTime * 0.0015f); |
| |
| // Calculate model view projection matrix |
| PVRTMat4 mMVP = m_mViewProjection * mModel; |
| |
| // Feeds Projection Model View matrix to the shaders |
| glUniformMatrix4fv(m_SceneShaderProgram.uiMVPMatrixLoc, 1, GL_FALSE, mMVP.ptr()); |
| |
| PVRTVec3 vMsLightDir = (PVRTVec3(1, 1, 1) * PVRTMat3(mModel)).normalized(); |
| glUniform3fv(m_SceneShaderProgram.uiLightDirLoc, 1, vMsLightDir.ptr()); |
| |
| glBindTexture(GL_TEXTURE_2D, m_uiMaskTexture); |
| |
| // Now that the uniforms are set, call another function to actually draw the mesh. |
| DrawMesh(0); |
| |
| // Unbind the VBO |
| glBindBuffer(GL_ARRAY_BUFFER, 0); |
| |
| // Give the drivers a hint that we don't want the depth and stencil information stored for future use. |
| const GLenum attachments[] = { GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT }; |
| glInvalidateFramebuffer(GL_FRAMEBUFFER, 2, attachments); |
| |
| // Blit and resolve the multisampled render buffer to the non-multisampled FBO |
| glBindFramebuffer(GL_READ_FRAMEBUFFER, m_uiFBOMultisampled); |
| glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_uiFBO); |
| glBlitFramebuffer(0, 0, PVRShellGet(prefWidth), PVRShellGet(prefHeight), 0, 0, PVRShellGet(prefWidth), PVRShellGet(prefHeight), GL_COLOR_BUFFER_BIT, GL_NEAREST); |
| |
| // We are done with rendering to our FBO so switch back to the back buffer. |
| glBindFramebuffer(GL_FRAMEBUFFER, m_i32OriginalFbo); |
| } |
| |
| glDisable(GL_CULL_FACE); |
| glDisable(GL_DEPTH_TEST); |
| |
| // Use shader program |
| glUseProgram(m_PostShaderProgram.uiId); |
| |
| // Bind the VBO |
| glBindBuffer(GL_ARRAY_BUFFER, m_ui32FullScreenRectVBO); |
| |
| // Enable the vertex attribute arrays |
| glEnableVertexAttribArray(VERTEX_ARRAY); |
| glEnableVertexAttribArray(TEXCOORD_ARRAY); |
| |
| // Set the vertex attribute offsets |
| glVertexAttribPointer(VERTEX_ARRAY, 4, GL_FLOAT, GL_FALSE, m_ui32VertexStride, 0); |
| glVertexAttribPointer(TEXCOORD_ARRAY, 2, GL_FLOAT, GL_FALSE, m_ui32VertexStride, (void*) (4 * sizeof(GLfloat))); |
| |
| // Bind texture |
| glActiveTexture(GL_TEXTURE0); |
| glBindTexture(GL_TEXTURE_2D, m_uiTextureToRenderTo); |
| |
| glActiveTexture(GL_TEXTURE1); |
| glBindTexture(GL_TEXTURE_3D, m_uiLUTs[m_iCurrentLUT]); |
| |
| // Draw a screen-aligned quad. |
| |
| // Draw the left-hand side that shows the scene with the colour grading applied |
| glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); |
| |
| // Draw the right-hande side showing the scene how it looks without |
| glBindTexture(GL_TEXTURE_3D, m_uiLUTs[eIdentity]); |
| glDrawArrays(GL_TRIANGLE_STRIP, 2, 4); |
| |
| // Safely disable the vertex attribute arrays |
| glDisableVertexAttribArray(VERTEX_ARRAY); |
| glDisableVertexAttribArray(TEXCOORD_ARRAY); |
| |
| // Unbind the VBO |
| glBindBuffer(GL_ARRAY_BUFFER, 0); |
| |
| // Render title |
| m_Print3D.DisplayDefaultTitle("Colour grading using 3D textures", c_pszLUTNames[m_iCurrentLUT], ePVRTPrint3DSDKLogo); |
| m_Print3D.Flush(); |
| |
| return true; |
| } |
| |
| /*!**************************************************************************** |
| @Function DrawMesh |
| @Input i32NodeIndex Node index of the mesh to draw |
| @Description Draws a SPODMesh after the model view matrix has been set and |
| the material prepared. |
| ******************************************************************************/ |
| void OGLES3ColourGrading::DrawMesh(const int i32NodeIndex) |
| { |
| int i32MeshIndex = m_Mask.pNode[i32NodeIndex].nIdx; |
| SPODMesh* pMesh = &m_Mask.pMesh[i32MeshIndex]; |
| |
| // bind the VBO for the mesh |
| glBindBuffer(GL_ARRAY_BUFFER, m_puiMaskVBO[i32MeshIndex]); |
| // bind the index buffer, won't hurt if the handle is 0 |
| glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_puiMaskIBO[i32MeshIndex]); |
| |
| // Enable the vertex attribute arrays |
| glEnableVertexAttribArray(VERTEX_ARRAY); |
| glEnableVertexAttribArray(NORMAL_ARRAY); |
| glEnableVertexAttribArray(TEXCOORD_ARRAY); |
| |
| // Set the vertex attribute offsets |
| glVertexAttribPointer(VERTEX_ARRAY, 3, GL_FLOAT, GL_FALSE, pMesh->sVertex.nStride, pMesh->sVertex.pData); |
| glVertexAttribPointer(NORMAL_ARRAY, 3, GL_FLOAT, GL_FALSE, pMesh->sNormals.nStride, pMesh->sNormals.pData); |
| glVertexAttribPointer(TEXCOORD_ARRAY, 2, GL_FLOAT, GL_FALSE, pMesh->psUVW[0].nStride, pMesh->psUVW[0].pData); |
| |
| // Indexed Triangle list |
| glDrawElements(GL_TRIANGLES, pMesh->nNumFaces*3, GL_UNSIGNED_SHORT, 0); |
| |
| // Safely disable the vertex attribute arrays |
| glDisableVertexAttribArray(VERTEX_ARRAY); |
| glDisableVertexAttribArray(NORMAL_ARRAY); |
| glDisableVertexAttribArray(TEXCOORD_ARRAY); |
| |
| glBindBuffer(GL_ARRAY_BUFFER, 0); |
| glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); |
| } |
| |
| /*!**************************************************************************** |
| @Function NewDemo |
| @Return PVRShell* The demo supplied by the user |
| @Description This function must be implemented by the user of the shell. |
| The user should return its PVRShell object defining the |
| behaviour of the application. |
| ******************************************************************************/ |
| PVRShell* NewDemo() |
| { |
| return new OGLES3ColourGrading(); |
| } |
| |
| /****************************************************************************** |
| End of file (OGLES3ColourGrading.cpp) |
| ******************************************************************************/ |
| |