From d702e0c6bf7ad48bb368343db47ca7a48b8f8449 Mon Sep 17 00:00:00 2001 From: d3BugErr Date: Tue, 10 Apr 2018 11:23:09 +0300 Subject: [PATCH] Spheres model matrix generation fix Fix of integer division resulting in some spheres being rendered at same position. Removed redundant float cast as all operands are being promoted to double anyways. --- src/6.pbr/1.1.lighting/lighting.cpp | 749 ++++++++++++++-------------- 1 file changed, 376 insertions(+), 373 deletions(-) diff --git a/src/6.pbr/1.1.lighting/lighting.cpp b/src/6.pbr/1.1.lighting/lighting.cpp index c209d36..c1f8c43 100644 --- a/src/6.pbr/1.1.lighting/lighting.cpp +++ b/src/6.pbr/1.1.lighting/lighting.cpp @@ -1,373 +1,376 @@ -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include - -#include - -void framebuffer_size_callback(GLFWwindow* window, int width, int height); -void mouse_callback(GLFWwindow* window, double xpos, double ypos); -void scroll_callback(GLFWwindow* window, double xoffset, double yoffset); -void processInput(GLFWwindow *window); -unsigned int loadTexture(const char *path); -void renderSphere(); - -// settings -const unsigned int SCR_WIDTH = 1280; -const unsigned int SCR_HEIGHT = 720; - -// camera -Camera camera(glm::vec3(0.0f, 0.0f, 3.0f)); -float lastX = 800.0f / 2.0; -float lastY = 600.0 / 2.0; -bool firstMouse = true; - -// timing -float deltaTime = 0.0f; -float lastFrame = 0.0f; - -int main() -{ - // glfw: initialize and configure - // ------------------------------ - glfwInit(); - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); - glfwWindowHint(GLFW_SAMPLES, 4); - glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); - //glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // uncomment this statement to fix compilation on OS X - - // glfw window creation - // -------------------- - GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL); - glfwMakeContextCurrent(window); - if (window == NULL) - { - std::cout << "Failed to create GLFW window" << std::endl; - glfwTerminate(); - return -1; - } - glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); - glfwSetCursorPosCallback(window, mouse_callback); - glfwSetScrollCallback(window, scroll_callback); - - // tell GLFW to capture our mouse - glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); - - // glad: load all OpenGL function pointers - // --------------------------------------- - if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) - { - std::cout << "Failed to initialize GLAD" << std::endl; - return -1; - } - - // configure global opengl state - // ----------------------------- - glEnable(GL_DEPTH_TEST); - - // build and compile shaders - // ------------------------- - Shader shader("1.1.pbr.vs", "1.1.pbr.fs"); - - shader.use(); - shader.setVec3("albedo", 0.5f, 0.0f, 0.0f); - shader.setFloat("ao", 1.0f); - - // lights - // ------ - glm::vec3 lightPositions[] = { - glm::vec3(-10.0f, 10.0f, 10.0f), - glm::vec3( 10.0f, 10.0f, 10.0f), - glm::vec3(-10.0f, -10.0f, 10.0f), - glm::vec3( 10.0f, -10.0f, 10.0f), - }; - glm::vec3 lightColors[] = { - glm::vec3(300.0f, 300.0f, 300.0f), - glm::vec3(300.0f, 300.0f, 300.0f), - glm::vec3(300.0f, 300.0f, 300.0f), - glm::vec3(300.0f, 300.0f, 300.0f) - }; - int nrRows = 7; - int nrColumns = 7; - float spacing = 2.5; - - // initialize static shader uniforms before rendering - // -------------------------------------------------- - glm::mat4 projection = glm::perspective(camera.Zoom, (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f); - shader.use(); - shader.setMat4("projection", projection); - - // render loop - // ----------- - while (!glfwWindowShouldClose(window)) - { - // per-frame time logic - // -------------------- - float currentFrame = glfwGetTime(); - deltaTime = currentFrame - lastFrame; - lastFrame = currentFrame; - - // input - // ----- - processInput(window); - - // render - // ------ - glClearColor(0.1f, 0.1f, 0.1f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - shader.use(); - glm::mat4 view = camera.GetViewMatrix(); - shader.setMat4("view", view); - shader.setVec3("camPos", camera.Position); - - // render rows*column number of spheres with varying metallic/roughness values scaled by rows and columns respectively - glm::mat4 model; - for (unsigned int row = 0; row < nrRows; ++row) - { - shader.setFloat("metallic", (float)row / (float)nrRows); - for (unsigned int col = 0; col < nrColumns; ++col) - { - // we clamp the roughness to 0.025 - 1.0 as perfectly smooth surfaces (roughness of 0.0) tend to look a bit off - // on direct lighting. - shader.setFloat("roughness", glm::clamp((float)col / (float)nrColumns, 0.05f, 1.0f)); - - model = glm::mat4(); - model = glm::translate(model, glm::vec3( - (float)(col - (nrColumns / 2)) * spacing, - (float)(row - (nrRows / 2)) * spacing, - 0.0f - )); - shader.setMat4("model", model); - renderSphere(); - } - } - - // render light source (simply re-render sphere at light positions) - // this looks a bit off as we use the same shader, but it'll make their positions obvious and - // keeps the codeprint small. - for (unsigned int i = 0; i < sizeof(lightPositions) / sizeof(lightPositions[0]); ++i) - { - glm::vec3 newPos = lightPositions[i] + glm::vec3(sin(glfwGetTime() * 5.0) * 5.0, 0.0, 0.0); - newPos = lightPositions[i]; - shader.setVec3("lightPositions[" + std::to_string(i) + "]", newPos); - shader.setVec3("lightColors[" + std::to_string(i) + "]", lightColors[i]); - - model = glm::mat4(); - model = glm::translate(model, newPos); - model = glm::scale(model, glm::vec3(0.5f)); - shader.setMat4("model", model); - renderSphere(); - } - - // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.) - // ------------------------------------------------------------------------------- - glfwSwapBuffers(window); - glfwPollEvents(); - } - - // glfw: terminate, clearing all previously allocated GLFW resources. - // ------------------------------------------------------------------ - glfwTerminate(); - return 0; -} - -// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly -// --------------------------------------------------------------------------------------------------------- -void processInput(GLFWwindow *window) -{ - if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) - glfwSetWindowShouldClose(window, true); - - float cameraSpeed = 2.5 * deltaTime; - if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) - camera.ProcessKeyboard(FORWARD, deltaTime); - if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) - camera.ProcessKeyboard(BACKWARD, deltaTime); - if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) - camera.ProcessKeyboard(LEFT, deltaTime); - if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) - camera.ProcessKeyboard(RIGHT, deltaTime); -} - -// glfw: whenever the window size changed (by OS or user resize) this callback function executes -// --------------------------------------------------------------------------------------------- -void framebuffer_size_callback(GLFWwindow* window, int width, int height) -{ - // make sure the viewport matches the new window dimensions; note that width and - // height will be significantly larger than specified on retina displays. - glViewport(0, 0, width, height); -} - - -// glfw: whenever the mouse moves, this callback is called -// ------------------------------------------------------- -void mouse_callback(GLFWwindow* window, double xpos, double ypos) -{ - if (firstMouse) - { - lastX = xpos; - lastY = ypos; - firstMouse = false; - } - - float xoffset = xpos - lastX; - float yoffset = lastY - ypos; // reversed since y-coordinates go from bottom to top - - lastX = xpos; - lastY = ypos; - - camera.ProcessMouseMovement(xoffset, yoffset); -} - -// glfw: whenever the mouse scroll wheel scrolls, this callback is called -// ---------------------------------------------------------------------- -void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) -{ - camera.ProcessMouseScroll(yoffset); -} - -// renders (and builds at first invocation) a sphere -// ------------------------------------------------- -unsigned int sphereVAO = 0; -unsigned int indexCount; -void renderSphere() -{ - if (sphereVAO == 0) - { - glGenVertexArrays(1, &sphereVAO); - - unsigned int vbo, ebo; - glGenBuffers(1, &vbo); - glGenBuffers(1, &ebo); - - std::vector positions; - std::vector uv; - std::vector normals; - std::vector indices; - - const unsigned int X_SEGMENTS = 64; - const unsigned int Y_SEGMENTS = 64; - const float PI = 3.14159265359; - for (unsigned int y = 0; y <= Y_SEGMENTS; ++y) - { - for (unsigned int x = 0; x <= X_SEGMENTS; ++x) - { - float xSegment = (float)x / (float)X_SEGMENTS; - float ySegment = (float)y / (float)Y_SEGMENTS; - float xPos = std::cos(xSegment * 2.0f * PI) * std::sin(ySegment * PI); - float yPos = std::cos(ySegment * PI); - float zPos = std::sin(xSegment * 2.0f * PI) * std::sin(ySegment * PI); - - positions.push_back(glm::vec3(xPos, yPos, zPos)); - uv.push_back(glm::vec2(xSegment, ySegment)); - normals.push_back(glm::vec3(xPos, yPos, zPos)); - } - } - - bool oddRow = false; - for (int y = 0; y < Y_SEGMENTS; ++y) - { - if (!oddRow) // even rows: y == 0, y == 2; and so on - { - for (int x = 0; x <= X_SEGMENTS; ++x) - { - indices.push_back(y * (X_SEGMENTS + 1) + x); - indices.push_back((y + 1) * (X_SEGMENTS + 1) + x); - } - } - else - { - for (int x = X_SEGMENTS; x >= 0; --x) - { - indices.push_back((y + 1) * (X_SEGMENTS + 1) + x); - indices.push_back(y * (X_SEGMENTS + 1) + x); - } - } - oddRow = !oddRow; - } - indexCount = indices.size(); - - std::vector data; - for (int i = 0; i < positions.size(); ++i) - { - data.push_back(positions[i].x); - data.push_back(positions[i].y); - data.push_back(positions[i].z); - if (uv.size() > 0) - { - data.push_back(uv[i].x); - data.push_back(uv[i].y); - } - if (normals.size() > 0) - { - data.push_back(normals[i].x); - data.push_back(normals[i].y); - data.push_back(normals[i].z); - } - } - glBindVertexArray(sphereVAO); - glBindBuffer(GL_ARRAY_BUFFER, vbo); - glBufferData(GL_ARRAY_BUFFER, data.size() * sizeof(float), &data[0], GL_STATIC_DRAW); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), &indices[0], GL_STATIC_DRAW); - float stride = (3 + 2 + 3) * sizeof(float); - glEnableVertexAttribArray(0); - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, stride, (void*)0); - glEnableVertexAttribArray(1); - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, stride, (void*)(3 * sizeof(float))); - glEnableVertexAttribArray(2); - glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, stride, (void*)(5 * sizeof(float))); - } - - glBindVertexArray(sphereVAO); - glDrawElements(GL_TRIANGLE_STRIP, indexCount, GL_UNSIGNED_INT, 0); -} - -// utility function for loading a 2D texture from file -// --------------------------------------------------- -unsigned int loadTexture(char const * path) -{ - unsigned int textureID; - glGenTextures(1, &textureID); - - int width, height, nrComponents; - unsigned char *data = stbi_load(path, &width, &height, &nrComponents, 0); - if (data) - { - GLenum format; - if (nrComponents == 1) - format = GL_RED; - else if (nrComponents == 3) - format = GL_RGB; - else if (nrComponents == 4) - format = GL_RGBA; - - glBindTexture(GL_TEXTURE_2D, textureID); - glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data); - glGenerateMipmap(GL_TEXTURE_2D); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - stbi_image_free(data); - } - else - { - std::cout << "Texture failed to load at path: " << path << std::endl; - stbi_image_free(data); - } - - return textureID; -} +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include + +void framebuffer_size_callback(GLFWwindow* window, int width, int height); +void mouse_callback(GLFWwindow* window, double xpos, double ypos); +void scroll_callback(GLFWwindow* window, double xoffset, double yoffset); +void processInput(GLFWwindow *window); +unsigned int loadTexture(const char *path); +void renderSphere(); + +// settings +const unsigned int SCR_WIDTH = 1280; +const unsigned int SCR_HEIGHT = 720; + +// camera +Camera camera(glm::vec3(0.0f, 0.0f, 3.0f)); +float lastX = 800.0f / 2.0; +float lastY = 600.0 / 2.0; +bool firstMouse = true; + +// timing +float deltaTime = 0.0f; +float lastFrame = 0.0f; + +int main() +{ + // glfw: initialize and configure + // ------------------------------ + glfwInit(); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_SAMPLES, 4); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + +#ifdef __APPLE__ + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // uncomment this statement to fix compilation on OS X +#endif + + // glfw window creation + // -------------------- + GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL); + glfwMakeContextCurrent(window); + if (window == NULL) + { + std::cout << "Failed to create GLFW window" << std::endl; + glfwTerminate(); + return -1; + } + glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); + glfwSetCursorPosCallback(window, mouse_callback); + glfwSetScrollCallback(window, scroll_callback); + + // tell GLFW to capture our mouse + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + + // glad: load all OpenGL function pointers + // --------------------------------------- + if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) + { + std::cout << "Failed to initialize GLAD" << std::endl; + return -1; + } + + // configure global opengl state + // ----------------------------- + glEnable(GL_DEPTH_TEST); + + // build and compile shaders + // ------------------------- + Shader shader("1.1.pbr.vs", "1.1.pbr.fs"); + + shader.use(); + shader.setVec3("albedo", 0.5f, 0.0f, 0.0f); + shader.setFloat("ao", 1.0f); + + // lights + // ------ + glm::vec3 lightPositions[] = { + glm::vec3(-10.0f, 10.0f, 10.0f), + glm::vec3( 10.0f, 10.0f, 10.0f), + glm::vec3(-10.0f, -10.0f, 10.0f), + glm::vec3( 10.0f, -10.0f, 10.0f), + }; + glm::vec3 lightColors[] = { + glm::vec3(300.0f, 300.0f, 300.0f), + glm::vec3(300.0f, 300.0f, 300.0f), + glm::vec3(300.0f, 300.0f, 300.0f), + glm::vec3(300.0f, 300.0f, 300.0f) + }; + int nrRows = 7; + int nrColumns = 7; + float spacing = 2.5; + + // initialize static shader uniforms before rendering + // -------------------------------------------------- + glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f); + shader.use(); + shader.setMat4("projection", projection); + + // render loop + // ----------- + while (!glfwWindowShouldClose(window)) + { + // per-frame time logic + // -------------------- + float currentFrame = glfwGetTime(); + deltaTime = currentFrame - lastFrame; + lastFrame = currentFrame; + + // input + // ----- + processInput(window); + + // render + // ------ + glClearColor(0.1f, 0.1f, 0.1f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + shader.use(); + glm::mat4 view = camera.GetViewMatrix(); + shader.setMat4("view", view); + shader.setVec3("camPos", camera.Position); + + // render rows*column number of spheres with varying metallic/roughness values scaled by rows and columns respectively + glm::mat4 model; + for (unsigned int row = 0; row < nrRows; ++row) + { + shader.setFloat("metallic", (float)row / (float)nrRows); + for (unsigned int col = 0; col < nrColumns; ++col) + { + // we clamp the roughness to 0.025 - 1.0 as perfectly smooth surfaces (roughness of 0.0) tend to look a bit off + // on direct lighting. + shader.setFloat("roughness", glm::clamp((float)col / (float)nrColumns, 0.05f, 1.0f)); + + model = glm::mat4(); + model = glm::translate(model, glm::vec3( + (col - (nrColumns / 2.)) * spacing, + (row - (nrRows / 2.)) * spacing, + 0.0f + )); + shader.setMat4("model", model); + renderSphere(); + } + } + + // render light source (simply re-render sphere at light positions) + // this looks a bit off as we use the same shader, but it'll make their positions obvious and + // keeps the codeprint small. + for (unsigned int i = 0; i < sizeof(lightPositions) / sizeof(lightPositions[0]); ++i) + { + glm::vec3 newPos = lightPositions[i] + glm::vec3(sin(glfwGetTime() * 5.0) * 5.0, 0.0, 0.0); + newPos = lightPositions[i]; + shader.setVec3("lightPositions[" + std::to_string(i) + "]", newPos); + shader.setVec3("lightColors[" + std::to_string(i) + "]", lightColors[i]); + + model = glm::mat4(); + model = glm::translate(model, newPos); + model = glm::scale(model, glm::vec3(0.5f)); + shader.setMat4("model", model); + renderSphere(); + } + + // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.) + // ------------------------------------------------------------------------------- + glfwSwapBuffers(window); + glfwPollEvents(); + } + + // glfw: terminate, clearing all previously allocated GLFW resources. + // ------------------------------------------------------------------ + glfwTerminate(); + return 0; +} + +// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly +// --------------------------------------------------------------------------------------------------------- +void processInput(GLFWwindow *window) +{ + if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) + glfwSetWindowShouldClose(window, true); + + float cameraSpeed = 2.5 * deltaTime; + if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) + camera.ProcessKeyboard(FORWARD, deltaTime); + if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) + camera.ProcessKeyboard(BACKWARD, deltaTime); + if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) + camera.ProcessKeyboard(LEFT, deltaTime); + if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) + camera.ProcessKeyboard(RIGHT, deltaTime); +} + +// glfw: whenever the window size changed (by OS or user resize) this callback function executes +// --------------------------------------------------------------------------------------------- +void framebuffer_size_callback(GLFWwindow* window, int width, int height) +{ + // make sure the viewport matches the new window dimensions; note that width and + // height will be significantly larger than specified on retina displays. + glViewport(0, 0, width, height); +} + + +// glfw: whenever the mouse moves, this callback is called +// ------------------------------------------------------- +void mouse_callback(GLFWwindow* window, double xpos, double ypos) +{ + if (firstMouse) + { + lastX = xpos; + lastY = ypos; + firstMouse = false; + } + + float xoffset = xpos - lastX; + float yoffset = lastY - ypos; // reversed since y-coordinates go from bottom to top + + lastX = xpos; + lastY = ypos; + + camera.ProcessMouseMovement(xoffset, yoffset); +} + +// glfw: whenever the mouse scroll wheel scrolls, this callback is called +// ---------------------------------------------------------------------- +void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) +{ + camera.ProcessMouseScroll(yoffset); +} + +// renders (and builds at first invocation) a sphere +// ------------------------------------------------- +unsigned int sphereVAO = 0; +unsigned int indexCount; +void renderSphere() +{ + if (sphereVAO == 0) + { + glGenVertexArrays(1, &sphereVAO); + + unsigned int vbo, ebo; + glGenBuffers(1, &vbo); + glGenBuffers(1, &ebo); + + std::vector positions; + std::vector uv; + std::vector normals; + std::vector indices; + + const unsigned int X_SEGMENTS = 64; + const unsigned int Y_SEGMENTS = 64; + const float PI = 3.14159265359; + for (unsigned int y = 0; y <= Y_SEGMENTS; ++y) + { + for (unsigned int x = 0; x <= X_SEGMENTS; ++x) + { + float xSegment = (float)x / (float)X_SEGMENTS; + float ySegment = (float)y / (float)Y_SEGMENTS; + float xPos = std::cos(xSegment * 2.0f * PI) * std::sin(ySegment * PI); + float yPos = std::cos(ySegment * PI); + float zPos = std::sin(xSegment * 2.0f * PI) * std::sin(ySegment * PI); + + positions.push_back(glm::vec3(xPos, yPos, zPos)); + uv.push_back(glm::vec2(xSegment, ySegment)); + normals.push_back(glm::vec3(xPos, yPos, zPos)); + } + } + + bool oddRow = false; + for (int y = 0; y < Y_SEGMENTS; ++y) + { + if (!oddRow) // even rows: y == 0, y == 2; and so on + { + for (int x = 0; x <= X_SEGMENTS; ++x) + { + indices.push_back(y * (X_SEGMENTS + 1) + x); + indices.push_back((y + 1) * (X_SEGMENTS + 1) + x); + } + } + else + { + for (int x = X_SEGMENTS; x >= 0; --x) + { + indices.push_back((y + 1) * (X_SEGMENTS + 1) + x); + indices.push_back(y * (X_SEGMENTS + 1) + x); + } + } + oddRow = !oddRow; + } + indexCount = indices.size(); + + std::vector data; + for (int i = 0; i < positions.size(); ++i) + { + data.push_back(positions[i].x); + data.push_back(positions[i].y); + data.push_back(positions[i].z); + if (uv.size() > 0) + { + data.push_back(uv[i].x); + data.push_back(uv[i].y); + } + if (normals.size() > 0) + { + data.push_back(normals[i].x); + data.push_back(normals[i].y); + data.push_back(normals[i].z); + } + } + glBindVertexArray(sphereVAO); + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferData(GL_ARRAY_BUFFER, data.size() * sizeof(float), &data[0], GL_STATIC_DRAW); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), &indices[0], GL_STATIC_DRAW); + float stride = (3 + 2 + 3) * sizeof(float); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, stride, (void*)0); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, stride, (void*)(3 * sizeof(float))); + glEnableVertexAttribArray(2); + glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, stride, (void*)(5 * sizeof(float))); + } + + glBindVertexArray(sphereVAO); + glDrawElements(GL_TRIANGLE_STRIP, indexCount, GL_UNSIGNED_INT, 0); +} + +// utility function for loading a 2D texture from file +// --------------------------------------------------- +unsigned int loadTexture(char const * path) +{ + unsigned int textureID; + glGenTextures(1, &textureID); + + int width, height, nrComponents; + unsigned char *data = stbi_load(path, &width, &height, &nrComponents, 0); + if (data) + { + GLenum format; + if (nrComponents == 1) + format = GL_RED; + else if (nrComponents == 3) + format = GL_RGB; + else if (nrComponents == 4) + format = GL_RGBA; + + glBindTexture(GL_TEXTURE_2D, textureID); + glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data); + glGenerateMipmap(GL_TEXTURE_2D); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + stbi_image_free(data); + } + else + { + std::cout << "Texture failed to load at path: " << path << std::endl; + stbi_image_free(data); + } + + return textureID; +}