XCode C++ (Part 3)

Neil HaddleyMarch 8, 2023

Building an OpenGL application using C++ and XCode

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

The OpenGL Extension Wrangler Library (GLEW) is a cross-platform loading library.

GLEW provides a way to determine (at runtime) which OpenGL implementations are available.

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

Running application

Running application

Overview

The GLFW library is used to create an application window with a given width and height and the title "LearnOpenGL".

The GLEW library state is initialized.

A GLEW library call is used to define a viewport.

In this example vertex shader and fragment shader code (written in the C-like language GLSL) is copied, compiled and executed on the computer's graphics processing unit (GPU).

In a loop the shaders are used to draw and redraw a triangle defined by a set of positions/vertices. Each position includes an x value between -1 (far left) and 1 (far right), a y value between -1 (bottom) and 1 (top) and a z value (always 0 in this example).

brew install glew

brew install glew

Add include folder to header search

Add include folder to header search

Add library

Add library

updated library references

updated library references

build and run application

build and run application

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// 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}