This is the logo owned by Apple Inc. for Xcode. Taken from the picture's app bundle. Fair use
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
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.
main.cpp
#include <iostream> // GLEW #define GLEW_STATIC #include <GL/glew.h> // GLFW #include <GLFW/glfw3.h> // GLM (OpenGL Mathematics) library #include <glm/glm.hpp> #include <glm/gtc/matrix_transform.hpp> #include <glm/gtc/type_ptr.hpp> // Window dimensions const GLuint WIDTH = 800, HEIGHT = 600; // Shaders /*const GLchar* vertexShaderSource = "#version 330 core\n" "layout (location = 0) in vec3 position;\n" "void main()\n" "{\n" "gl_Position = vec4(position.x, position.y, position.z, 1.0);\n" "}\0";*/ 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"; const GLchar* fragmentShaderSource = "#version 330 core\n" "out vec4 color;\n" "void main()\n" "{\n" "color = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n" "}\n\0"; // The MAIN function, from here we start the application and run the game loop int main() { // Init GLFW glfwInit( ); // Set all the required options for GLFW glfwWindowHint( GLFW_CONTEXT_VERSION_MAJOR, 3 ); glfwWindowHint( GLFW_CONTEXT_VERSION_MINOR, 3 ); glfwWindowHint( GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE ); glfwWindowHint( GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE ); glfwWindowHint( GLFW_RESIZABLE, GL_FALSE ); // Create a GLFWwindow object that we can use for GLFW's functions GLFWwindow *window = glfwCreateWindow( WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr ); int screenWidth, screenHeight; glfwGetFramebufferSize( window, &screenWidth, &screenHeight ); if ( nullptr == window ) { std::cout << "Failed to create GLFW window" << std::endl; glfwTerminate( ); return EXIT_FAILURE; } glfwMakeContextCurrent( window ); // Set this to true so GLEW knows to use a modern approach to retrieving function pointers and extensions glewExperimental = GL_TRUE; // Initialize GLEW to setup the OpenGL Function pointers if ( GLEW_OK != glewInit( ) ) { std::cout << "Failed to initialize GLEW" << std::endl; return EXIT_FAILURE; } std::cout << '\n'; std::cout << '\n'; std::cout << glGetString(GL_VERSION); std::cout << '\n'; std::cout << '\n'; // Define the viewport dimensions glViewport( 0, 0, screenWidth, screenHeight ); // Build and compile our shader program // Vertex shader GLuint vertexShader = glCreateShader( GL_VERTEX_SHADER ); glShaderSource( vertexShader, 1, &vertexShaderSource, NULL ); glCompileShader( vertexShader ); // Check for compile time errors GLint success; GLchar infoLog[512]; glGetShaderiv( vertexShader, GL_COMPILE_STATUS, &success ); if ( !success ) { glGetShaderInfoLog( vertexShader, 512, NULL, infoLog ); std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl; } // Fragment shader GLuint fragmentShader = glCreateShader( GL_FRAGMENT_SHADER ); glShaderSource( fragmentShader, 1, &fragmentShaderSource, NULL ); glCompileShader( fragmentShader ); // Check for compile time errors glGetShaderiv( fragmentShader, GL_COMPILE_STATUS, &success ); if ( !success ) { glGetShaderInfoLog( fragmentShader, 512, NULL, infoLog ); std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl; } // Link shaders GLuint shaderProgram = glCreateProgram( ); glAttachShader( shaderProgram, vertexShader ); glAttachShader( shaderProgram, fragmentShader ); glLinkProgram( shaderProgram ); // Check for linking errors glGetProgramiv( shaderProgram, GL_LINK_STATUS, &success ); if ( !success ) { glGetProgramInfoLog( shaderProgram, 512, NULL, infoLog ); std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl; } glDeleteShader( vertexShader ); glDeleteShader( fragmentShader ); // Set up vertex data (and buffer(s)) and attribute pointers GLfloat vertices[] = { -0.5f, -0.5f, 0.0f, // Left 0.5f, -0.5f, 0.0f, // Right 0.0f, 0.5f, 0.0f // Top }; GLuint VBO, VAO; glGenVertexArrays( 1, &VAO ); glGenBuffers( 1, &VBO ); // Bind the Vertex Array Object first, then bind and set vertex buffer(s) and attribute pointer(s). glBindVertexArray( VAO ); glBindBuffer( GL_ARRAY_BUFFER, VBO ); glBufferData( GL_ARRAY_BUFFER, sizeof( vertices ), vertices, GL_STATIC_DRAW ); glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof( GLfloat ), ( GLvoid * ) 0 ); glEnableVertexAttribArray( 0 ); 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 glBindVertexArray( 0 ); // Unbind VAO (it's always a good thing to unbind any buffer/array to prevent strange bugs) // Game loop while ( !glfwWindowShouldClose( window ) ) { // Check if any events have been activiated (key pressed, mouse moved etc.) and call corresponding response functions glfwPollEvents( ); // Render // Clear the colorbuffer glClearColor( 0.2f, 0.3f, 0.3f, 1.0f ); glClear( GL_COLOR_BUFFER_BIT ); // Draw our first triangle glUseProgram( shaderProgram ); glBindVertexArray( VAO ); // 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)); glDrawArrays( GL_TRIANGLES, 0, 3 ); glBindVertexArray( 0 ); // Swap the screen buffers glfwSwapBuffers( window ); } // Properly de-allocate all resources once they've outlived their purpose glDeleteVertexArrays( 1, &VAO ); glDeleteBuffers( 1, &VBO ); // Terminate GLFW, clearing any resources allocated by GLFW. glfwTerminate( ); return EXIT_SUCCESS; }
I added the glm include folder to header search
For fun I changed the code above to update the rotation applied to the triangle in the "game loop".
Program.cs
#include <iostream> // GLEW #define GLEW_STATIC #include <GL/glew.h> // GLFW #include <GLFW/glfw3.h> // GLM (OpenGL Mathematics) library #include <glm/glm.hpp> #include <glm/gtc/matrix_transform.hpp> #include <glm/gtc/type_ptr.hpp> // Window dimensions const GLuint WIDTH = 800, HEIGHT = 600; // Shaders 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"; const GLchar* fragmentShaderSource = "#version 330 core\n" "out vec4 color;\n" "void main()\n" "{\n" "color = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n" "}\n\0"; // The MAIN function, from here we start the application and run the game loop int main() { // Init GLFW glfwInit( ); // Set all the required options for GLFW glfwWindowHint( GLFW_CONTEXT_VERSION_MAJOR, 3 ); glfwWindowHint( GLFW_CONTEXT_VERSION_MINOR, 3 ); glfwWindowHint( GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE ); glfwWindowHint( GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE ); glfwWindowHint( GLFW_RESIZABLE, GL_FALSE ); // Create a GLFWwindow object that we can use for GLFW's functions GLFWwindow *window = glfwCreateWindow( WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr ); int screenWidth, screenHeight; glfwGetFramebufferSize( window, &screenWidth, &screenHeight ); if ( nullptr == window ) { std::cout << "Failed to create GLFW window" << std::endl; glfwTerminate( ); return EXIT_FAILURE; } glfwMakeContextCurrent( window ); // Set this to true so GLEW knows to use a modern approach to retrieving function pointers and extensions glewExperimental = GL_TRUE; // Initialize GLEW to setup the OpenGL Function pointers if ( GLEW_OK != glewInit( ) ) { std::cout << "Failed to initialize GLEW" << std::endl; return EXIT_FAILURE; } std::cout << '\n'; std::cout << '\n'; std::cout << glGetString(GL_VERSION); std::cout << '\n'; std::cout << '\n'; // Define the viewport dimensions glViewport( 0, 0, screenWidth, screenHeight ); // Build and compile our shader program // Vertex shader GLuint vertexShader = glCreateShader( GL_VERTEX_SHADER ); glShaderSource( vertexShader, 1, &vertexShaderSource, NULL ); glCompileShader( vertexShader ); // Check for compile time errors GLint success; GLchar infoLog[512]; glGetShaderiv( vertexShader, GL_COMPILE_STATUS, &success ); if ( !success ) { glGetShaderInfoLog( vertexShader, 512, NULL, infoLog ); std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl; } // Fragment shader GLuint fragmentShader = glCreateShader( GL_FRAGMENT_SHADER ); glShaderSource( fragmentShader, 1, &fragmentShaderSource, NULL ); glCompileShader( fragmentShader ); // Check for compile time errors glGetShaderiv( fragmentShader, GL_COMPILE_STATUS, &success ); if ( !success ) { glGetShaderInfoLog( fragmentShader, 512, NULL, infoLog ); std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl; } // Link shaders GLuint shaderProgram = glCreateProgram( ); glAttachShader( shaderProgram, vertexShader ); glAttachShader( shaderProgram, fragmentShader ); glLinkProgram( shaderProgram ); // Check for linking errors glGetProgramiv( shaderProgram, GL_LINK_STATUS, &success ); if ( !success ) { glGetProgramInfoLog( shaderProgram, 512, NULL, infoLog ); std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl; } glDeleteShader( vertexShader ); glDeleteShader( fragmentShader ); // Set up vertex data (and buffer(s)) and attribute pointers GLfloat vertices[] = { -0.5f, -0.5f, 0.0f, // Left 0.5f, -0.5f, 0.0f, // Right 0.0f, 0.5f, 0.0f // Top }; GLuint VBO, VAO; glGenVertexArrays( 1, &VAO ); glGenBuffers( 1, &VBO ); // Bind the Vertex Array Object first, then bind and set vertex buffer(s) and attribute pointer(s). glBindVertexArray( VAO ); glBindBuffer( GL_ARRAY_BUFFER, VBO ); glBufferData( GL_ARRAY_BUFFER, sizeof( vertices ), vertices, GL_STATIC_DRAW ); glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof( GLfloat ), ( GLvoid * ) 0 ); glEnableVertexAttribArray( 0 ); 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 glBindVertexArray( 0 ); // Unbind VAO (it's always a good thing to unbind any buffer/array to prevent strange bugs) // Define the rotation angle in degrees GLfloat angle = 0.0f; // Get the location of the "transform" uniform GLint transformLoc = glGetUniformLocation(shaderProgram, "transform"); // Game loop while ( !glfwWindowShouldClose( window ) ) { // Check if any events have been activiated (key pressed, mouse moved etc.) and call corresponding response functions glfwPollEvents( ); // Animate angle++; if (angle>359.0f){ angle=0.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(1.0f, 1.0f, 1.0f)); // Set the value of the "transform" uniform glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(rotation)); // Render // Clear the colorbuffer glClearColor( 0.2f, 0.3f, 0.3f, 1.0f ); glClear( GL_COLOR_BUFFER_BIT ); // Draw our first triangle glUseProgram( shaderProgram ); glBindVertexArray( VAO ); glDrawArrays( GL_TRIANGLES, 0, 3 ); glBindVertexArray( 0 ); // Swap the screen buffers glfwSwapBuffers( window ); } // Properly de-allocate all resources once they've outlived their purpose glDeleteVertexArrays( 1, &VAO ); glDeleteBuffers( 1, &VBO ); // Terminate GLFW, clearing any resources allocated by GLFW. glfwTerminate( ); return EXIT_SUCCESS; }