XCode C++ (Part 4)

Neil HaddleyMarch 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

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

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}