This is similar to this question, but I have a 2D triangle I'm trying to rotate solely about the z-axis, so I don't think I have to do any negative rotations. I have intentionally picked a triangle that is centered around the center of the screen (I calculated the centroid with this webpage). Essentially, I want it to look like the triangle is rotating clockwise or counter-clockwise about the center of the window, but by changing around the order I define my vertices, I've discovered that it seems to be rotating around the last vertex I define.
Here's my C++ code; I've removed comments for brevity. I don't have any input handling at the moment - I'm running in vscode so I've been terminating it through the IDE.
// Third-party library
#include <SDL2/SDL.h>
// Include GLAD
#include <glad/glad.h>
#include <glm/vec3.hpp>
#include <glm/mat3x3.hpp>
#include <glm/mat4x4.hpp>
#include <glm/ext/matrix_transform.hpp>
#include <glm/gtx/string_cast.hpp>
// C++ Standard Libraries
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
// Screen dimensions
int gScreenHeight = 480;
int gScreenWidth = 640;
SDL_Window* gGraphicsApplicationWindow = nullptr;
SDL_GLContext gOpenGLContext = nullptr;
// Main loop flag
bool gQuit = false; // if true, we quit
GLuint gGraphicsPipelineShaderProgram = 0;
GLuint gVertexArrayObject = 0;
GLuint gVertexBufferObject = 0;
GLuint gVertexBufferObject2 = 0;
glm::mat4 gVertexData(
-0.4f, -0.4f, 0.0f, 0.0f, // point 1 - bottom left
0.4f, -0.4f, 0.0f, 0.0f, // point 2 - bottom right
0.0f, 0.8f, 0.0f, 0.0f, // point 3 - top
0.0f, 0.0f, 0.0f, 1.0f // 4th dimension
);
const auto gVertexBytes = gVertexData.length() * gVertexData[0].length()
* sizeof(gVertexData[0][0]);
std::string loadShaderAsString(const std::string& filename) {
std::string result = "";
std::string line = "";
std::ifstream myFile(filename);
if (myFile.is_open()) {
while (std::getline(myFile, line)) {
result += line + '\n';
}
myFile.close();
}
return result;
}
GLuint compileShader(GLuint type, const std::string& source) {
GLuint shaderObject;
if (type == GL_VERTEX_SHADER) {
shaderObject = glCreateShader(GL_VERTEX_SHADER);
} else if (type == GL_FRAGMENT_SHADER) {
shaderObject = glCreateShader(GL_FRAGMENT_SHADER);
}
const char* src = source.c_str();
glShaderSource(shaderObject, 1, &src, nullptr);
glCompileShader(shaderObject);
int result;
glGetShaderiv(shaderObject, GL_COMPILE_STATUS, &result);
if (result == GL_FALSE) {
int length;
glGetShaderiv(shaderObject, GL_INFO_LOG_LENGTH, &length);
char* errorMessages = new char[length]; // Could also use alloca here.
glGetShaderInfoLog(shaderObject, length, &length, errorMessages);
if (type == GL_VERTEX_SHADER) {
std::cout << "ERROR: GL_VERTEX_SHADER compilation failed!\n"
<< errorMessages << "\n";
} else if (type == GL_FRAGMENT_SHADER) {
std::cout << "ERROR: GL_FRAGMENT_SHADER compilation failed!\n"
<< errorMessages << "\n";
}
delete[] errorMessages;
glDeleteShader(shaderObject);
return 0;
}
return shaderObject;
}
GLuint createShaderProgram(const std::string& vertexShaderSource,
const std::string& fragmentShaderSource) {
GLuint programObject = glCreateProgram();
GLuint myVertexShader = compileShader(GL_VERTEX_SHADER, vertexShaderSource);
GLuint myFragmentShader = compileShader(GL_FRAGMENT_SHADER, fragmentShaderSource);
glAttachShader(programObject, myVertexShader);
glAttachShader(programObject, myFragmentShader);
glLinkProgram(programObject);
glValidateProgram(programObject);
glDetachShader(programObject, myVertexShader);
glDetachShader(programObject, myFragmentShader);
glDeleteShader(myVertexShader);
glDeleteShader(myFragmentShader);
return programObject;
}
void createGraphicsPipeline() {
std::string vertexShaderSource = loadShaderAsString("../shaders/vert.glsl");
std::string fragmentShaderSource = loadShaderAsString("../shaders/frag.glsl");
gGraphicsPipelineShaderProgram = createShaderProgram(vertexShaderSource,
fragmentShaderSource);
}
void getOpenGLVersionInfo() {
std::cout << "Vendor: " << glGetString(GL_VENDOR) << std::endl;
std::cout << "Renderer: " << glGetString(GL_RENDERER) << std::endl;
std::cout << "Version: " << glGetString(GL_VERSION) << std::endl;
std::cout << "Shading Language: " << glGetString(GL_SHADING_LANGUAGE_VERSION) << std::endl;
}
void vertexSpecification() {
const std::vector<GLfloat> vertexColors {
1.0f, 0.0f, 0.0f, // vertex 1 - Left
0.0f, 1.0f, 0.0f, // vertex 2 - Right
0.0f, 0.0f, 1.0f // vertex 3 - Top
};
glGenVertexArrays(1, &gVertexArrayObject);
glBindVertexArray(gVertexArrayObject);
glGenBuffers(1, &gVertexBufferObject);
glBindBuffer(GL_ARRAY_BUFFER, gVertexBufferObject);
glBufferData(GL_ARRAY_BUFFER,
gVertexBytes,
&gVertexData[0][0],
GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0,
gVertexData[0].length(),
GL_FLOAT,
GL_FALSE,
0,
nullptr);
glGenBuffers(1, &gVertexBufferObject2);
glBindBuffer(GL_ARRAY_BUFFER, gVertexBufferObject2);
glBufferData(GL_ARRAY_BUFFER,
vertexColors.size() * sizeof(GL_FLOAT),
vertexColors.data(),
GL_STATIC_DRAW);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT,
GL_FALSE, 0, nullptr);
glBindVertexArray(0);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
}
void initializeProgram() {
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
std::cout << "SDL could not be initialized: " <<
SDL_GetError();
exit(1);
}
std::cout << "SDL video system is ready to go\n";
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
gGraphicsApplicationWindow = SDL_CreateWindow("C++ SDL2 Window",
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
gScreenWidth,
gScreenHeight,
SDL_WINDOW_OPENGL);
if (gGraphicsApplicationWindow == nullptr) {
std::cout << "SDL WIndow was not able to be created" << std::endl;
exit(1);
}
gOpenGLContext = SDL_GL_CreateContext(gGraphicsApplicationWindow);
if (gOpenGLContext == nullptr) {
std::cout << "OpenGL context not available" << std::endl;
exit(1);
}
if (!gladLoadGLLoader(SDL_GL_GetProcAddress)) {
std::cout << "Glad was not initialized" << std::endl;
exit(1);
}
getOpenGLVersionInfo();
}
void predraw() {
static glm::vec3 zNorm(0.0f, 0.0f, 1.0f);
gVertexData = glm::rotate(gVertexData, glm::radians(0.10f), zNorm);
glBindBuffer(GL_ARRAY_BUFFER, gVertexBufferObject);
glBufferData(GL_ARRAY_BUFFER,
gVertexBytes,
&gVertexData[0][0],
GL_DYNAMIC_DRAW);
glDisable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
glViewport(0, 0, gScreenWidth, gScreenHeight);
glClearColor(1.0f, 1.0f, 0.0f, 1.0f);
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
glUseProgram(gGraphicsPipelineShaderProgram);
}
void draw() {
glBindVertexArray(gVertexArrayObject);
glBindBuffer(GL_ARRAY_BUFFER, gVertexBufferObject);
glDrawArrays(GL_TRIANGLES, 0, 3);
glUseProgram(0);
}
void mainLoop() {
while (!gQuit) {
predraw();
draw();
SDL_GL_SwapWindow(gGraphicsApplicationWindow);
}
}
void cleanup() {
SDL_DestroyWindow(gGraphicsApplicationWindow);
SDL_Quit();
}
int main(int argc, char* argv[]) {
initializeProgram();
vertexSpecification();
createGraphicsPipeline();
mainLoop();
cleanup();
return 0;
}
Here's the vertex shader code:
#version 410 core
layout(location=0) in vec4 position;
layout(location=1) in vec3 vertexColors;
out vec3 v_vertexColors;
void main()
{
v_vertexColors = vertexColors;
gl_Position = vec4(position.x, position.y, position.z, 1.0f);
}
And the fragment shader:
#version 410 core
in vec3 v_vertexColors;
out vec4 color;
void main()
{
color = vec4(v_vertexColors.r, v_vertexColors.g, v_vertexColors.b, 1.0f);
}
I've gone through these matrices and rotations tutorials, but I haven't been able to figure out what I'm doing wrong.
You have to transform the vertex coordinates in the vertex shader. See LearnOpenGL - Transformations (bottom of the page)
Use a uniform variable of type mat4
and transform the vertices in the vertex shader:
#version 410 core
layout(location=0) in vec4 position;
layout(location=1) in vec3 vertexColors;
out vec3 v_vertexColors;
uniform mat4 model_matrix;
void main()
{
v_vertexColors = vertexColors;
gl_Position = model_matrix * vec4(position.xyz, 1.0f);
}
Set the matrix after installing the program:
int model_loc = glGetUniformLocation(gGraphicsPipelineShaderProgram, "model_matrix")
glm::mat4 rotation_matrix(1.0f);
rotation_matrix = glm::rotate(rotation_matrix, glm::radians(angle), zNorm);
glUseProgram(gGraphicsPipelineShaderProgram);
glUniformMatrix4fv(model_loc, 1, false, glm::value_ptr(rotation_matrix));
The variable angle
needs to be increased in every frame. e.g.: angle = 0.1f;