简体   繁体   中英

OpenGL cube and pyramid

I have this OpenGL code the draws a cube and pyramid. However, this program rotates the pyramid and cube together. I am tasked with only making the cube itself move not both objects at the same time. I know for this to happen I have to implement shaders for both. I'm not sure how to go about implementing both of the shaders at once. Any tips?

/*
This program demonstrates simple lighting.
A pyramid is lighted by a point light and can be rotated by mouse.

Ying Zhu
Georgia State University

October 2016
*/

// GLEW header
#include <GL/glew.h> // This must appear before freeglut.h

// Freeglut header
#include <GL/freeglut.h>

// GLM header files
#include <glm/glm.hpp> 

#include <glm/gtc/matrix_transform.hpp> 
// #include <glm/gtx/transform2.hpp>
#include <glm/gtc/matrix_access.hpp>
// #include <glm/gtx/projection.hpp>
#include <glm/gtc/matrix_inverse.hpp>
#include <glm/gtc/type_ptr.hpp> 

// C++ header files
#include <iostream>

using namespace std;
using namespace glm;

#define BUFFER_OFFSET(offset) ((GLvoid *) offset)

// VBO buffer IDs
GLuint vertexArrayBufferID = 0;
GLuint normalArrayBufferID = 0;
GLuint cubePosition = 0;
GLuint cubeElements = 0;

GLuint program; // shader program ID

// Shader variable IDs
GLint vPos; // vertex attribute: position
GLint normalID; // vertex attribute: normal

GLint mvpMatrixID; // uniform variable: model, view, projection matrix
GLint modelMatrixID; // uniform variable: model, view matrix
GLint normalMatrixID; // uniform variable: normal matrix for transforming normals
GLint lightSourcePositionID; // uniform variable: for lighting calculation
GLint diffuseLightProductID; // uniform variable: for lighting calculation
GLint ambientID;
GLint attenuationAID;
GLint attenuationBID;
GLint attenuationCID;

// Transformation matrices
mat4 projMatrix;
mat4 mvpMatrix;
mat4 modelMatrix;
mat4 viewMatrix;
mat3 normalMatrix;  // Normal matrix for transforming normals

// Light parameters
vec4 lightSourcePosition = vec4(0.0f, 4.0f, 0.0f, 1.0f);
vec4 diffuseMaterial = vec4(0.5f, 0.5f, 0.0f, 1.0f);
vec4 diffuseLightIntensity = vec4(1.0f, 1.0f, 1.0f, 1.0f);
vec4 ambient = vec4(0.2f, 0.2f, 0.2f, 1.0f);
float attenuationA = 1.0f;
float attenuationB = 0.2f;
float attenuationC = 0.0f;

vec4 diffuseLightProduct;

// Camera parameters
vec3 eyePosition = vec3(0.0f, 0.0f, 4.0f);
vec3 lookAtCenter = vec3(0.0f, 0.0f, 0.0f);
vec3 upVector = vec3(0.0f, 1.0f, 0.0f);
float fieldOfView = 30.0f;
float nearPlane = 0.1f;
float farPlane = 1000.0f;

// Mouse controlled rotation angles
float rotateX = 0;
float rotateY = 0;

struct VertexData {
    GLfloat vertex[3];

    VertexData(GLfloat x, GLfloat y, GLfloat z) {
        vertex[0] = x; vertex[1] = y; vertex[2] = z;
    }
};

//---------------------------------------------------------------
// Initialize vertex arrays and VBOs
void prepareVBOs() {
    // Define a 3D pyramid. 
    GLfloat vertices[][4] = {
        {1.0f, -1.0f, 1.0f, 1.0f}, // face 1
        {-1.0f, -1.0f, -1.0f, 1.0f},
        {1.0f, -1.0f, -1.0f, 1.0f},
        { 1.0f, -1.0f, -1.0f, 1.0f }, // face 2
        {0.0f, 1.0f, 0.0f, 1.0f},
        { 1.0f, -1.0f, 1.0f, 1.0f },
        { 1.0f, -1.0f, 1.0f, 1.0f }, // face 3
        { 0.0f, 1.0f, 0.0f, 1.0f },
        {-1.0f, -1.0f, 1.0f, 1.0f},
        { -1.0f, -1.0f, 1.0f, 1.0f }, // face 4
        { 0.0f, 1.0f, 0.0f, 1.0f },
        { -1.0f, -1.0f, -1.0f, 1.0f },
        { 0.0f, 1.0f, 0.0f, 1.0f }, // face 5
        { 1.0f, -1.0f, -1.0f, 1.0f },
        { -1.0f, -1.0f, -1.0f, 1.0f },
        { 1.0f, -1.0f, 1.0f, 1.0f }, // face 6
        { -1.0f, -1.0f, 1.0f, 1.0f },
        { -1.0f, -1.0f, -1.0f, 1.0f }
    };

    GLfloat normals[][4] = {
        {0.0f, -1.0f, 0.0f, 1.0f}, // normal 1
        {0.0f, -1.0f, 0.0f, 1.0f },
        {0.0f, -1.0f, 0.0f, 1.0f },
        {0.8944f, 0.4472f, 0.0f, 1.0f}, // normal 2
        { 0.8944f, 0.4472f, 0.0f, 1.0f },
        { 0.8944f, 0.4472f, 0.0f, 1.0f },
        {-0.0f, 0.4472f, 0.8944f, 1.0f}, // normal 3
        { -0.0f, 0.4472f, 0.8944f, 1.0f },
        { -0.0f, 0.4472f, 0.8944f, 1.0f },
        {-0.8944f, 0.4472f, 0.0f, 1.0f}, // normal 4
        { -0.8944f, 0.4472f, 0.0f, 1.0f },
        { -0.8944f, 0.4472f, 0.0f, 1.0f },
        {0.0f, 0.4472f, -0.8944f, 1.0f}, // normal 5
        { 0.0f, 0.4472f, -0.8944f, 1.0f },
        { 0.0f, 0.4472f, -0.8944f, 1.0f },
        { 0.0f, -1.0f, 0.0f, 1.0f }, // normal 6
        { 0.0f, -1.0f, 0.0f, 1.0f },
        { 0.0f, -1.0f, 0.0f, 1.0f }
    };

    // Cube positioins 
    VertexData vertexData[] = {
        VertexData(0.0, 0.0, 0.0), /* Index 0 */
        VertexData(0.0, 0.0, 1.0), /* Index 1 */
        VertexData(0.0, 1.0, 0.0), /* Index 2 */
        VertexData(0.0, 1.0, 1.0), /* Index 3 */
        VertexData(1.0, 0.0, 0.0), /* Index 4 */
        VertexData(1.0, 0.0, 1.0), /* Index 5 */
        VertexData(1.0, 1.0, 0.0), /* Index 6 */
        VertexData(1.0, 1.0, 1.0), /* Index 7 */
    };

    // Cube elements
    GLubyte indices[] = {
        4, 5, 7, // +X face
        4, 7, 6,
        0, 2, 3, // ‐X face
        0, 3, 1,
        2, 6, 7, // +Y face
        2, 7, 3,
        0, 1, 5, // ‐Y face
        0, 5, 4,
        0, 4, 6, // +Z face
        0, 6, 2,
        1, 3, 7, // ‐Z face
        1, 7, 5
    };
    // Get an unused buffer object name. Required after OpenGL 3.1. 
    glGenBuffers(1, &vertexArrayBufferID);

    // If it's the first time the buffer object name is used, create that buffer. 
    glBindBuffer(GL_ARRAY_BUFFER, vertexArrayBufferID);

    // Allocate memory for the active buffer object. 
    // 1. Allocate memory on the graphics card for the amount specified by the 2nd parameter.
    // 2. Copy the data referenced by the third parameter (a pointer) from the main memory to the 
    //    memory on the graphics card. 
    // 3. If you want to dynamically load the data, then set the third parameter to be NULL. 
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    glGenBuffers(1, &normalArrayBufferID);
    glBindBuffer(GL_ARRAY_BUFFER, normalArrayBufferID);
    glBufferData(GL_ARRAY_BUFFER, sizeof(normals), normals, GL_STATIC_DRAW);

    glGenBuffers(1, &cubePosition);
    glBindBuffer(GL_ARRAY_BUFFER, cubePosition);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertexData),
        vertexData, GL_STATIC_DRAW);

    glGenBuffers(1, &cubeElements);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cubeElements);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices),
        indices, GL_STATIC_DRAW);
}

//---------------------------------------------------------------
// Print out the output of the shader compiler
void printLog(GLuint obj)
{
    int infologLength = 0;
    char infoLog[1024];

    if (glIsShader(obj)) {
        glGetShaderInfoLog(obj, 1024, &infologLength, infoLog);
    }
    else {
        glGetProgramInfoLog(obj, 1024, &infologLength, infoLog);
    }

    if (infologLength > 0) {
        cout << infoLog;
    }
}

//-------------------------------------------------------------------
void prepareShaders() {
    // Vertex shader source code
    // A point light source is implemented. 
    // For simplicity, only the ambient and diffuse components are implemented. 
    // The lighting is calculated in world space, not in camera space. 
    const char* vSource = {
        "#version 330\n"
        "in vec4 vPos;"
        "in vec4 normal;"

        "uniform mat4x4 mvpMatrix;"
        "uniform mat4x4 modelMatrix;"
        "uniform mat3x3 normalMatrix;"
        "uniform vec4 lightSourcePosition;"
        "uniform vec4 diffuseLightProduct;"
        "uniform vec4 ambient;"
        "uniform float attenuationA;"
        "uniform float attenuationB;"
        "uniform float attenuationC;"
        "out vec4 color;"

        "void main() {"
        "   gl_Position = mvpMatrix * vPos;"
        // Transform the vertex position to the world space. 
        "   vec4 transformedVertex = modelMatrix * vPos;"
        // Transform the normal vector to the world space. 
        "   vec3 transformedNormal = normalize(normalMatrix * normal.xyz);"
        // Light direction
        "   vec3 lightVector = normalize(transformedVertex.xyz - lightSourcePosition.xyz);"
        // Distance between the light source and vertex
        "   float dist = distance(lightSourcePosition.xyz, transformedVertex.xyz);"
        // Attenuation factor
        "   float attenuation = 1.0f / (attenuationA + (attenuationB * dist) + (attenuationC * dist * dist));"
        // Calculate the diffuse component of the lighting equation.
        "   vec4 diffuse = attenuation * (max(dot(transformedNormal, lightVector), 0.0) * diffuseLightProduct);"
        // Combine the ambient component and diffuse component. 
        "   color = ambient + diffuse;"
        "}"
    };

    // Fragment shader source code
    const char* fSource = {
        "#version 330\n"
        "in vec4 color;"
        "out vec4 fragColor;"
        "void main() {"
        "   fragColor = color;"
        "}"
    };

    // Declare shader IDs
    GLuint vShader, fShader;

    // Create empty shader objects
    vShader = glCreateShader(GL_VERTEX_SHADER);
    fShader = glCreateShader(GL_FRAGMENT_SHADER);

    // Attach shader source code the shader objects
    glShaderSource(vShader, 1, &vSource, NULL);
    glShaderSource(fShader, 1, &fSource, NULL);

    // Compile shader objects
    glCompileShader(vShader);
    printLog(vShader);

    glCompileShader(fShader);
    printLog(fShader);

    // Create an empty shader program object
    program = glCreateProgram();

    // Attach vertex and fragment shaders to the shader program
    glAttachShader(program, vShader);
    glAttachShader(program, fShader);

    // Link the shader program
    glLinkProgram(program);
    printLog(program);
}

//---------------------------------------------------------------
// Retrieve the IDs of the shader variables. Later we will
// use these IDs to pass data to the shaders. 
void getShaderVariableLocations(GLuint shaderProgram) {

    // Retrieve the ID of a vertex attribute, i.e. position
    vPos = glGetAttribLocation(shaderProgram, "vPos");
    normalID = glGetAttribLocation(shaderProgram, "normal");

    mvpMatrixID = glGetUniformLocation(shaderProgram, "mvpMatrix");

    modelMatrixID = glGetUniformLocation(shaderProgram, "modelMatrix");
    normalMatrixID = glGetUniformLocation(shaderProgram, "normalMatrix");

    lightSourcePositionID = glGetUniformLocation(shaderProgram, "lightSourcePosition");
    diffuseLightProductID = glGetUniformLocation(shaderProgram, "diffuseLightProduct");
    ambientID = glGetUniformLocation(shaderProgram, "ambient");

    attenuationAID = glGetUniformLocation(shaderProgram, "attenuationA");
    attenuationBID = glGetUniformLocation(shaderProgram, "attenuationB");
    attenuationCID = glGetUniformLocation(shaderProgram, "attenuationC");
}

//---------------------------------------------------------------
void setShaderVariables() {
    // value_ptr is a glm function
    glUniformMatrix4fv(mvpMatrixID, 1, GL_FALSE, value_ptr(mvpMatrix));
    glUniformMatrix4fv(modelMatrixID, 1, GL_FALSE, value_ptr(modelMatrix));
    glUniformMatrix3fv(normalMatrixID, 1, GL_FALSE, value_ptr(normalMatrix));

    glUniform4fv(lightSourcePositionID, 1, value_ptr(lightSourcePosition));
    glUniform4fv(diffuseLightProductID, 1, value_ptr(diffuseLightProduct));
    glUniform4fv(ambientID, 1, value_ptr(ambient));
    glUniform1f(attenuationAID, attenuationA);
    glUniform1f(attenuationBID, attenuationB);
    glUniform1f(attenuationCID, attenuationC);
}

//---------------------------------------------------------------
// Set lighting related parameters
void setLightingParam() {
    diffuseLightProduct = diffuseMaterial * diffuseLightIntensity;
}

//---------------------------------------------------------------
// Build the model matrix. This matrix will transform the 3D object to the proper place. 
mat4 buildModelMatrix() {

    mat4 rotationXMatrix = rotate(mat4(1.0f), radians(rotateX), vec3(1.0f, 0.0f, 0.0f));
    mat4 rotationYMatrix = rotate(mat4(1.0f), radians(rotateY), vec3(0.0f, 1.0f, 0.0f));

    mat4 matrix = rotationYMatrix * rotationXMatrix;

    return matrix;
}

//---------------------------------------------------------------
void buildMatrices() {
    modelMatrix = buildModelMatrix();

    mvpMatrix = projMatrix * viewMatrix * modelMatrix;

    normalMatrix = column(normalMatrix, 0, vec3(modelMatrix[0][0], modelMatrix[0][1], modelMatrix[0][2]));
    normalMatrix = column(normalMatrix, 1, vec3(modelMatrix[1][0], modelMatrix[1][1], modelMatrix[1][2]));
    normalMatrix = column(normalMatrix, 2, vec3(modelMatrix[2][0], modelMatrix[2][1], modelMatrix[2][2]));

    // Use glm::inverseTranspose() to create a normal matrix, which is used to transform normal vectors. 
    normalMatrix = inverseTranspose(normalMatrix);
}

//---------------------------------------------------------------
// Handles the display event
void display()
{
    // Clear the window with the background color
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    buildMatrices();

    setShaderVariables();

    // Activate the shader program
    glUseProgram(program);

    // If the buffer object already exists, make that buffer the current active one. 
    // If the buffer object name is 0, disable buffer objects. 
    glBindBuffer(GL_ARRAY_BUFFER, vertexArrayBufferID);

    // Associate the vertex array in the buffer object with the vertex attribute: "position"
    glVertexAttribPointer(vPos, 4, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));

    // Enable the vertex attribute: "position"
    glEnableVertexAttribArray(vPos);

    glBindBuffer(GL_ARRAY_BUFFER, normalArrayBufferID);
    glVertexAttribPointer(normalID, 4, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
    glEnableVertexAttribArray(normalID);

    // Start the shader program. Draw the object. The third parameter is the number of triangles. 
    glDrawArrays(GL_TRIANGLES, 0, 18);

    glBindBuffer(GL_ARRAY_BUFFER, cubePosition);
    glVertexAttribPointer(vPos, 3, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));

    glEnableVertexAttribArray(vPos);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cubeElements);
    glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_BYTE, BUFFER_OFFSET(0));

    // Refresh the window
    glutSwapBuffers();
}

//---------------------------------------------------------------
// Handles the reshape event
void reshape(int width, int height)
{
    // Specify the width and height of the picture within the window
    glViewport(0, 0, width, height);

    projMatrix = perspective(fieldOfView, (float)width / (float)height, nearPlane, farPlane);

    viewMatrix = lookAt(eyePosition, lookAtCenter, upVector);
}

//---------------------------------------------------------------
// Read mouse motion data and convert them to rotation angles. 
void passiveMotion(int x, int y) {

    rotateY = (float)x * -0.8f;
    rotateX = (float)y * -0.8f;

    // Generate a dislay event to force refreshing the window. 
    glutPostRedisplay();
}

//-----------------------------------------------------------------
void init() {
    prepareVBOs();

    prepareShaders();

    getShaderVariableLocations(program);

    setLightingParam();

    // Specify the background color
    glClearColor(1, 1, 1, 1);

    glEnable(GL_DEPTH_TEST);

    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
}

//---------------------------------------------------------------
void main(int argc, char *argv[])
{
    glutInit(&argc, argv);

    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);

    glutCreateWindow("Lighting Demo");

    glutReshapeWindow(800, 800);

    glewInit();

    init();

    // Register the display callback function
    glutDisplayFunc(display);

    // Register the reshape callback function
    glutReshapeFunc(reshape);

    // Register the passive mouse motion call back function
    // This function is called when the mouse moves within the window
    // while no mouse buttons are pressed. 
    glutPassiveMotionFunc(passiveMotion);

    // Start the event loop
    glutMainLoop();
}

Well, the most obvious culprit here would be setting a single ModelMatrix for both - I can't see any logic in your code to set them independently for each object you're rendering.

Since each object has a different rotation (and presumably, unless you're planning to draw one on top of the other, a different translation), you would want to be generating / loading a different model matrix for each draw call.

You dont need to use different shaders, you just need to use different model matricies. Say you have two objects in you scene something like this:

while (!myWindow(shouldClose))
{
    myShader.use();

    glBindVertexArray(myVao1);
    glDrawArrays(GL_TRIANGLES, 0, x); // Draw pyramid

    glBindVertaxArray(myVao2);
    glDrawArrays(GL_TRIANGLES, 0, x); // Draw cube
}

Say you want only the second model to rotate on the y axis, you could do something like this:

float rotationDegree = 0;
while (!myWindow(shouldClose))
{
    myShader.use();
    myShader.setMat4(glm::mat4(1.0f)) // Make sure to set it to normal matrix for the pyrmamid
    glBindVertexArray(myVao1);
    glDrawArrays(GL_TRIANGLES, 0, x); // Draw pyramid

    glBindVertaxArray(myVao2);
    glm::mat4 model = glm::mat4(1.0f);
    glm::rotate(model, glm::radians(rotationDegree), glm::vec3(0.0f, 1.0f, 0.0f));
    rotateionDegree += 0.01;
    myShader.setMat4("model", model); // Set you model matrix in your shader.
    glDrawArrays(GL_TRIANGLES, 0, x); // Draw cube
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM