blob: f705b7f161a04f27b64479fc2fa9892e192952b3 [file] [log] [blame]
/******************************************************************************
@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)
******************************************************************************/