void LightManager::init(GLuint program) { numLights = 0; setUniformAttrib(uniLightPosition, "position", program); setUniformAttrib(uniLightIntensity, "intensity", program); uniNumLights = glGetUniformLocation(program, "numLights"); }
// Initialization code void init() { // Initializes the glew library glewInit(); // Enables the depth test, which you will want in most cases. You can disable this in the render loop if you need to. glEnable(GL_DEPTH_TEST); // Create an std::vector and put our vertices into it. These are just hardcoded values here defined once. std::vector<VertexFormat> vertices;//our vertex positions vertices.push_back(VertexFormat(glm::vec3(-0.25, -0.25, -0.25), glm::vec4(1.0, 0.0, 0.0, 1.0), glm::vec3(0.0, 0.0, 1.0))); vertices.push_back(VertexFormat(glm::vec3(-0.25, 0.25, -0.25), glm::vec4(1.0, 0.0, 0.0, 1.0), glm::vec3(0.0, 0.0, 1.0))); vertices.push_back(VertexFormat(glm::vec3(0.25, 0.25, -0.25), // 3 glm::vec4(1.0, 0.0, 0.0, 1.0), glm::vec3(0.0, 0.0, 1.0))); vertices.push_back(VertexFormat(glm::vec3(-0.25, -0.25, -0.25), glm::vec4(1.0, 0.0, 0.0, 1.0), glm::vec3(0.0, 0.0, 1.0))); vertices.push_back(VertexFormat(glm::vec3(0.25, 0.25, -0.25), glm::vec4(1.0, 0.0, 0.0, 1.0), glm::vec3(0.0, 0.0, 1.0))); vertices.push_back(VertexFormat(glm::vec3(0.25, -0.25, -0.25), // 6 end front face | NORMAL: (0.0, 0.0, 1.0) glm::vec4(1.0, 0.0, 0.0, 1.0), glm::vec3(0.0, 0.0, 1.0))); vertices.push_back(VertexFormat(glm::vec3(0.25, -0.25, -0.25), glm::vec4(1.0, 1.0, 0.0, 1.0), glm::vec3(1.0, 0.0, 0.0))); vertices.push_back(VertexFormat(glm::vec3(0.25, 0.25, -0.25), glm::vec4(1.0, 1.0, 0.0, 1.0), glm::vec3(1.0, 0.0, 0.0))); vertices.push_back(VertexFormat(glm::vec3(0.25, 0.25, -0.75), // 9 glm::vec4(1.0, 1.0, 0.0, 1.0), glm::vec3(1.0, 0.0, 0.0))); vertices.push_back(VertexFormat(glm::vec3(0.25, -0.25, -0.25), glm::vec4(1.0, 1.0, 0.0, 1.0), glm::vec3(1.0, 0.0, 0.0))); vertices.push_back(VertexFormat(glm::vec3(0.25, 0.25, -0.75), glm::vec4(1.0, 1.0, 0.0, 1.0), glm::vec3(1.0, 0.0, 0.0))); vertices.push_back(VertexFormat(glm::vec3(0.25, -0.25, -0.75), // 12 end right face | NORMAL: (1.0, 0.0, 0.0) glm::vec4(1.0, 1.0, 0.0, 1.0), glm::vec3(1.0, 0.0, 0.0))); vertices.push_back(VertexFormat(glm::vec3(0.25, -0.25, -0.75), glm::vec4(1.0, 0.0, 1.0, 1.0), glm::vec3(0.0, 0.0, -1.0))); vertices.push_back(VertexFormat(glm::vec3(0.25, 0.25, -0.75), glm::vec4(1.0, 0.0, 1.0, 1.0), glm::vec3(0.0, 0.0, -1.0))); vertices.push_back(VertexFormat(glm::vec3(-0.25, 0.25, -0.75), // 15 glm::vec4(1.0, 0.0, 1.0, 1.0), glm::vec3(0.0, 0.0, -1.0))); vertices.push_back(VertexFormat(glm::vec3(0.25, -0.25, -0.75), glm::vec4(1.0, 0.0, 1.0, 1.0), glm::vec3(0.0, 0.0, -1.0))); vertices.push_back(VertexFormat(glm::vec3(-0.25, 0.25, -0.75), glm::vec4(1.0, 0.0, 1.0, 1.0), glm::vec3(0.0, 0.0, -1.0))); vertices.push_back(VertexFormat(glm::vec3(-0.25, -0.25, -0.75), // 18 end back face | NORMAL: (0.0, 0.0, -1.0) glm::vec4(1.0, 0.0, 1.0, 1.0), glm::vec3(0.0, 0.0, -1.0))); vertices.push_back(VertexFormat(glm::vec3(-0.25, -0.25, -0.75), glm::vec4(0.0, 1.0, 0.0, 1.0), glm::vec3(-1.0, 0.0, 0.0))); vertices.push_back(VertexFormat(glm::vec3(-0.25, 0.25, -0.75), glm::vec4(0.0, 1.0, 0.0, 1.0), glm::vec3(-1.0, 0.0, 0.0))); vertices.push_back(VertexFormat(glm::vec3(-0.25, 0.25, -0.25), // 21 glm::vec4(0.0, 1.0, 0.0, 1.0), glm::vec3(-1.0, 0.0, 0.0))); vertices.push_back(VertexFormat(glm::vec3(-0.25, -0.25, -0.75), glm::vec4(0.0, 1.0, 0.0, 1.0), glm::vec3(-1.0, 0.0, 0.0))); vertices.push_back(VertexFormat(glm::vec3(-0.25, 0.25, -0.25), glm::vec4(0.0, 1.0, 0.0, 1.0), glm::vec3(-1.0, 0.0, 0.0))); vertices.push_back(VertexFormat(glm::vec3(-0.25, -0.25, -0.25), // 24 end left face | NORMAL: (-1.0, 0.0, 0.0) glm::vec4(0.0, 1.0, 0.0, 1.0), glm::vec3(-1.0, 0.0, 0.0))); vertices.push_back(VertexFormat(glm::vec3(-0.25, 0.25, -0.25), glm::vec4(0.0, 0.0, 1.0, 1.0), glm::vec3(0.0, 1.0, 0.0))); vertices.push_back(VertexFormat(glm::vec3(-0.25, 0.25, -0.75), glm::vec4(0.0, 0.0, 1.0, 1.0), glm::vec3(0.0, 1.0, 0.0))); vertices.push_back(VertexFormat(glm::vec3(0.25, 0.25, -0.75), // 27 glm::vec4(0.0, 0.0, 1.0, 1.0), glm::vec3(0.0, 1.0, 0.0))); vertices.push_back(VertexFormat(glm::vec3(-0.25, 0.25, -0.25), glm::vec4(0.0, 0.0, 1.0, 1.0), glm::vec3(0.0, 1.0, 0.0))); vertices.push_back(VertexFormat(glm::vec3(0.25, 0.25, -0.75), glm::vec4(0.0, 0.0, 1.0, 1.0), glm::vec3(0.0, 1.0, 0.0))); vertices.push_back(VertexFormat(glm::vec3(0.25, 0.25, -0.25), // 30 end top face | NORMAL: (0.0, 1.0, 0.0) glm::vec4(0.0, 0.0, 1.0, 1.0), glm::vec3(0.0, 1.0, 0.0))); vertices.push_back(VertexFormat(glm::vec3(-0.25, -0.25, -0.25), glm::vec4(0.0, 1.0, 1.0, 1.0), glm::vec3(0.0, -1.0, 0.0))); vertices.push_back(VertexFormat(glm::vec3(0.25, -0.25, -0.25), glm::vec4(0.0, 1.0, 1.0, 1.0), glm::vec3(0.0, -1.0, 0.0))); vertices.push_back(VertexFormat(glm::vec3(0.25, -0.25, -0.75), // 33 glm::vec4(0.0, 1.0, 1.0, 1.0), glm::vec3(0.0, -1.0, 0.0))); vertices.push_back(VertexFormat(glm::vec3(-0.25, -0.25, -0.25), glm::vec4(0.0, 1.0, 1.0, 1.0), glm::vec3(0.0, -1.0, 0.0))); vertices.push_back(VertexFormat(glm::vec3(0.25, -0.25, -0.75), glm::vec4(0.0, 1.0, 1.0, 1.0), glm::vec3(0.0, -1.0, 0.0))); vertices.push_back(VertexFormat(glm::vec3(-0.25, -0.25, -0.75), // 36 end back face | NORMAL: (0.0, -1.0, 0.0) glm::vec4(0.0, 1.0, 1.0, 1.0), glm::vec3(0.0, -1.0, 0.0))); // This generates buffer object names // The first parameter is the number of buffer objects, and the second parameter is a pointer to an array of buffer objects (yes, before this call, vbo was an empty variable) // (In this example, there's only one buffer object.) glGenBuffers(1, &vbo); // Binds a named buffer object to the specified buffer binding point. Give it a target (GL_ARRAY_BUFFER) to determine where to bind the buffer. // There are several different target parameters, GL_ARRAY_BUFFER is for vertex attributes, feel free to Google the others to find out what else there is. // The second paramter is the buffer object reference. If no buffer object with the given name exists, it will create one. // Buffer object names are unsigned integers (like vbo). Zero is a reserved value, and there is no default buffer for each target (targets, like GL_ARRAY_BUFFER). // Passing in zero as the buffer name (second parameter) will result in unbinding any buffer bound to that target, and frees up the memory. glBindBuffer(GL_ARRAY_BUFFER, vbo); // Creates and initializes a buffer object's data. // First parameter is the target, second parameter is the size of the buffer, third parameter is a pointer to the data that will copied into the buffer, and fourth parameter is the // expected usage pattern of the data. Possible usage patterns: GL_STREAM_DRAW, GL_STREAM_READ, GL_STREAM_COPY, GL_STATIC_DRAW, GL_STATIC_READ, GL_STATIC_COPY, GL_DYNAMIC_DRAW, // GL_DYNAMIC_READ, or GL_DYNAMIC_COPY // Stream means that the data will be modified once, and used only a few times at most. Static means that the data will be modified once, and used a lot. Dynamic means that the data // will be modified repeatedly, and used a lot. Draw means that the data is modified by the application, and used as a source for GL drawing. Read means the data is modified by // reading data from GL, and used to return that data when queried by the application. Copy means that the data is modified by reading from the GL, and used as a source for drawing. glBufferData(GL_ARRAY_BUFFER, sizeof(VertexFormat) * 36, &vertices[0], GL_STATIC_DRAW); // By default, all client-side capabilities are disabled, including all generic vertex attribute arrays. // When enabled, the values in a generic vertex attribute array will be accessed and used for rendering when calls are made to vertex array commands (like glDrawArrays/glDrawElements) // A GL_INVALID_VALUE will be generated if the index parameter is greater than or equal to GL_MAX_VERTEX_ATTRIBS glEnableVertexAttribArray(0); // Defines an array of generic vertex attribute data. Takes an index, a size specifying the number of components (in this case, floats)(has a max of 4) // The third parameter, type, can be GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT, GL_UNSIGNED_SHORT, GL_FIXED, or GL_FLOAT // The fourth parameter specifies whether to normalize fixed-point data values, the fifth parameter is the stride which is the offset (in bytes) between generic vertex attributes // The fifth parameter is a pointer to the first component of the first generic vertex attribute in the array. If named buffer object is bound to GL_ARRAY_BUFFER (and it is, in this case) // then the pointer parameter is treated as a byte offset into the buffer object's data. glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(VertexFormat), (void*)16); // You'll note sizeof(VertexFormat) is our stride, because each vertex contains data that adds up to that size. // You'll also notice we offset this parameter by 16 bytes, this is because the vec3 position attribute is after the vec4 color attribute. A vec4 has 4 floats, each being 4 bytes // so we offset by 4*4=16 to make sure that our first attribute is actually the position. The reason we put position after color in the struct has to do with padding. // For more info on padding, Google it. // This is our color attribute, so the offset is 0, and the size is 4 since there are 4 floats for color. glEnableVertexAttribArray(1); glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(VertexFormat), (void*)0); // This is our normal attribute, so the offset is 16(4*4) + 12(4*3) = 28 glEnableVertexAttribArray(2); glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(VertexFormat), (void*)28); // Read in the shader code from a file. std::string vertShader = readShader("VertexShader.glsl"); std::string fragShader = readShader("FragmentShader.glsl"); // createShader consolidates all of the shader compilation code vertex_shader = createShader(vertShader, GL_VERTEX_SHADER); fragment_shader = createShader(fragShader, GL_FRAGMENT_SHADER); // A shader is a program that runs on your GPU instead of your CPU. In this sense, OpenGL refers to your groups of shaders as "programs". // Using glCreateProgram creates a shader program and returns a GLuint reference to it. program = glCreateProgram(); glAttachShader(program, vertex_shader); // This attaches our vertex shader to our program. glAttachShader(program, fragment_shader); // This attaches our fragment shader to our program. // This links the program, using the vertex and fragment shaders to create executables to run on the GPU. glLinkProgram(program); // End of shader and program creation // Tell OpenGL to use the shader program you've created. glUseProgram(program); // This gets us a reference to the uniform variable in the vertex shader, which is called "trans". // We're using this variable as a 4x4 transformation matrix // Only 2 parameters required: A reference to the shader program and the name of the uniform variable within the shader code. uniTrans = glGetUniformLocation(program, "trans"); // We'll also need references to the rest of our uniforms too. uniNumLights = glGetUniformLocation(program, "numLights"); // Because there are so many different uniforms for the Light struct, we'll want a function to make this easier. setUniformAttrib(uniLightPosition, "position"); setUniformAttrib(uniLightColor, "diffuseColor"); setUniformAttrib(uniLightConeDir, "coneDirection"); setUniformAttrib(uniLightConeAng, "coneAngle"); setUniformAttrib(uniLightAttenuation, "attenuation"); setUniformAttrib(uniLightAmbient, "ambientCoefficient"); // Set the number of lights in your shader. glUniform1i(uniNumLights, 3); // All the Z values passed into the shader are getting reversed somehow, so we will flip them in the shader code. // Alternatively, you can just remember to flip them beforehand. This example has it flipping them in the shader code, so don't flip them here too or you'll just // end up leaving them in the wrong direction. Light dirLight; dirLight.ambientCoefficient = 0.15f; // Passing in 0 for the W value identifies this as a directional light, thus the only relevant values are the position (direction), color, and ambientCoefficient. dirLight.position = glm::vec4(-0.2f, -0.2f, 1.0f, 0.0f); dirLight.diffuseColor = glm::vec4(0.10f, 0.10f, 0.10f, 1.0f); Light pointLight; pointLight.ambientCoefficient = 0.0f; pointLight.position = glm::vec4(-1.0f, 0.0f, 0.0f, 1.0f); // The Z value appears to be flipped for some reason, we'll have to look into that. pointLight.coneAngle = 360.0f; // Setting a coneAngle of 180 (I set 360 to be extra safe) guarantees that this will not be a spotlight. pointLight.diffuseColor = glm::vec4(0.15f, 0.15f, 0.15f, 1.0f); pointLight.attenuation = 0.25f; pointLight.coneDirection = glm::vec4(1.0f, 1.0f, 1.0f, 1.0f); // Since coneAngle is 180+, this value is irreleavant. Light spotLight; spotLight.ambientCoefficient = 0.0f; spotLight.position = glm::vec4(0.0f, 0.0f, -1.0f, 1.0f); // The Z value appears to be flipped for some reason, we'll have to look into that. spotLight.coneAngle = 7.0f; spotLight.diffuseColor = glm::vec4(0.5f, 0.5f, 0.5f, 1.0f); spotLight.attenuation = 0.25f; spotLight.coneDirection = glm::vec4(0.0f, 0.0f, 1.0f, 1.0f); // A function to help setup a given light. setLight(0, &dirLight); setLight(1, &pointLight); setLight(2, &spotLight); // This is not necessary, but I prefer to handle my vertices in the clockwise order. glFrontFace defines which face of the triangles you're drawing is the front. // Essentially, if you draw your vertices in counter-clockwise order, by default (in OpenGL) the front face will be facing you/the screen. If you draw them clockwise, the front face // will face away from you. By passing in GL_CW to this function, we are saying the opposite, and now the front face will face you if you draw in the clockwise order. // If you don't use this, just reverse the order of the vertices in your array when you define them so that you draw the points in a counter-clockwise order. glFrontFace(GL_CW); // This is also not necessary, but more efficient and is generally good practice. By default, OpenGL will render both sides of a triangle that you draw. By enabling GL_CULL_FACE, // we are telling OpenGL to only render the front face. This means that if you rotated the triangle over the X-axis, you wouldn't see the other side of the triangle as it rotated. glEnable(GL_CULL_FACE); // Determines the interpretation of polygons for rasterization. The first parameter, face, determines which polygons the mode applies to. // The face can be either GL_FRONT, GL_BACK, or GL_FRONT_AND_BACK // The mode determines how the polygons will be rasterized. GL_POINT will draw points at each vertex, GL_LINE will draw lines between the vertices, and // GL_FILL will fill the area inside those lines. glPolygonMode(GL_FRONT, GL_FILL); }