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

I ran the 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));*
I calculated a rotation matrix using the glm::rotate() function.
The glm::rotate() function takes a matrix, an angle (in radians), and an axis of rotation. I rotated around the z-axis by passing a vector with x and y values of 0 and a z value of 1.
I retrieved the location of the transform uniform using the glGetUniformLocation() function.
I set the value of the transform uniform using glUniformMatrix4fv(), passing the uniform location, the number of matrices (1), whether to transpose (false), and a pointer to the matrix data from glm::value_ptr().
With these changes, I rotated the triangle 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
C++
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
CSHARP
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}