• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  ******************************************************************************/
InitApplication()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  ******************************************************************************/
QuitApplication()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  ******************************************************************************/
LoadShaders(CPVRTString & ErrorStr)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 ******************************************************************************/
LoadVbos(const bool bRotated)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 
CreateFBO()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  ******************************************************************************/
InitView()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  ******************************************************************************/
ReleaseView()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  ******************************************************************************/
RenderScene()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 ******************************************************************************/
DrawMesh(const int i32NodeIndex)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  ******************************************************************************/
NewDemo()863 PVRShell* NewDemo()
864 {
865 	return new OGLES3ColourGrading();
866 }
867 
868 /******************************************************************************
869  End of file (OGLES3ColourGrading.cpp)
870  ******************************************************************************/
871 
872