XCode C++ (Part 4)

Neil HaddleyMarch 27, 2023

Rotating the triangle

macOSxcodeopenglrotationc-plus-plus

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

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

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}