OpenGL学习--08--基本渲染(灯光)
Posted ParamousGIS
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OpenGL学习--08--基本渲染(灯光)相关的知识,希望对你有一定的参考价值。
1.tutorial08.cpp
// 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, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // To make MacOS happy; should not be needed glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // Open a window and create its OpenGL context window = glfwCreateWindow( 1024, 768, "Tutorial 08 - Basic Shading", NULL, NULL); if( window == NULL ){ fprintf( stderr, "Failed to open GLFW window. If you have an Intel GPU, they are not 3.3 compatible. Try the 2.1 version of the tutorials.\\n" ); getchar(); glfwTerminate(); return -1; } glfwMakeContextCurrent(window); // Initialize GLEW glewExperimental = true; // Needed for core profile 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); GLuint VertexArrayID; glGenVertexArrays(1, &VertexArrayID); glBindVertexArray(VertexArrayID); // Create and compile our GLSL program from the shaders GLuint programID = LoadShaders( "StandardShading.vertexshader", "StandardShading.fragmentshader" ); // Get a handle for our "MVP" uniform GLuint MatrixID = glGetUniformLocation(programID, "MVP"); GLuint ViewMatrixID = glGetUniformLocation(programID, "V"); GLuint ModelMatrixID = glGetUniformLocation(programID, "M"); // 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); // Load it into a VBO GLuint vertexbuffer; glGenBuffers(1, &vertexbuffer); glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer); glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(glm::vec3), &vertices[0], GL_STATIC_DRAW); GLuint uvbuffer; glGenBuffers(1, &uvbuffer); glBindBuffer(GL_ARRAY_BUFFER, uvbuffer); glBufferData(GL_ARRAY_BUFFER, uvs.size() * sizeof(glm::vec2), &uvs[0], GL_STATIC_DRAW); GLuint normalbuffer; glGenBuffers(1, &normalbuffer); glBindBuffer(GL_ARRAY_BUFFER, normalbuffer); glBufferData(GL_ARRAY_BUFFER, normals.size() * sizeof(glm::vec3), &normals[0], GL_STATIC_DRAW); // Get a handle for our "LightPosition" uniform glUseProgram(programID); GLuint LightID = glGetUniformLocation(programID, "LightPosition_worldspace"); do{ // 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(0); glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer); glVertexAttribPointer( 0, // attribute 3, // size GL_FLOAT, // type GL_FALSE, // normalized? 0, // stride (void*)0 // array buffer offset ); // 2nd attribute buffer : UVs glEnableVertexAttribArray(1); glBindBuffer(GL_ARRAY_BUFFER, uvbuffer); glVertexAttribPointer( 1, // attribute 2, // size GL_FLOAT, // type GL_FALSE, // normalized? 0, // stride (void*)0 // array buffer offset ); // 3rd attribute buffer : normals glEnableVertexAttribArray(2); glBindBuffer(GL_ARRAY_BUFFER, normalbuffer); glVertexAttribPointer( 2, // attribute 3, // size GL_FLOAT, // type GL_FALSE, // normalized? 0, // stride (void*)0 // array buffer offset ); // Draw the triangles ! glDrawArrays(GL_TRIANGLES, 0, vertices.size() ); glDisableVertexAttribArray(0); glDisableVertexAttribArray(1); glDisableVertexAttribArray(2); // 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); glDeleteProgram(programID); glDeleteTextures(1, &Texture); glDeleteVertexArrays(1, &VertexArrayID); // Close OpenGL window and terminate GLFW glfwTerminate(); return 0; }
2. common/objloader.cpp
#include <vector> #include <stdio.h> #include <string> #include <cstring> #include <glm/glm.hpp> #include "objloader.hpp" // Very, VERY simple OBJ loader. // Here is a short list of features a real function would provide : // - Binary files. Reading a model should be just a few memcpy\'s away, not parsing a file at runtime. In short : OBJ is not very great. // - Animations & bones (includes bones weights) // - Multiple UVs // - All attributes should be optional, not "forced" // - More stable. Change a line in the OBJ file and it crashes. // - More secure. Change another line and you can inject code. // - Loading from memory, stream, etc bool loadOBJ( const char * path, std::vector<glm::vec3> & out_vertices, std::vector<glm::vec2> & out_uvs, std::vector<glm::vec3> & out_normals ){ printf("Loading OBJ file %s...\\n", path); std::vector<unsigned int> vertexIndices, uvIndices, normalIndices; std::vector<glm::vec3> temp_vertices; std::vector<glm::vec2> temp_uvs; std::vector<glm::vec3> temp_normals; FILE * file = fopen(path, "r"); if( file == NULL ){ printf("Impossible to open the file ! Are you in the right path ? See Tutorial 1 for details\\n"); getchar(); return false; } while( 1 ){ char lineHeader[128]; // read the first word of the line int res = fscanf(file, "%s", lineHeader); if (res == EOF) break; // EOF = End Of File. Quit the loop. // else : parse lineHeader if ( strcmp( lineHeader, "v" ) == 0 ){ glm::vec3 vertex; fscanf(file, "%f %f %f\\n", &vertex.x, &vertex.y, &vertex.z ); temp_vertices.push_back(vertex); }else if ( strcmp( lineHeader, "vt" ) == 0 ){ glm::vec2 uv; fscanf(file, "%f %f\\n", &uv.x, &uv.y ); uv.y = -uv.y; // Invert V coordinate since we will only use DDS texture, which are inverted. Remove if you want to use TGA or BMP loaders. temp_uvs.push_back(uv); }else if ( strcmp( lineHeader, "vn" ) == 0 ){ glm::vec3 normal; fscanf(file, "%f %f %f\\n", &normal.x, &normal.y, &normal.z ); temp_normals.push_back(normal); }else if ( strcmp( lineHeader, "f" ) == 0 ){ std::string vertex1, vertex2, vertex3; unsigned int vertexIndex[3], uvIndex[3], normalIndex[3]; int matches = fscanf(file, "%d/%d/%d %d/%d/%d %d/%d/%d\\n", &vertexIndex[0], &uvIndex[0], &normalIndex[0], &vertexIndex[1], &uvIndex[1], &normalIndex[1], &vertexIndex[2], &uvIndex[2], &normalIndex[2] ); if (matches != 9){ printf("File can\'t be read by our simple parser :-( Try exporting with other options\\n"); return false; } vertexIndices.push_back(vertexIndex[0]); vertexIndices.push_back(vertexIndex[1]); vertexIndices.push_back(vertexIndex[2]); uvIndices .push_back(uvIndex[0]); uvIndices .push_back(uvIndex[1]); uvIndices .push_back(uvIndex[2]); normalIndices.push_back(normalIndex[0]); normalIndices.push_back(normalIndex[1]); normalIndices.push_back(normalIndex[2]); }else{ // Probably a comment, eat up the rest of the line char stupidBuffer[1000]; fgets(stupidBuffer, 1000, file); } } // For each vertex of each triangle for( unsigned int i=0; i<vertexIndices.size(); i++ ){ // Get the indices of its attributes unsigned int vertexIndex = vertexIndices[i]; unsigned int uvIndex = uvIndices[i]; unsigned int normalIndex = normalIndices[i]; // Get the attributes thanks to the index glm::vec3 vertex = temp_vertices[ vertexIndex-1 ]; glm::vec2 uv = temp_uvs[ uvIndex-1 ]; glm::vec3 normal = temp_normals[ normalIndex-1 ]; // Put the attributes in buffers out_vertices.push_back(vertex); out_uvs .push_back(uv); out_normals .push_back(normal); } return true; } #ifdef USE_ASSIMP // don\'t use this #define, it\'s only for me (it AssImp fails to compile on your machine, at least all the other tutorials still work) // Include AssImp #include <assimp/Importer.hpp> // C++ importer interface #include <assimp/scene.h> // Output data structure #include <assimp/postprocess.h> // Post processing flags bool loadAssImp( const char * path, std::vector<unsigned short> & indices, std::vector<glm::vec3> & vertices, std::vector<glm::vec2> & uvs, std::vector<glm::vec3> & normals ){ Assimp::Importer importer; const aiScene* scene = importer.ReadFile(path, 0/*aiProcess_JoinIdenticalVertices | aiProcess_SortByPType*/); if( !scene) { fprintf( stderr, importer.GetErrorString()); getchar(); return false; } const aiMesh* mesh = scene->mMeshes[0]; // In this simple example code we always use the 1rst mesh (in OBJ files there is often only one anyway) // Fill vertices positions vertices.reserve(mesh->mNumVertices); for(unsigned int i=0; i<mesh->mNumVertices; i++){ aiVector3D pos = mesh->mVertices[i]; vertices.push_back(glm::vec3(pos.x, pos.y, pos.z)); } // Fill vertices texture coordinates uvs.reserve(mesh->mNumVertices); for(unsigned int i=0; i<mesh->mNumVertices; i++){ aiVector3D UVW = mesh->mTextureCoords[0][i]; // Assume only 1 set of UV coords; AssImp supports 8 UV sets. uvs.push_back(glm::vec2(UVW.x, UVW.y)); } // Fill vertices normals normals.reserve(mesh->mNumVertices); for(unsigned int i=0; i<mesh->mNumVertices; i++){ aiVector3D n = mesh->mNormals[i]; normals.push_back(glm::vec3(n.x, n.y, n.z)); } // Fill face indices indices.reserve(3*mesh->mNumFaces); for (unsigned int i=0; i<mesh->mNumFaces; i++){ // Assume the model has only triangles. indices.push_back(mesh->mFaces[i].mIndices[0]); indices.push_back(mesh->mFaces[i].mIndices[1]); indices.push_back(mesh->mFaces[i].mIndices[2]); } // The "scene" pointer will be deleted automatically by "importer" } #endif
3.common/objloader.hpp
#ifndef OBJLOADER_H #define OBJLOADER_H bool loadOBJ( const char * path, std::vector<glm::vec3> & out_vertices, std::vector<glm::vec2> & out_uvs, std::vector<glm::vec3> & out_normals ); bool loadAssImp( const char * path, std::vector<unsigned short> & indices, std::vector<glm::vec3> & vertices, std::vector<glm::vec2> & uvs, std::vector<glm::vec3> & normals ); #endif
4. common/controls.cpp
// Include GLFW #include <glfw3.h> extern GLFWwindow* window; // The "extern" keyword here is to access the variable "window" declared in tutorialXXX.cpp. This is a hack to keep the tutorials simple. Please avoid this. // Include GLM #include <glm/glm.hpp> #include <glm/gtc/matrix_transform.hpp> using namespace glm; #include "controls.hpp" glm::mat4 ViewMatrix; glm::mat4 ProjectionMatrix; glm::mat4 getViewMatrix(){ return ViewMatrix; } glm::mat4 getProjectionMatrix(){ return ProjectionMatrix; } // Initial position : on +Z glm::vec3 position = glm::vec3( 0, 0, 5 ); // Initial horizontal angle : toward -Z float horizontalAngle = 3.14f; // Initial vertical angle : none float verticalAngle = 0.0f; // Initial Field of View float initialFoV = 45.0f; float speed = 3.0f; // 3 units / second float mouseSpeed = 0.005f; void computeMatricesFromInputs(){ // glfwGetTime is called only once, the first time this function is called static double lastTime = glfwGetTime(); // Compute time difference between current and last frame double currentTime = glfwGetTime(); float deltaTime = float(currentTime - lastTime); // Get mouse position double xpos, ypos; glfwGetCursorPos(window, &xpos, &ypos); // Reset mouse position for next frame glfwSetCursorPos(window, 1024/2, 768/2); // Compute new orientation horizontalAngle += mouseSpeed * float(1024/2 - xpos ); verticalAngle += mouseSpeed * float( 768/2 - ypos ); // Direction : Spherical coordinates to Cartesian coordinates conversion glm::vec3 direction( cos(verticalAngle) * sin(horizontalAngle), sin(verticalAngle), cos(verticalAngle) * cos(horizontalAngle) ); // Right vector glm::vec3 right = glm::vec3( sin(horizontalAngle - 3.14f/2.0f), 0, cos(horizontalAngle - 3.14f/2.0f) ); // Up vector glm::vec3 up = glm::cross( right, direction ); // Move forward if (glfwGetKey( window, GLFW_KEY_UP ) == GLFW_PRESS){ position += direction * deltaTime * speed; } // Move backward if (glfwGetKey( window, GLFW_KEY_DOWN ) == GLFW_PRESS){ position -= direction * deltaTime * speed; } // Strafe right if (glfwGetKey( window, GLFW_KEY_RIGHT ) == GLFW_PRESS){ position += right * deltaTime * speed; } // Strafe left if (glfwGetKey( window, GLFW_KEY_LEFT ) == GLFW_PRESS){ position -= right * deltaTime * speed; } float FoV = initialFoV;// - 5 * glfwGetMouseWheel(); // Now GLFW 3 requires setting up a callback for this. It\'s a bit too complicated for this beginner\'s tutorial, so it\'s disabled instead. // Projection matrix : 45?Field of View, 4:3 ratio, display range : 0.1 unit <-> 100 units ProjectionMatrix = glm::perspective(FoV, 4.0f / 3.0f, 0.1f, 100.0f); // Camera matrix ViewMatrix = glm::lookAt( position, // Camera is here position+direction, // and looks here : at the same position, plus "direction" up // Head is up (set to 0,-1,0 to look upside-down) ); // For the next frame, the "last time" will be "now" lastTime = currentTime; }
5.common/controls.hpp
#ifndef CONTROLS_HPP #define CONTROLS_HPP void computeMatricesFromInputs(); glm::mat4 getViewMatrix(); glm::mat4 getProjectionMatrix(); #endif
6. common/texture.cpp
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <GL/glew.h> #include <glfw3.h> GLuint loadBMP_custom(const char * imagepath){ printf("Reading image %s\\n", imagepath); // Data read from the header of the BMP file unsigned char header[54]; unsigned int dataPos; unsigned int imageSize; unsigned int width, height; // Actual RGB data unsigned char * data; // Open the file FILE * file = fopen(imagepath,"rb"); if (!file) {printf("%s could not be opened. Are you in the right directory ? Don\'t forget to read the FAQ !\\n", imagepath); getchar(); return 0;} // Read the header, i.e. the 54 first bytes // If less than 54 bytes are read, problem if ( fread(header, 1, 54, file)!=54 ){ printf("Not a correct BMP file\\n"); return 0; } // A BMP files always begins with "BM" if ( header[0]!=\'B\' || header[1]!=\'M\' ){ printf("Not a correct BMP file\\n"); return 0; } // Make sure this is a 24bpp file if ( *(int*)&(header[0x1E])!=0 ) {printf("Not a correct BMP file\\n"); return 0;} if ( *(int*)&(header[0x1C])!=24 ) {printf("Not a correct BMP file\\n"); return 0;} // Read the information about the image dataPos = *(int*)&(header[0x0A]); imageSize = *(int*)&(header[0x22]); width = *(int*)&(header[0x12]); height = *(int*)&(header[0x16]); // Some BMP files are misformatted, guess missing information if (imageSize==0) imageSize=width*height*3; // 3 : one byte for each Red, Green and Blue component if (dataPos==0) dataPos=54; // The BMP header is done that way // Create a buffer data = new unsigned char [imageSize]; // Read the actual data from the file into the buffer fread(data,1,imageSize,file); // Everything is in memory now, the file wan be closed fclose (file); // Create one OpenGL texture GLuint textureID; glGenTextures(1, &textureID); // "Bind" the newly created texture : all future texture functions will modify this texture glBindTexture(GL_TEXTURE_2D, textureID); // Give the image to OpenGL glTexImage2D(GL_TEXTURE_2D, 0,GL_RGB, width, height, 0, GL_BGR, GL_UNSIGNED_BYTE, data);以上是关于OpenGL学习--08--基本渲染(灯光)的主要内容,如果未能解决你的问题,请参考以下文章