text: #include <GLFW/glfw3.h>
  #include <glad/glad.h>
  
  #include <glm/glm.hpp>
  #include <glm/gtc/matrix_transform.hpp>
  #include <glm/gtc/type_ptr.hpp>
  
  #include "ReviewCamera.h"
  #include "ReviewShader.h"
  #include "Model.h"
  
  #include <vector>
  #include <iostream>
  using namespace std;
  
  
  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);
  unsigned int loadCubemap(vector<string> faces);
  void ConfigureShaderAndMatrices(ReviewShader shader);
  void RenderScene(const ReviewShader shader);
  void RenderCube();
  
  // size 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 = (float)SCR_WIDTH  / 2.0;
  float lastY = (float)SCR_HEIGHT / 2.0;
  bool firstMouse = true;
  
  // position
  glm::vec3 lightPos(0.0, 0.0, 0.0);
  
  // timing
  float deltaTime = 0.0f;
  float lastFrame = 0.0f;
  
  // globle vertex
  unsigned int planeVAO;
  
  int main()
  {
      // glfw: initialize and configure
      // ------------------------------
      glfwInit();
      glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
      glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
      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);
      if (window == NULL)
      {
          std::cout << "Failed to create GLFW window" << std::endl;
          glfwTerminate();
          return -1;
      }
      glfwMakeContextCurrent(window);
      glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
      glfwSetCursorPosCallback(window, mouse_callback);
      glfwSetScrollCallback(window, scroll_callback);
      glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);        // tell GLFW to capture our mouse
  
      // glad: load all OpenGL function pointers
      // ---------------------------------------
      if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
      {
          std::cout << "Failed to initialize GLAD" << std::endl;
          return -1;
      }
  
      // Enable Depth Test
      // -----------------
      glEnable(GL_DEPTH_TEST);
      glEnable(GL_CULL_FACE);
  
      // Set plane vertices
      // ------------------
      float planeVertices[] = {
          // positions            // normals         // texcoords
          -25.0f, -0.5f,  25.0f,  0.0f, 1.0f, 0.0f,   0.0f,  0.0f,
           25.0f, -0.5f,  25.0f,  0.0f, 1.0f, 0.0f,  25.0f,  0.0f,
          -25.0f, -0.5f, -25.0f,  0.0f, 1.0f, 0.0f,   0.0f, 25.0f,
  
          -25.0f, -0.5f, -25.0f,  0.0f, 1.0f, 0.0f,   0.0f, 25.0f,
           25.0f, -0.5f,  25.0f,  0.0f, 1.0f, 0.0f,  25.0f,  0.0f,
           25.0f, -0.5f, -25.0f,  0.0f, 1.0f, 0.0f,  25.0f, 10.0f
      };
      stbi_set_flip_vertically_on_load(true);
      // Create Depth plane VAO, VBO
      // ---------------------------
      unsigned int planeVBO;
      glGenVertexArrays(1, &planeVAO);
      glBindVertexArray(planeVAO);
      glGenBuffers(1, &planeVBO);
      glBindBuffer(GL_ARRAY_BUFFER, planeVBO);
      glBufferData(GL_ARRAY_BUFFER, sizeof(planeVertices), planeVertices, GL_STATIC_DRAW);
      glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
      glEnableVertexAttribArray(0);
      glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(sizeof(float) * 3));
      glEnableVertexAttribArray(1);
      glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(sizeof(float) * 6));
      glEnableVertexAttribArray(2);
      glBindVertexArray(0);
  
  
  
      // build and compile shaders
      // -------------------------
      ReviewShader sampleDepthShader("shader/sampleDepthShader.vs", "shader/sampleDepthShader.fs", "shader/sampleDepthShader.gs");
      ReviewShader shader("shader/shader.vs", "shader/shader.fs");
  
      // Load floor texture
      // ------------------
      unsigned int floorTexture = loadTexture("img/wood.png");
  
      const unsigned int SHADOW_WIDTH = 1024;
      const unsigned int SHADOW_HEIGHT = 1024;
  
      // create FBO
      // ----------
      unsigned int depthMapFBO;
      glGenFramebuffers(1, &depthMapFBO);
  
      // create depth buffer texture
      // ---------------------------
      unsigned int cubeDepthTexture;
      glGenTextures(1, &cubeDepthTexture);
      glBindTexture(GL_TEXTURE_CUBE_MAP, cubeDepthTexture);
      for(int i = 0; i < 6; i++){
          glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_DEPTH_COMPONENT, SHADOW_WIDTH, SHADOW_HEIGHT, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
      }
      glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
      glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
      glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
      glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
      glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  
      // bind the depth buffer texture to FBO
      // ------------------------------------
      glBindFramebuffer(GL_FRAMEBUFFER, depthMapFBO);
      glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, cubeDepthTexture, 0);
      glDrawBuffer(GL_NONE);
      glReadBuffer(GL_NONE);
      glBindFramebuffer(GL_FRAMEBUFFER, 0);
  
      float near = 0.1;
      float far = 25.0;
      glm::mat4 shadowProj = glm::perspective(glm::radians(90.0f), (float)SHADOW_WIDTH / (float)SHADOW_HEIGHT, near, far);
      vector<glm::mat4> shadowTransform;
      shadowTransform.push_back(shadowProj * glm::lookAt(lightPos, lightPos + glm::vec3( 1.0,  0.0,  0.0), glm::vec3(0.0, -1.0, 0.0)));    // right
      shadowTransform.push_back(shadowProj * glm::lookAt(lightPos, lightPos + glm::vec3(-1.0,  0.0,  0.0), glm::vec3(0.0, -1.0, 0.0)));    // left
      shadowTransform.push_back(shadowProj * glm::lookAt(lightPos, lightPos + glm::vec3( 0.0,  1.0,  0.0), glm::vec3(0.0, 0.0, 1.0)));     // up
      shadowTransform.push_back(shadowProj * glm::lookAt(lightPos, lightPos + glm::vec3( 0.0, -1.0,  0.0), glm::vec3(0.0, 0.0, -1.0)));    // bottom
      shadowTransform.push_back(shadowProj * glm::lookAt(lightPos, lightPos + glm::vec3( 0.0,  0.0,  1.0), glm::vec3(0.0, -1.0, 0.0)));    // front
      shadowTransform.push_back(shadowProj * glm::lookAt(lightPos, lightPos + glm::vec3( 0.0,  0.0, -1.0), glm::vec3(0.0, -1.0, 0.0)));    // back
      sampleDepthShader.use();
      for(int i = 0; i < 6; i++){
          sampleDepthShader.setMat4("shadowMatrices[" + to_string(i) + "]", shadowTransform[i]);
      }
      sampleDepthShader.setFloat("far_plane", far);
      sampleDepthShader.setVec3("lightPos", lightPos);
      shader.use();
      shader.setInt("diffuseTexture", 0);
      shader.setInt("depthMap", 1);
      // render loop
      // -----------
  
      while(!glfwWindowShouldClose(window))
      {
          // avoid delay
          // -----------
          float currentFrame = glfwGetTime();
          deltaTime = currentFrame - lastFrame;
          lastFrame = currentFrame;
  
          // process key input
          // -----------------
          processInput(window);
  
          // render
          // ------
          glClearColor(1.0, 1.0, 1.0, 1.0);
          glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
          // get the depth
          // -------------
          glViewport(0, 0, SHADOW_WIDTH ,SHADOW_HEIGHT);
          glBindFramebuffer(GL_FRAMEBUFFER, depthMapFBO);
          glClear(GL_DEPTH_BUFFER_BIT);
          sampleDepthShader.use();
          RenderScene(sampleDepthShader);
          glBindFramebuffer(GL_FRAMEBUFFER, 0);
  
          // Render the scene
          // ----------------
          glViewport(0, 0, SCR_WIDTH * 2, SCR_HEIGHT * 2);
          glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
          shader.use();
          glm::mat4 projection = glm::perspective(camera.Zoom, (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
          glm::mat4 view = camera.getViewMatrix();
          shader.setMat4("projection", projection);
          shader.setMat4("view", view);
          shader.setVec3("lightPos", lightPos);
          shader.setVec3("viewPos", camera.Position);
  
          shader.setFloat("far_plane", far);
          glActiveTexture(GL_TEXTURE0);
          glBindTexture(GL_TEXTURE_2D, floorTexture);
          glActiveTexture(GL_TEXTURE1);
          glBindTexture(GL_TEXTURE_CUBE_MAP, cubeDepthTexture);
          RenderScene(shader);
  
  
          glm::mat4 model = glm::mat4(1.0f);
          model = glm::translate(model, lightPos);
          model = glm::scale(model, glm::vec3(0.09f));
          shader.setMat4("model", model);
          RenderCube();
          glfwSwapBuffers(window);
          glfwPollEvents();
      }
      glfwTerminate();
      return 0;
  }
  
  
  void RenderScene(const ReviewShader shader){
      // room cube
      glm::mat4 model = glm::mat4(1.0f);
      model = glm::scale(model, glm::vec3(5.0f));
      shader.setMat4("model", model);
      glDisable(GL_CULL_FACE);
      shader.setInt("reverse_normals", 1);
      RenderCube();
      shader.setInt("reverse_normals", 0); // and of course disable it
      glEnable(GL_CULL_FACE);
  
      // cubes
      model = glm::mat4(1.0f);
      model = glm::translate(model, glm::vec3(4.0f, -3.5f, 1.0));
      model = glm::scale(model, glm::vec3(0.5f));
      shader.setMat4("model", model);
      RenderCube();
      model = glm::mat4(1.0f);
      model = glm::translate(model, glm::vec3(2.0f, 3.0f, 1.0));
      model = glm::scale(model, glm::vec3(0.75f));
      shader.setMat4("model", model);
      RenderCube();
      model = glm::mat4(1.0f);
      model = glm::translate(model, glm::vec3(-3.0f, -1.0f, 0.0));
      model = glm::scale(model, glm::vec3(0.5f));
      shader.setMat4("model", model);
      RenderCube();
      model = glm::mat4(1.0f);
      model = glm::translate(model, glm::vec3(-1.5f, 1.0f, 1.5));
      model = glm::scale(model, glm::vec3(0.5f));
      shader.setMat4("model", model);
      RenderCube();
      model = glm::mat4(1.0f);
      model = glm::translate(model, glm::vec3(-1.5f, 2.0f, -3.0));
      model = glm::rotate(model, glm::radians(60.0f), glm::normalize(glm::vec3(1.0, 0.0, 1.0)));
      model = glm::scale(model, glm::vec3(0.75f));
      shader.setMat4("model", model);
      RenderCube();
  }
  
  unsigned int cubeVAO = 0;
  unsigned int cubeVBO;
  void RenderCube(){
      // bind vertices
      // -------------
      if(cubeVAO == 0){
          float cubeVertices[] = {
              // back face
              -1.0f, -1.0f, -1.0f,  0.0f,  0.0f, -1.0f, 0.0f, 0.0f, // bottom-left
              1.0f,  1.0f, -1.0f,  0.0f,  0.0f, -1.0f, 1.0f, 1.0f, // top-right
              1.0f, -1.0f, -1.0f,  0.0f,  0.0f, -1.0f, 1.0f, 0.0f, // bottom-right
              1.0f,  1.0f, -1.0f,  0.0f,  0.0f, -1.0f, 1.0f, 1.0f, // top-right
              -1.0f, -1.0f, -1.0f,  0.0f,  0.0f, -1.0f, 0.0f, 0.0f, // bottom-left
              -1.0f,  1.0f, -1.0f,  0.0f,  0.0f, -1.0f, 0.0f, 1.0f, // top-left
              // front face
              -1.0f, -1.0f,  1.0f,  0.0f,  0.0f,  1.0f, 0.0f, 0.0f, // bottom-left
              1.0f, -1.0f,  1.0f,  0.0f,  0.0f,  1.0f, 1.0f, 0.0f, // bottom-right
              1.0f,  1.0f,  1.0f,  0.0f,  0.0f,  1.0f, 1.0f, 1.0f, // top-right
              1.0f,  1.0f,  1.0f,  0.0f,  0.0f,  1.0f, 1.0f, 1.0f, // top-right
              -1.0f,  1.0f,  1.0f,  0.0f,  0.0f,  1.0f, 0.0f, 1.0f, // top-left
              -1.0f, -1.0f,  1.0f,  0.0f,  0.0f,  1.0f, 0.0f, 0.0f, // bottom-left
              // left face
              -1.0f,  1.0f,  1.0f, -1.0f,  0.0f,  0.0f, 1.0f, 0.0f, // top-right
              -1.0f,  1.0f, -1.0f, -1.0f,  0.0f,  0.0f, 1.0f, 1.0f, // top-left
              -1.0f, -1.0f, -1.0f, -1.0f,  0.0f,  0.0f, 0.0f, 1.0f, // bottom-left
              -1.0f, -1.0f, -1.0f, -1.0f,  0.0f,  0.0f, 0.0f, 1.0f, // bottom-left
              -1.0f, -1.0f,  1.0f, -1.0f,  0.0f,  0.0f, 0.0f, 0.0f, // bottom-right
              -1.0f,  1.0f,  1.0f, -1.0f,  0.0f,  0.0f, 1.0f, 0.0f, // top-right
              // right face
              1.0f,  1.0f,  1.0f,  1.0f,  0.0f,  0.0f, 1.0f, 0.0f, // top-left
              1.0f, -1.0f, -1.0f,  1.0f,  0.0f,  0.0f, 0.0f, 1.0f, // bottom-right
              1.0f,  1.0f, -1.0f,  1.0f,  0.0f,  0.0f, 1.0f, 1.0f, // top-right
              1.0f, -1.0f, -1.0f,  1.0f,  0.0f,  0.0f, 0.0f, 1.0f, // bottom-right
              1.0f,  1.0f,  1.0f,  1.0f,  0.0f,  0.0f, 1.0f, 0.0f, // top-left
              1.0f, -1.0f,  1.0f,  1.0f,  0.0f,  0.0f, 0.0f, 0.0f, // bottom-left
              // bottom face
              -1.0f, -1.0f, -1.0f,  0.0f, -1.0f,  0.0f, 0.0f, 1.0f, // top-right
              1.0f, -1.0f, -1.0f,  0.0f, -1.0f,  0.0f, 1.0f, 1.0f, // top-left
              1.0f, -1.0f,  1.0f,  0.0f, -1.0f,  0.0f, 1.0f, 0.0f, // bottom-left
              1.0f, -1.0f,  1.0f,  0.0f, -1.0f,  0.0f, 1.0f, 0.0f, // bottom-left
              -1.0f, -1.0f,  1.0f,  0.0f, -1.0f,  0.0f, 0.0f, 0.0f, // bottom-right
              -1.0f, -1.0f, -1.0f,  0.0f, -1.0f,  0.0f, 0.0f, 1.0f, // top-right
              // top face
              -1.0f,  1.0f, -1.0f,  0.0f,  1.0f,  0.0f, 0.0f, 1.0f, // top-left
              1.0f,  1.0f , 1.0f,  0.0f,  1.0f,  0.0f, 1.0f, 0.0f, // bottom-right
              1.0f,  1.0f, -1.0f,  0.0f,  1.0f,  0.0f, 1.0f, 1.0f, // top-right
              1.0f,  1.0f,  1.0f,  0.0f,  1.0f,  0.0f, 1.0f, 0.0f, // bottom-right
              -1.0f,  1.0f, -1.0f,  0.0f,  1.0f,  0.0f, 0.0f, 1.0f, // top-left
              -1.0f,  1.0f,  1.0f,  0.0f,  1.0f,  0.0f, 0.0f, 0.0f  // bottom-left
          };
          glGenVertexArrays(1, &cubeVAO);
          glBindVertexArray(cubeVAO);
          glGenBuffers(1, &cubeVBO);
          glBindBuffer(GL_ARRAY_BUFFER, cubeVBO);
          glBufferData(GL_ARRAY_BUFFER, sizeof(cubeVertices), cubeVertices, GL_STATIC_DRAW);
          glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
          glEnableVertexAttribArray(0);
          glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
          glEnableVertexAttribArray(1);
          glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
          glEnableVertexAttribArray(2);
          glBindVertexArray(0);
      }
      glBindVertexArray(cubeVAO);
      glDrawArrays(GL_TRIANGLES, 0, 36);
      glBindVertexArray(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);
  
      // Camera move
      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, true);
  }
  
  // glfw: whenever the mouse scroll wheel scrolls, this callback is called
  // ----------------------------------------------------------------------
  void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
  {
      camera.processMouseScroll(yoffset);
  }
  
  // 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;
  }
  
  unsigned int loadCubemap(vector<string> faces){
      unsigned int textureID;
      glGenTextures(1, &textureID);
      glBindTexture(GL_TEXTURE_CUBE_MAP, textureID);
  
      int width, height, urChannel;
      for(int i = 0; i < faces.size(); i++){
          unsigned char *data = stbi_load(("skybox/" + faces[i]).c_str(), &width, &height, &urChannel, 0);
          if(data){
              GLenum format;
              if(urChannel == 1)
                  format = GL_RED;
              if(urChannel == 3)
                  format = GL_RGB;
              if(urChannel == 4)
                  format = GL_RGBA;
  
  //            glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
              glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
              stbi_image_free(data);
          }
          else{
              cout << "Read texture: " << faces[i] << " failed\n";
              stbi_image_free(data);
          }
      }
      glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
      glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
      glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
      glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
      glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
  
     return textureID;
  }
  