XCode C++ (Part 3)

Neil HaddleyMarch 8, 2023

Building an OpenGL application using C++ and XCode

macOSxcodeopenglc-plus-plusgraphics

I compiled an OpenGL Triangle app on my M1 MacBook Air.

I used the OpenGL Extension Wrangler Library (GLEW), a cross-platform loading library that determines at runtime which OpenGL implementations are available.

The "4.1 Metal - 76.3" OpenGL implementation is available on my M1 MacBook Air.

I ran the application

I ran the application

Overview

I used GLFW to create an application window titled "LearnOpenGL". I initialized GLEW and defined a viewport. I compiled vertex and fragment shaders (written in GLSL) and executed them on the GPU. In a loop, the shaders draw a triangle defined by three positions/vertices.

brew install glew

brew install glew

I added the include folder to header search

I added the include folder to header search

I added a library

I added a library

I updated the library references

I updated the library references

I built and ran the application

I built and ran the application

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// Window dimensions
11const GLuint WIDTH = 800, HEIGHT = 600;
12
13// Shaders
14const GLchar* vertexShaderSource = "#version 330 core\n"
15"layout (location = 0) in vec3 position;\n"
16"void main()\n"
17"{\n"
18"gl_Position = vec4(position.x, position.y, position.z, 1.0);\n"
19"}\0";
20const GLchar* fragmentShaderSource = "#version 330 core\n"
21"out vec4 color;\n"
22"void main()\n"
23"{\n"
24"color = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
25"}\n\0";
26
27// The MAIN function, from here we start the application and run the game loop
28int main()
29{
30    // Init GLFW
31    glfwInit( );
32    
33    // Set all the required options for GLFW
34    glfwWindowHint( GLFW_CONTEXT_VERSION_MAJOR, 3 );
35    glfwWindowHint( GLFW_CONTEXT_VERSION_MINOR, 3 );
36    glfwWindowHint( GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE );
37    glfwWindowHint( GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE );
38
39    glfwWindowHint( GLFW_RESIZABLE, GL_FALSE );
40    
41    // Create a GLFWwindow object that we can use for GLFW's functions
42    GLFWwindow *window = glfwCreateWindow( WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr );
43    
44    int screenWidth, screenHeight;
45    glfwGetFramebufferSize( window, &screenWidth, &screenHeight );
46    
47    if ( nullptr == window )
48    {
49        std::cout << "Failed to create GLFW window" << std::endl;
50        glfwTerminate( );
51        
52        return EXIT_FAILURE;
53    }
54    
55    glfwMakeContextCurrent( window );
56    
57    // Set this to true so GLEW knows to use a modern approach to retrieving function pointers and extensions
58    glewExperimental = GL_TRUE;
59    // Initialize GLEW to setup the OpenGL Function pointers
60    if ( GLEW_OK != glewInit( ) )
61    {
62        std::cout << "Failed to initialize GLEW" << std::endl;
63        return EXIT_FAILURE;
64    }
65    
66    std::cout << glGetString(GL_VERSION);
67    
68    // Define the viewport dimensions
69    glViewport( 0, 0, screenWidth, screenHeight );
70    
71    
72    // Build and compile our shader program
73    // Vertex shader
74    GLuint vertexShader = glCreateShader( GL_VERTEX_SHADER );
75    glShaderSource( vertexShader, 1, &vertexShaderSource, NULL );
76    glCompileShader( vertexShader );
77    
78    // Check for compile time errors
79    GLint success;
80    GLchar infoLog[512];
81    
82    glGetShaderiv( vertexShader, GL_COMPILE_STATUS, &success );
83    if ( !success )
84    {
85        glGetShaderInfoLog( vertexShader, 512, NULL, infoLog );
86        std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
87    }
88    
89    // Fragment shader
90    GLuint fragmentShader = glCreateShader( GL_FRAGMENT_SHADER );
91    glShaderSource( fragmentShader, 1, &fragmentShaderSource, NULL );
92    glCompileShader( fragmentShader );
93    
94    // Check for compile time errors
95    glGetShaderiv( fragmentShader, GL_COMPILE_STATUS, &success );
96    
97    if ( !success )
98    {
99        glGetShaderInfoLog( fragmentShader, 512, NULL, infoLog );
100        std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
101    }
102    
103    // Link shaders
104    GLuint shaderProgram = glCreateProgram( );
105    glAttachShader( shaderProgram, vertexShader );
106    glAttachShader( shaderProgram, fragmentShader );
107    glLinkProgram( shaderProgram );
108    
109    // Check for linking errors
110    glGetProgramiv( shaderProgram, GL_LINK_STATUS, &success );
111    
112    if ( !success )
113    {
114        glGetProgramInfoLog( shaderProgram, 512, NULL, infoLog );
115        std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
116    }
117    
118    glDeleteShader( vertexShader );
119    glDeleteShader( fragmentShader );
120    
121    
122    // Set up vertex data (and buffer(s)) and attribute pointers
123    GLfloat vertices[] =
124    {
125        -0.5f, -0.5f, 0.0f, // Left
126        0.5f, -0.5f, 0.0f, // Right
127        0.0f,  0.5f, 0.0f  // Top
128    };
129    
130    GLuint VBO, VAO;
131    glGenVertexArrays( 1, &VAO );
132    glGenBuffers( 1, &VBO );
133    // Bind the Vertex Array Object first, then bind and set vertex buffer(s) and attribute pointer(s).
134    glBindVertexArray( VAO );
135    
136    glBindBuffer( GL_ARRAY_BUFFER, VBO );
137    glBufferData( GL_ARRAY_BUFFER, sizeof( vertices ), vertices, GL_STATIC_DRAW );
138    
139    glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof( GLfloat ), ( GLvoid * ) 0 );
140    glEnableVertexAttribArray( 0 );
141    
142    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
143    
144    glBindVertexArray( 0 ); // Unbind VAO (it's always a good thing to unbind any buffer/array to prevent strange bugs)
145    
146    // Game loop
147    while ( !glfwWindowShouldClose( window ) )
148    {
149        // Check if any events have been activiated (key pressed, mouse moved etc.) and call corresponding response functions
150        glfwPollEvents( );
151        
152        // Render
153        // Clear the colorbuffer
154        glClearColor( 0.2f, 0.3f, 0.3f, 1.0f );
155        glClear( GL_COLOR_BUFFER_BIT );
156        
157        // Draw our first triangle
158        glUseProgram( shaderProgram );
159        glBindVertexArray( VAO );
160        glDrawArrays( GL_TRIANGLES, 0, 3 );
161        glBindVertexArray( 0 );
162        
163        // Swap the screen buffers
164        glfwSwapBuffers( window );
165    }
166    
167    // Properly de-allocate all resources once they've outlived their purpose
168    glDeleteVertexArrays( 1, &VAO );
169    glDeleteBuffers( 1, &VBO );
170    
171    // Terminate GLFW, clearing any resources allocated by GLFW.
172    glfwTerminate( );
173    
174    return EXIT_SUCCESS;
175}