renderer.cpp
/* |
Copyright (C) 2015 Apple Inc. All Rights Reserved. |
See LICENSE.txt for this sample’s licensing information |
Abstract: |
The renderer class creates and draws the OpenGL shaders. |
*/ |
#include "renderer.h" |
extern "C" { |
#include "matrixUtil.h" |
} |
#include <stdlib.h> |
#include <stdio.h> |
#include <string.h> |
#include <assert.h> |
#include "teapot.h" |
static inline const char * GetGLErrorString(GLenum error) |
{ |
const char *str; |
switch( error ) |
{ |
case GL_NO_ERROR: |
str = "GL_NO_ERROR"; |
break; |
case GL_INVALID_ENUM: |
str = "GL_INVALID_ENUM"; |
break; |
case GL_INVALID_VALUE: |
str = "GL_INVALID_VALUE"; |
break; |
case GL_INVALID_OPERATION: |
str = "GL_INVALID_OPERATION"; |
break; |
#if defined __gl_h_ || defined __gl3_h_ |
case GL_OUT_OF_MEMORY: |
str = "GL_OUT_OF_MEMORY"; |
break; |
case GL_INVALID_FRAMEBUFFER_OPERATION: |
str = "GL_INVALID_FRAMEBUFFER_OPERATION"; |
break; |
#endif |
#if defined __gl_h_ |
case GL_STACK_OVERFLOW: |
str = "GL_STACK_OVERFLOW"; |
break; |
case GL_STACK_UNDERFLOW: |
str = "GL_STACK_UNDERFLOW"; |
break; |
case GL_TABLE_TOO_LARGE: |
str = "GL_TABLE_TOO_LARGE"; |
break; |
#endif |
default: |
str = "(ERROR: Unknown Error Enum)"; |
break; |
} |
return str; |
} |
#define glError() { \ |
GLenum err = glGetError(); \ |
while (err != GL_NO_ERROR) { \ |
__builtin_printf("glError: %s caught at %s:%u\n", (char *)GetGLErrorString(err), __FILE__, __LINE__); \ |
assert(0 && "Dying..."); \ |
} \ |
} |
#define VERTEX_SHADER_TRANSFORM_NAME "vertexShaderTransform.vs" |
#define FRAGMENT_SHADER_RECORD_GBUFFER_NAME "fragmentShaderRecordGBuffer.fs" |
#define PASSTHROUGH_NAME "passthrough.vs" |
#define DEFERRED_LIGHTNING_NAME "deferredLighting.fs" |
#define BUFFER_DISPLAY_NAME "bufferDisplay.fs" |
#define BUFFER_OFFSET(i) ((char *)NULL + (i)) |
enum {Quad_VAO, Teapot_VAO, NumVAOs}; // Vertex array object names |
GLuint VAOs[NumVAOs]; |
// Indexes to bind user-defined varying out fragment shader variables |
enum { |
FRAG_POS_ATTRIB_IDX, |
FRAG_COLOR_ATTRIB_IDX, |
FRAG_NORMAL_ATTRIB_IDX |
}; |
// Indicies to which we will set vertex array attibutes |
enum { |
POS_ATTRIB_IDX, |
NORMAL_ATTRIB_IDX, |
TEXCOORD_ATTRIB_IDX |
}; |
GLboolean loadShader(GLenum shaderType, const GLchar** shaderText, GLint* shaderID); |
GLboolean linkShaders(GLint* program, GLint vertShaderID, GLint fragShaderID); |
#pragma mark Constructor/Destructor |
OpenGLRenderer::OpenGLRenderer(const char* pathToResources, size_t len) |
{ |
resourcePath = (char*) malloc(len+1); |
bzero(resourcePath, len+1); |
strncpy(resourcePath, pathToResources, len); |
rendererString = glGetString(GL_RENDERER); |
showBuffer = kFinalImage; |
animate = GL_FALSE; |
animationStep = 0; |
animationDelta = 1.5; |
kAnimationLoopValue = 360.0 / animationDelta; |
} |
OpenGLRenderer::~OpenGLRenderer() |
{ |
glFinish(); |
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); |
glBindBuffer(GL_ARRAY_BUFFER, 0); |
glDeleteBuffers(2, (const GLuint*) &quadID); |
glDeleteTextures(kNumTextures, tex); |
glDeleteFramebuffers(kNumFBO, fbos); |
free(resourcePath); |
glUseProgram(0); |
unsigned int i = 0; |
glDeleteProgram(recordGBufferDataPass); |
glDeleteProgram(deferredLightingPass); |
glDeleteProgram(displayTextureContentsPass); |
for (; i < kShaderCount; i++) { |
glDeleteShader(shaders[i]); |
} |
} |
#pragma mark Shader Loading |
GLboolean OpenGLRenderer::loadShaderFromFile(const char* shaderName, GLenum shaderType, GLint* shaderID) |
{ |
char pathToShader[255]; |
sprintf(&pathToShader[0], "%s/%s", resourcePath, shaderName); |
FILE* f = fopen(pathToShader, "rb"); |
if(!f) |
{ |
__builtin_printf("Could not open file %s\n", pathToShader); |
return NULL; |
} |
fseek(f, 0, SEEK_END); |
size_t shaderLen = ftell(f); |
fseek(f, 0, SEEK_SET); |
GLchar* code = (GLchar*) malloc(shaderLen+1); |
fread(code, sizeof(char), shaderLen, f); |
fclose(f); |
code[shaderLen] = '\0'; |
GLboolean r = loadShader(shaderType, (const GLchar**) &code, shaderID); |
if(!r) |
{ |
__builtin_printf("Failed to load %s\n", shaderName); |
} |
free(code); |
return r; |
} |
GLboolean loadShader(GLenum shaderType, const GLchar** shaderText, GLint* shaderID) |
{ |
GLint status = 0; |
// Create a shader object |
*shaderID = glCreateShader(shaderType); |
// Set the source code in the shader object |
glShaderSource(*shaderID, 1, shaderText, NULL); |
// Compile the shader object |
glCompileShader(*shaderID); |
// Verify compile status |
glGetShaderiv(*shaderID, GL_COMPILE_STATUS, &status); |
if(status == GL_FALSE) |
{ |
GLint logLength = 0; |
glGetShaderiv(*shaderID, GL_INFO_LOG_LENGTH, &logLength); |
GLcharARB *log = (GLcharARB*) malloc(logLength); |
glGetShaderInfoLog(*shaderID, logLength, &logLength, log); |
__builtin_printf("Shader compile log\n %s", log); |
free(log); |
return GL_FALSE; |
} |
return GL_TRUE; |
} |
void OpenGLRenderer::attachShadersToProgram(GLint program, GLint vertShaderID, GLint fragShaderID) |
{ |
// Attach the shader to our program |
glAttachShader(program, vertShaderID); |
glAttachShader(program, fragShaderID); |
} |
GLboolean OpenGLRenderer::linkAndValidateProgram(GLint program, GLint vertShaderID, GLint fragShaderID) |
{ |
GLint logLength; |
glLinkProgram(program); |
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logLength); |
if (logLength > 0) { |
GLchar *log = (GLchar*) malloc(logLength); |
glGetProgramInfoLog(program, logLength, &logLength, log); |
printf("Program link log:\n%s\n", log); |
free(log); |
glDeleteShader(vertShaderID); |
glDeleteShader(fragShaderID); |
return GL_FALSE; |
} |
glValidateProgram(program); |
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logLength); |
if (logLength > 0) { |
GLchar *log = (GLchar*)malloc(logLength); |
glGetProgramInfoLog(program, logLength, &logLength, log); |
printf("Program validate log:\n%s\n", log); |
free(log); |
return GL_FALSE; |
} |
GLint status = 0; |
glGetProgramiv(program, GL_VALIDATE_STATUS, &status); |
if (status == 0) |
{ |
printf("Failed to validate program %d\n", program); |
return GL_FALSE; |
} |
return GL_TRUE; |
} |
GLshort OpenGLRenderer::loadShaders() |
{ |
if(!loadShaderFromFile(VERTEX_SHADER_TRANSFORM_NAME, GL_VERTEX_SHADER, &shaders[0])) |
return 1; |
if(!loadShaderFromFile(FRAGMENT_SHADER_RECORD_GBUFFER_NAME, GL_FRAGMENT_SHADER, &shaders[1])) |
return 2; |
// Create a program object |
recordGBufferDataPass = glCreateProgram(); |
// Indicate the attribute indicies on which vertex arrays will be |
// set with glVertexAttribPointer |
glBindAttribLocation(recordGBufferDataPass, POS_ATTRIB_IDX, "inPosition"); |
glBindAttribLocation(recordGBufferDataPass, NORMAL_ATTRIB_IDX, "inNormal"); |
glError(); |
attachShadersToProgram(recordGBufferDataPass, shaders[0], shaders[1]); |
// Bind user-defined varying out variables to a fragment shader color number |
glBindFragDataLocation(recordGBufferDataPass, FRAG_COLOR_ATTRIB_IDX, "fragColor"); |
glBindFragDataLocation(recordGBufferDataPass, FRAG_POS_ATTRIB_IDX, "fragPosition"); |
glBindFragDataLocation(recordGBufferDataPass, FRAG_NORMAL_ATTRIB_IDX, "fragNormal"); |
if (!linkAndValidateProgram(recordGBufferDataPass, shaders[0], shaders[1])) |
{ |
return 3; |
} |
// Get the location of uniform variables. This location value will |
// then be passed to glUniform() to set the value of the variable |
projectionUniformIdx = glGetUniformLocation(recordGBufferDataPass, "projectionMatrix"); |
orientationMatrixLocation = glGetUniformLocation(recordGBufferDataPass, "orientationMatrix"); |
cameraTransformLocation = glGetUniformLocation(recordGBufferDataPass, "cameraMatrix"); |
colorLocation = glGetUniformLocation(recordGBufferDataPass, "color"); |
if(!loadShaderFromFile(PASSTHROUGH_NAME, GL_VERTEX_SHADER, &shaders[2])) |
return 1; |
if(!loadShaderFromFile(DEFERRED_LIGHTNING_NAME, GL_FRAGMENT_SHADER, &shaders[3])) |
return 2; |
// Create a program object |
deferredLightingPass = glCreateProgram(); |
// Indicate the attribute indicies on which vertex arrays will be |
// set with glVertexAttribPointer |
glBindAttribLocation(deferredLightingPass, POS_ATTRIB_IDX, "inPosition"); |
glBindAttribLocation(deferredLightingPass, TEXCOORD_ATTRIB_IDX, "inTexcoord"); |
attachShadersToProgram(deferredLightingPass, shaders[2], shaders[3]); |
if (!linkAndValidateProgram(deferredLightingPass, shaders[2], shaders[3])) |
{ |
return 3; |
} |
// Get the location of uniform variables. This location value will |
// then be passed to glUniform() to set the value of the variable |
normTexLocation = glGetUniformLocation(deferredLightingPass, "normalTex"); |
positionTexLocation = glGetUniformLocation(deferredLightingPass, "posTex"); |
colorTexLocation = glGetUniformLocation(deferredLightingPass, "colorTex"); |
light1Pos = glGetUniformLocation(deferredLightingPass, "light1Pos"); |
light1Color = glGetUniformLocation(deferredLightingPass, "light1Color"); |
light2Pos = glGetUniformLocation(deferredLightingPass, "light2Pos"); |
light2Color = glGetUniformLocation(deferredLightingPass, "light2Color"); |
light3Pos = glGetUniformLocation(deferredLightingPass, "light3Pos"); |
light3Color = glGetUniformLocation(deferredLightingPass, "light3Color"); |
light4Pos = glGetUniformLocation(deferredLightingPass, "light4Pos"); |
light4Color = glGetUniformLocation(deferredLightingPass, "light4Color"); |
if(!loadShaderFromFile(BUFFER_DISPLAY_NAME, GL_FRAGMENT_SHADER, &shaders[4])) |
{ |
return 2; |
} |
// Create a program object |
displayTextureContentsPass = glCreateProgram(); |
// Indicate the attribute indicies on which vertex arrays will be |
// set with glVertexAttribPointer |
glBindAttribLocation(displayTextureContentsPass, POS_ATTRIB_IDX, "inPosition"); |
glBindAttribLocation(displayTextureContentsPass, TEXCOORD_ATTRIB_IDX, "inTexcoord"); |
attachShadersToProgram(displayTextureContentsPass, shaders[2], shaders[4]); |
if (!linkAndValidateProgram(displayTextureContentsPass, shaders[2], shaders[4])) |
{ |
return 3; |
} |
displayTexLocation = glGetUniformLocation(displayTextureContentsPass, "displayTex"); |
glError(); |
return 0; |
} |
#pragma mark Geometry Setup |
bool OpenGLRenderer::setupQuad() |
{ |
// Quad vertex positions |
GLfloat quad[] = |
{ |
// x y s t |
-1.0,-1.0, 0.0, 0.0, |
1.0,-1.0, 1.0, 0.0, |
1.0, 1.0, 1.0, 1.0, |
-1.0, 1.0, 0.0, 1.0, |
}; |
// Quad vertex indices |
GLushort indexes[] = |
{ |
0,1,3,2, |
}; |
glBindVertexArray( VAOs[Quad_VAO] ); |
// Create a vertex buffer object (VBO) to store positions |
glGenBuffers(1, &quadID); |
glBindBuffer(GL_ARRAY_BUFFER, quadID); |
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat)*16, &quad[0], GL_STATIC_DRAW); |
// Enable the position attribute for this VAO |
glEnableVertexAttribArray(POS_ATTRIB_IDX); |
// Set up parmeters for position attribute in the VAO including, |
// size, type, stride, and offset in the currenly bound VAO |
// This also attaches the position VBO to the VAO |
glVertexAttribPointer(POS_ATTRIB_IDX, // What attibute index will this array feed in the vertex shader |
4, // How many elements are there per position? |
GL_FLOAT, // What is the type of this data? |
GL_FALSE, // Do we want to normalize this data (0-1 range for fixed-pont types) |
4*sizeof(GLfloat), // What is the stride (i.e. bytes between positions)? |
BUFFER_OFFSET(0)); // What is the offset in the VBO to the position data? |
// Create a VBO to store vertex array elements |
// This also attaches the element array buffer to the VAO |
glGenBuffers(1, &quadElementID); |
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quadElementID); |
// Allocate and load vertex array element data into VBO |
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLushort)*4, &indexes[0], GL_STATIC_DRAW); |
return true; |
} |
bool OpenGLRenderer::setupTeapot() |
{ |
// Create vertex buffer objects (VBOs) to store the teapot vertex positions, normals and indices |
glGenBuffers(1, &teapotVertexId); |
glGenBuffers(1, &teapotNormalId); |
glGenBuffers(1, &teapotElementId); |
glBindVertexArray( VAOs[Teapot_VAO] ); |
// teapot vertices |
glBindBuffer(GL_ARRAY_BUFFER, teapotVertexId); |
// Allocate and load position data into the VBO |
glBufferData(GL_ARRAY_BUFFER, num_teapot_vertices*3*sizeof(GLfloat), teapot_vertices, GL_STATIC_DRAW); |
// Enable the position attribute for this VAO |
glEnableVertexAttribArray(POS_ATTRIB_IDX); |
// Set up parmeters for position attribute in the VAO including, |
// size, type, stride, and offset in the currenly bound VAO |
// This also attaches the position VBO to the VAO |
glVertexAttribPointer(POS_ATTRIB_IDX, // What attibute index will this array feed in the vertex shader |
3, // How many elements are there per position? |
GL_FLOAT, // What is the type of this data? |
GL_FALSE, // Do we want to normalize this data (0-1 range for fixed-pont types) |
3*sizeof(GLfloat), // What is the stride (i.e. bytes between positions)? |
BUFFER_OFFSET(0)); // What is the offset in the VBO to the position data? |
// normals |
glBindBuffer(GL_ARRAY_BUFFER, teapotNormalId); |
// Allocate and load normal data into the VBO |
glBufferData(GL_ARRAY_BUFFER, num_teapot_vertices*3*sizeof(GLfloat), teapot_normals, GL_STATIC_DRAW); |
// Enable the position attribute for this VAO |
glEnableVertexAttribArray(NORMAL_ATTRIB_IDX); |
glError(); |
// Set up parmeters for normal attribute in the VAO including, |
// size, type, stride, and offset in the currenly bound VAO |
// This also attaches the normal VBO to the VAO |
glVertexAttribPointer(NORMAL_ATTRIB_IDX, // What attibute index will this array feed in the vertex shader |
3, // How many elements are there per position? |
GL_FLOAT, // What is the type of this data? |
GL_FALSE, // Do we want to normalize this data (0-1 range for fixed-pont types) |
3*sizeof(GLfloat), // What is the stride (i.e. bytes between positions)? |
BUFFER_OFFSET(0)); // What is the offset in the VBO to the position data? |
// Create a VBO to store vertex array elements |
// This also attaches the element array buffer to the VAO |
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, teapotElementId); |
// Allocate and load vertex array element data into VBO |
glBufferData(GL_ELEMENT_ARRAY_BUFFER, num_teapot_indices*sizeof(GLushort), teapot_indices, GL_STATIC_DRAW); |
return true; |
} |
#pragma mark General Setup |
bool OpenGLRenderer::setupGL() |
{ |
// Create vertex array objects (VAO) to cache model parameters |
glGenVertexArrays( NumVAOs, VAOs); |
glBindVertexArray(VAOs[Teapot_VAO]); |
int error = 0; |
error = loadShaders(); |
if(error != 0) |
{ |
__builtin_printf("Got err loading shaders %u\n", error); |
return false; |
} |
if(!setupQuad()) |
{ |
return false; |
} |
if(!setupTeapot()) |
{ |
return false; |
} |
//set up a default camera matrix |
memcpy(&cameraMatrix, &IdentityMatrix, sizeof(mat4)); |
cameraMatrix.columns[3].z = -6.0; |
//Deferred Shading needs an FBO and various color attachments |
glGenTextures(kNumTextures, tex); |
glGenFramebuffers(kNumFBO, fbos); |
glBindFramebuffer(GL_FRAMEBUFFER, fbos[0]); |
//the position of the pixel in world space |
glBindTexture(GL_TEXTURE_2D, tex[0]); |
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_FLOAT, NULL); |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex[0], 0); |
buffers[0] = GL_COLOR_ATTACHMENT0; |
//the albedo (reflectivity) of the pixel |
glBindTexture(GL_TEXTURE_2D, tex[1]); |
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_FLOAT, NULL); |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, tex[1], 0); |
buffers[1] = GL_COLOR_ATTACHMENT1; |
//the pixel's normal. we will store 2 components and reconstruct the 3rd in the fragment shader |
glBindTexture(GL_TEXTURE_2D, tex[2]); |
glTexImage2D(GL_TEXTURE_2D, 0, GL_RG32F, width, height, 0, GL_RG, GL_FLOAT, NULL); |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, tex[2], 0); |
buffers[2] = GL_COLOR_ATTACHMENT2; |
//depth |
glBindTexture(GL_TEXTURE_2D, tex[3]); |
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, width, height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL); |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, tex[3], 0); |
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); |
if (status != GL_FRAMEBUFFER_COMPLETE) |
{ |
__builtin_printf("FBO status expected complete but got 0x%04X", status); |
glDeleteTextures(kNumTextures, tex); |
glDeleteFramebuffers(kNumFBO, fbos); |
return false; |
} |
glEnable(GL_DEPTH_TEST); |
glEnable(GL_CULL_FACE); |
glViewport(0, 0, width, height); |
l1Pos.x = 10.0; l1Pos.y = 0.0; l1Pos.z = 6.0; l1Pos.w = 1.0; |
l2Pos.x =-10.0; l2Pos.y = 0.0; l2Pos.z = 6.0; l2Pos.w = 1.0; |
l3Pos.x = 0.0; l3Pos.y = 10.0; l3Pos.z = 6.0; l3Pos.w = 1.0; |
l4Pos.x = 0.0; l4Pos.y =-10.0; l4Pos.z = 6.0; l4Pos.w = 1.0; |
l1Color.x=0.0; l1Color.y=0.5; l1Color.z=0.0; |
l2Color.x=0.75;l2Color.y=0.5; l2Color.z=0.0; |
l3Color.x=0.0; l3Color.y=0.5; l3Color.z=0.75; |
l4Color.x=0.75;l4Color.y=0.0; l4Color.z=0.0; |
glError(); |
return true; |
} |
#pragma mark Draw |
void OpenGLRenderer::resizeViewport(uint32_t w, uint32_t h) |
{ |
width = w; |
height = h; |
glViewport(0, 0, width, height); |
} |
void OpenGLRenderer::updateGBufferContents() |
{ |
// Set up the modelview and projection matricies |
GLfloat modelView[16]; |
GLfloat projection[16]; |
//First pass |
//Record data into G-buffer |
glBindFramebuffer(GL_FRAMEBUFFER, fbos[0]); |
glDrawBuffers(kNumMRT, buffers); |
//If your scene covers every pixel, you won't actually need to clear here. |
//Most scenes will have some sort of skybox, so each pixel should have data. |
//In that case, this clear is redundant. |
glClearColor(0.0,0.0,0.0,0.0); |
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); |
glEnable(GL_DEPTH_TEST); |
mtxLoadPerspective(projection, 90, (float)width / (float)height,0.1, 35.0); |
mtxLoadIdentity(modelView); |
// add an initial rotation to teapot for a better point of view |
mtxRotateApply(modelView, 30.0, 1.0, 0.0, 0.0); |
mtxRotateApply(modelView, animationStep*animationDelta, 1.0, 0.0, 1.0); |
float scale = 15.0; |
mtxScaleApply(modelView, scale, scale, scale); |
glUseProgram(recordGBufferDataPass); |
glUniformMatrix4fv(projectionUniformIdx, 1, GL_FALSE, projection); |
glUniformMatrix4fv(cameraTransformLocation, 1, GL_FALSE, (const GLfloat*) &cameraMatrix); |
glUniformMatrix4fv(orientationMatrixLocation, 1, GL_FALSE, modelView); |
glUniform4f(colorLocation, 0.25, 0.25, 0.25, 1.0); |
// Bind our teapot vertex array object (VAO) |
glBindVertexArray(VAOs[Teapot_VAO]); |
// Enable the position attribute for this VAO |
glEnableVertexAttribArray(POS_ATTRIB_IDX); |
// Enable the normal attribute for this VAO |
glEnableVertexAttribArray(NORMAL_ATTRIB_IDX); |
glBindBuffer(GL_ARRAY_BUFFER, teapotVertexId); |
glVertexAttribPointer(POS_ATTRIB_IDX, 3, GL_FLOAT, GL_FALSE, 0, NULL); |
glBindBuffer(GL_ARRAY_BUFFER, teapotNormalId); |
glVertexAttribPointer(NORMAL_ATTRIB_IDX, 3, GL_FLOAT, GL_FALSE, 0, NULL); |
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, teapotElementId); |
//Draw the teapot |
int start = 0, i = 0; |
while(i < num_teapot_indices) { |
if(teapot_indices[i] == -1) { |
glDrawElements(GL_TRIANGLE_STRIP, i - start, GL_UNSIGNED_SHORT, BUFFER_OFFSET(start*sizeof(ushort))); |
start = i + 1; |
} |
i++; |
} |
if(start < num_teapot_indices) |
glDrawElements(GL_TRIANGLE_STRIP, i - start - 1, GL_UNSIGNED_SHORT, BUFFER_OFFSET(start*sizeof(ushort))); |
glDisableVertexAttribArray(POS_ATTRIB_IDX); |
glDisableVertexAttribArray(NORMAL_ATTRIB_IDX); |
} |
void OpenGLRenderer::updateAnimations() |
{ |
// Animate the model when the user presses the <space> key |
if(animate) |
{ |
animationStep = (animationStep+1) % kAnimationLoopValue; |
} |
} |
void OpenGLRenderer::showPositionBuffer() |
{ |
glUseProgram(displayTextureContentsPass); |
glActiveTexture(GL_TEXTURE0); |
glBindTexture(GL_TEXTURE_2D, tex[0]); |
glUniform1i(displayTexLocation, 0); |
} |
void OpenGLRenderer::showNormalBuffer() |
{ |
glUseProgram(displayTextureContentsPass); |
glActiveTexture(GL_TEXTURE0); |
glBindTexture(GL_TEXTURE_2D, tex[2]); |
glUniform1i(displayTexLocation, 0); |
} |
void OpenGLRenderer::showAlbedoBuffer() |
{ |
glUseProgram(displayTextureContentsPass); |
glActiveTexture(GL_TEXTURE0); |
glBindTexture(GL_TEXTURE_2D, tex[1]); |
glUniform1i(displayTexLocation, 0); |
} |
void OpenGLRenderer::showFinalImage() |
{ |
//move lights into eye space |
vec4 light1Position,light2Position,light3Position,light4Position; |
matVecMul(&cameraMatrix, &l1Pos, &light1Position); |
matVecMul(&cameraMatrix, &l2Pos, &light2Position); |
matVecMul(&cameraMatrix, &l3Pos, &light3Position); |
matVecMul(&cameraMatrix, &l4Pos, &light4Position); |
glUseProgram(deferredLightingPass); |
//draw all 3 buffers as textures |
//set up multitexturing |
glActiveTexture(GL_TEXTURE0); |
glBindTexture(GL_TEXTURE_2D, tex[0]); |
glActiveTexture(GL_TEXTURE1); |
glBindTexture(GL_TEXTURE_2D, tex[1]); |
glActiveTexture(GL_TEXTURE2); |
glBindTexture(GL_TEXTURE_2D, tex[2]); |
glUniform1i(positionTexLocation, 0); |
glUniform1i(colorTexLocation, 1); |
glUniform1i(normTexLocation, 2); |
glUniform3fv(light1Pos, 1, (const GLfloat*) &light1Position); |
glUniform3fv(light1Color, 1, (const GLfloat*) &l1Color); |
glUniform3fv(light2Pos, 1, (const GLfloat*) &light2Position); |
glUniform3fv(light2Color, 1, (const GLfloat*) &l2Color); |
glUniform3fv(light3Pos, 1, (const GLfloat*) &light3Position); |
glUniform3fv(light3Color, 1, (const GLfloat*) &l3Color); |
glUniform3fv(light4Pos, 1, (const GLfloat*) &light4Position); |
glUniform3fv(light4Color, 1, (const GLfloat*) &l4Color); |
} |
void OpenGLRenderer::drawQuad() |
{ |
glBindVertexArray( VAOs[Quad_VAO] ); |
glEnableVertexAttribArray(POS_ATTRIB_IDX); |
glEnableVertexAttribArray(TEXCOORD_ATTRIB_IDX); |
glBindBuffer(GL_ARRAY_BUFFER, quadID); |
glVertexAttribPointer(POS_ATTRIB_IDX, 2, GL_FLOAT, GL_FALSE, 4*sizeof(GLfloat), NULL); |
glVertexAttribPointer(TEXCOORD_ATTRIB_IDX, 2, GL_FLOAT, GL_FALSE, 4*sizeof(GLfloat), BUFFER_OFFSET(2*sizeof(GLfloat))); |
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quadElementID); |
// draw quad |
glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, NULL); |
glDisableVertexAttribArray(POS_ATTRIB_IDX); |
glDisableVertexAttribArray(TEXCOORD_ATTRIB_IDX); |
} |
void OpenGLRenderer::draw() |
{ |
updateAnimations(); |
updateGBufferContents(); |
//draw to window |
glFrontFace(GL_CCW); |
glDisable(GL_DEPTH_TEST); |
glBindFramebuffer(GL_FRAMEBUFFER,0); |
glDrawBuffer(GL_BACK); |
//A note about this clear: |
//This is also a redundant clear. Normally, you'd be drawing a full screen |
//quad here, so every pixel will be touched. |
//However, I'm dropping out in the fragment shader based on alpha |
//so if you don't clear here, you'll get VRAM garbage where there's no color/normal/pos |
//info in the G-buffer. |
glClearColor(1.0,1.0,1.0,0.0); |
glClear(GL_COLOR_BUFFER_BIT); |
switch (showBuffer) { |
case kPositionBuffer: |
showPositionBuffer(); |
break; |
case kAlbedoBuffer: |
showAlbedoBuffer(); |
break; |
case kNormalBuffer: |
showNormalBuffer(); |
break; |
default: |
case kFinalImage: |
//draw the final image |
showFinalImage(); |
break; |
} |
drawQuad(); |
glError(); |
// unbind existing shader program |
glUseProgram(0); |
} |
Copyright © 2015 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2015-07-08