0

I'm trying to combine texture and lighting on a pyramid in OpenGL. I basically started by merging two separate codes, and now, I'm working to make changes to smooth out the merge. However, I am having 2 issues.

  1. I need to remove the object color and replace it with texture, but I'm not sure how to approach that issue with this code since object color is deeply ingrained in the code.
  2. I'm not sure how to list the coordinates for position, normals, and texture. Their current arrangement seems to be causing a lot of issues with the output.

For issue one, I have tried replacing pyramidColor and objectColor with texture, but it seemed to create more issues.

For issue two, I have tried rearranging the list order as position, texture, and normals, which helped for a few of the triangles. However, it still isn't right.

/*Header Inclusions*/
#include <iostream>
#include <GL/glew.h>
#include <GL/freeglut.h>

//GLM Math Header Inclusions
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

//SOIL image loader Inclusion
#include "SOIL2/SOIL2.h"

using namespace std; //Standard namespace

#define WINDOW_TITLE "Pyramid" //Window title Macro

/*Shader program Macro*/
#ifndef GLSL
#define GLSL(Version, Source) "#version " #Version "\n" #Source
#endif


/*Variable declarations for shader, window size initialization, buffer and array objects */
GLint pyramidShaderProgram, lampShaderProgram, WindowWidth = 800, WindowHeight = 600;
GLuint VBO, PyramidVAO, LightVAO, texture;

//Subject position and scale
glm::vec3 pyramidPosition(0.0f, 0.0f, 0.0f);
glm::vec3 pyramidScale(2.0f);

//pyramid and light color
glm::vec3 objectColor(1.0f, 1.0f, 1.0f);
glm::vec3 lightColor(1.0f, 1.0f, 1.0f);

//Light position and scale
glm::vec3 lightPosition(0.5f, 0.5f, -3.0f);
glm::vec3 lightScale(0.3f);

//Camera position
glm::vec3 cameraPosition(0.0f, 0.0f, -6.0f);

//Camera rotation
float cameraRotation = glm::radians(-25.0f);

/*Function prototypes*/
void UResizeWindow(int, int);
void URenderGraphics(void);
void UCreateShader(void);
void UCreateBuffers(void);
void UGenerateTexture(void);


/*Pyramid Vertex Shader Source Code*/
const GLchar * pyramidVertexShaderSource = GLSL(330,
        layout (location = 0) in vec3 position; //Vertex data from Vertex Attrib Pointer 0
        layout (location = 1) in vec3 normal; //VAP position 1 for normals
        layout (location = 2) in vec2 textureCoordinate;

        out vec3 FragmentPos; //For outgoing color / pixels to fragment shader
        out vec3 Normal; //For outgoing normals to fragment shader
        out vec2 mobileTextureCoordinate;


        //Global variables for the transform matrices
        uniform mat4 model;
        uniform mat4 view;
        uniform mat4 projection;

void main(){
        gl_Position = projection * view * model * vec4(position, 1.0f); //transforms vertices to clip coordinates

        FragmentPos = vec3(model * vec4(position, 1.0f)); //Gets fragment / pixel position in world space only (exclude view and projection)

        Normal = mat3(transpose(inverse(model))) *  normal; //get normal vectors in world space only and exclude normal translation properties

        mobileTextureCoordinate = vec2(textureCoordinate.x, 1 - textureCoordinate.y); //flips the texture horizontal
    }
);


/*Pyramid Fragment Shader Source Code*/
const GLchar * pyramidFragmentShaderSource = GLSL(330,

        in vec3 FragmentPos; //For incoming fragment position
        in vec3 Normal; //For incoming normals
        in vec2 mobileTextureCoordinate;

        out vec4 pyramidColor; //For outgoing pyramid color to the GPU
        out vec4 gpuTexture; //Variable to pass color data to the GPU

        //Uniform / Global variables for object color, light color, light position, and camera/view position
        uniform vec3 objectColor;
        uniform vec3 lightColor;
        uniform vec3 lightPos;
        uniform vec3 viewPosition;

        uniform sampler2D uTexture; //Useful when working with multiple textures

        void main(){

            /*Phong lighting model calculations to generate ambient, diffuse, and specular components*/

            //Calculate Ambient Lighting
            float ambientStrength = 0.1f; //Set ambient or global lighting strength
            vec3 ambient = ambientStrength * lightColor; //Generate ambient light color


            //Calculate Diffuse Lighting
            vec3 norm = normalize(Normal); //Normalize vectors to 1 unit
            vec3 lightDirection = normalize(lightPos - FragmentPos); //Calculate distance (light direction) between light source and fragments/pixels on
            float impact = max(dot(norm, lightDirection), 0.0); //Calculate diffuse impact by generating dot product of normal and light
            vec3 diffuse = impact * lightColor; //Generate diffuse light color


            //Calculate Specular lighting
            float specularIntensity = 0.8f; //Set specular light strength
            float highlightSize = 128.0f; //Set specular highlight size
            vec3 viewDir = normalize(viewPosition - FragmentPos); //Calculate view direction
            vec3 reflectDir = reflect(-lightDirection, norm); //Calculate reflection vector
            //Calculate specular component
            float specularComponent = pow(max(dot(viewDir, reflectDir), 0.0), highlightSize);
            vec3 specular = specularIntensity * specularComponent * lightColor;

            //Calculate phong result
            vec3 phong = (ambient + diffuse + specular) * objectColor;

            pyramidColor = vec4(phong, 1.0f); //Send lighting results to GPU

            gpuTexture = texture(uTexture, mobileTextureCoordinate);

        }
);


/*Lamp Shader Source Code*/
const GLchar * lampVertexShaderSource = GLSL(330,

        layout (location = 0) in vec3 position; //VAP position 0 for vertex position data

        //Uniform / Global variables for the transform matrices
        uniform mat4 model;
        uniform mat4 view;
        uniform mat4 projection;

        void main()
        {
            gl_Position = projection * view *model * vec4(position, 1.0f); //Transforms vertices into clip coordinates
        }
);


/*Fragment Shader Source Code*/
const GLchar * lampFragmentShaderSource = GLSL(330,

        out vec4 color; //For outgoing lamp color (smaller pyramid) to the GPU

        void main()
        {
            color = vec4(1.0f); //Set color to white (1.0f, 1.0f, 1.0f) with alpha 1.0

        }
);


/*Main Program*/
int main(int argc, char* argv[])
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
    glutInitWindowSize(WindowWidth, WindowHeight);
    glutCreateWindow(WINDOW_TITLE);

    glutReshapeFunc(UResizeWindow);


    glewExperimental = GL_TRUE;
            if (glewInit() != GLEW_OK)
            {
                std::cout<< "Failed to initialize GLEW" << std::endl;
                return -1;
            }

    UCreateShader();

    UCreateBuffers();

    UGenerateTexture();

    glClearColor(0.0f, 0.0f, 0.0f, 1.0f); //Set background color

    glutDisplayFunc(URenderGraphics);

    glutMainLoop();

    //Destroys Buffer objects once used
    glDeleteVertexArrays(1, &PyramidVAO);
    glDeleteVertexArrays(1, &LightVAO);
    glDeleteBuffers(1, &VBO);

    return 0;
}

/*Resizes the window*/
void UResizeWindow(int w, int h)
{
    WindowWidth = w;
    WindowHeight = h;
    glViewport(0, 0, WindowWidth, WindowHeight);
}


/*Renders graphics*/
void URenderGraphics(void)
{

    glEnable(GL_DEPTH_TEST); //Enable z-depth

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //Clears the screen

    GLint modelLoc, viewLoc, projLoc, objectColorLoc, lightColorLoc, lightPositionLoc, viewPositionLoc;

    glm::mat4 model;
    glm::mat4 view;
    glm::mat4 projection;

    /*********Use the pyramid Shader to activate the pyramid Vertex Array Object for rendering and transforming*********/
    glUseProgram(pyramidShaderProgram);
    glBindVertexArray(PyramidVAO);

    //Transform the pyramid
    model = glm::translate(model, pyramidPosition);
    model = glm::scale(model, pyramidScale);

    //Transform the camera
    view = glm::translate(view, cameraPosition);
    view = glm::rotate(view, cameraRotation, glm::vec3(0.0f, 1.0f, 0.0f));

    //Set the camera projection to perspective
    projection = glm::perspective(45.0f,(GLfloat)WindowWidth / (GLfloat)WindowHeight, 0.1f, 100.0f);

    //Reference matrix uniforms from the pyramid Shader program
    modelLoc = glGetUniformLocation(pyramidShaderProgram, "model");
    viewLoc = glGetUniformLocation(pyramidShaderProgram, "view");
    projLoc = glGetUniformLocation(pyramidShaderProgram, "projection");

    //Pass matrix data to the pyramid Shader program's matrix uniforms
    glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
    glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
    glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));

    //Reference matrix uniforms from the pyramid Shader program for the pyramid color, light color, light position, and camera position
    objectColorLoc = glGetUniformLocation(pyramidShaderProgram, "objectColor");
    lightColorLoc = glGetUniformLocation(pyramidShaderProgram, "lightColor");
    lightPositionLoc = glGetUniformLocation(pyramidShaderProgram, "lightPos");
    viewPositionLoc = glGetUniformLocation(pyramidShaderProgram, "viewPosition");

    //Pass color, light, and camera data to the pyramid Shader programs corresponding uniforms
    glUniform3f(objectColorLoc, objectColor.r, objectColor.g, objectColor.b);
    glUniform3f(lightColorLoc, lightColor.r, lightColor.g, lightColor.b);
    glUniform3f(lightPositionLoc, lightPosition.x, lightPosition.y, lightPosition.z);
    glUniform3f(viewPositionLoc, cameraPosition.x, cameraPosition.y, cameraPosition.z);

    glDrawArrays(GL_TRIANGLES, 0, 18); //Draw the primitives / pyramid

    glBindVertexArray(0); //Deactivate the Pyramid Vertex Array Object

    /***************Use the Lamp Shader and activate the Lamp Vertex Array Object for rendering and transforming ************/
    glUseProgram(lampShaderProgram);
    glBindVertexArray(LightVAO);

    //Transform the smaller pyramid used as a visual cue for the light source
    model = glm::translate(model, lightPosition);
    model = glm::scale(model, lightScale);

    //Reference matrix uniforms from the Lamp Shader program
    modelLoc = glGetUniformLocation(lampShaderProgram, "model");
    viewLoc = glGetUniformLocation(lampShaderProgram, "view");
    projLoc = glGetUniformLocation(lampShaderProgram, "projection");

    //Pass matrix uniforms from the Lamp Shader Program
    glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
    glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
    glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));

    glBindTexture(GL_TEXTURE_2D, texture);

    //Draws the triangles
    glDrawArrays(GL_TRIANGLES, 0, 18);

    glBindVertexArray(0); //Deactivate the Lamp Vertex Array Object

    glutPostRedisplay();
    glutSwapBuffers(); //Flips the back buffer with the front buffer every frame. Similar to GL Flush

}

/*Create the Shader program*/
void UCreateShader()
{

    //Pyramid Vertex shader
    GLint pyramidVertexShader = glCreateShader(GL_VERTEX_SHADER); //Creates the Vertex shader
    glShaderSource(pyramidVertexShader, 1, &pyramidVertexShaderSource, NULL); //Attaches the Vertex shader to the source code
    glCompileShader(pyramidVertexShader); //Compiles the Vertex shader

    //Pyramid Fragment Shader
    GLint pyramidFragmentShader = glCreateShader(GL_FRAGMENT_SHADER); //Creates the Fragment Shader
    glShaderSource(pyramidFragmentShader, 1, &pyramidFragmentShaderSource, NULL); //Attaches the Fragment shader to the source code
    glCompileShader(pyramidFragmentShader); //Compiles the Fragment Shader

    //Pyramid Shader program
    pyramidShaderProgram = glCreateProgram(); //Creates the Shader program and returns an id
    glAttachShader(pyramidShaderProgram, pyramidVertexShader); //Attaches Vertex shader to the Shader program
    glAttachShader(pyramidShaderProgram, pyramidFragmentShader); //Attaches Fragment shader to the Shader program
    glLinkProgram(pyramidShaderProgram); //Link Vertex and Fragment shaders to the Shader program

    //Delete the Vertex and Fragment shaders once linked
    glDeleteShader(pyramidVertexShader);
    glDeleteShader(pyramidFragmentShader);

    //Lamp Vertex shader
    GLint lampVertexShader = glCreateShader(GL_VERTEX_SHADER); //Creates the Vertex shader
    glShaderSource(lampVertexShader, 1, &lampVertexShaderSource, NULL); //Attaches the Vertex shader to the source code
    glCompileShader(lampVertexShader); //Compiles the Vertex shader

    //Lamp Fragment shader
    GLint lampFragmentShader = glCreateShader(GL_FRAGMENT_SHADER); //Creates the Fragment shader
    glShaderSource(lampFragmentShader, 1, &lampFragmentShaderSource, NULL); //Attaches the Fragment shader to the source code
    glCompileShader(lampFragmentShader); //Compiles the Fragment shader

    //Lamp Shader Program
    lampShaderProgram = glCreateProgram(); //Creates the Shader program and returns an id
    glAttachShader(lampShaderProgram, lampVertexShader); //Attach Vertex shader to the Shader program
    glAttachShader(lampShaderProgram, lampFragmentShader); //Attach Fragment shader to the Shader program
    glLinkProgram(lampShaderProgram); //Link Vertex and Fragment shaders to the Shader program

    //Delete the lamp shaders once linked
    glDeleteShader(lampVertexShader);
    glDeleteShader(lampFragmentShader);

}


/*Creates the Buffer and Array Objects*/
void UCreateBuffers()
{
    //Position and Texture coordinate data for 18 triangles
    GLfloat vertices[] = {

                        //Positions             //Normals               //Texture Coordinates

                        //Back Face             //Negative Z Normals
                         0.0f,  0.5f,  0.0f,     0.0f,  0.0f, -1.0f,    0.5f, 1.0f,
                         0.5f, -0.5f, -0.5f,     0.0f,  0.0f, -1.0f,    0.0f, 0.0f,
                        -0.5f, -0.5f, -0.5f,     0.0f,  0.0f, -1.0f,    1.0f, 0.0f,

                        //Front Face            //Positive Z Normals
                         0.0f,  0.5f,  0.0f,     0.0f,  0.0f,  1.0f,    0.5f, 1.0f,
                        -0.5f, -0.5f,  0.5f,     0.0f,  0.0f,  1.0f,    0.0f, 0.0f,
                         0.5f, -0.5f,  0.5f,     0.0f,  0.0f,  1.0f,    1.0f, 0.0f,

                         //Left Face            //Negative X Normals
                         0.0f,  0.5f,  0.0f,    -1.0f,  0.0f,  0.0f,    0.5f, 1.0f,
                        -0.5f, -0.5f, -0.5f,    -1.0f,  0.0f,  0.0f,    0.0f, 0.0f,
                        -0.5f, -0.5f,  0.5f,    -1.0f,  0.0f,  0.0f,    1.0f, 0.0f,

                         //Right Face           //Positive X Normals
                         0.0f,  0.5f,  0.0f,     1.0f,  0.0f,  0.0f,    0.5f, 1.0f,
                         0.5f, -0.5f,  0.5f,     1.0f,  0.0f,  0.0f,    0.0f, 0.0f,
                         0.5f, -0.5f, -0.5f,     1.0f,  0.0f,  0.0f,    1.0f, 0.0f,

                         //Bottom Face          //Negative Y Normals
                        -0.5f, -0.5f, -0.5f,     0.0f, -1.0f,  0.0f,    0.0f, 1.0f,
                         0.5f, -0.5f, -0.5f,     0.0f, -1.0f,  0.0f,    0.0f, 0.0f,
                        -0.5f, -0.5f,  0.5f,     0.0f, -1.0f,  0.0f,    1.0f, 1.0f,
                        -0.5f, -0.5f,  0.5f,     0.0f, -1.0f,  0.0f,    1.0f, 1.0f,
                         0.5f, -0.5f, -0.5f,     0.0f, -1.0f,  0.0f,    0.0f, 0.0f,
                         0.5f, -0.5f,  0.5f,     0.0f, -1.0f,  0.0f,    1.0f, 0.0f,

    };


    //Generate buffer ids
    glGenVertexArrays(1, &PyramidVAO);
    glGenBuffers(1, &VBO);

    //Activate the PyramidVAO before binding and setting VBOs and VAPs
    glBindVertexArray(PyramidVAO);

    //Activate the VBO
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); //Copy vertices to VBO

    //Set attribute pointer 0 to hold position data
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0); //Enables vertex attribute

    //Set attribute pointer 1 to hold Normal data
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
    glEnableVertexAttribArray(1);

    //Set attribute pointer 2 to hold Texture coordinate data
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)(6 * sizeof(GLfloat)));
    glEnableVertexAttribArray(2);

    glBindVertexArray(0); //Unbind the pyramid VAO

    //Generate buffer ids for lamp (smaller pyramid)
    glGenVertexArrays(1, &LightVAO); //Vertex Array for pyramid vertex copies to serve as light source

    //Activate the Vertex Array Object before binding and setting any VBOs and Vertex Attribute Pointers
    glBindVertexArray(LightVAO);

    //Referencing the same VBO for its vertices
    glBindBuffer(GL_ARRAY_BUFFER, VBO);

    //Set attribute pointer to 0 to hold Position data (used for the lamp)
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);
    glBindVertexArray(0);

}

/*Generate and load the texture*/
void UGenerateTexture(){

    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);

    int width, height;

    unsigned char* image = SOIL_load_image("brick.jpg", &width, &height, 0, SOIL_LOAD_RGB); //Loads texture file

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
    glGenerateMipmap(GL_TEXTURE_2D);
    SOIL_free_image_data(image);
    glBindTexture(GL_TEXTURE_2D, 0); //Unbind the texture
}

Expected results: A brick textured pyramid with lighting. Actual results: A bunch of assorted triangles.

Yakov Galka
  • 70,775
  • 16
  • 139
  • 220
Tracy Harrison
  • 91
  • 2
  • 11
  • Why does your `pyramidFragmentShader` output two different colors, `pyramidColor` and `gpuTexture`, but you render to the default framebuffer that has only one color attachment? – Yakov Galka Apr 21 '19 at 01:33
  • I'm not sure how to combine the two and output a texture with lighting. Right now, I have literally combined the two files. Confusing. I know. – Tracy Harrison Apr 21 '19 at 01:50
  • See [complete GL+GLSL+VAO/VBO C++ example](https://stackoverflow.com/a/31913542/2521214) – Spektre Apr 23 '19 at 08:21

1 Answers1

3

I see the following issues with your code:

  1. In the fragment shader:

    • Remove the objectColor uniform and the gpuTexture output.

    • Replace the last three lines of main() with:

      //Calculate phong result
      vec3 objectColor = texture(uTexture, mobileTextureCoordinate).xyz;
      vec3 phong = (ambient + diffuse) * objectColor + specular;
      pyramidColor = vec4(phong, 1.0f); //Send lighting results to GPU
      

    In your rendering code:

    • Replace all mentions of objectColor with texture setup:

      uTextureLoc = glGetUniformLocation(pyramidShaderProgram, "uTexture");
      glUniform1i(uTextureLoc, 0); // texture unit 0
      
    • Bind the texture before you call glDrawArrays of the textured pyramid:

      glActiveTexture(GL_TEXTURE0);
      glBindTexture(GL_TEXTURE_2D, texture);
      glDrawArrays(GL_TRIANGLES, 0, 18);
      

      (Right now you bind it before drawing the LightVAO, which doesn't use the texture.)

  2. All your glVertexAttribPointer calls have an incorrect stride of 6 * sizeof(GLfloat), but the buffer you provide has eight (8) floats per vertex, so it shall be 8 * sizeof(GLfloat). Remember that this parameter is the number of bytes that the GL has to advance to fetch the next vertex. Other than that your VAO setup is alright.

Yakov Galka
  • 70,775
  • 16
  • 139
  • 220
  • Thank you! This has been unbelievably helpful! Your explanations helped a lot too. – Tracy Harrison Apr 21 '19 at 02:46
  • When you do `(ambient + diffuse) * objectColor`, doesn't that mean that a black object/texture will never receive any diffuse lighting? – Raildex Jun 30 '22 at 12:44
  • @Raildex yes -- that's what a zero albedo means. IRL there aren't any perfectly black objects, which is why in a PBR pipeline you'd use dark but not black colors. – Yakov Galka Jun 30 '22 at 13:24