Spinning Triangles: Implementing Independent Rotations in OpenGL 3.3
The Problem: Imagine you're building a simple 3D scene with two triangles. You want them to rotate independently, one spinning around the X-axis and the other around the Y-axis. How do you achieve this in OpenGL 3.3?
Rephrasing: This article will guide you through implementing separate rotations for two triangles in OpenGL 3.3, providing a clear understanding of how to control individual object movements within a scene.
Scenario and Original Code:
Let's start with a basic OpenGL 3.3 setup. We'll have two triangles, defined as vertex arrays, and a shader program for rendering them.
#include <GL/glew.h>
#include <GLFW/glfw3.h>
const char* vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"void main()\n"
"{\n"
" gl_Position = vec4(aPos, 1.0);\n"
"}\0";
const char* fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"void main()\n"
"{\n"
" FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
"}\0";
int main() {
// ... (GLFW initialization) ...
// Create and compile shaders
// ...
// Create Vertex Array Objects (VAOs) and Vertex Buffer Objects (VBOs)
GLuint VAOs[2], VBOs[2];
glGenVertexArrays(2, VAOs);
glGenBuffers(2, VBOs);
// Triangle 1: Rotating around X-axis
GLfloat triangle1[] = {
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
0.0f, 0.5f, 0.0f
};
glBindVertexArray(VAOs[0]);
glBindBuffer(GL_ARRAY_BUFFER, VBOs[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(triangle1), triangle1, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (void*)0);
glEnableVertexAttribArray(0);
// Triangle 2: Rotating around Y-axis
GLfloat triangle2[] = {
-0.5f, 0.5f, 0.0f,
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f
};
glBindVertexArray(VAOs[1]);
glBindBuffer(GL_ARRAY_BUFFER, VBOs[1]);
glBufferData(GL_ARRAY_BUFFER, sizeof(triangle2), triangle2, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (void*)0);
glEnableVertexAttribArray(0);
// ... (GLFW render loop) ...
// Render triangles
glBindVertexArray(VAOs[0]);
glDrawArrays(GL_TRIANGLES, 0, 3);
glBindVertexArray(VAOs[1]);
glDrawArrays(GL_TRIANGLES, 0, 3);
// ... (GLFW swap buffers) ...
// ... (GLFW termination) ...
}
The Missing Link: Transformations
Currently, the code lacks the necessary transformations for the rotation. We need to introduce rotation matrices and apply them to each triangle independently.
Solution: Introducing Model Matrices
To achieve independent rotations, we need to introduce a separate model matrix for each triangle. These matrices will handle the transformations for each object.
- Create Model Matrices: Create two 4x4 matrices, one for each triangle. Initialize them with identity matrices.
- Update Matrices in the Render Loop: In the render loop, update the model matrices based on the desired rotation angles. For example, rotate the first matrix around the X-axis and the second around the Y-axis.
- Pass Matrices to the Shader: Pass the model matrices to the vertex shader as uniform variables.
- Transform Vertices in the Shader: In the vertex shader, multiply each vertex position by the corresponding model matrix before calculating the final gl_Position.
Modified Code:
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
// ... (Shader sources) ...
int main() {
// ... (GLFW initialization) ...
// ... (Shader creation) ...
// ... (VAO and VBO creation) ...
// ... (Triangle data) ...
// Model matrices for triangles
glm::mat4 modelMatrices[2];
modelMatrices[0] = glm::mat4(1.0f);
modelMatrices[1] = glm::mat4(1.0f);
// ... (GLFW render loop) ...
// Update model matrices (example: rotating around X and Y axes)
float angle = glfwGetTime();
modelMatrices[0] = glm::rotate(modelMatrices[0], glm::radians(angle), glm::vec3(1.0f, 0.0f, 0.0f));
modelMatrices[1] = glm::rotate(modelMatrices[1], glm::radians(angle), glm::vec3(0.0f, 1.0f, 0.0f));
// Render triangles with updated model matrices
for (int i = 0; i < 2; ++i) {
glBindVertexArray(VAOs[i]);
glUseProgram(shaderProgram);
// Pass model matrix to shader
GLint modelLoc = glGetUniformLocation(shaderProgram, "model");
glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(modelMatrices[i]));
glDrawArrays(GL_TRIANGLES, 0, 3);
}
// ... (GLFW swap buffers) ...
// ... (GLFW termination) ...
}
Modified Vertex Shader:
#version 330 core\n
layout (location = 0) in vec3 aPos;\n
uniform mat4 model; // Model matrix
void main()\n
{
gl_Position = model * vec4(aPos, 1.0); // Apply model matrix to vertex
}
Explanation:
- glm: We're using the GLM (OpenGL Mathematics) library for matrix operations. Include its header files in your code.
- Model Matrices: We've created two model matrices,
modelMatrices[0]
andmodelMatrices[1]
, to control each triangle's transformations. - Rotation: We use
glm::rotate
to rotate the model matrices around their respective axes. - Shader Uniform: The
model
uniform is passed to the shader to access the model matrix. - Vertex Transformation: In the vertex shader,
gl_Position
is calculated by multiplying the vertex position by themodel
matrix.
Key Points:
- This approach allows for independent transformations on multiple objects in the scene.
- Model matrices provide a convenient way to handle transformations at the object level.
- You can use other transformation functions from GLM (translation, scaling, etc.) to further manipulate objects.
Additional Resources:
By implementing these techniques, you can create dynamic 3D scenes where objects can move and interact independently, bringing your OpenGL creations to life.