XCode C++ (Part 4)
Neil Haddley • March 27, 2023
Rotating the triangle
OpenGL Mathematics (GLM) is a header only C++ mathematics library for graphics software based on the OpenGL Shading Language (GLSL) specifications.
I installed GLM using this command
$ brew install glm

Running application
Overview
To rotate the triangle by 10 degrees I had to make changes to the vertex shader.
I had to update the code to apply a rotation matrix to the vertex positions.
const GLchar vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 position;\n"
"uniform mat4 transform;\n"
"void main()\n"
"{\n"
"gl_Position = transform * vec4(position, 1.0);\n"
"}\0";*
In the updated code the uniform variable "transform" represents the rotation matrix that will be applied to the vertex positions.
To set the value of the transform uniform, I needed to add some code to the main function.
To rotate the triangle by 10 degrees around the z-axis I added this code
*// Define the rotation angle in degrees
GLfloat angle = 10.0f;
// Convert the angle to radians
GLfloat radians = glm::radians(angle);
// Calculate the rotation matrix
glm::mat4 rotation = glm::rotate(glm::mat4(1.0f), radians, glm::vec3(0.0f, 0.0f, 1.0f));
// Get the location of the "transform" uniform
GLint transformLoc = glGetUniformLocation(shaderProgram, "transform");
// Set the value of the "transform" uniform
glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(rotation));*
A rotation matrix "rotation" is calculated using the glm::rotate() function.
The glm::rotate() function takes a matrix, an angle (in radians), and an axis of rotation (In this case, we are rotating around the z-axis, so we pass in a vector with x and y values of 0 and a z value of 1).
The location of the transform uniform in the vertex shader is retrieved using the glGetUniformLocation() function.
The value of the transform uniform is set using the glUniformMatrix4fv() function, which takes the location of the uniform, the number of matrices being passed (1), whether or not to transpose the matrix (in this case, we don't want to transpose it), and a pointer to the matrix data (which is obtained using the glm::value_ptr() function).
With these changes, the triangle is rotated by 10 degrees around the z-axis.

I added the glm include folder to header search
Animation
For fun I changed the code above to update the rotation applied to the triangle in the "game loop".
main.cpp
TEXT
1#include <iostream> 2 3// GLEW 4#define GLEW_STATIC 5#include <GL/glew.h> 6 7// GLFW 8#include <GLFW/glfw3.h> 9 10// GLM (OpenGL Mathematics) library 11#include <glm/glm.hpp> 12#include <glm/gtc/matrix_transform.hpp> 13#include <glm/gtc/type_ptr.hpp> 14 15// Window dimensions 16const GLuint WIDTH = 800, HEIGHT = 600; 17 18// Shaders 19/*const GLchar* vertexShaderSource = "#version 330 core\n" 20"layout (location = 0) in vec3 position;\n" 21"void main()\n" 22"{\n" 23"gl_Position = vec4(position.x, position.y, position.z, 1.0);\n" 24"}\0";*/ 25 26const GLchar* vertexShaderSource = "#version 330 core\n" 27"layout (location = 0) in vec3 position;\n" 28"uniform mat4 transform;\n" 29"void main()\n" 30"{\n" 31"gl_Position = transform * vec4(position, 1.0);\n" 32"}\0"; 33 34const GLchar* fragmentShaderSource = "#version 330 core\n" 35"out vec4 color;\n" 36"void main()\n" 37"{\n" 38"color = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n" 39"}\n\0"; 40 41 42 43 44// The MAIN function, from here we start the application and run the game loop 45int main() 46{ 47 // Init GLFW 48 glfwInit( ); 49 50 // Set all the required options for GLFW 51 glfwWindowHint( GLFW_CONTEXT_VERSION_MAJOR, 3 ); 52 glfwWindowHint( GLFW_CONTEXT_VERSION_MINOR, 3 ); 53 glfwWindowHint( GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE ); 54 glfwWindowHint( GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE ); 55 56 glfwWindowHint( GLFW_RESIZABLE, GL_FALSE ); 57 58 // Create a GLFWwindow object that we can use for GLFW's functions 59 GLFWwindow *window = glfwCreateWindow( WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr ); 60 61 int screenWidth, screenHeight; 62 glfwGetFramebufferSize( window, &screenWidth, &screenHeight ); 63 64 if ( nullptr == window ) 65 { 66 std::cout << "Failed to create GLFW window" << std::endl; 67 glfwTerminate( ); 68 69 return EXIT_FAILURE; 70 } 71 72 glfwMakeContextCurrent( window ); 73 74 // Set this to true so GLEW knows to use a modern approach to retrieving function pointers and extensions 75 glewExperimental = GL_TRUE; 76 // Initialize GLEW to setup the OpenGL Function pointers 77 if ( GLEW_OK != glewInit( ) ) 78 { 79 std::cout << "Failed to initialize GLEW" << std::endl; 80 return EXIT_FAILURE; 81 } 82 83 std::cout << '\n'; 84 std::cout << '\n'; 85 86 std::cout << glGetString(GL_VERSION); 87 std::cout << '\n'; 88 std::cout << '\n'; 89 90 // Define the viewport dimensions 91 glViewport( 0, 0, screenWidth, screenHeight ); 92 93 94 // Build and compile our shader program 95 // Vertex shader 96 GLuint vertexShader = glCreateShader( GL_VERTEX_SHADER ); 97 glShaderSource( vertexShader, 1, &vertexShaderSource, NULL ); 98 glCompileShader( vertexShader ); 99 100 // Check for compile time errors 101 GLint success; 102 GLchar infoLog[512]; 103 104 glGetShaderiv( vertexShader, GL_COMPILE_STATUS, &success ); 105 if ( !success ) 106 { 107 glGetShaderInfoLog( vertexShader, 512, NULL, infoLog ); 108 std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl; 109 } 110 111 // Fragment shader 112 GLuint fragmentShader = glCreateShader( GL_FRAGMENT_SHADER ); 113 glShaderSource( fragmentShader, 1, &fragmentShaderSource, NULL ); 114 glCompileShader( fragmentShader ); 115 116 // Check for compile time errors 117 glGetShaderiv( fragmentShader, GL_COMPILE_STATUS, &success ); 118 119 if ( !success ) 120 { 121 glGetShaderInfoLog( fragmentShader, 512, NULL, infoLog ); 122 std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl; 123 } 124 125 // Link shaders 126 GLuint shaderProgram = glCreateProgram( ); 127 glAttachShader( shaderProgram, vertexShader ); 128 glAttachShader( shaderProgram, fragmentShader ); 129 glLinkProgram( shaderProgram ); 130 131 // Check for linking errors 132 glGetProgramiv( shaderProgram, GL_LINK_STATUS, &success ); 133 134 if ( !success ) 135 { 136 glGetProgramInfoLog( shaderProgram, 512, NULL, infoLog ); 137 std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl; 138 } 139 140 glDeleteShader( vertexShader ); 141 glDeleteShader( fragmentShader ); 142 143 144 // Set up vertex data (and buffer(s)) and attribute pointers 145 GLfloat vertices[] = 146 { 147 -0.5f, -0.5f, 0.0f, // Left 148 0.5f, -0.5f, 0.0f, // Right 149 0.0f, 0.5f, 0.0f // Top 150 }; 151 152 153 GLuint VBO, VAO; 154 glGenVertexArrays( 1, &VAO ); 155 glGenBuffers( 1, &VBO ); 156 // Bind the Vertex Array Object first, then bind and set vertex buffer(s) and attribute pointer(s). 157 glBindVertexArray( VAO ); 158 159 glBindBuffer( GL_ARRAY_BUFFER, VBO ); 160 glBufferData( GL_ARRAY_BUFFER, sizeof( vertices ), vertices, GL_STATIC_DRAW ); 161 162 glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof( GLfloat ), ( GLvoid * ) 0 ); 163 glEnableVertexAttribArray( 0 ); 164 165 glBindBuffer( GL_ARRAY_BUFFER, 0 ); // Note that this is allowed, the call to glVertexAttribPointer registered VBO as the currently bound vertex buffer object so afterwards we can safely unbind 166 167 glBindVertexArray( 0 ); // Unbind VAO (it's always a good thing to unbind any buffer/array to prevent strange bugs) 168 169 170 171 // Game loop 172 while ( !glfwWindowShouldClose( window ) ) 173 { 174 // Check if any events have been activiated (key pressed, mouse moved etc.) and call corresponding response functions 175 glfwPollEvents( ); 176 177 // Render 178 // Clear the colorbuffer 179 glClearColor( 0.2f, 0.3f, 0.3f, 1.0f ); 180 glClear( GL_COLOR_BUFFER_BIT ); 181 182 183 184 // Draw our first triangle 185 glUseProgram( shaderProgram ); 186 glBindVertexArray( VAO ); 187 188 189 190 // Define the rotation angle in degrees 191 GLfloat angle = 10.0f; 192 // Convert the angle to radians 193 GLfloat radians = glm::radians(angle); 194 // Calculate the rotation matrix 195 glm::mat4 rotation = glm::rotate(glm::mat4(1.0f), radians, glm::vec3(0.0f, 0.0f, 1.0f)); 196 // Get the location of the "transform" uniform 197 GLint transformLoc = glGetUniformLocation(shaderProgram, "transform"); 198 // Set the value of the "transform" uniform 199 glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(rotation)); 200 201 202 203 glDrawArrays( GL_TRIANGLES, 0, 3 ); 204 glBindVertexArray( 0 ); 205 206 // Swap the screen buffers 207 glfwSwapBuffers( window ); 208 } 209 210 // Properly de-allocate all resources once they've outlived their purpose 211 glDeleteVertexArrays( 1, &VAO ); 212 glDeleteBuffers( 1, &VBO ); 213 214 // Terminate GLFW, clearing any resources allocated by GLFW. 215 glfwTerminate( ); 216 217 return EXIT_SUCCESS; 218}
Program.cs
TEXT
1#include <iostream> 2 3// GLEW 4#define GLEW_STATIC 5#include <GL/glew.h> 6 7// GLFW 8#include <GLFW/glfw3.h> 9 10// GLM (OpenGL Mathematics) library 11#include <glm/glm.hpp> 12#include <glm/gtc/matrix_transform.hpp> 13#include <glm/gtc/type_ptr.hpp> 14 15// Window dimensions 16const GLuint WIDTH = 800, HEIGHT = 600; 17 18// Shaders 19const GLchar* vertexShaderSource = "#version 330 core\n" 20"layout (location = 0) in vec3 position;\n" 21"uniform mat4 transform;\n" 22"void main()\n" 23"{\n" 24"gl_Position = transform * vec4(position, 1.0);\n" 25"}\0"; 26 27const GLchar* fragmentShaderSource = "#version 330 core\n" 28"out vec4 color;\n" 29"void main()\n" 30"{\n" 31"color = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n" 32"}\n\0"; 33 34 35 36// The MAIN function, from here we start the application and run the game loop 37int main() 38{ 39 // Init GLFW 40 glfwInit( ); 41 42 // Set all the required options for GLFW 43 glfwWindowHint( GLFW_CONTEXT_VERSION_MAJOR, 3 ); 44 glfwWindowHint( GLFW_CONTEXT_VERSION_MINOR, 3 ); 45 glfwWindowHint( GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE ); 46 glfwWindowHint( GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE ); 47 48 glfwWindowHint( GLFW_RESIZABLE, GL_FALSE ); 49 50 // Create a GLFWwindow object that we can use for GLFW's functions 51 GLFWwindow *window = glfwCreateWindow( WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr ); 52 53 int screenWidth, screenHeight; 54 glfwGetFramebufferSize( window, &screenWidth, &screenHeight ); 55 56 if ( nullptr == window ) 57 { 58 std::cout << "Failed to create GLFW window" << std::endl; 59 glfwTerminate( ); 60 61 return EXIT_FAILURE; 62 } 63 64 glfwMakeContextCurrent( window ); 65 66 // Set this to true so GLEW knows to use a modern approach to retrieving function pointers and extensions 67 glewExperimental = GL_TRUE; 68 // Initialize GLEW to setup the OpenGL Function pointers 69 if ( GLEW_OK != glewInit( ) ) 70 { 71 std::cout << "Failed to initialize GLEW" << std::endl; 72 return EXIT_FAILURE; 73 } 74 75 std::cout << '\n'; 76 std::cout << '\n'; 77 78 std::cout << glGetString(GL_VERSION); 79 std::cout << '\n'; 80 std::cout << '\n'; 81 82 // Define the viewport dimensions 83 glViewport( 0, 0, screenWidth, screenHeight ); 84 85 86 // Build and compile our shader program 87 // Vertex shader 88 GLuint vertexShader = glCreateShader( GL_VERTEX_SHADER ); 89 glShaderSource( vertexShader, 1, &vertexShaderSource, NULL ); 90 glCompileShader( vertexShader ); 91 92 // Check for compile time errors 93 GLint success; 94 GLchar infoLog[512]; 95 96 glGetShaderiv( vertexShader, GL_COMPILE_STATUS, &success ); 97 if ( !success ) 98 { 99 glGetShaderInfoLog( vertexShader, 512, NULL, infoLog ); 100 std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl; 101 } 102 103 // Fragment shader 104 GLuint fragmentShader = glCreateShader( GL_FRAGMENT_SHADER ); 105 glShaderSource( fragmentShader, 1, &fragmentShaderSource, NULL ); 106 glCompileShader( fragmentShader ); 107 108 // Check for compile time errors 109 glGetShaderiv( fragmentShader, GL_COMPILE_STATUS, &success ); 110 111 if ( !success ) 112 { 113 glGetShaderInfoLog( fragmentShader, 512, NULL, infoLog ); 114 std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl; 115 } 116 117 // Link shaders 118 GLuint shaderProgram = glCreateProgram( ); 119 glAttachShader( shaderProgram, vertexShader ); 120 glAttachShader( shaderProgram, fragmentShader ); 121 glLinkProgram( shaderProgram ); 122 123 // Check for linking errors 124 glGetProgramiv( shaderProgram, GL_LINK_STATUS, &success ); 125 126 if ( !success ) 127 { 128 glGetProgramInfoLog( shaderProgram, 512, NULL, infoLog ); 129 std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl; 130 } 131 132 glDeleteShader( vertexShader ); 133 glDeleteShader( fragmentShader ); 134 135 136 // Set up vertex data (and buffer(s)) and attribute pointers 137 GLfloat vertices[] = 138 { 139 -0.5f, -0.5f, 0.0f, // Left 140 0.5f, -0.5f, 0.0f, // Right 141 0.0f, 0.5f, 0.0f // Top 142 }; 143 144 145 GLuint VBO, VAO; 146 glGenVertexArrays( 1, &VAO ); 147 glGenBuffers( 1, &VBO ); 148 // Bind the Vertex Array Object first, then bind and set vertex buffer(s) and attribute pointer(s). 149 glBindVertexArray( VAO ); 150 151 glBindBuffer( GL_ARRAY_BUFFER, VBO ); 152 glBufferData( GL_ARRAY_BUFFER, sizeof( vertices ), vertices, GL_STATIC_DRAW ); 153 154 glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof( GLfloat ), ( GLvoid * ) 0 ); 155 glEnableVertexAttribArray( 0 ); 156 157 glBindBuffer( GL_ARRAY_BUFFER, 0 ); // Note that this is allowed, the call to glVertexAttribPointer registered VBO as the currently bound vertex buffer object so afterwards we can safely unbind 158 159 glBindVertexArray( 0 ); // Unbind VAO (it's always a good thing to unbind any buffer/array to prevent strange bugs) 160 161 162 // Define the rotation angle in degrees 163 GLfloat angle = 0.0f; 164 // Get the location of the "transform" uniform 165 GLint transformLoc = glGetUniformLocation(shaderProgram, "transform"); 166 167 // Game loop 168 while ( !glfwWindowShouldClose( window ) ) 169 { 170 // Check if any events have been activiated (key pressed, mouse moved etc.) and call corresponding response functions 171 glfwPollEvents( ); 172 173 // Animate 174 angle++; 175 if (angle>359.0f){ 176 angle=0.0f; 177 } 178 // Convert the angle to radians 179 GLfloat radians = glm::radians(angle); 180 // Calculate the rotation matrix 181 glm::mat4 rotation = glm::rotate(glm::mat4(1.0f), radians, glm::vec3(1.0f, 1.0f, 1.0f)); 182 // Set the value of the "transform" uniform 183 glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(rotation)); 184 185 // Render 186 // Clear the colorbuffer 187 glClearColor( 0.2f, 0.3f, 0.3f, 1.0f ); 188 glClear( GL_COLOR_BUFFER_BIT ); 189 190 // Draw our first triangle 191 glUseProgram( shaderProgram ); 192 193 glBindVertexArray( VAO ); 194 195 glDrawArrays( GL_TRIANGLES, 0, 3 ); 196 glBindVertexArray( 0 ); 197 198 // Swap the screen buffers 199 glfwSwapBuffers( window ); 200 } 201 202 // Properly de-allocate all resources once they've outlived their purpose 203 glDeleteVertexArrays( 1, &VAO ); 204 glDeleteBuffers( 1, &VBO ); 205 206 // Terminate GLFW, clearing any resources allocated by GLFW. 207 glfwTerminate( ); 208 209 return EXIT_SUCCESS; 210}