Permalink
| // Include standard headers | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <vector> | |
| // Include GLEW | |
| #include <GL/glew.h> | |
| // Include GLFW | |
| #include <glfw3.h> | |
| GLFWwindow* window; | |
| // Include GLM | |
| #include <glm/glm.hpp> | |
| #include <glm/gtc/matrix_transform.hpp> | |
| using namespace glm; | |
| #include <common/shader.hpp> | |
| #include <common/texture.hpp> | |
| #include <common/controls.hpp> | |
| #include <common/objloader.hpp> | |
| #include <common/vboindexer.hpp> | |
| int main( void ) | |
| { | |
| // Initialise GLFW | |
| if( !glfwInit() ) | |
| { | |
| fprintf( stderr, "Failed to initialize GLFW\n" ); | |
| getchar(); | |
| return -1; | |
| } | |
| glfwWindowHint(GLFW_SAMPLES, 4); | |
| glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); | |
| glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); | |
| // Open a window and create its OpenGL context | |
| window = glfwCreateWindow( 1024, 768, "Tutorial 14 - Render To Texture", NULL, NULL); | |
| if( window == NULL ){ | |
| fprintf( stderr, "Failed to open GLFW window.\n" ); | |
| getchar(); | |
| glfwTerminate(); | |
| return -1; | |
| } | |
| glfwMakeContextCurrent(window); | |
| // We would expect width and height to be 1024 and 768 | |
| int windowWidth = 1024; | |
| int windowHeight = 768; | |
| // But on MacOS X with a retina screen it'll be 1024*2 and 768*2, so we get the actual framebuffer size: | |
| glfwGetFramebufferSize(window, &windowWidth, &windowHeight); | |
| // Initialize GLEW | |
| if (glewInit() != GLEW_OK) { | |
| fprintf(stderr, "Failed to initialize GLEW\n"); | |
| getchar(); | |
| glfwTerminate(); | |
| return -1; | |
| } | |
| // Ensure we can capture the escape key being pressed below | |
| glfwSetInputMode(window, GLFW_STICKY_KEYS, GL_TRUE); | |
| // Hide the mouse and enable unlimited mouvement | |
| glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); | |
| // Set the mouse at the center of the screen | |
| glfwPollEvents(); | |
| glfwSetCursorPos(window, 1024/2, 768/2); | |
| // Dark blue background | |
| glClearColor(0.0f, 0.0f, 0.4f, 0.0f); | |
| // Enable depth test | |
| glEnable(GL_DEPTH_TEST); | |
| // Accept fragment if it closer to the camera than the former one | |
| glDepthFunc(GL_LESS); | |
| // Cull triangles which normal is not towards the camera | |
| glEnable(GL_CULL_FACE); | |
| // Create and compile our GLSL program from the shaders | |
| GLuint programID = LoadShaders( "StandardShadingRTT.vertexshader", "StandardShadingRTT.fragmentshader" ); | |
| // Get a handle for our "MVP" uniform | |
| GLuint MatrixID = glGetUniformLocation(programID, "MVP"); | |
| GLuint ViewMatrixID = glGetUniformLocation(programID, "V"); | |
| GLuint ModelMatrixID = glGetUniformLocation(programID, "M"); | |
| // Get a handle for our buffers | |
| GLuint vertexPosition_modelspaceID = glGetAttribLocation(programID, "vertexPosition_modelspace"); | |
| GLuint vertexUVID = glGetAttribLocation(programID, "vertexUV"); | |
| GLuint vertexNormal_modelspaceID = glGetAttribLocation(programID, "vertexNormal_modelspace"); | |
| // Load the texture | |
| GLuint Texture = loadDDS("uvmap.DDS"); | |
| // Get a handle for our "myTextureSampler" uniform | |
| GLuint TextureID = glGetUniformLocation(programID, "myTextureSampler"); | |
| // Read our .obj file | |
| std::vector<glm::vec3> vertices; | |
| std::vector<glm::vec2> uvs; | |
| std::vector<glm::vec3> normals; | |
| bool res = loadOBJ("suzanne.obj", vertices, uvs, normals); | |
| std::vector<unsigned short> indices; | |
| std::vector<glm::vec3> indexed_vertices; | |
| std::vector<glm::vec2> indexed_uvs; | |
| std::vector<glm::vec3> indexed_normals; | |
| indexVBO(vertices, uvs, normals, indices, indexed_vertices, indexed_uvs, indexed_normals); | |
| // Load it into a VBO | |
| GLuint vertexbuffer; | |
| glGenBuffers(1, &vertexbuffer); | |
| glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer); | |
| glBufferData(GL_ARRAY_BUFFER, indexed_vertices.size() * sizeof(glm::vec3), &indexed_vertices[0], GL_STATIC_DRAW); | |
| GLuint uvbuffer; | |
| glGenBuffers(1, &uvbuffer); | |
| glBindBuffer(GL_ARRAY_BUFFER, uvbuffer); | |
| glBufferData(GL_ARRAY_BUFFER, indexed_uvs.size() * sizeof(glm::vec2), &indexed_uvs[0], GL_STATIC_DRAW); | |
| GLuint normalbuffer; | |
| glGenBuffers(1, &normalbuffer); | |
| glBindBuffer(GL_ARRAY_BUFFER, normalbuffer); | |
| glBufferData(GL_ARRAY_BUFFER, indexed_normals.size() * sizeof(glm::vec3), &indexed_normals[0], GL_STATIC_DRAW); | |
| // Generate a buffer for the indices as well | |
| GLuint elementbuffer; | |
| glGenBuffers(1, &elementbuffer); | |
| glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementbuffer); | |
| glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned short), &indices[0], GL_STATIC_DRAW); | |
| // Get a handle for our "LightPosition" uniform | |
| glUseProgram(programID); | |
| GLuint LightID = glGetUniformLocation(programID, "LightPosition_worldspace"); | |
| // --------------------------------------------- | |
| // Render to Texture - specific code begins here | |
| // --------------------------------------------- | |
| // The framebuffer, which regroups 0, 1, or more textures, and 0 or 1 depth buffer. | |
| GLuint FramebufferName = 0; | |
| glGenFramebuffers(1, &FramebufferName); | |
| glBindFramebuffer(GL_FRAMEBUFFER, FramebufferName); | |
| // The texture we're going to render to | |
| GLuint renderedTexture; | |
| glGenTextures(1, &renderedTexture); | |
| // "Bind" the newly created texture : all future texture functions will modify this texture | |
| glBindTexture(GL_TEXTURE_2D, renderedTexture); | |
| // Give an empty image to OpenGL ( the last "0" means "empty" ) | |
| glTexImage2D(GL_TEXTURE_2D, 0,GL_RGB, windowWidth, windowHeight, 0,GL_RGB, GL_UNSIGNED_BYTE, 0); | |
| // Poor filtering | |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | |
| // The depth buffer | |
| if ( !GLEW_ARB_framebuffer_object ){ // OpenGL 2.1 doesn't require this, 3.1+ does | |
| printf("Your GPU does not provide framebuffer objects. Use a texture instead."); | |
| return -1; | |
| } | |
| GLuint depthrenderbuffer; | |
| glGenRenderbuffers(1, &depthrenderbuffer); | |
| glBindRenderbuffer(GL_RENDERBUFFER, depthrenderbuffer); | |
| glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, windowWidth, windowHeight); | |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthrenderbuffer); | |
| //// Alternative : Depth texture. Slower, but you can sample it later in your shader | |
| //GLuint depthTexture; | |
| //glGenTextures(1, &depthTexture); | |
| //glBindTexture(GL_TEXTURE_2D, depthTexture); | |
| //glTexImage2D(GL_TEXTURE_2D, 0,GL_DEPTH_COMPONENT24, 1024, 768, 0,GL_DEPTH_COMPONENT, GL_FLOAT, 0); | |
| //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | |
| //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | |
| //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | |
| //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | |
| //glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthTexture, 0); | |
| // Set "renderedTexture" as our colour attachement #0 | |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, renderedTexture, 0); | |
| // Set the list of draw buffers. | |
| GLenum DrawBuffers[1] = {GL_COLOR_ATTACHMENT0}; | |
| glDrawBuffers(1, DrawBuffers); // "1" is the size of DrawBuffers | |
| // Always check that our framebuffer is ok | |
| if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) | |
| return false; | |
| static const GLfloat g_quad_vertex_buffer_data[] = { | |
| -1.0f, -1.0f, 0.0f, | |
| 1.0f, -1.0f, 0.0f, | |
| -1.0f, 1.0f, 0.0f, | |
| -1.0f, 1.0f, 0.0f, | |
| 1.0f, -1.0f, 0.0f, | |
| 1.0f, 1.0f, 0.0f, | |
| }; | |
| GLuint quad_vertexbuffer; | |
| glGenBuffers(1, &quad_vertexbuffer); | |
| glBindBuffer(GL_ARRAY_BUFFER, quad_vertexbuffer); | |
| glBufferData(GL_ARRAY_BUFFER, sizeof(g_quad_vertex_buffer_data), g_quad_vertex_buffer_data, GL_STATIC_DRAW); | |
| // Create and compile our GLSL program from the shaders | |
| GLuint quad_programID = LoadShaders( "Passthrough.vertexshader", "WobblyTexture.fragmentshader" ); | |
| GLuint quad_vertexPosition_modelspace = glGetAttribLocation(quad_programID, "vertexPosition_modelspace"); | |
| GLuint texID = glGetUniformLocation(quad_programID, "renderedTexture"); | |
| GLuint timeID = glGetUniformLocation(quad_programID, "time"); | |
| do{ | |
| // Render to our framebuffer | |
| glBindFramebuffer(GL_FRAMEBUFFER, FramebufferName); | |
| glViewport(0,0,windowWidth,windowHeight); // Render on the whole framebuffer, complete from the lower left corner to the upper right | |
| // Clear the screen | |
| glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | |
| // Use our shader | |
| glUseProgram(programID); | |
| // Compute the MVP matrix from keyboard and mouse input | |
| computeMatricesFromInputs(); | |
| glm::mat4 ProjectionMatrix = getProjectionMatrix(); | |
| glm::mat4 ViewMatrix = getViewMatrix(); | |
| glm::mat4 ModelMatrix = glm::mat4(1.0); | |
| glm::mat4 MVP = ProjectionMatrix * ViewMatrix * ModelMatrix; | |
| // Send our transformation to the currently bound shader, | |
| // in the "MVP" uniform | |
| glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &MVP[0][0]); | |
| glUniformMatrix4fv(ModelMatrixID, 1, GL_FALSE, &ModelMatrix[0][0]); | |
| glUniformMatrix4fv(ViewMatrixID, 1, GL_FALSE, &ViewMatrix[0][0]); | |
| glm::vec3 lightPos = glm::vec3(4,4,4); | |
| glUniform3f(LightID, lightPos.x, lightPos.y, lightPos.z); | |
| // Bind our texture in Texture Unit 0 | |
| glActiveTexture(GL_TEXTURE0); | |
| glBindTexture(GL_TEXTURE_2D, Texture); | |
| // Set our "myTextureSampler" sampler to user Texture Unit 0 | |
| glUniform1i(TextureID, 0); | |
| // 1rst attribute buffer : vertices | |
| glEnableVertexAttribArray(vertexPosition_modelspaceID); | |
| glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer); | |
| glVertexAttribPointer( | |
| vertexPosition_modelspaceID, // The attribute we want to configure | |
| 3, // size | |
| GL_FLOAT, // type | |
| GL_FALSE, // normalized? | |
| 0, // stride | |
| (void*)0 // array buffer offset | |
| ); | |
| // 2nd attribute buffer : UVs | |
| glEnableVertexAttribArray(vertexUVID); | |
| glBindBuffer(GL_ARRAY_BUFFER, uvbuffer); | |
| glVertexAttribPointer( | |
| vertexUVID, // The attribute we want to configure | |
| 2, // size : U+V => 2 | |
| GL_FLOAT, // type | |
| GL_FALSE, // normalized? | |
| 0, // stride | |
| (void*)0 // array buffer offset | |
| ); | |
| // 3rd attribute buffer : normals | |
| glEnableVertexAttribArray(vertexNormal_modelspaceID); | |
| glBindBuffer(GL_ARRAY_BUFFER, normalbuffer); | |
| glVertexAttribPointer( | |
| vertexNormal_modelspaceID, // The attribute we want to configure | |
| 3, // size | |
| GL_FLOAT, // type | |
| GL_FALSE, // normalized? | |
| 0, // stride | |
| (void*)0 // array buffer offset | |
| ); | |
| // Index buffer | |
| glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementbuffer); | |
| // Draw the triangles ! | |
| glDrawElements( | |
| GL_TRIANGLES, // mode | |
| indices.size(), // count | |
| GL_UNSIGNED_SHORT, // type | |
| (void*)0 // element array buffer offset | |
| ); | |
| glDisableVertexAttribArray(vertexPosition_modelspaceID); | |
| glDisableVertexAttribArray(vertexUVID); | |
| glDisableVertexAttribArray(vertexNormal_modelspaceID); | |
| // Render to the screen | |
| glBindFramebuffer(GL_FRAMEBUFFER, 0); | |
| // Render on the whole framebuffer, complete from the lower left corner to the upper right | |
| glViewport(0,0,windowWidth,windowHeight); | |
| // Clear the screen | |
| glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | |
| // Use our shader | |
| glUseProgram(quad_programID); | |
| // Bind our texture in Texture Unit 0 | |
| glActiveTexture(GL_TEXTURE0); | |
| glBindTexture(GL_TEXTURE_2D, renderedTexture); | |
| // Set our "renderedTexture" sampler to user Texture Unit 0 | |
| glUniform1i(texID, 0); | |
| glUniform1f(timeID, (float)(glfwGetTime()*10.0f) ); | |
| // 1rst attribute buffer : vertices | |
| glEnableVertexAttribArray(0); | |
| glBindBuffer(GL_ARRAY_BUFFER, quad_vertexbuffer); | |
| glVertexAttribPointer( | |
| quad_vertexPosition_modelspace, // attribute | |
| 3, // size | |
| GL_FLOAT, // type | |
| GL_FALSE, // normalized? | |
| 0, // stride | |
| (void*)0 // array buffer offset | |
| ); | |
| // Draw the triangles ! | |
| glDrawArrays(GL_TRIANGLES, 0, 6); // 2*3 indices starting at 0 -> 2 triangles | |
| glDisableVertexAttribArray(0); | |
| // Swap buffers | |
| glfwSwapBuffers(window); | |
| glfwPollEvents(); | |
| } // Check if the ESC key was pressed or the window was closed | |
| while( glfwGetKey(window, GLFW_KEY_ESCAPE ) != GLFW_PRESS && | |
| glfwWindowShouldClose(window) == 0 ); | |
| // Cleanup VBO and shader | |
| glDeleteBuffers(1, &vertexbuffer); | |
| glDeleteBuffers(1, &uvbuffer); | |
| glDeleteBuffers(1, &normalbuffer); | |
| glDeleteBuffers(1, &elementbuffer); | |
| glDeleteProgram(programID); | |
| glDeleteTextures(1, &TextureID); | |
| glDeleteFramebuffers(1, &FramebufferName); | |
| glDeleteTextures(1, &renderedTexture); | |
| glDeleteRenderbuffers(1, &depthrenderbuffer); | |
| glDeleteBuffers(1, &quad_vertexbuffer); | |
| // Close OpenGL window and terminate GLFW | |
| glfwTerminate(); | |
| return 0; | |
| } | |