# OpenGL
# Basic OpenGL in Qt Creator
- Create a widget project.
- At the UI drag and drop an
OpenGL Widget
and adjust the layout. - at CMkae file include:
set(CMAKE_CXX_STANDARD 17)
and
find_package(OpenGL)
and
target_link_libraries(... ${OPENGL_LIBRARIES})
2
3
4
5
6
7
8
9
10
- right click on the project name and
add new
C++ Class. Choose and insert the class name likeMyGLWidget
, copy the class name; Base class select<Custom>
and QOpenGLWidget. Include QWidget and Add Q_OBJECT.
- include the files on the CMake:
add_executable(OpenGL_BoilerPlate
main.cpp
widget.cpp
widget.h
widget.ui
myglwidget.cpp
myglwidget.h
)
2
3
4
5
6
7
8
- on the myglwidget.h include
#ifndef MYGLWIDGET_H
#define MYGLWIDGET_H
#include <QOpenGLWidget>
#include <QWidget>
class MyGLWidget : public QOpenGLWidget
{
Q_OBJECT
public:
MyGLWidget(QWidget* parent=nullptr);
};
#endif // MYGLWIDGET_H
2
3
4
5
6
7
8
9
10
11
12
13
14
- At the myglwidget.cpp include:
#include "myglwidget.h"
MyGLWidget::MyGLWidget(QWidget* parent):
QOpenGLWidget(parent)
{
}
2
3
4
5
6
7
- include
#include <QOpenGLWidget> #include <QOpenGLFunctions>
at the head file.
#ifndef MYGLWIDGET_H
#define MYGLWIDGET_H
#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QWidget>
class MyGLWidget : public QOpenGLWidget, public QOpenGLFunctions
{
Q_OBJECT
public:
MyGLWidget(QWidget* parent=nullptr);
};
#endif // MYGLWIDGET_H
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- add the functions:
#ifndef MYGLWIDGET_H
#define MYGLWIDGET_H
#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QWidget>
class MyGLWidget : public QOpenGLWidget, public QOpenGLFunctions
{
Q_OBJECT
public:
MyGLWidget(QWidget* parent=nullptr);
protected:
//It is a virtual function
void initializeGL() override;
//It is also a virtual function
void paintGL() override;
};
#endif // MYGLWIDGET_H
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
- complete the function with code from here. Don't forget to
#include <QSurfaceFormat>
#include "myglwidget.h"
MyGLWidget::MyGLWidget(QWidget* parent):
QOpenGLWidget(parent)
{
QSurfaceFormat format;
format.setDepthBufferSize(24);
format.setStencilBufferSize(8);
format.setVersion(3, 2);
format.setProfile(QSurfaceFormat::CoreProfile);
QSurfaceFormat::setDefaultFormat(format);
}
void MyGLWidget::initializeGL()
{
this->initializeOpenGLFunctions();
}
void MyGLWidget::paintGL()
{
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
- Paste some code to
paintGL()
:
#include "myglwidget.h"
MyGLWidget::MyGLWidget(QWidget* parent):
QOpenGLWidget(parent)
{
QSurfaceFormat format;
format.setDepthBufferSize(24);
format.setStencilBufferSize(8);
format.setVersion(3, 2);
format.setProfile(QSurfaceFormat::CoreProfile);
QSurfaceFormat::setDefaultFormat(format);
}
void MyGLWidget::initializeGL()
{
this->initializeOpenGLFunctions();
}
void MyGLWidget::paintGL()
{
glClearColor(1.0f, 1.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
glBegin(GL_QUADS);
glColor3f(1.0f, 0.0f, 1.0f);
glVertex2f(-0.5f, -0.5f);
glVertex2f( 0.5f, -0.5f);
glVertex2f( 0.5f, 0.5f);
glVertex2f(-0.5f, 0.5f);
glEnd();
glFlush();
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
- Change the OpenGL Widget, right click and promote to, on Promoted Class name write MyGLWidget, add and Promote
This should be the result:
myglwidget.h
#ifndef MYGLWIDGET_H
#define MYGLWIDGET_H
#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QWidget>
#include <QSurfaceFormat>
class MyGLWidget : public QOpenGLWidget, public QOpenGLFunctions
{
Q_OBJECT
public:
MyGLWidget(QWidget* parent=nullptr);
protected:
//It is a virtual function
void initializeGL() override;
//It is also a virtual function
void paintGL() override;
};
#endif // MYGLWIDGET_H
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
myglwidget.cpp
#include "myglwidget.h"
MyGLWidget::MyGLWidget(QWidget* parent):
QOpenGLWidget(parent)
{
QSurfaceFormat format;
format.setDepthBufferSize(24);
format.setStencilBufferSize(8);
format.setVersion(3, 2);
format.setProfile(QSurfaceFormat::CoreProfile);
QSurfaceFormat::setDefaultFormat(format);
}
void MyGLWidget::initializeGL()
{
this->initializeOpenGLFunctions();
}
void MyGLWidget::paintGL()
{
glClearColor(1.0f, 1.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
glBegin(GL_QUADS);
glColor3f(1.0f, 0.0f, 1.0f);
glVertex2f(-0.5f, -0.5f);
glVertex2f( 0.5f, -0.5f);
glVertex2f( 0.5f, 0.5f);
glVertex2f(-0.5f, 0.5f);
glEnd();
glFlush();
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
CMake
cmake_minimum_required(VERSION 3.5)
project(OpenGL_BoilerPlate LANGUAGES CXX)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# QtCreator supports the following variables for Android, which are identical to qmake Android variables.
# Check http://doc.qt.io/qt-5/deployment-android.html for more information.
# They need to be set before the find_package(Qt5 ...) call.
#if(ANDROID)
# set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android")
# if (ANDROID_ABI STREQUAL "armeabi-v7a")
# set(ANDROID_EXTRA_LIBS
# ${CMAKE_CURRENT_SOURCE_DIR}/path/to/libcrypto.so
# ${CMAKE_CURRENT_SOURCE_DIR}/path/to/libssl.so)
# endif()
#endif()
find_package(QT NAMES Qt6 Qt5 COMPONENTS Widgets REQUIRED)
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Widgets REQUIRED)
find_package(Qt5OpenGL)
find_package(OpenGL)
if(ANDROID)
add_library(OpenGL_BoilerPlate SHARED
main.cpp
widget.cpp
widget.h
widget.ui
myglwidget.cpp
myglwidget.h
)
else()
add_executable(OpenGL_BoilerPlate
main.cpp
widget.cpp
widget.h
widget.ui
myglwidget.cpp
myglwidget.h
)
endif()
target_link_libraries(OpenGL_BoilerPlate PRIVATE Qt${QT_VERSION_MAJOR}::Widgets ${OPENGL_LIBRARIES})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# Introduction to GLEW, GLFW and SDL
# What is GLEW?
- OpenGL Extension Wrangler
- Interface for OpenGL versions above 1.1
- Load OpenGL extensions
- Some extensions are platform specific, GLEW can check id they exist on that platform
- Alternatives: GL3W, glLoadGen, glad, glsdk, glbinding, libepoxy, Glee
- Best to use GLEW. Most people do.
# Using GLEW
#include <GL/glew.>
- After initialization OpenGL context: glewExperimental = GL_TRUE
glewInit()
;- Should return
GLEW_OK
. If it fails, it returns the error. - Can read the error with
glewGetErrorString(result);
- Check extenisions exist:
if (!GLEW_EXT_framebuffer_object){}
- wglew.h for windows only functions
# GLFW
Creates the context for GLEW to work, It's a library that handle windows.
- OpenGL Framework
- Handles window creation and control
- Pick up and process input from the keyboard, mouse, joystick and gamepad
- Even allows multiple monitor support!
- uses OpenGL context for windows.
# SDL
- Simple DirectMedia Layer
- Can do almost everything GLFW can do...
- and more (Audio, Threading, Filesystems, etc.)
- Used in: FTL, AMnesia, Starbound and Dying light
- Even used in level editors for Source Engine and Cryengine!
# Alternatives
- SFML (Simple and Fast Multimedia Library): Like SDL but with even more features
- ... but the OpenGL context is very week. Based on 2D only graphics.
- GLUT (OpenGL Utility Toolkit) : Is no longer maintained. Try to avoid it.
- Win32 API: For the purists. Lowest level for window creation. Only attempt if you know what you are doing!
# Setting up GLEW with GLFW
Download the 32-bit libraries below:
Obs.: the 32-bit will work on the 64-bit.
Linking the libraries in Visual Studio:
- include the GLEW and GLFW libraries on
External_Libraries
folder outside the project folder.
Open Project properties and set
Configuration
to All Configurations andPlatform
to All Platforms.Got to C/C++ and include on Additional Include Directories:
$(SolutionDir)/../../External_Libraries/GLEW/include
$(SolutionDir)/../../External_Libraries/GLFW/include
2
- Open project properties and go to
Linker
->Additional Libraries
and include:
$(SolutionDir)/../../External_Libraries/GLFW/lib-vc2019
$(SolutionDir)/../../External_Libraries/GLEW/lib/Release/Win32
2
- At the
linker
->Input
->Additional Dependencies
include:
opengl32.lib
glew32.lib
glfw3.lib
2
3
- Copy the file
glew32.dll
fromExternal_Libraries/GLEW/bin/Release/Win32
and paste inside the projects folder.
DONE!!!!
# First Program
#include <iostream>
#include <GL\glew.h>
#include <GLFW\glfw3.h>
//Window dimensions
const GLint WIDTH = 800, HEIGHT = 600;
int main()
{
//Initialize GLFW
if (!glfwInit()) {
std::cout << "GLFW Initialization failed!";
glfwTerminate();
}
//Setup GLFW window properties
//OpenGL version
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
//Core Profile = No Backwards Compatibility
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
//Allow forward compatibility
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
GLFWwindow* mainWindow = glfwCreateWindow(WIDTH, HEIGHT, "Test Window", NULL, NULL);
if (!mainWindow) {
std::cout << " GLFW window creation failed!";
glfwTerminate();
}
// Get Buffer size information
int bufferWidth, bufferHeight;
glfwGetFramebufferSize(mainWindow, &bufferWidth, &bufferHeight);
//Set the context for GLEW to use
glfwMakeContextCurrent(mainWindow);
//Allow modern extension features
glewExperimental = GL_TRUE;
if (glewInit() != GLEW_OK) {
std::cout << "GLEW Initialization failed!";
glfwDestroyWindow(mainWindow);
glfwTerminate();
}
//Setup viewport size
glViewport(0, 0, bufferWidth, bufferHeight);
// LLop until window close
while (!glfwWindowShouldClose(mainWindow)) {
// Get + Handle user input events
glfwPollEvents();
// Clear window
glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glfwSwapBuffers(mainWindow);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# Shaders and the Rendering Pipeline
It's somewhat important to understand what's going on behind the scenes.
It's not as simple as just the CPU receiving the commands and then running some function in the background.
What actually happens is it gets passed to your graphics card or the GPU in order to make things as fast as possible. There's something that we call pipeline.
So it's a set series of operations that get performed every time you go through. Everything is part of the GPU itself it runs through all of this and the rendering pipeline is the name for that. Whereas Shaders are parts of the rendering pipeline that we can actually reprogramme.
# Vertex Specification
Here is an example of a very simple vertex shader:
The first thing you need to do in a vertical shader is define the version of glsl you are using.
#version 330
layout (location = 0) in vec 3 pos;
void main()
{
gl_Position = vec4(pos, 1.0);
}
2
3
4
5
6
7
8
# Second Program - Triangle
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <GL\glew.h>
#include <GLFW\glfw3.h>
//Window dimensions
const GLint WIDTH = 800, HEIGHT = 600;
GLuint VAO, VBO, shader;
// Vertex Shader
static const char* vShader = " \n\
#version 410 \n\
\n\
layout (location = 0) in vec3 pos; \n\
\n\
void main() \n\
{ \n\
gl_Position = vec4(0.4 * pos.x, 0.4 * pos.y, pos.z, 1.0); \n\
}";
static const char* fShader = " \n\
#version 410 \n\
\n\
out vec4 colour; \n\
\n\
layout (location = 0) in vec3 pos; \n\
\n\
void main() \n\
{ \n\
colour = vec4(0.0, 1.0, 0.0, 1.0); \n\
}";
/*!
Here we are going to create a VAO (Vertex Array Object) and a VBO (Vertex Buffer Object)
The VAO will hold multiples VBOs and other types of buffer to define how a triangle should be drawn,
In this funciton is just the vertices of the triangles.
*/
void CreateTriangle()
{
// Triangle Points in an array of floats (The center of the screen is 0,0)
GLfloat vertices[] = {
-1.0f, -1.0f, 0.0f,
1.0f, -1.0f, 0.0f,
0.0f, 1.0f, 0.0f
};
/*
Creating a VAO
Takes the amount of arrays we want to create and where to store the ID of the array.
When we are calling this function on the graphics card is now creating a vertex array, its defining some space in the memory for some vertex array.
So if you want to access that space in the memory you just pass the ID(VAO)).
*/
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
// Now that we have a VAO we need to create a buffer object to actually go inside of that.
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO); //We have to enter which buffer we want to bind, as VBO has multiple targets it can bind to.
// Now we have to connect the buffer data, the vertices that we created to the GL_BUFFER for this VBO.
// GL_STATIC_DRAW means we are not going to be changing the values we are putting in there, GL_DYNAMIC_DRAW is more complex.
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); // First attribute is location of the attribute, then amount of values(x,y,z), then type, then if normalizing(we are not normalizing), then striding value(to jump 3 or whatever to read next value), then offset)
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0); // undoing the binding in the VBO
glBindVertexArray(0); //undoing the binding in the VAO
}
/*!
Add the shaders to the program funtion
*/
void AddShader(GLuint theProgram, const char* shaderCode, GLenum shaderType)
{
GLuint theShader = glCreateShader(shaderType); // Creating an empty shader for that type
const GLchar* theCode[1]; // Passing a pointer to the first element of GLchar, so we can pass a pointer directly to it
theCode[0] = shaderCode;
GLint codeLength[1]; // Hold the length of the code
codeLength[0] = strlen(shaderCode);
glShaderSource(theShader, 1, theCode, codeLength); // Now the empty shader is modified to have this code
glCompileShader(theShader); // compile the shader
GLint result = 0;
GLchar eLog[1024] = { 0 };
// Error check before attach the shader to the program
glGetShaderiv(theShader, GL_COMPILE_STATUS, &result);
if (!result)
{
glGetShaderInfoLog(theShader, sizeof(eLog), NULL, eLog);
printf("Error compiling the %d shader: '%s'\n", shaderType, eLog);
return;
}
glAttachShader(theProgram, theShader);
}
/*!
*/
void CompileShaders() {
shader = glCreateProgram(); // Create the program and give shader the ID, then we can modify the program and do something with it
// Make sure the shader was created correctly
if (!shader) {
printf("Error creating shader program!\n");
return;
}
//Add the shaders to the program using the function
AddShader(shader, vShader, GL_VERTEX_SHADER);
AddShader(shader, fShader, GL_FRAGMENT_SHADER);
// Getting the error codes, with the results of the two functions we are about to perform and a place to put the errors
GLint result = 0;
GLchar eLog[1024] = { 0 };
glLinkProgram(shader); // Create the exectables in the graphics card.
// Make sure the program is linked properlly
glGetProgramiv(shader, GL_LINK_STATUS, &result);
if (!result)
{
glGetProgramInfoLog(shader, sizeof(eLog), NULL, eLog); // Getting the InfoLog for this program, passing the eLog char array and then print
printf("Error linking program: '%s'\n", eLog);
return;
}
// Validating if the program is valid on the current context that OpenGL is working
glValidateProgram(shader);
glGetProgramiv(shader, GL_VALIDATE_STATUS, &result);
if (!result)
{
glGetProgramInfoLog(shader, sizeof(eLog), NULL, eLog);
printf("Error validating program: '%s'\n", eLog);
return;
}
}
int main()
{
//Initialize GLFW
if (!glfwInit()) {
std::cout << "GLFW Initialization failed!";
glfwTerminate();
}
//Setup GLFW window properties
//OpenGL version
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
//Core Profile = No Backwards Compatibility
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
//Allow forward compatibility
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
GLFWwindow* mainWindow = glfwCreateWindow(WIDTH, HEIGHT, "Test Window", NULL, NULL);
if (!mainWindow) {
std::cout << " GLFW window creation failed!";
glfwTerminate();
}
// Get Buffer size information
int bufferWidth, bufferHeight;
glfwGetFramebufferSize(mainWindow, &bufferWidth, &bufferHeight);
//Set the context for GLEW to use
glfwMakeContextCurrent(mainWindow);
//Allow modern extension features
glewExperimental = GL_TRUE;
if (glewInit() != GLEW_OK) {
std::cout << "GLEW Initialization failed!";
glfwDestroyWindow(mainWindow);
glfwTerminate();
}
//Setup viewport size
glViewport(0, 0, bufferWidth, bufferHeight);
CreateTriangle();
CompileShaders();
// LLop until window close
while (!glfwWindowShouldClose(mainWindow)) {
// Get + Handle user input events
glfwPollEvents();
// Clear window
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// Draw the triangle
glUseProgram(shader); // This will now grab that ID, It will go through the graphics card and tell it to use the one with the ID of shader
glBindVertexArray(VAO); // We saying now work with this VAO
glDrawArrays(GL_TRIANGLES, 0, 3); // draw 3 points starting on the first
glBindVertexArray(0); // Unassign the VAO
glUseProgram(0); // Unassign the shader
glfwSwapBuffers(mainWindow);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
TIP
Maybe this error can show up, so just cange the shader version for 410.
Error compiling the 35632 shader: '0(6) : error C7548: 'layout(location)' requires "#extension GL_ARB_separate_shader_objects : enable" before use
0(6) : error C0000: ... or #version 410
2
# Vectors, Matrices and Uniform Variables
Translating:
Scaling:
Rotation:
GLM will do most of the matrix maths for us.
Combining:
GLM:
Uniform Variables
Summary
# Uniform Variables
Just a quick one down the difference between an attribute variable and an uniform variable in the context of shade is an attribute variable is distinct for each point pass through. This shader here will be running for each coordinate that is passed through each of the points on the triangle and this piece of code will be run on those points. Each time this shader runs this position he will change.
However, a uniform variable will be a variable which is constant over the whole flow. We pass on one variable and that variable will be the same on every single instance. Every single time that shader runs for that set of coordinates.
static const char* vShader = " \n\
#version 410 \n\
\n\
layout (location = 0) in vec3 pos; \n\
\n\
void main() \n\
{ \n\
gl_Position = vec4(0.4 * pos.x, 0.4 * pos.y, pos.z, 1.0); \n\
}";
2
3
4
5
6
7
8
9
# Moving a triangle left and right in the screen
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <cmath>
#include <GL\glew.h>
#include <GLFW\glfw3.h>
//Window dimensions
const GLint WIDTH = 800, HEIGHT = 600;
GLuint VAO, VBO, shader, uniformXMove;
bool direction = true;
float triOffset = 0.0f;
float triMaxoffset = 0.7f;
float triIncrement = 0.005f;
// Vertex Shader
static const char* vShader = " \n\
#version 410 \n\
\n\
layout (location = 0) in vec3 pos; \n\
\n\
uniform float xMove; \n\
\n\
void main() \n\
{ \n\
gl_Position = vec4(0.4 * pos.x + xMove, 0.4 * pos.y, pos.z, 1.0); \n\
}";
static const char* fShader = " \n\
#version 410 \n\
\n\
out vec4 colour; \n\
\n\
layout (location = 0) in vec3 pos; \n\
\n\
void main() \n\
{ \n\
colour = vec4(0.0, 1.0, 0.0, 1.0); \n\
}";
/*!
Here we are going to create a VAO (Vertex Array Object) and a VBO (Vertex Buffer Object)
The VAO will hold multiples VBOs and other types of buffer to define how a triangle should be drawn,
In this funciton is just the vertices of the triangles.
*/
void CreateTriangle()
{
// Triangle Points in an array of floats (The center of the screen is 0,0)
GLfloat vertices[] = {
-1.0f, -1.0f, 0.0f,
1.0f, -1.0f, 0.0f,
0.0f, 1.0f, 0.0f
};
/*
Creating a VAO
Takes the amount of arrays we want to create and where to store the ID of the array.
When we are calling this function on the graphics card is now creating a vertex array, its defining some space in the memory for some vertex array.
So if you want to access that space in the memory you just pass the ID(VAO)).
*/
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
// Now that we have a VAO we need to create a buffer object to actually go inside of that.
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO); //We have to enter which buffer we want to bind, as VBO has multiple targets it can bind to.
// Now we have to connect the buffer data, the vertices that we created to the GL_BUFFER for this VBO.
// GL_STATIC_DRAW means we are not going to be changing the values we are putting in there, GL_DYNAMIC_DRAW is more complex.
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); // First attribute is location of the attribute, then amount of values(x,y,z), then type, then if normalizing(we are not normalizing), then striding value(to jump 3 or whatever to read next value), then offset)
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0); // undoing the binding in the VBO
glBindVertexArray(0); //undoing the binding in the VAO
}
/*!
Add the shaders to the program funtion
*/
void AddShader(GLuint theProgram, const char* shaderCode, GLenum shaderType)
{
GLuint theShader = glCreateShader(shaderType); // Creating an empty shader for that type
const GLchar* theCode[1]; // Passing a pointer to the first element of GLchar, so we can pass a pointer directly to it
theCode[0] = shaderCode;
GLint codeLength[1]; // Hold the length of the code
codeLength[0] = strlen(shaderCode);
glShaderSource(theShader, 1, theCode, codeLength); // Now the empty shader is modified to have this code
glCompileShader(theShader); // compile the shader
GLint result = 0;
GLchar eLog[1024] = { 0 };
// Error check before attach the shader to the program
glGetShaderiv(theShader, GL_COMPILE_STATUS, &result);
if (!result)
{
glGetShaderInfoLog(theShader, sizeof(eLog), NULL, eLog);
printf("Error compiling the %d shader: '%s'\n", shaderType, eLog);
return;
}
glAttachShader(theProgram, theShader);
}
/*!
*/
void CompileShaders() {
shader = glCreateProgram(); // Create the program and give shader the ID, then we can modify the program and do something with it
// Make sure the shader was created correctly
if (!shader) {
printf("Error creating shader program!\n");
return;
}
//Add the shaders to the program using the function
AddShader(shader, vShader, GL_VERTEX_SHADER);
AddShader(shader, fShader, GL_FRAGMENT_SHADER);
// Getting the error codes, with the results of the two functions we are about to perform and a place to put the errors
GLint result = 0;
GLchar eLog[1024] = { 0 };
glLinkProgram(shader); // Create the exectables in the graphics card.
// Make sure the program is linked properlly
glGetProgramiv(shader, GL_LINK_STATUS, &result);
if (!result)
{
glGetProgramInfoLog(shader, sizeof(eLog), NULL, eLog); // Getting the InfoLog for this program, passing the eLog char array and then print
printf("Error linking program: '%s'\n", eLog);
return;
}
// Validating if the program is valid on the current context that OpenGL is working
glValidateProgram(shader);
glGetProgramiv(shader, GL_VALIDATE_STATUS, &result);
if (!result)
{
glGetProgramInfoLog(shader, sizeof(eLog), NULL, eLog);
printf("Error validating program: '%s'\n", eLog);
return;
}
// Get the actual ID or location of the uniform variable
uniformXMove = glGetUniformLocation(shader, "xMove");
}
int main()
{
//Initialize GLFW
if (!glfwInit()) {
std::cout << "GLFW Initialization failed!";
glfwTerminate();
}
//Setup GLFW window properties
//OpenGL version
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
//Core Profile = No Backwards Compatibility
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
//Allow forward compatibility
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
GLFWwindow* mainWindow = glfwCreateWindow(WIDTH, HEIGHT, "Test Window", NULL, NULL);
if (!mainWindow) {
std::cout << " GLFW window creation failed!";
glfwTerminate();
}
// Get Buffer size information
int bufferWidth, bufferHeight;
glfwGetFramebufferSize(mainWindow, &bufferWidth, &bufferHeight);
//Set the context for GLEW to use
glfwMakeContextCurrent(mainWindow);
//Allow modern extension features
glewExperimental = GL_TRUE;
if (glewInit() != GLEW_OK) {
std::cout << "GLEW Initialization failed!";
glfwDestroyWindow(mainWindow);
glfwTerminate();
}
//Setup viewport size
glViewport(0, 0, bufferWidth, bufferHeight);
CreateTriangle();
CompileShaders();
// LLop until window close
while (!glfwWindowShouldClose(mainWindow)) {
// Get + Handle user input events
glfwPollEvents();
// Checking the direction
if (direction)
{
triOffset += triIncrement;
}
else {
triOffset -= triIncrement;
}
// Check if the location is between max and min offset
if (abs(triOffset) >= triMaxoffset) {
direction = !direction; // Easy way of doing a bool
}
// Clear window
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// Draw the triangle
glUseProgram(shader); // This will now grab that ID, It will go through the graphics card and tell it to use the one with the ID of shader
// Assign the value to the shader itself
// Pass the location in the shader, everytime this goes trough triOffset will be updated,
// It will update the Uniform value and
// that means the shader value being used for xMove will be getting changed everytime this shader runs.
glUniform1f(uniformXMove, triOffset);
glBindVertexArray(VAO); // We saying now work with this VAO
glDrawArrays(GL_TRIANGLES, 0, 3); // draw 3 points starting on the first
glBindVertexArray(0); // Unassign the VAO
glUseProgram(0); // Unassign the shader
glfwSwapBuffers(mainWindow);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
# GLM
To install GLM go to the github, and download the latest non alpha version.
Extract the file and delete all folder except the glm folder.
Copy the folder into the External_Libraries folder and add on the project in visual studio code, at C/C++ Additional Include Directories.
Done!
# Transforming Translation
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <cmath>
#include <GL\glew.h>
#include <GLFW\glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
//Window dimensions
const GLint WIDTH = 800, HEIGHT = 600;
GLuint VAO, VBO, shader, uniformModel;
bool direction = true;
float triOffset = 0.0f;
float triMaxoffset = 0.7f;
float triIncrement = 0.02f;
// Vertex Shader
static const char* vShader = " \n\
#version 410 \n\
\n\
layout (location = 0) in vec3 pos; \n\
\n\
uniform mat4 model; \n\
\n\
void main() \n\
{ \n\
gl_Position = model * vec4(0.4 * pos.x, 0.4 * pos.y, pos.z, 1.0); \n\
}";
static const char* fShader = " \n\
#version 410 \n\
\n\
out vec4 colour; \n\
\n\
layout (location = 0) in vec3 pos; \n\
\n\
void main() \n\
{ \n\
colour = vec4(0.0, 1.0, 0.0, 1.0); \n\
}";
/*!
Here we are going to create a VAO (Vertex Array Object) and a VBO (Vertex Buffer Object)
The VAO will hold multiples VBOs and other types of buffer to define how a triangle should be drawn,
In this funciton is just the vertices of the triangles.
*/
void CreateTriangle()
{
// Triangle Points in an array of floats (The center of the screen is 0,0)
GLfloat vertices[] = {
-1.0f, -1.0f, 0.0f,
1.0f, -1.0f, 0.0f,
0.0f, 1.0f, 0.0f
};
/*
Creating a VAO
Takes the amount of arrays we want to create and where to store the ID of the array.
When we are calling this function on the graphics card is now creating a vertex array, its defining some space in the memory for some vertex array.
So if you want to access that space in the memory you just pass the ID(VAO)).
*/
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
// Now that we have a VAO we need to create a buffer object to actually go inside of that.
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO); //We have to enter which buffer we want to bind, as VBO has multiple targets it can bind to.
// Now we have to connect the buffer data, the vertices that we created to the GL_BUFFER for this VBO.
// GL_STATIC_DRAW means we are not going to be changing the values we are putting in there, GL_DYNAMIC_DRAW is more complex.
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); // First attribute is location of the attribute, then amount of values(x,y,z), then type, then if normalizing(we are not normalizing), then striding value(to jump 3 or whatever to read next value), then offset)
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0); // undoing the binding in the VBO
glBindVertexArray(0); //undoing the binding in the VAO
}
/*!
Add the shaders to the program funtion
*/
void AddShader(GLuint theProgram, const char* shaderCode, GLenum shaderType)
{
GLuint theShader = glCreateShader(shaderType); // Creating an empty shader for that type
const GLchar* theCode[1]; // Passing a pointer to the first element of GLchar, so we can pass a pointer directly to it
theCode[0] = shaderCode;
GLint codeLength[1]; // Hold the length of the code
codeLength[0] = strlen(shaderCode);
glShaderSource(theShader, 1, theCode, codeLength); // Now the empty shader is modified to have this code
glCompileShader(theShader); // compile the shader
GLint result = 0;
GLchar eLog[1024] = { 0 };
// Error check before attach the shader to the program
glGetShaderiv(theShader, GL_COMPILE_STATUS, &result);
if (!result)
{
glGetShaderInfoLog(theShader, sizeof(eLog), NULL, eLog);
printf("Error compiling the %d shader: '%s'\n", shaderType, eLog);
return;
}
glAttachShader(theProgram, theShader);
}
/*!
*/
void CompileShaders() {
shader = glCreateProgram(); // Create the program and give shader the ID, then we can modify the program and do something with it
// Make sure the shader was created correctly
if (!shader) {
printf("Error creating shader program!\n");
return;
}
//Add the shaders to the program using the function
AddShader(shader, vShader, GL_VERTEX_SHADER);
AddShader(shader, fShader, GL_FRAGMENT_SHADER);
// Getting the error codes, with the results of the two functions we are about to perform and a place to put the errors
GLint result = 0;
GLchar eLog[1024] = { 0 };
glLinkProgram(shader); // Create the exectables in the graphics card.
// Make sure the program is linked properlly
glGetProgramiv(shader, GL_LINK_STATUS, &result);
if (!result)
{
glGetProgramInfoLog(shader, sizeof(eLog), NULL, eLog); // Getting the InfoLog for this program, passing the eLog char array and then print
printf("Error linking program: '%s'\n", eLog);
return;
}
// Validating if the program is valid on the current context that OpenGL is working
glValidateProgram(shader);
glGetProgramiv(shader, GL_VALIDATE_STATUS, &result);
if (!result)
{
glGetProgramInfoLog(shader, sizeof(eLog), NULL, eLog);
printf("Error validating program: '%s'\n", eLog);
return;
}
// Get the actual ID or location of the uniform variable
uniformModel = glGetUniformLocation(shader, "model"); // uniformModel will be the location of the model matrix
}
int main()
{
//Initialize GLFW
if (!glfwInit()) {
std::cout << "GLFW Initialization failed!";
glfwTerminate();
}
//Setup GLFW window properties
//OpenGL version
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
//Core Profile = No Backwards Compatibility
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
//Allow forward compatibility
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
GLFWwindow* mainWindow = glfwCreateWindow(WIDTH, HEIGHT, "Test Window", NULL, NULL);
if (!mainWindow) {
std::cout << " GLFW window creation failed!";
glfwTerminate();
}
// Get Buffer size information
int bufferWidth, bufferHeight;
glfwGetFramebufferSize(mainWindow, &bufferWidth, &bufferHeight);
//Set the context for GLEW to use
glfwMakeContextCurrent(mainWindow);
//Allow modern extension features
glewExperimental = GL_TRUE;
if (glewInit() != GLEW_OK) {
std::cout << "GLEW Initialization failed!";
glfwDestroyWindow(mainWindow);
glfwTerminate();
}
//Setup viewport size
glViewport(0, 0, bufferWidth, bufferHeight);
CreateTriangle();
CompileShaders();
// LLop until window close
while (!glfwWindowShouldClose(mainWindow)) {
// Get + Handle user input events
glfwPollEvents();
// Checking the direction
if (direction)
{
triOffset += triIncrement;
}
else {
triOffset -= triIncrement;
}
// Check if the location is between max and min offset
if (abs(triOffset) >= triMaxoffset) {
direction = !direction; // Easy way of doing a bool
}
// Clear window
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// Draw the triangle
glUseProgram(shader); // This will now grab that ID, It will go through the graphics card and tell it to use the one with the ID of shader
glm::mat4 model(1.0f); // Remenber, You have to initialize the model matrix variable glm::mat4 model with (1.0f) for a identity matrix
model = glm::translate(model, glm::vec3(triOffset, 0.0f, 0.0f)); // Applying a translation that will only be changing the x value.
// First is the location, then quantity, then we don't want to transpose
// Last we pass the value pointer, because the model is not exactly in a format that will work with the shader
glUniformMatrix4fv(uniformModel, 1, GL_FALSE, glm::value_ptr(model));
// Now we modify the shaders to handle the new values
glBindVertexArray(VAO); // We saying now work with this VAO
glDrawArrays(GL_TRIANGLES, 0, 3); // draw 3 points starting on the first
glBindVertexArray(0); // Unassign the VAO
glUseProgram(0); // Unassign the shader
glfwSwapBuffers(mainWindow);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
# Checking for Errors in OpenGL
To check for error we define the ASSERT
macro and the functions GLClearError
and GLLogCall
, another macro GLCall(x)
will call the function GLClearError
to make sure the error comes from the function enveloped and then will ASSERT
the GLLogCall
.
Like shown below:
#define ASSERT(x) if (!(x)) __debugbreak();
#define GLCall(x) GLClearError();\
x;\
ASSERT(GLLogCall(#x, __FILE__, __LINE__))
//Checking for errors
static void GLClearError()
{
while (glGetError() != GL_NO_ERROR);
}
static bool GLLogCall(const char* function, const char* file, int line)
{
while (GLenum error = glGetError())
{
std::cout << "[OpenGL Error] (" << error << ")" << function <<
" " << file << ":" << line << std::endl;
return false;
}
return true;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Then envelope any GL function in GLCall(), like so:
GLCall(uniformModel = glGetUniformLocation(shader, "model"));
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <cmath>
#include <GL\glew.h>
#include <GLFW\glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <fstream>
#include <sstream>
#define ASSERT(x) if (!(x)) __debugbreak();
#define GLCall(x) GLClearError();\
x;\
ASSERT(GLLogCall(#x, __FILE__, __LINE__))
//Checking for errors
static void GLClearError()
{
while (glGetError() != GL_NO_ERROR);
}
static bool GLLogCall(const char* function, const char* file, int line)
{
while (GLenum error = glGetError())
{
std::cout << "[OpenGL Error] (" << error << ")" << function <<
" " << file << ":" << line << std::endl;
return false;
}
return true;
}
//Window dimensions
const GLint WIDTH = 800, HEIGHT = 600;
GLuint VAO, VBO, shader, uniformModel;
bool direction = true;
float triOffset = 0.0f;
float triMaxoffset = 0.7f;
float triIncrement = 0.02f;
// Vertex Shader
static const char* vShader = " \n\
#version 410 \n\
\n\
layout (location = 0) in vec3 pos; \n\
\n\
uniform mat4 model; \n\
\n\
void main() \n\
{ \n\
gl_Position = model * vec4(0.4 * pos.x, 0.4 * pos.y, 0.4 * pos.z, 1.0); \n\
}";
static const char* fShader = " \n\
#version 410 \n\
\n\
out vec4 colour; \n\
\n\
layout (location = 0) in vec3 pos; \n\
\n\
void main() \n\
{ \n\
colour = vec4(0.0, 1.0, 0.0, 1.0); \n\
}";
/*!
Here we are going to create a VAO (Vertex Array Object) and a VBO (Vertex Buffer Object)
The VAO will hold multiples VBOs and other types of buffer to define how a triangle should be drawn,
In this funciton is just the vertices of the triangles.
*/
void CreateTriangle()
{
// Triangle Points in an array of floats (The center of the screen is 0,0)
GLfloat vertices[] = {
-1.0f, -1.0f, 0.0f,
1.0f, -1.0f, 0.0f,
0.0f, 1.0f, 0.0f
};
/*
Creating a VAO
Takes the amount of arrays we want to create and where to store the ID of the array.
When we are calling this function on the graphics card is now creating a vertex array, its defining some space in the memory for some vertex array.
So if you want to access that space in the memory you just pass the ID(VAO)).
*/
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
// Now that we have a VAO we need to create a buffer object to actually go inside of that.
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO); //We have to enter which buffer we want to bind, as VBO has multiple targets it can bind to.
// Now we have to connect the buffer data, the vertices that we created to the GL_BUFFER for this VBO.
// GL_STATIC_DRAW means we are not going to be changing the values we are putting in there, GL_DYNAMIC_DRAW is more complex.
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); // First attribute is location of the attribute, then amount of values(x,y,z), then type, then if normalizing(we are not normalizing), then striding value(to jump 3 or whatever to read next value), then offset)
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0); // undoing the binding in the VBO
glBindVertexArray(0); //undoing the binding in the VAO
}
/*!
Add the shaders to the program funtion
*/
void AddShader(GLuint theProgram, const char* shaderCode, GLenum shaderType)
{
GLuint theShader = glCreateShader(shaderType); // Creating an empty shader for that type
const GLchar* theCode[1]; // Passing a pointer to the first element of GLchar, so we can pass a pointer directly to it
theCode[0] = shaderCode;
GLint codeLength[1]; // Hold the length of the code
codeLength[0] = strlen(shaderCode);
glShaderSource(theShader, 1, theCode, codeLength); // Now the empty shader is modified to have this code
glCompileShader(theShader); // compile the shader
GLint result = 0;
GLchar eLog[1024] = { 0 };
// Error check before attach the shader to the program
glGetShaderiv(theShader, GL_COMPILE_STATUS, &result);
if (!result)
{
glGetShaderInfoLog(theShader, sizeof(eLog), NULL, eLog);
printf("Error compiling the %d shader: '%s'\n", shaderType, eLog);
return;
}
glAttachShader(theProgram, theShader);
}
/*!
*/
void CompileShaders() {
shader = glCreateProgram(); // Create the program and give shader the ID, then we can modify the program and do something with it
// Make sure the shader was created correctly
if (!shader) {
printf("Error creating shader program!\n");
return;
}
//Add the shaders to the program using the function
AddShader(shader, vShader, GL_VERTEX_SHADER);
AddShader(shader, fShader, GL_FRAGMENT_SHADER);
// Getting the error codes, with the results of the two functions we are about to perform and a place to put the errors
GLint result = 0;
GLchar eLog[1024] = { 0 };
glLinkProgram(shader); // Create the exectables in the graphics card.
// Make sure the program is linked properlly
glGetProgramiv(shader, GL_LINK_STATUS, &result);
if (!result)
{
glGetProgramInfoLog(shader, sizeof(eLog), NULL, eLog); // Getting the InfoLog for this program, passing the eLog char array and then print
printf("Error linking program: '%s'\n", eLog);
return;
}
// Validating if the program is valid on the current context that OpenGL is working
glValidateProgram(shader);
glGetProgramiv(shader, GL_VALIDATE_STATUS, &result);
if (!result)
{
glGetProgramInfoLog(shader, sizeof(eLog), NULL, eLog);
printf("Error validating program: '%s'\n", eLog);
return;
}
// Get the actual ID or location of the uniform variable
uniformModel = glGetUniformLocation(shader, "model"); // uniformModel will be the location of the model matrix
}
int main()
{
//Initialize GLFW
if (!glfwInit()) {
std::cout << "GLFW Initialization failed!";
glfwTerminate();
}
//Setup GLFW window properties
//OpenGL version
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
//Core Profile = No Backwards Compatibility
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
//Allow forward compatibility
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
GLFWwindow* mainWindow = glfwCreateWindow(WIDTH, HEIGHT, "Test Window", NULL, NULL);
if (!mainWindow) {
std::cout << " GLFW window creation failed!";
glfwTerminate();
}
// Get Buffer size information
int bufferWidth, bufferHeight;
glfwGetFramebufferSize(mainWindow, &bufferWidth, &bufferHeight);
//Set the context for GLEW to use
glfwMakeContextCurrent(mainWindow);
//Allow modern extension features
glewExperimental = GL_TRUE;
if (glewInit() != GLEW_OK) {
std::cout << "GLEW Initialization failed!";
glfwDestroyWindow(mainWindow);
glfwTerminate();
}
//Setup viewport size
glViewport(0, 0, bufferWidth, bufferHeight);
CreateTriangle();
CompileShaders();
// LLop until window close
while (!glfwWindowShouldClose(mainWindow)) {
// Get + Handle user input events
glfwPollEvents();
// Checking the direction
if (direction)
{
triOffset += triIncrement;
}
else {
triOffset -= triIncrement;
}
// Check if the location is between max and min offset
if (abs(triOffset) >= triMaxoffset) {
direction = !direction; // Easy way of doing a bool
}
// Clear window
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// Draw the triangle
glUseProgram(shader); // This will now grab that ID, It will go through the graphics card and tell it to use the one with the ID of shader
glm::mat4 model(1.0f);
model = glm::translate(model, glm::vec3(triOffset, 0.0f, 0.0f)); // Applying a translation that will only be changing the x value.
// First is the location, then quantity, then we don't want to transpose
// Last we pass the value pointer, because the model is not exactly in a format that will work with the shader
glUniformMatrix4fv(uniformModel, 1, GL_FALSE, glm::value_ptr(model));
// Now we modify the shaders to handle the new values
glBindVertexArray(VAO); // We saying now work with this VAO
glDrawArrays(GL_TRIANGLES, 0, 3); // draw 3 points starting on the first
glBindVertexArray(0); // Unassign the VAO
glUseProgram(0); // Unassign the shader
glfwSwapBuffers(mainWindow);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
# Transforming Rotation
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <cmath>
#include <GL\glew.h>
#include <GLFW\glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <fstream>
#include <sstream>
#define ASSERT(x) if (!(x)) __debugbreak();
#define GLCall(x) GLClearError();\
x;\
ASSERT(GLLogCall(#x, __FILE__, __LINE__))
//Checking for errors
static void GLClearError()
{
while (glGetError() != GL_NO_ERROR);
}
static bool GLLogCall(const char* function, const char* file, int line)
{
while (GLenum error = glGetError())
{
std::cout << "[OpenGL Error] (" << error << ")" << function <<
" " << file << ":" << line << std::endl;
return false;
}
return true;
}
//Window dimensions
const GLint WIDTH = 800, HEIGHT = 600;
const float toRadians = 3.14159265f / 180.0f;
GLuint VAO, VBO, shader, uniformModel;
bool direction = true;
float triOffset = 0.0f;
float triMaxoffset = 0.7f;
float triIncrement = 0.02f;
float curAngle = 0.0f;
// Vertex Shader
static const char* vShader = " \n\
#version 410 \n\
\n\
layout (location = 0) in vec3 pos; \n\
\n\
uniform mat4 model; \n\
\n\
void main() \n\
{ \n\
gl_Position = model * vec4(0.4 * pos.x, 0.4 * pos.y, 0.4 * pos.z, 1.0); \n\
}";
static const char* fShader = " \n\
#version 410 \n\
\n\
out vec4 colour; \n\
\n\
layout (location = 0) in vec3 pos; \n\
\n\
void main() \n\
{ \n\
colour = vec4(0.0, 1.0, 0.0, 1.0); \n\
}";
/*!
Here we are going to create a VAO (Vertex Array Object) and a VBO (Vertex Buffer Object)
The VAO will hold multiples VBOs and other types of buffer to define how a triangle should be drawn,
In this funciton is just the vertices of the triangles.
*/
void CreateTriangle()
{
// Triangle Points in an array of floats (The center of the screen is 0,0)
GLfloat vertices[] = {
-1.0f, -1.0f, 0.0f,
1.0f, -1.0f, 0.0f,
0.0f, 1.0f, 0.0f
};
/*
Creating a VAO
Takes the amount of arrays we want to create and where to store the ID of the array.
When we are calling this function on the graphics card is now creating a vertex array, its defining some space in the memory for some vertex array.
So if you want to access that space in the memory you just pass the ID(VAO)).
*/
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
// Now that we have a VAO we need to create a buffer object to actually go inside of that.
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO); //We have to enter which buffer we want to bind, as VBO has multiple targets it can bind to.
// Now we have to connect the buffer data, the vertices that we created to the GL_BUFFER for this VBO.
// GL_STATIC_DRAW means we are not going to be changing the values we are putting in there, GL_DYNAMIC_DRAW is more complex.
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); // First attribute is location of the attribute, then amount of values(x,y,z), then type, then if normalizing(we are not normalizing), then striding value(to jump 3 or whatever to read next value), then offset)
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0); // undoing the binding in the VBO
glBindVertexArray(0); //undoing the binding in the VAO
}
/*!
Add the shaders to the program funtion
*/
void AddShader(GLuint theProgram, const char* shaderCode, GLenum shaderType)
{
GLuint theShader = glCreateShader(shaderType); // Creating an empty shader for that type
const GLchar* theCode[1]; // Passing a pointer to the first element of GLchar, so we can pass a pointer directly to it
theCode[0] = shaderCode;
GLint codeLength[1]; // Hold the length of the code
codeLength[0] = strlen(shaderCode);
glShaderSource(theShader, 1, theCode, codeLength); // Now the empty shader is modified to have this code
glCompileShader(theShader); // compile the shader
GLint result = 0;
GLchar eLog[1024] = { 0 };
// Error check before attach the shader to the program
glGetShaderiv(theShader, GL_COMPILE_STATUS, &result);
if (!result)
{
glGetShaderInfoLog(theShader, sizeof(eLog), NULL, eLog);
printf("Error compiling the %d shader: '%s'\n", shaderType, eLog);
return;
}
glAttachShader(theProgram, theShader);
}
/*!
*/
void CompileShaders() {
shader = glCreateProgram(); // Create the program and give shader the ID, then we can modify the program and do something with it
// Make sure the shader was created correctly
if (!shader) {
printf("Error creating shader program!\n");
return;
}
//Add the shaders to the program using the function
AddShader(shader, vShader, GL_VERTEX_SHADER);
AddShader(shader, fShader, GL_FRAGMENT_SHADER);
// Getting the error codes, with the results of the two functions we are about to perform and a place to put the errors
GLint result = 0;
GLchar eLog[1024] = { 0 };
glLinkProgram(shader); // Create the exectables in the graphics card.
// Make sure the program is linked properlly
glGetProgramiv(shader, GL_LINK_STATUS, &result);
if (!result)
{
glGetProgramInfoLog(shader, sizeof(eLog), NULL, eLog); // Getting the InfoLog for this program, passing the eLog char array and then print
printf("Error linking program: '%s'\n", eLog);
return;
}
// Validating if the program is valid on the current context that OpenGL is working
glValidateProgram(shader);
glGetProgramiv(shader, GL_VALIDATE_STATUS, &result);
if (!result)
{
glGetProgramInfoLog(shader, sizeof(eLog), NULL, eLog);
printf("Error validating program: '%s'\n", eLog);
return;
}
// Get the actual ID or location of the uniform variable
uniformModel = glGetUniformLocation(shader, "model"); // uniformModel will be the location of the model matrix
}
int main()
{
//Initialize GLFW
if (!glfwInit()) {
std::cout << "GLFW Initialization failed!";
glfwTerminate();
}
//Setup GLFW window properties
//OpenGL version
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
//Core Profile = No Backwards Compatibility
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
//Allow forward compatibility
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
GLFWwindow* mainWindow = glfwCreateWindow(WIDTH, HEIGHT, "Test Window", NULL, NULL);
if (!mainWindow) {
std::cout << " GLFW window creation failed!";
glfwTerminate();
}
// Get Buffer size information
int bufferWidth, bufferHeight;
glfwGetFramebufferSize(mainWindow, &bufferWidth, &bufferHeight);
//Set the context for GLEW to use
glfwMakeContextCurrent(mainWindow);
//Allow modern extension features
glewExperimental = GL_TRUE;
if (glewInit() != GLEW_OK) {
std::cout << "GLEW Initialization failed!";
glfwDestroyWindow(mainWindow);
glfwTerminate();
}
//Setup viewport size
glViewport(0, 0, bufferWidth, bufferHeight);
CreateTriangle();
CompileShaders();
// LLop until window close
while (!glfwWindowShouldClose(mainWindow)) {
// Get + Handle user input events
glfwPollEvents();
// Checking the direction
if (direction)
{
triOffset += triIncrement;
}
else {
triOffset -= triIncrement;
}
// Check if the location is between max and min offset
if (abs(triOffset) >= triMaxoffset) {
direction = !direction; // Easy way of doing a bool
}
curAngle += 1.0f;
if (curAngle >= 360)
{
curAngle -= 360;
}
// Clear window
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// Draw the triangle
glUseProgram(shader); // This will now grab that ID, It will go through the graphics card and tell it to use the one with the ID of shader
glm::mat4 model(1.0f); // Remenber, You have to initialize the model matrix variable glm::mat4 model with (1.0f) for a identity matrix
//model = glm::translate(model, glm::vec3(triOffset, 0.0f, 0.0f)); // Applying a translation that will only be changing the x value.
model = glm::rotate(model, curAngle * toRadians, glm::vec3(0.0f, 0.0f, 1.0f));
// First is the location, then quantity, then we don't want to transpose
// Last we pass the value pointer, because the model is not exactly in a format that will work with the shader
glUniformMatrix4fv(uniformModel, 1, GL_FALSE, glm::value_ptr(model));
// Now we modify the shaders to handle the new values
glBindVertexArray(VAO); // We saying now work with this VAO
glDrawArrays(GL_TRIANGLES, 0, 3); // draw 3 points starting on the first
glBindVertexArray(0); // Unassign the VAO
glUseProgram(0); // Unassign the shader
glfwSwapBuffers(mainWindow);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
# Transform Scale
For scaling just add:
model = glm::scale(model, glm::vec3(2.0f, 2.0f, 1.0f));
and this will substitute the 0.4 we included in the shaders:
gl_Position = model * vec4(0.4 * pos.x, 0.4 * pos.y, 0.4 * pos.z, 1.0); \n\
to be like this:
gl_Position = model * vec4(pos.x, pos.y, 0.4 * pos.z, 1.0); \n\
and simplifying even more we can do only the pos, since it's a vec3, and we have our simplified shader:
// Vertex Shader
static const char* vShader = " \n\
#version 410 \n\
\n\
layout (location = 0) in vec3 pos; \n\
\n\
uniform mat4 model; \n\
\n\
void main() \n\
{ \n\
gl_Position = model * vec4(pos, 1.0); \n\
}";
2
3
4
5
6
7
8
9
10
11
12
Complete program with triangle scaling up and down:
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <cmath>
#include <GL\glew.h>
#include <GLFW\glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <fstream>
#include <sstream>
#define ASSERT(x) if (!(x)) __debugbreak();
#define GLCall(x) GLClearError();\
x;\
ASSERT(GLLogCall(#x, __FILE__, __LINE__))
//Checking for errors
static void GLClearError()
{
while (glGetError() != GL_NO_ERROR);
}
static bool GLLogCall(const char* function, const char* file, int line)
{
while (GLenum error = glGetError())
{
std::cout << "[OpenGL Error] (" << error << ")" << function <<
" " << file << ":" << line << std::endl;
return false;
}
return true;
}
//Window dimensions
const GLint WIDTH = 800, HEIGHT = 600;
const float toRadians = 3.14159265f / 180.0f;
GLuint VAO, VBO, shader, uniformModel;
bool direction = true;
float triOffset = 0.0f;
float triMaxoffset = 0.7f;
float triIncrement = 0.02f;
float curAngle = 0.0f;
bool sizeDirection = true;
float curSize = 0.4f;
float maxSize = 0.8f;
float minSize = 0.1f;
// Vertex Shader
static const char* vShader = " \n\
#version 410 \n\
\n\
layout (location = 0) in vec3 pos; \n\
\n\
uniform mat4 model; \n\
\n\
void main() \n\
{ \n\
gl_Position = model * vec4(pos, 1.0); \n\
}";
static const char* fShader = " \n\
#version 410 \n\
\n\
out vec4 colour; \n\
\n\
layout (location = 0) in vec3 pos; \n\
\n\
void main() \n\
{ \n\
colour = vec4(0.0, 1.0, 0.0, 1.0); \n\
}";
/*!
Here we are going to create a VAO (Vertex Array Object) and a VBO (Vertex Buffer Object)
The VAO will hold multiples VBOs and other types of buffer to define how a triangle should be drawn,
In this funciton is just the vertices of the triangles.
*/
void CreateTriangle()
{
// Triangle Points in an array of floats (The center of the screen is 0,0)
GLfloat vertices[] = {
-1.0f, -1.0f, 0.0f,
1.0f, -1.0f, 0.0f,
0.0f, 1.0f, 0.0f
};
/*
Creating a VAO
Takes the amount of arrays we want to create and where to store the ID of the array.
When we are calling this function on the graphics card is now creating a vertex array, its defining some space in the memory for some vertex array.
So if you want to access that space in the memory you just pass the ID(VAO)).
*/
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
// Now that we have a VAO we need to create a buffer object to actually go inside of that.
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO); //We have to enter which buffer we want to bind, as VBO has multiple targets it can bind to.
// Now we have to connect the buffer data, the vertices that we created to the GL_BUFFER for this VBO.
// GL_STATIC_DRAW means we are not going to be changing the values we are putting in there, GL_DYNAMIC_DRAW is more complex.
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); // First attribute is location of the attribute, then amount of values(x,y,z), then type, then if normalizing(we are not normalizing), then striding value(to jump 3 or whatever to read next value), then offset)
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0); // undoing the binding in the VBO
glBindVertexArray(0); //undoing the binding in the VAO
}
/*!
Add the shaders to the program funtion
*/
void AddShader(GLuint theProgram, const char* shaderCode, GLenum shaderType)
{
GLuint theShader = glCreateShader(shaderType); // Creating an empty shader for that type
const GLchar* theCode[1]; // Passing a pointer to the first element of GLchar, so we can pass a pointer directly to it
theCode[0] = shaderCode;
GLint codeLength[1]; // Hold the length of the code
codeLength[0] = strlen(shaderCode);
glShaderSource(theShader, 1, theCode, codeLength); // Now the empty shader is modified to have this code
glCompileShader(theShader); // compile the shader
GLint result = 0;
GLchar eLog[1024] = { 0 };
// Error check before attach the shader to the program
glGetShaderiv(theShader, GL_COMPILE_STATUS, &result);
if (!result)
{
glGetShaderInfoLog(theShader, sizeof(eLog), NULL, eLog);
printf("Error compiling the %d shader: '%s'\n", shaderType, eLog);
return;
}
glAttachShader(theProgram, theShader);
}
/*!
*/
void CompileShaders() {
shader = glCreateProgram(); // Create the program and give shader the ID, then we can modify the program and do something with it
// Make sure the shader was created correctly
if (!shader) {
printf("Error creating shader program!\n");
return;
}
//Add the shaders to the program using the function
AddShader(shader, vShader, GL_VERTEX_SHADER);
AddShader(shader, fShader, GL_FRAGMENT_SHADER);
// Getting the error codes, with the results of the two functions we are about to perform and a place to put the errors
GLint result = 0;
GLchar eLog[1024] = { 0 };
glLinkProgram(shader); // Create the exectables in the graphics card.
// Make sure the program is linked properlly
glGetProgramiv(shader, GL_LINK_STATUS, &result);
if (!result)
{
glGetProgramInfoLog(shader, sizeof(eLog), NULL, eLog); // Getting the InfoLog for this program, passing the eLog char array and then print
printf("Error linking program: '%s'\n", eLog);
return;
}
// Validating if the program is valid on the current context that OpenGL is working
glValidateProgram(shader);
glGetProgramiv(shader, GL_VALIDATE_STATUS, &result);
if (!result)
{
glGetProgramInfoLog(shader, sizeof(eLog), NULL, eLog);
printf("Error validating program: '%s'\n", eLog);
return;
}
// Get the actual ID or location of the uniform variable
uniformModel = glGetUniformLocation(shader, "model"); // uniformModel will be the location of the model matrix
}
int main()
{
//Initialize GLFW
if (!glfwInit()) {
std::cout << "GLFW Initialization failed!";
glfwTerminate();
}
//Setup GLFW window properties
//OpenGL version
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
//Core Profile = No Backwards Compatibility
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
//Allow forward compatibility
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
GLFWwindow* mainWindow = glfwCreateWindow(WIDTH, HEIGHT, "Test Window", NULL, NULL);
if (!mainWindow) {
std::cout << " GLFW window creation failed!";
glfwTerminate();
}
// Get Buffer size information
int bufferWidth, bufferHeight;
glfwGetFramebufferSize(mainWindow, &bufferWidth, &bufferHeight);
//Set the context for GLEW to use
glfwMakeContextCurrent(mainWindow);
//Allow modern extension features
glewExperimental = GL_TRUE;
if (glewInit() != GLEW_OK) {
std::cout << "GLEW Initialization failed!";
glfwDestroyWindow(mainWindow);
glfwTerminate();
}
//Setup viewport size
glViewport(0, 0, bufferWidth, bufferHeight);
CreateTriangle();
CompileShaders();
// LLop until window close
while (!glfwWindowShouldClose(mainWindow)) {
// Get + Handle user input events
glfwPollEvents();
// Checking the direction
if (direction)
{
triOffset += triIncrement;
}
else {
triOffset -= triIncrement;
}
// Check if the location is between max and min offset
if (abs(triOffset) >= triMaxoffset) {
direction = !direction; // Easy way of doing a bool
}
curAngle += 1.0f;
if (curAngle >= 360)
{
curAngle -= 360;
}
if (sizeDirection)
{
curSize += 0.001f;
}
else {
curSize -= 0.001f;
}
if (curSize >= maxSize || curSize <= minSize) {
sizeDirection = !sizeDirection;
}
// Clear window
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// Draw the triangle
glUseProgram(shader); // This will now grab that ID, It will go through the graphics card and tell it to use the one with the ID of shader
glm::mat4 model(1.0f); // Remenber, You have to initialize the model matrix variable glm::mat4 model with (1.0f) for a identity matrix
//model = glm::translate(model, glm::vec3(triOffset, 0.0f, 0.0f)); // Applying a translation that will only be changing the x value.
//model = glm::rotate(model, curAngle * toRadians, glm::vec3(0.0f, 0.0f, 1.0f));
model = glm::scale(model, glm::vec3(curSize, curSize, 1.0f));
// First is the location, then quantity, then we don't want to transpose
// Last we pass the value pointer, because the model is not exactly in a format that will work with the shader
glUniformMatrix4fv(uniformModel, 1, GL_FALSE, glm::value_ptr(model));
// Now we modify the shaders to handle the new values
glBindVertexArray(VAO); // We saying now work with this VAO
glDrawArrays(GL_TRIANGLES, 0, 3); // draw 3 points starting on the first
glBindVertexArray(0); // Unassign the VAO
glUseProgram(0); // Unassign the shader
glfwSwapBuffers(mainWindow);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
# Implementing Rotation, Translation and Scaling in QtCreator
CMakeLists.txt
cmake_minimum_required(VERSION 3.8)
project(OpenGLCourse LANGUAGES CXX)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(QT NAMES Qt6 Qt5 COMPONENTS Widgets REQUIRED)
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Widgets REQUIRED)
# Unsed previous parameters from cache for the sake of dynamism
unset(KITGL_SUPPORT_GLFW CACHE)
unset(KITGL_SUPPORT_GLEW CACHE)
###
### Initialization
###
# Project parameters
set(PROJ_LINK_LIBS)
#set(PROJ_SRC)
# Add all files/folders under src folder automatically to the project
#file(GLOB_RECURSE PROJ_SRC src/*.h src/*.cpp)
###
### Options
###
# Unsed previous parameters from cache for the sake of dynamism
unset(KITGL_SUPPORT_GLFW CACHE)
unset(KITGL_SUPPORT_GLAD CACHE)
unset(KITGL_SUPPORT_GLEW CACHE)
unset(KITGL_SUPPORT_FREEGLUT CACHE)
# Enable desired libs
option(KITGL_SUPPORT_GLFW "Set ON/OFF to enable/disable GLFW support." ON)
option(KITGL_SUPPORT_GLAD "Set ON/OFF to enable/disable GLAD support." ON)
option(KITGL_SUPPORT_GLEW "Set ON/OFF to enable/disable GLEW support." ON)
option(KITGL_SUPPORT_FREEGLUT "Set ON/OFF to enable/disable FreeGLUT support." ON)
###
### OpenGL
###
find_package(OpenGL REQUIRED)
include_directories(${OpenGL_INCLUDE_DIRS})
link_directories(${OpenGL_LIBRARY_DIRS})
add_definitions(${OpenGL_DEFINITIONS})
if(NOT OpenGL_FOUND)
message(Error "OpenGL not found")
endif(NOT OpenGL_FOUND)
list(APPEND PROJ_LINK_LIBS ${OPENGL_LIBRARIES})
###
### GLFW
###
#if(KITGL_SUPPORT_GLFW)
# add_subdirectory(libs/glfw)
# include_directories(libs/glfw/include)
# include_directories(libs/glfw/deps)
# list(APPEND PROJ_LINK_LIBS glfw)
#endif(KITGL_SUPPORT_GLFW)
# Disable internal builds
unset(GLFW_BUILD_EXAMPLES CACHE)
unset(GLFW_BUILD_TESTS CACHE)
unset(GLFW_BUILD_DOCS CACHE)
option(GLFW_BUILD_EXAMPLES "Build the GLFW example programs" OFF)
option(GLFW_BUILD_TESTS "Build the GLFW test programs" OFF)
option(GLFW_BUILD_DOCS "Build the GLFW documentation" OFF)
add_subdirectory(libs/glfw)
include_directories(libs/glfw/include)
include_directories(libs/glfw/deps)
list(APPEND PROJ_LINK_LIBS glfw)
###
### GLEW
###
#if(KITGL_SUPPORT_GLEW)
# add_subdirectory(libs/glew/build/cmake)
# include_directories(libs/glew/include)
# list(APPEND PROJ_LINK_LIBS glew)
#endif(KITGL_SUPPORT_GLEW)
# Disable internal builds
unset(BUILD_UTILS CACHE)
option(BUILD_UTILS "utilities" OFF)
add_subdirectory(libs/glew/build/cmake)
include_directories(libs/glew/include)
list(APPEND PROJ_LINK_LIBS glew)
###
### GLM
###
include_directories(/libs/GLM/)
add_executable(OpenGLCourse
main.cpp
widget.cpp
widget.h
widget.ui
)
target_link_libraries(OpenGLCourse PRIVATE Qt${QT_VERSION_MAJOR}::Widgets ${PROJ_LINK_LIBS})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
Main.cpp
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
w.drawOpenGL();
return a.exec();
}
2
3
4
5
6
7
8
9
10
11
Widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <cmath>
#include <GL\glew.h>
#include <GLFW\glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
const GLint WIDTH = 800, HEIGHT = 600;
GLFWwindow* mainWindow = glfwCreateWindow(WIDTH, HEIGHT, "Test Window", NULL, NULL);
GLuint VAO, VBO, shader, uniformModel;
const float toRadians = 3.14159265f / 180.0f;
bool direction = true;
float triOffset = 0.0f;
float triMaxoffset = 0.7f;
float triIncrement = 0.02f;
float curAngle = 0.0f;
bool sizeDirection = true;
float curSize = 0.4f;
float maxSize = 0.8f;
float minSize = 0.1f;
// Vertex Shader
inline static const char* vShader = " \n\
#version 410 \n\
\n\
layout (location = 0) in vec3 pos; \n\
\n\
uniform mat4 model; \n\
\n\
void main() \n\
{ \n\
gl_Position = model * vec4(0.4 * pos.x, 0.4 * pos.y, pos.z, 1.0); \n\
}";
inline static const char* fShader = " \n\
#version 410 \n\
\n\
out vec4 colour; \n\
\n\
layout (location = 0) in vec3 pos; \n\
\n\
void main() \n\
{ \n\
colour = vec4(0.0, 1.0, 0.0, 1.0); \n\
}";
void CreateTriangle();
void AddShader(GLuint theProgram, const char* shaderCode, GLenum shaderType);
void CompileShaders();
int drawOpenGL();
private slots:
void on_horizontalSlider_sliderMoved(int position);
void on_horizontalSlider_2_sliderMoved(int position);
void on_horizontalSlider_3_sliderMoved(int position);
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
Widget.cpp
#include "widget.h"
#include "./ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::CreateTriangle()
{
// Triangle Points in an array of floats (The center of the screen is 0,0)
GLfloat vertices[] = {
-1.0f, -1.0f, 0.0f,
1.0f, -1.0f, 0.0f,
0.0f, 1.0f, 0.0f
};
/*
Creating a VAO
Takes the amount of arrays we want to create and where to store the ID of the array.
When we are calling this function on the graphics card is now creating a vertex array, its defining some space in the memory for some vertex array.
So if you want to access that space in the memory you just pass the ID(VAO)).
*/
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
// Now that we have a VAO we need to create a buffer object to actually go inside of that.
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO); //We have to enter which buffer we want to bind, as VBO has multiple targets it can bind to.
// Now we have to connect the buffer data, the vertices that we created to the GL_BUFFER for this VBO.
// GL_STATIC_DRAW means we are not going to be changing the values we are putting in there, GL_DYNAMIC_DRAW is more complex.
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); // First attribute is location of the attribute, then amount of values(x,y,z), then type, then if normalizing(we are not normalizing), then striding value(to jump 3 or whatever to read next value), then offset)
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0); // undoing the binding in the VBO
glBindVertexArray(0); //undoing the binding in the VAO
}
void Widget::AddShader(GLuint theProgram, const char *shaderCode, GLenum shaderType)
{
GLuint theShader = glCreateShader(shaderType); // Creating an empty shader for that type
const GLchar* theCode[1]; // Passing a pointer to the first element of GLchar, so we can pass a pointer directly to it
theCode[0] = shaderCode;
GLint codeLength[1]; // Hold the length of the code
codeLength[0] = strlen(shaderCode);
glShaderSource(theShader, 1, theCode, codeLength); // Now the empty shader is modified to have this code
glCompileShader(theShader); // compile the shader
GLint result = 0;
GLchar eLog[1024] = { 0 };
// Error check before attach the shader to the program
glGetShaderiv(theShader, GL_COMPILE_STATUS, &result);
if (!result)
{
glGetShaderInfoLog(theShader, sizeof(eLog), NULL, eLog);
printf("Error compiling the %d shader: '%s'\n", shaderType, eLog);
return;
}
glAttachShader(theProgram, theShader);
}
void Widget::CompileShaders()
{
shader = glCreateProgram(); // Create the program and give shader the ID, then we can modify the program and do something with it
// Make sure the shader was created correctly
if (!shader) {
printf("Error creating shader program!\n");
return;
}
//Add the shaders to the program using the function
AddShader(shader, vShader, GL_VERTEX_SHADER);
AddShader(shader, fShader, GL_FRAGMENT_SHADER);
// Getting the error codes, with the results of the two functions we are about to perform and a place to put the errors
GLint result = 0;
GLchar eLog[1024] = { 0 };
glLinkProgram(shader); // Create the exectables in the graphics card.
// Make sure the program is linked properlly
glGetProgramiv(shader, GL_LINK_STATUS, &result);
if (!result)
{
glGetProgramInfoLog(shader, sizeof(eLog), NULL, eLog); // Getting the InfoLog for this program, passing the eLog char array and then print
printf("Error linking program: '%s'\n", eLog);
return;
}
// Validating if the program is valid on the current context that OpenGL is working
glValidateProgram(shader);
glGetProgramiv(shader, GL_VALIDATE_STATUS, &result);
if (!result)
{
glGetProgramInfoLog(shader, sizeof(eLog), NULL, eLog);
printf("Error validating program: '%s'\n", eLog);
return;
}
// Get the actual ID or location of the uniform variable
uniformModel = glGetUniformLocation(shader, "model"); // uniformModel will be the location of the model matrix
}
int Widget::drawOpenGL()
{
//Initialize GLFW
if (!glfwInit()) {
std::cout << "GLFW Initialization failed!";
glfwTerminate();
}
//Setup GLFW window properties
//OpenGL version
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
//Core Profile = No Backwards Compatibility
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
//Allow forward compatibility
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
GLFWwindow* mainWindow = glfwCreateWindow(WIDTH, HEIGHT, "Test Window", NULL, NULL);
if (!mainWindow) {
std::cout << " GLFW window creation failed!";
glfwTerminate();
}
// Get Buffer size information
int bufferWidth, bufferHeight;
glfwGetFramebufferSize(mainWindow, &bufferWidth, &bufferHeight);
//Set the context for GLEW to use
glfwMakeContextCurrent(mainWindow);
//Allow modern extension features
glewExperimental = GL_TRUE;
if (glewInit() != GLEW_OK) {
std::cout << "GLEW Initialization failed!";
glfwDestroyWindow(mainWindow);
glfwTerminate();
}
//Setup viewport size
glViewport(0, 0, bufferWidth, bufferHeight);
CreateTriangle();
CompileShaders();
// LLop until window close
while (!glfwWindowShouldClose(mainWindow)) {
// Get + Handle user input events
glfwPollEvents();
// // Checking the direction
// if (direction)
// {
// triOffset += triIncrement;
// }
// else {
// triOffset -= triIncrement;
// }
// // Check if the location is between max and min offset
// if (abs(triOffset) >= triMaxoffset) {
// direction = !direction; // Easy way of doing a bool
// }
// curAngle += 1.0f;
// if (curAngle >= 360)
// {
// curAngle -= 360;
// }
// if (sizeDirection)
// {
// curSize += 0.001f;
// }
// else {
// curSize -= 0.001f;
// }
// if (curSize >= maxSize || curSize <= minSize) {
// sizeDirection = !sizeDirection;
// }
// Clear window
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// Draw the triangle
glUseProgram(shader); // This will now grab that ID, It will go through the graphics card and tell it to use the one with the ID of shader
glm::mat4 model(1.0f); // Remenber, You have to initialize the model matrix variable glm::mat4 model with (1.0f) for a identity matrix
model = glm::translate(model, glm::vec3(triOffset, 0.0f, 0.0f)); // Applying a translation that will only be changing the x value.
model = glm::rotate(model, curAngle * toRadians, glm::vec3(0.0f, 0.0f, 1.0f));
model = glm::scale(model, glm::vec3(curSize, curSize, 1.0f));
// First is the location, then quantity, then we don't want to transpose
// Last we pass the value pointer, because the model is not exactly in a format that will work with the shader
glUniformMatrix4fv(uniformModel, 1, GL_FALSE, glm::value_ptr(model));
// Now we modify the shaders to handle the new values
glBindVertexArray(VAO); // We saying now work with this VAO
glDrawArrays(GL_TRIANGLES, 0, 3); // draw 3 points starting on the first
glBindVertexArray(0); // Unassign the VAO
glUseProgram(0); // Unassign the shader
glfwSwapBuffers(mainWindow);
}
glfwDestroyWindow(mainWindow);
glfwTerminate();
return 0;
}
void Widget::on_horizontalSlider_sliderMoved(int position)
{
triOffset = float(position)/100;
ui->TriOffsetlabel->setText(QString::number(triOffset));
ui->Sliderlabel->setText(QString::number(position));
}
void Widget::on_horizontalSlider_2_sliderMoved(int position)
{
curAngle = position;
ui->TriOffsetlabel_2->setText(QString::number(curAngle));
ui->Sliderlabel_2->setText(QString::number(position));
}
void Widget::on_horizontalSlider_3_sliderMoved(int position)
{
curSize = float(position)/100;
ui->TriOffsetlabel_3->setText(QString::number(curSize));
ui->Sliderlabel_3->setText(QString::number(position));
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
File Structure:
# Interpolation, Indexed Draws and Projections
# Interpolation
An important concept is that we can get a variable out of the shader.
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <cmath>
#include <GL\glew.h>
#include <GLFW\glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <fstream>
#include <sstream>
#define ASSERT(x) if (!(x)) __debugbreak();
#define GLCall(x) GLClearError();\
x;\
ASSERT(GLLogCall(#x, __FILE__, __LINE__))
//Checking for errors
static void GLClearError()
{
while (glGetError() != GL_NO_ERROR);
}
static bool GLLogCall(const char* function, const char* file, int line)
{
while (GLenum error = glGetError())
{
std::cout << "[OpenGL Error] (" << error << ")" << function <<
" " << file << ":" << line << std::endl;
return false;
}
return true;
}
//Window dimensions
const GLint WIDTH = 800, HEIGHT = 600;
const float toRadians = 3.14159265f / 180.0f;
GLuint VAO, VBO, shader, uniformModel;
bool direction = true;
float triOffset = 0.0f;
float triMaxoffset = 0.7f;
float triIncrement = 0.02f;
float curAngle = 0.0f;
bool sizeDirection = true;
float curSize = 0.4f;
float maxSize = 0.8f;
float minSize = 0.1f;
// Vertex Shader
static const char* vShader = " \n\
#version 410 \n\
\n\
out vec4 vCol; \n\
\n\
layout (location = 0) in vec3 pos; \n\
\n\
uniform mat4 model; \n\
\n\
void main() \n\
{ \n\
gl_Position = model * vec4(pos, 1.0); \n\
vCol = vec4(clamp(pos, 0.0f, 1.0f), 1.0f); \n\
}";
// clamp make th values outside 0 and 1 to be inside 0 and 1
static const char* fShader = " \n\
#version 410 \n\
\n\
in vec4 vCol; \n\
\n\
out vec4 colour; \n\
\n\
layout (location = 0) in vec3 pos; \n\
\n\
void main() \n\
{ \n\
colour = vCol; \n\
}";
/*!
Here we are going to create a VAO (Vertex Array Object) and a VBO (Vertex Buffer Object)
The VAO will hold multiples VBOs and other types of buffer to define how a triangle should be drawn,
In this funciton is just the vertices of the triangles.
*/
void CreateTriangle()
{
// Triangle Points in an array of floats (The center of the screen is 0,0)
GLfloat vertices[] = {
-1.0f, -1.0f, 0.0f,
1.0f, -1.0f, 0.0f,
0.0f, 1.0f, 0.0f
};
/*
Creating a VAO
Takes the amount of arrays we want to create and where to store the ID of the array.
When we are calling this function on the graphics card is now creating a vertex array, its defining some space in the memory for some vertex array.
So if you want to access that space in the memory you just pass the ID(VAO)).
*/
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
// Now that we have a VAO we need to create a buffer object to actually go inside of that.
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO); //We have to enter which buffer we want to bind, as VBO has multiple targets it can bind to.
// Now we have to connect the buffer data, the vertices that we created to the GL_BUFFER for this VBO.
// GL_STATIC_DRAW means we are not going to be changing the values we are putting in there, GL_DYNAMIC_DRAW is more complex.
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); // First attribute is location of the attribute, then amount of values(x,y,z), then type, then if normalizing(we are not normalizing), then striding value(to jump 3 or whatever to read next value), then offset)
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0); // undoing the binding in the VBO
glBindVertexArray(0); //undoing the binding in the VAO
}
/*!
Add the shaders to the program funtion
*/
void AddShader(GLuint theProgram, const char* shaderCode, GLenum shaderType)
{
GLuint theShader = glCreateShader(shaderType); // Creating an empty shader for that type
const GLchar* theCode[1]; // Passing a pointer to the first element of GLchar, so we can pass a pointer directly to it
theCode[0] = shaderCode;
GLint codeLength[1]; // Hold the length of the code
codeLength[0] = strlen(shaderCode);
glShaderSource(theShader, 1, theCode, codeLength); // Now the empty shader is modified to have this code
glCompileShader(theShader); // compile the shader
GLint result = 0;
GLchar eLog[1024] = { 0 };
// Error check before attach the shader to the program
glGetShaderiv(theShader, GL_COMPILE_STATUS, &result);
if (!result)
{
glGetShaderInfoLog(theShader, sizeof(eLog), NULL, eLog);
printf("Error compiling the %d shader: '%s'\n", shaderType, eLog);
return;
}
glAttachShader(theProgram, theShader);
}
/*!
*/
void CompileShaders() {
shader = glCreateProgram(); // Create the program and give shader the ID, then we can modify the program and do something with it
// Make sure the shader was created correctly
if (!shader) {
printf("Error creating shader program!\n");
return;
}
//Add the shaders to the program using the function
AddShader(shader, vShader, GL_VERTEX_SHADER);
AddShader(shader, fShader, GL_FRAGMENT_SHADER);
// Getting the error codes, with the results of the two functions we are about to perform and a place to put the errors
GLint result = 0;
GLchar eLog[1024] = { 0 };
glLinkProgram(shader); // Create the exectables in the graphics card.
// Make sure the program is linked properlly
glGetProgramiv(shader, GL_LINK_STATUS, &result);
if (!result)
{
glGetProgramInfoLog(shader, sizeof(eLog), NULL, eLog); // Getting the InfoLog for this program, passing the eLog char array and then print
printf("Error linking program: '%s'\n", eLog);
return;
}
// Validating if the program is valid on the current context that OpenGL is working
glValidateProgram(shader);
glGetProgramiv(shader, GL_VALIDATE_STATUS, &result);
if (!result)
{
glGetProgramInfoLog(shader, sizeof(eLog), NULL, eLog);
printf("Error validating program: '%s'\n", eLog);
return;
}
// Get the actual ID or location of the uniform variable
uniformModel = glGetUniformLocation(shader, "model"); // uniformModel will be the location of the model matrix
}
int main()
{
//Initialize GLFW
if (!glfwInit()) {
std::cout << "GLFW Initialization failed!";
glfwTerminate();
}
//Setup GLFW window properties
//OpenGL version
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
//Core Profile = No Backwards Compatibility
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
//Allow forward compatibility
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
GLFWwindow* mainWindow = glfwCreateWindow(WIDTH, HEIGHT, "Test Window", NULL, NULL);
if (!mainWindow) {
std::cout << " GLFW window creation failed!";
glfwTerminate();
}
// Get Buffer size information
int bufferWidth, bufferHeight;
glfwGetFramebufferSize(mainWindow, &bufferWidth, &bufferHeight);
//Set the context for GLEW to use
glfwMakeContextCurrent(mainWindow);
//Allow modern extension features
glewExperimental = GL_TRUE;
if (glewInit() != GLEW_OK) {
std::cout << "GLEW Initialization failed!";
glfwDestroyWindow(mainWindow);
glfwTerminate();
}
//Setup viewport size
glViewport(0, 0, bufferWidth, bufferHeight);
CreateTriangle();
CompileShaders();
// LLop until window close
while (!glfwWindowShouldClose(mainWindow)) {
// Get + Handle user input events
glfwPollEvents();
// Checking the direction
if (direction)
{
triOffset += triIncrement;
}
else {
triOffset -= triIncrement;
}
// Check if the location is between max and min offset
if (abs(triOffset) >= triMaxoffset) {
direction = !direction; // Easy way of doing a bool
}
curAngle += 1.0f;
if (curAngle >= 360)
{
curAngle -= 360;
}
if (sizeDirection)
{
curSize += 0.001f;
}
else {
curSize -= 0.001f;
}
if (curSize >= maxSize || curSize <= minSize) {
sizeDirection = !sizeDirection;
}
// Clear window
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// Draw the triangle
glUseProgram(shader); // This will now grab that ID, It will go through the graphics card and tell it to use the one with the ID of shader
glm::mat4 model(1.0f); // Remenber, You have to initialize the model matrix variable glm::mat4 model with (1.0f) for a identity matrix
//model = glm::translate(model, glm::vec3(triOffset, 0.0f, 0.0f)); // Applying a translation that will only be changing the x value.
//model = glm::rotate(model, curAngle * toRadians, glm::vec3(0.0f, 0.0f, 1.0f));
model = glm::scale(model, glm::vec3(0.4f, 0.4f, 1.0f));
// First is the location, then quantity, then we don't want to transpose
// Last we pass the value pointer, because the model is not exactly in a format that will work with the shader
glUniformMatrix4fv(uniformModel, 1, GL_FALSE, glm::value_ptr(model));
// Now we modify the shaders to handle the new values
glBindVertexArray(VAO); // We saying now work with this VAO
glDrawArrays(GL_TRIANGLES, 0, 3); // draw 3 points starting on the first
glBindVertexArray(0); // Unassign the VAO
glUseProgram(0); // Unassign the shader
glfwSwapBuffers(mainWindow);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
# Indexed Draws
Building a pyramid with indexed indices.
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <cmath>
#include <GL\glew.h>
#include <GLFW\glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <fstream>
#include <sstream>
#define ASSERT(x) if (!(x)) __debugbreak();
#define GLCall(x) GLClearError();\
x;\
ASSERT(GLLogCall(#x, __FILE__, __LINE__))
//Checking for errors
static void GLClearError()
{
while (glGetError() != GL_NO_ERROR);
}
static bool GLLogCall(const char* function, const char* file, int line)
{
while (GLenum error = glGetError())
{
std::cout << "[OpenGL Error] (" << error << ")" << function <<
" " << file << ":" << line << std::endl;
return false;
}
return true;
}
//Window dimensions
const GLint WIDTH = 800, HEIGHT = 600;
const float toRadians = 3.14159265f / 180.0f;
GLuint VAO, VBO, IBO, shader, uniformModel;
bool direction = true;
float triOffset = 0.0f;
float triMaxoffset = 0.7f;
float triIncrement = 0.02f;
float curAngle = 0.0f;
bool sizeDirection = true;
float curSize = 0.4f;
float maxSize = 0.8f;
float minSize = 0.1f;
// Vertex Shader
static const char* vShader = " \n\
#version 410 \n\
\n\
out vec4 vCol; \n\
\n\
layout (location = 0) in vec3 pos; \n\
\n\
uniform mat4 model; \n\
\n\
void main() \n\
{ \n\
gl_Position = model * vec4(pos, 1.0); \n\
vCol = vec4(clamp(pos, 0.0f, 1.0f), 1.0f); \n\
}";
// clamp make th values outside 0 and 1 to be inside 0 and 1
static const char* fShader = " \n\
#version 410 \n\
\n\
in vec4 vCol; \n\
\n\
out vec4 colour; \n\
\n\
layout (location = 0) in vec3 pos; \n\
\n\
void main() \n\
{ \n\
colour = vCol; \n\
}";
/*!
Here we are going to create a VAO (Vertex Array Object) and a VBO (Vertex Buffer Object)
The VAO will hold multiples VBOs and other types of buffer to define how a triangle should be drawn,
In this funciton is just the vertices of the triangles.
*/
void CreateTriangle()
{
unsigned int indices[] = {
0, 3, 1,
1, 3, 2,
2, 3, 0,
0, 1, 2
};
// Triangle Points in an array of floats (The center of the screen is 0,0)
GLfloat vertices[] = {
-1.0f, -1.0f, 0.0f,
0.0f, -1.0f, 1.0f,
1.0f, -1.0f, 0.0f,
0.0f, 1.0f, 0.0f
};
/*
Creating a VAO
Takes the amount of arrays we want to create and where to store the ID of the array.
When we are calling this function on the graphics card is now creating a vertex array, its defining some space in the memory for some vertex array.
So if you want to access that space in the memory you just pass the ID(VAO)).
*/
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
// Creating another buffer for the indices;
glGenBuffers(1, &IBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
// Now that we have a VAO we need to create a buffer object to actually go inside of that.
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO); //We have to enter which buffer we want to bind, as VBO has multiple targets it can bind to.
// Now we have to connect the buffer data, the vertices that we created to the GL_BUFFER for this VBO.
// GL_STATIC_DRAW means we are not going to be changing the values we are putting in there, GL_DYNAMIC_DRAW is more complex.
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); // First attribute is location of the attribute, then amount of values(x,y,z), then type, then if normalizing(we are not normalizing), then striding value(to jump 3 or whatever to read next value), then offset)
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0); // undoing the binding in the VBO
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindVertexArray(0); //undoing the binding in the VAO
}
/*!
Add the shaders to the program funtion
*/
void AddShader(GLuint theProgram, const char* shaderCode, GLenum shaderType)
{
GLuint theShader = glCreateShader(shaderType); // Creating an empty shader for that type
const GLchar* theCode[1]; // Passing a pointer to the first element of GLchar, so we can pass a pointer directly to it
theCode[0] = shaderCode;
GLint codeLength[1]; // Hold the length of the code
codeLength[0] = strlen(shaderCode);
glShaderSource(theShader, 1, theCode, codeLength); // Now the empty shader is modified to have this code
glCompileShader(theShader); // compile the shader
GLint result = 0;
GLchar eLog[1024] = { 0 };
// Error check before attach the shader to the program
glGetShaderiv(theShader, GL_COMPILE_STATUS, &result);
if (!result)
{
glGetShaderInfoLog(theShader, sizeof(eLog), NULL, eLog);
printf("Error compiling the %d shader: '%s'\n", shaderType, eLog);
return;
}
glAttachShader(theProgram, theShader);
}
/*!
*/
void CompileShaders() {
shader = glCreateProgram(); // Create the program and give shader the ID, then we can modify the program and do something with it
// Make sure the shader was created correctly
if (!shader) {
printf("Error creating shader program!\n");
return;
}
//Add the shaders to the program using the function
AddShader(shader, vShader, GL_VERTEX_SHADER);
AddShader(shader, fShader, GL_FRAGMENT_SHADER);
// Getting the error codes, with the results of the two functions we are about to perform and a place to put the errors
GLint result = 0;
GLchar eLog[1024] = { 0 };
glLinkProgram(shader); // Create the exectables in the graphics card.
// Make sure the program is linked properlly
glGetProgramiv(shader, GL_LINK_STATUS, &result);
if (!result)
{
glGetProgramInfoLog(shader, sizeof(eLog), NULL, eLog); // Getting the InfoLog for this program, passing the eLog char array and then print
printf("Error linking program: '%s'\n", eLog);
return;
}
// Validating if the program is valid on the current context that OpenGL is working
glValidateProgram(shader);
glGetProgramiv(shader, GL_VALIDATE_STATUS, &result);
if (!result)
{
glGetProgramInfoLog(shader, sizeof(eLog), NULL, eLog);
printf("Error validating program: '%s'\n", eLog);
return;
}
// Get the actual ID or location of the uniform variable
uniformModel = glGetUniformLocation(shader, "model"); // uniformModel will be the location of the model matrix
}
int main()
{
//Initialize GLFW
if (!glfwInit()) {
std::cout << "GLFW Initialization failed!";
glfwTerminate();
}
//Setup GLFW window properties
//OpenGL version
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
//Core Profile = No Backwards Compatibility
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
//Allow forward compatibility
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
GLFWwindow* mainWindow = glfwCreateWindow(WIDTH, HEIGHT, "Test Window", NULL, NULL);
if (!mainWindow) {
std::cout << " GLFW window creation failed!";
glfwTerminate();
}
// Get Buffer size information
int bufferWidth, bufferHeight;
glfwGetFramebufferSize(mainWindow, &bufferWidth, &bufferHeight);
//Set the context for GLEW to use
glfwMakeContextCurrent(mainWindow);
//Allow modern extension features
glewExperimental = GL_TRUE;
if (glewInit() != GLEW_OK) {
std::cout << "GLEW Initialization failed!";
glfwDestroyWindow(mainWindow);
glfwTerminate();
}
//Setup viewport size
glViewport(0, 0, bufferWidth, bufferHeight);
CreateTriangle();
CompileShaders();
// LLop until window close
while (!glfwWindowShouldClose(mainWindow)) {
// Get + Handle user input events
glfwPollEvents();
// Checking the direction
if (direction)
{
triOffset += triIncrement;
}
else {
triOffset -= triIncrement;
}
// Check if the location is between max and min offset
if (abs(triOffset) >= triMaxoffset) {
direction = !direction; // Easy way of doing a bool
}
curAngle += 0.01f;
if (curAngle >= 360)
{
curAngle -= 360;
}
if (sizeDirection)
{
curSize += 0.001f;
}
else {
curSize -= 0.001f;
}
if (curSize >= maxSize || curSize <= minSize) {
sizeDirection = !sizeDirection;
}
// Clear window
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// Draw the triangle
glUseProgram(shader); // This will now grab that ID, It will go through the graphics card and tell it to use the one with the ID of shader
glm::mat4 model(1.0f); // Remenber, You have to initialize the model matrix variable glm::mat4 model with (1.0f) for a identity matrix
//model = glm::translate(model, glm::vec3(triOffset, 0.0f, 0.0f)); // Applying a translation that will only be changing the x value.
model = glm::rotate(model, curAngle * toRadians, glm::vec3(0.0f, 1.0f, 0.0f));
model = glm::scale(model, glm::vec3(0.4f, 0.4f, 1.0f));
// First is the location, then quantity, then we don't want to transpose
// Last we pass the value pointer, because the model is not exactly in a format that will work with the shader
glUniformMatrix4fv(uniformModel, 1, GL_FALSE, glm::value_ptr(model));
// Now we modify the shaders to handle the new values
glBindVertexArray(VAO); // We saying now work with this VAO
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);
glDrawElements(GL_TRIANGLES, 12, GL_UNSIGNED_INT, 0); //draw by the elements IDs
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindVertexArray(0); // Unassign the VAO
glUseProgram(0); // Unassign the shader
glfwSwapBuffers(mainWindow);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
TIP
We need to setup the Depth Buffer for the colors to be correct, now it doesn't know which triangle is in fron of which.
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <cmath>
#include <GL\glew.h>
#include <GLFW\glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <fstream>
#include <sstream>
#define ASSERT(x) if (!(x)) __debugbreak();
#define GLCall(x) GLClearError();\
x;\
ASSERT(GLLogCall(#x, __FILE__, __LINE__))
//Checking for errors
static void GLClearError()
{
while (glGetError() != GL_NO_ERROR);
}
static bool GLLogCall(const char* function, const char* file, int line)
{
while (GLenum error = glGetError())
{
std::cout << "[OpenGL Error] (" << error << ")" << function <<
" " << file << ":" << line << std::endl;
return false;
}
return true;
}
//Window dimensions
const GLint WIDTH = 800, HEIGHT = 600;
const float toRadians = 3.14159265f / 180.0f;
GLuint VAO, VBO, IBO, shader, uniformModel;
bool direction = true;
float triOffset = 0.0f;
float triMaxoffset = 0.7f;
float triIncrement = 0.02f;
float curAngle = 0.0f;
bool sizeDirection = true;
float curSize = 0.4f;
float maxSize = 0.8f;
float minSize = 0.1f;
// Vertex Shader
static const char* vShader = " \n\
#version 410 \n\
\n\
out vec4 vCol; \n\
\n\
layout (location = 0) in vec3 pos; \n\
\n\
uniform mat4 model; \n\
\n\
void main() \n\
{ \n\
gl_Position = model * vec4(pos, 1.0); \n\
vCol = vec4(clamp(pos, 0.0f, 1.0f), 1.0f); \n\
}";
// clamp make th values outside 0 and 1 to be inside 0 and 1
static const char* fShader = " \n\
#version 410 \n\
\n\
in vec4 vCol; \n\
\n\
out vec4 colour; \n\
\n\
layout (location = 0) in vec3 pos; \n\
\n\
void main() \n\
{ \n\
colour = vCol; \n\
}";
/*!
Here we are going to create a VAO (Vertex Array Object) and a VBO (Vertex Buffer Object)
The VAO will hold multiples VBOs and other types of buffer to define how a triangle should be drawn,
In this funciton is just the vertices of the triangles.
*/
void CreateTriangle()
{
unsigned int indices[] = {
0, 3, 1,
1, 3, 2,
2, 3, 0,
0, 1, 2
};
// Triangle Points in an array of floats (The center of the screen is 0,0)
GLfloat vertices[] = {
-1.0f, -1.0f, 0.0f,
0.0f, -1.0f, 1.0f,
1.0f, -1.0f, 0.0f,
0.0f, 1.0f, 0.0f
};
/*
Creating a VAO
Takes the amount of arrays we want to create and where to store the ID of the array.
When we are calling this function on the graphics card is now creating a vertex array, its defining some space in the memory for some vertex array.
So if you want to access that space in the memory you just pass the ID(VAO)).
*/
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
// Creating another buffer for the indices;
glGenBuffers(1, &IBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
// Now that we have a VAO we need to create a buffer object to actually go inside of that.
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO); //We have to enter which buffer we want to bind, as VBO has multiple targets it can bind to.
// Now we have to connect the buffer data, the vertices that we created to the GL_BUFFER for this VBO.
// GL_STATIC_DRAW means we are not going to be changing the values we are putting in there, GL_DYNAMIC_DRAW is more complex.
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); // First attribute is location of the attribute, then amount of values(x,y,z), then type, then if normalizing(we are not normalizing), then striding value(to jump 3 or whatever to read next value), then offset)
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0); // undoing the binding in the VBO
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindVertexArray(0); //undoing the binding in the VAO
}
/*!
Add the shaders to the program funtion
*/
void AddShader(GLuint theProgram, const char* shaderCode, GLenum shaderType)
{
GLuint theShader = glCreateShader(shaderType); // Creating an empty shader for that type
const GLchar* theCode[1]; // Passing a pointer to the first element of GLchar, so we can pass a pointer directly to it
theCode[0] = shaderCode;
GLint codeLength[1]; // Hold the length of the code
codeLength[0] = strlen(shaderCode);
glShaderSource(theShader, 1, theCode, codeLength); // Now the empty shader is modified to have this code
glCompileShader(theShader); // compile the shader
GLint result = 0;
GLchar eLog[1024] = { 0 };
// Error check before attach the shader to the program
glGetShaderiv(theShader, GL_COMPILE_STATUS, &result);
if (!result)
{
glGetShaderInfoLog(theShader, sizeof(eLog), NULL, eLog);
printf("Error compiling the %d shader: '%s'\n", shaderType, eLog);
return;
}
glAttachShader(theProgram, theShader);
}
/*!
*/
void CompileShaders() {
shader = glCreateProgram(); // Create the program and give shader the ID, then we can modify the program and do something with it
// Make sure the shader was created correctly
if (!shader) {
printf("Error creating shader program!\n");
return;
}
//Add the shaders to the program using the function
AddShader(shader, vShader, GL_VERTEX_SHADER);
AddShader(shader, fShader, GL_FRAGMENT_SHADER);
// Getting the error codes, with the results of the two functions we are about to perform and a place to put the errors
GLint result = 0;
GLchar eLog[1024] = { 0 };
glLinkProgram(shader); // Create the exectables in the graphics card.
// Make sure the program is linked properlly
glGetProgramiv(shader, GL_LINK_STATUS, &result);
if (!result)
{
glGetProgramInfoLog(shader, sizeof(eLog), NULL, eLog); // Getting the InfoLog for this program, passing the eLog char array and then print
printf("Error linking program: '%s'\n", eLog);
return;
}
// Validating if the program is valid on the current context that OpenGL is working
glValidateProgram(shader);
glGetProgramiv(shader, GL_VALIDATE_STATUS, &result);
if (!result)
{
glGetProgramInfoLog(shader, sizeof(eLog), NULL, eLog);
printf("Error validating program: '%s'\n", eLog);
return;
}
// Get the actual ID or location of the uniform variable
uniformModel = glGetUniformLocation(shader, "model"); // uniformModel will be the location of the model matrix
}
int main()
{
//Initialize GLFW
if (!glfwInit()) {
std::cout << "GLFW Initialization failed!";
glfwTerminate();
}
//Setup GLFW window properties
//OpenGL version
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
//Core Profile = No Backwards Compatibility
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
//Allow forward compatibility
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
GLFWwindow* mainWindow = glfwCreateWindow(WIDTH, HEIGHT, "Test Window", NULL, NULL);
if (!mainWindow) {
std::cout << " GLFW window creation failed!";
glfwTerminate();
}
// Get Buffer size information
int bufferWidth, bufferHeight;
glfwGetFramebufferSize(mainWindow, &bufferWidth, &bufferHeight);
//Set the context for GLEW to use
glfwMakeContextCurrent(mainWindow);
//Allow modern extension features
glewExperimental = GL_TRUE;
if (glewInit() != GLEW_OK) {
std::cout << "GLEW Initialization failed!";
glfwDestroyWindow(mainWindow);
glfwTerminate();
}
glEnable(GL_DEPTH_TEST);
//Setup viewport size
glViewport(0, 0, bufferWidth, bufferHeight);
CreateTriangle();
CompileShaders();
// LLop until window close
while (!glfwWindowShouldClose(mainWindow)) {
// Get + Handle user input events
glfwPollEvents();
// Checking the direction
if (direction)
{
triOffset += triIncrement;
}
else {
triOffset -= triIncrement;
}
// Check if the location is between max and min offset
if (abs(triOffset) >= triMaxoffset) {
direction = !direction; // Easy way of doing a bool
}
curAngle += 0.01f;
if (curAngle >= 360)
{
curAngle -= 360;
}
if (sizeDirection)
{
curSize += 0.001f;
}
else {
curSize -= 0.001f;
}
if (curSize >= maxSize || curSize <= minSize) {
sizeDirection = !sizeDirection;
}
// Clear window
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear the color bubber bit and also the depth buffer bit, now the colors willbe correct.
// Draw the triangle
glUseProgram(shader); // This will now grab that ID, It will go through the graphics card and tell it to use the one with the ID of shader
glm::mat4 model(1.0f); // Remenber, You have to initialize the model matrix variable glm::mat4 model with (1.0f) for a identity matrix
//model = glm::translate(model, glm::vec3(triOffset, 0.0f, 0.0f)); // Applying a translation that will only be changing the x value.
model = glm::rotate(model, curAngle * toRadians, glm::vec3(0.0f, 1.0f, 0.0f));
model = glm::scale(model, glm::vec3(0.4f, 0.4f, 1.0f));
// First is the location, then quantity, then we don't want to transpose
// Last we pass the value pointer, because the model is not exactly in a format that will work with the shader
glUniformMatrix4fv(uniformModel, 1, GL_FALSE, glm::value_ptr(model));
// Now we modify the shaders to handle the new values
glBindVertexArray(VAO); // We saying now work with this VAO
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);
glDrawElements(GL_TRIANGLES, 12, GL_UNSIGNED_INT, 0); //draw by the elements IDs
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindVertexArray(0); // Unassign the VAO
glUseProgram(0); // Unassign the shader
glfwSwapBuffers(mainWindow);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
# Projections
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <cmath>
#include <GL\glew.h>
#include <GLFW\glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <fstream>
#include <sstream>
#define ASSERT(x) if (!(x)) __debugbreak();
#define GLCall(x) GLClearError();\
x;\
ASSERT(GLLogCall(#x, __FILE__, __LINE__))
//Checking for errors
static void GLClearError()
{
while (glGetError() != GL_NO_ERROR);
}
static bool GLLogCall(const char* function, const char* file, int line)
{
while (GLenum error = glGetError())
{
std::cout << "[OpenGL Error] (" << error << ")" << function <<
" " << file << ":" << line << std::endl;
return false;
}
return true;
}
//Window dimensions
const GLint WIDTH = 800, HEIGHT = 600;
const float toRadians = 3.14159265f / 180.0f;
GLuint VAO, VBO, IBO, shader, uniformModel, uniformProjection;
bool direction = true;
float triOffset = 0.0f;
float triMaxoffset = 0.7f;
float triIncrement = 0.02f;
float curAngle = 0.0f;
bool sizeDirection = true;
float curSize = 0.4f;
float maxSize = 0.8f;
float minSize = 0.1f;
// Vertex Shader
static const char* vShader = " \n\
#version 410 \n\
\n\
out vec4 vCol; \n\
\n\
layout (location = 0) in vec3 pos; \n\
\n\
uniform mat4 model; \n\
uniform mat4 projection; \n\
\n\
void main() \n\
{ \n\
gl_Position = projection * model * vec4(pos, 1.0); \n\
vCol = vec4(clamp(pos, 0.0f, 1.0f), 1.0f); \n\
}";
// clamp make th values outside 0 and 1 to be inside 0 and 1
static const char* fShader = " \n\
#version 410 \n\
\n\
in vec4 vCol; \n\
\n\
out vec4 colour; \n\
\n\
layout (location = 0) in vec3 pos; \n\
\n\
void main() \n\
{ \n\
colour = vCol; \n\
}";
/*!
Here we are going to create a VAO (Vertex Array Object) and a VBO (Vertex Buffer Object)
The VAO will hold multiples VBOs and other types of buffer to define how a triangle should be drawn,
In this funciton is just the vertices of the triangles.
*/
void CreateTriangle()
{
unsigned int indices[] = {
0, 3, 1,
1, 3, 2,
2, 3, 0,
0, 1, 2
};
// Triangle Points in an array of floats (The center of the screen is 0,0)
GLfloat vertices[] = {
-1.0f, -1.0f, 0.0f,
0.0f, -1.0f, 1.0f,
1.0f, -1.0f, 0.0f,
0.0f, 1.0f, 0.0f
};
/*
Creating a VAO
Takes the amount of arrays we want to create and where to store the ID of the array.
When we are calling this function on the graphics card is now creating a vertex array, its defining some space in the memory for some vertex array.
So if you want to access that space in the memory you just pass the ID(VAO)).
*/
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
// Creating another buffer for the indices;
glGenBuffers(1, &IBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
// Now that we have a VAO we need to create a buffer object to actually go inside of that.
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO); //We have to enter which buffer we want to bind, as VBO has multiple targets it can bind to.
// Now we have to connect the buffer data, the vertices that we created to the GL_BUFFER for this VBO.
// GL_STATIC_DRAW means we are not going to be changing the values we are putting in there, GL_DYNAMIC_DRAW is more complex.
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); // First attribute is location of the attribute, then amount of values(x,y,z), then type, then if normalizing(we are not normalizing), then striding value(to jump 3 or whatever to read next value), then offset)
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0); // undoing the binding in the VBO
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindVertexArray(0); //undoing the binding in the VAO
}
/*!
Add the shaders to the program funtion
*/
void AddShader(GLuint theProgram, const char* shaderCode, GLenum shaderType)
{
GLuint theShader = glCreateShader(shaderType); // Creating an empty shader for that type
const GLchar* theCode[1]; // Passing a pointer to the first element of GLchar, so we can pass a pointer directly to it
theCode[0] = shaderCode;
GLint codeLength[1]; // Hold the length of the code
codeLength[0] = strlen(shaderCode);
glShaderSource(theShader, 1, theCode, codeLength); // Now the empty shader is modified to have this code
glCompileShader(theShader); // compile the shader
GLint result = 0;
GLchar eLog[1024] = { 0 };
// Error check before attach the shader to the program
glGetShaderiv(theShader, GL_COMPILE_STATUS, &result);
if (!result)
{
glGetShaderInfoLog(theShader, sizeof(eLog), NULL, eLog);
printf("Error compiling the %d shader: '%s'\n", shaderType, eLog);
return;
}
glAttachShader(theProgram, theShader);
}
/*!
*/
void CompileShaders() {
shader = glCreateProgram(); // Create the program and give shader the ID, then we can modify the program and do something with it
// Make sure the shader was created correctly
if (!shader) {
printf("Error creating shader program!\n");
return;
}
//Add the shaders to the program using the function
AddShader(shader, vShader, GL_VERTEX_SHADER);
AddShader(shader, fShader, GL_FRAGMENT_SHADER);
// Getting the error codes, with the results of the two functions we are about to perform and a place to put the errors
GLint result = 0;
GLchar eLog[1024] = { 0 };
glLinkProgram(shader); // Create the exectables in the graphics card.
// Make sure the program is linked properlly
glGetProgramiv(shader, GL_LINK_STATUS, &result);
if (!result)
{
glGetProgramInfoLog(shader, sizeof(eLog), NULL, eLog); // Getting the InfoLog for this program, passing the eLog char array and then print
printf("Error linking program: '%s'\n", eLog);
return;
}
// Validating if the program is valid on the current context that OpenGL is working
glValidateProgram(shader);
glGetProgramiv(shader, GL_VALIDATE_STATUS, &result);
if (!result)
{
glGetProgramInfoLog(shader, sizeof(eLog), NULL, eLog);
printf("Error validating program: '%s'\n", eLog);
return;
}
// Get the actual ID or location of the uniform variable
uniformModel = glGetUniformLocation(shader, "model"); // uniformModel will be the location of the model matrix
uniformProjection = glGetUniformLocation(shader, "projection"); // Get the projection variable and place the ID of the location intp the variable uniformProjection
}
int main()
{
//Initialize GLFW
if (!glfwInit()) {
std::cout << "GLFW Initialization failed!";
glfwTerminate();
}
//Setup GLFW window properties
//OpenGL version
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
//Core Profile = No Backwards Compatibility
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
//Allow forward compatibility
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
GLFWwindow* mainWindow = glfwCreateWindow(WIDTH, HEIGHT, "Test Window", NULL, NULL);
if (!mainWindow) {
std::cout << " GLFW window creation failed!";
glfwTerminate();
}
// Get Buffer size information
int bufferWidth, bufferHeight;
glfwGetFramebufferSize(mainWindow, &bufferWidth, &bufferHeight);
//Set the context for GLEW to use
glfwMakeContextCurrent(mainWindow);
//Allow modern extension features
glewExperimental = GL_TRUE;
if (glewInit() != GLEW_OK) {
std::cout << "GLEW Initialization failed!";
glfwDestroyWindow(mainWindow);
glfwTerminate();
}
glEnable(GL_DEPTH_TEST);
//Setup viewport size
glViewport(0, 0, bufferWidth, bufferHeight);
CreateTriangle();
CompileShaders();
glm::mat4 projection = glm::perspective(45.0f, (GLfloat)bufferWidth / (GLfloat)bufferHeight, 0.1f, 100.0f); //(field of view, aspect(width/height), zNear, ZFar
// LLop until window close
while (!glfwWindowShouldClose(mainWindow)) {
// Get + Handle user input events
glfwPollEvents();
// Checking the direction
if (direction)
{
triOffset += triIncrement;
}
else {
triOffset -= triIncrement;
}
// Check if the location is between max and min offset
if (abs(triOffset) >= triMaxoffset) {
direction = !direction; // Easy way of doing a bool
}
curAngle += 1.0f;
if (curAngle >= 360)
{
curAngle -= 360;
}
if (sizeDirection)
{
curSize += 0.001f;
}
else {
curSize -= 0.001f;
}
if (curSize >= maxSize || curSize <= minSize) {
sizeDirection = !sizeDirection;
}
// Clear window
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear the color bubber bit and also the depth buffer bit, now the colors willbe correct.
// Draw the triangle
glUseProgram(shader); // This will now grab that ID, It will go through the graphics card and tell it to use the one with the ID of shader
glm::mat4 model(1.0f); // Remenber, You have to initialize the model matrix variable glm::mat4 model with (1.0f) for a identity matrix
model = glm::translate(model, glm::vec3(0.0f, 0.0f, -2.5f)); // Applying a translation that will only be changing the x value.
model = glm::rotate(model, curAngle * toRadians, glm::vec3(0.0f, 1.0f, 0.0f));
model = glm::scale(model, glm::vec3(0.4f, 0.4f, 1.0f));
// First is the location, then quantity, then we don't want to transpose
// Last we pass the value pointer, because the model is not exactly in a format that will work with the shader
glUniformMatrix4fv(uniformModel, 1, GL_FALSE, glm::value_ptr(model));
glUniformMatrix4fv(uniformProjection, 1, GL_FALSE, glm::value_ptr(projection));
// Now we modify the shaders to handle the new values
glBindVertexArray(VAO); // We saying now work with this VAO
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);
glDrawElements(GL_TRIANGLES, 12, GL_UNSIGNED_INT, 0); //draw by the elements IDs
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindVertexArray(0); // Unassign the VAO
glUseProgram(0); // Unassign the shader
glfwSwapBuffers(mainWindow);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
# Cleaning up the code
# Mesh Class - Putting the VAO, VBO and IBO on its own class
Making a class will enable us to reuse the code and create multiple objects.
First let's create the class Mesh
:
First we create our variables private, so that nobody can access them from the outside. Then the functions headers.
#pragma once
#include <GL/glew.h>
class Mesh
{
public:
Mesh();
void CreateMesh(GLfloat *vertices, unsigned int *indices, unsigned int numofVertices, unsigned int numofIndices);
void RenderMesh();
void ClearMesh();
~Mesh();
private:
GLuint VAO, VBO, IBO;
GLsizei indexCount; // to say how many indeces we have to draw like here, with 12: glDrawElements(GL_TRIANGLES, 12, GL_UNSIGNED_INT, 0);
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Now everytime we call create mesh we will create this mesh object, VAO, VBO, IBO and indexCount will be initialized to 0, it will call CreateMesh
; then we will get the indexCount
and we will know how much we will pass in when we are drawing; then we will create the VAO, the IBO for it, the VBO; and this will be stored locally to this one object they are all member variables of this object, and when we call Draw()
it will simple reference these values. This way we can know we don't have the same object being drawn over and over.
#include "Mesh.h"
Mesh::Mesh()
{
VAO = 0;
VBO = 0;
IBO = 0;
indexCount = 0;
}
void Mesh::CreateMesh(GLfloat* vertices, unsigned int* indices, unsigned int numofVertices, unsigned int numofIndices)
{
indexCount = numofIndices;
/*
Creating a VAO
Takes the amount of arrays we want to create and where to store the ID of the array.
When we are calling this function on the graphics card is now creating a vertex array, its defining some space in the memory for some vertex array.
So if you want to access that space in the memory you just pass the ID(VAO)).
*/
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
// Creating another buffer for the indices;
glGenBuffers(1, &IBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices[0]) * numofIndices, indices, GL_STATIC_DRAW);
// Now that we have a VAO we need to create a buffer object to actually go inside of that.
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO); //We have to enter which buffer we want to bind, as VBO has multiple targets it can bind to.
// Now we have to connect the buffer data, the vertices that we created to the GL_BUFFER for this VBO.
// GL_STATIC_DRAW means we are not going to be changing the values we are putting in there, GL_DYNAMIC_DRAW is more complex.
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices[0]) * numofVertices, vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); // First attribute is location of the attribute, then amount of values(x,y,z), then type, then if normalizing(we are not normalizing), then striding value(to jump 3 or whatever to read next value), then offset)
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0); // undoing the binding in the VBO
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindVertexArray(0); //undoing the binding in the VAO
}
Mesh::~Mesh()
{
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
Now we need to do the RenderMesh()
function.
void Mesh::RenderMesh()
{
glBindVertexArray(VAO); // We saying now work with this VAO
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);
glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, 0); //draw by the elements IDs
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindVertexArray(0); // Unassign the VAO
}
2
3
4
5
6
7
8
9
Then the function ClearMesh(), to clean the buffer
of the graphics card memory to make room for more space, this avoid memory overflow. There is no garbage collection like in Java.
void Mesh::ClearMesh()
{
if (IBO != 0)
{
glDeleteBuffers(1, &IBO);
IBO = 0;
}
if (VBO != 0)
{
glDeleteBuffers(1, &VBO);
VBO = 0;
}
if (VAO != 0)
{
glDeleteVertexArrays(1, &VAO);
VAO = 0;
}
indexCount = 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
And the destructor:
Mesh::~Mesh()
{
ClearMesh();
}
2
3
4
The whole Class:
#include "Mesh.h"
Mesh::Mesh()
{
VAO = 0;
VBO = 0;
IBO = 0;
indexCount = 0;
}
void Mesh::CreateMesh(GLfloat *vertices, unsigned int *indices, unsigned int numofVertices, unsigned int numofIndices)
{
indexCount = numofIndices;
/*
Creating a VAO
Takes the amount of arrays we want to create and where to store the ID of the array.
When we are calling this function on the graphics card is now creating a vertex array, its defining some space in the memory for some vertex array.
So if you want to access that space in the memory you just pass the ID(VAO)).
*/
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
// Creating another buffer for the indices;
glGenBuffers(1, &IBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices[0]) * numofIndices, indices, GL_STATIC_DRAW);
// Now that we have a VAO we need to create a buffer object to actually go inside of that.
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO); //We have to enter which buffer we want to bind, as VBO has multiple targets it can bind to.
// Now we have to connect the buffer data, the vertices that we created to the GL_BUFFER for this VBO.
// GL_STATIC_DRAW means we are not going to be changing the values we are putting in there, GL_DYNAMIC_DRAW is more complex.
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices[0]) * numofVertices, vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); // First attribute is location of the attribute, then amount of values(x,y,z), then type, then if normalizing(we are not normalizing), then striding value(to jump 3 or whatever to read next value), then offset)
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0); // undoing the binding in the VBO
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindVertexArray(0); //undoing the binding in the VAO
}
void Mesh::RenderMesh()
{
glBindVertexArray(VAO); // We saying now work with this VAO
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);
glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, 0); //draw by the elements IDs
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindVertexArray(0); // Unassign the VAO
}
void Mesh::ClearMesh()
{
if (IBO != 0)
{
glDeleteBuffers(1, &IBO);
IBO = 0;
}
if (VBO != 0)
{
glDeleteBuffers(1, &VBO);
VBO = 0;
}
if (VAO != 0)
{
glDeleteVertexArrays(1, &VAO);
VAO = 0;
}
indexCount = 0;
}
Mesh::~Mesh()
{
ClearMesh();
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
To implement the class into the program we do:
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <cmath>
#include <GL\glew.h>
#include <GLFW\glfw3.h>
#include <vector>
#include <glm\glm.hpp>
#include <glm\gtc\matrix_transform.hpp>
#include <glm\gtc\type_ptr.hpp>
#include <fstream>
#include <sstream>
#include "Mesh.h"
//Checking for errors
#define ASSERT(x) if (!(x)) __debugbreak();
#define GLCall(x) GLClearError();\
x;\
ASSERT(GLLogCall(#x, __FILE__, __LINE__))
static void GLClearError()
{
while (glGetError() != GL_NO_ERROR);
}
static bool GLLogCall(const char* function, const char* file, int line)
{
while (GLenum error = glGetError())
{
std::cout << "[OpenGL Error] (" << error << ")" << function <<
" " << file << ":" << line << std::endl;
return false;
}
return true;
}
//Window dimensions
const GLint WIDTH = 800, HEIGHT = 600;
const float toRadians = 3.14159265f / 180.0f;
std::vector<Mesh*> meshList;
GLuint shader, uniformModel, uniformProjection;
bool direction = true;
float triOffset = 0.0f;
float triMaxoffset = 0.7f;
float triIncrement = 0.02f;
float curAngle = 0.0f;
bool sizeDirection = true;
float curSize = 0.4f;
float maxSize = 0.8f;
float minSize = 0.1f;
// Vertex Shader
static const char* vShader = " \n\
#version 410 \n\
\n\
layout (location = 0) in vec3 pos; \n\
\n\
out vec4 vCol; \n\
\n\
uniform mat4 model; \n\
uniform mat4 projection; \n\
\n\
void main() \n\
{ \n\
gl_Position = projection * model * vec4(pos, 1.0); \n\
vCol = vec4(clamp(pos, 0.0f, 1.0f), 1.0f); \n\
}";
// clamp make th values outside 0 and 1 to be inside 0 and 1
static const char* fShader = " \n\
#version 410 \n\
\n\
in vec4 vCol; \n\
\n\
out vec4 colour; \n\
\n\
layout (location = 0) in vec3 pos; \n\
\n\
void main() \n\
{ \n\
colour = vCol; \n\
}";
/*!
Here we are going to create a VAO (Vertex Array Object) and a VBO (Vertex Buffer Object)
The VAO will hold multiples VBOs and other types of buffer to define how a triangle should be drawn,
In this funciton is just the vertices of the triangles.
*/
void CreateTriangle()
{
unsigned int indices[] = {
0, 3, 1,
1, 3, 2,
2, 3, 0,
0, 1, 2
};
// Triangle Points in an array of floats (The center of the screen is 0,0)
GLfloat vertices[] = {
-1.0f, -1.0f, 0.0f,
0.0f, -1.0f, 1.0f,
1.0f, -1.0f, 0.0f,
0.0f, 1.0f, 0.0f
};
Mesh *obj1 = new Mesh(); // Make sure to use pointers
obj1->CreateMesh(vertices, indices, 12, 12);
meshList.push_back(obj1); // add to the end of the list
std::cout << "meslist: " << meshList[0] << std::endl;
}
/*!
Add the shaders to the program funtion
*/
void AddShader(GLuint theProgram, const char* shaderCode, GLenum shaderType)
{
GLuint theShader = glCreateShader(shaderType); // Creating an empty shader for that type
const GLchar* theCode[1]; // Passing a pointer to the first element of GLchar, so we can pass a pointer directly to it
theCode[0] = shaderCode;
GLint codeLength[1]; // Hold the length of the code
codeLength[0] = strlen(shaderCode);
glShaderSource(theShader, 1, theCode, codeLength); // Now the empty shader is modified to have this code
glCompileShader(theShader); // compile the shader
GLint result = 0;
GLchar eLog[1024] = { 0 };
// Error check before attach the shader to the program
glGetShaderiv(theShader, GL_COMPILE_STATUS, &result);
if (!result)
{
glGetShaderInfoLog(theShader, sizeof(eLog), NULL, eLog);
printf("Error compiling the %d shader: '%s'\n", shaderType, eLog);
return;
}
glAttachShader(theProgram, theShader);
}
/*!
*/
void CompileShaders() {
shader = glCreateProgram(); // Create the program and give shader the ID, then we can modify the program and do something with it
// Make sure the shader was created correctly
if (!shader) {
printf("Error creating shader program!\n");
return;
}
//Add the shaders to the program using the function
AddShader(shader, vShader, GL_VERTEX_SHADER);
AddShader(shader, fShader, GL_FRAGMENT_SHADER);
// Getting the error codes, with the results of the two functions we are about to perform and a place to put the errors
GLint result = 0;
GLchar eLog[1024] = { 0 };
glLinkProgram(shader); // Create the exectables in the graphics card.
// Make sure the program is linked properlly
glGetProgramiv(shader, GL_LINK_STATUS, &result);
if (!result)
{
glGetProgramInfoLog(shader, sizeof(eLog), NULL, eLog); // Getting the InfoLog for this program, passing the eLog char array and then print
printf("Error linking program: '%s'\n", eLog);
return;
}
// Validating if the program is valid on the current context that OpenGL is working
glValidateProgram(shader);
glGetProgramiv(shader, GL_VALIDATE_STATUS, &result);
if (!result)
{
glGetProgramInfoLog(shader, sizeof(eLog), NULL, eLog);
printf("Error validating program: '%s'\n", eLog);
return;
}
// Get the actual ID or location of the uniform variable
uniformModel = glGetUniformLocation(shader, "model"); // uniformModel will be the location of the model matrix
uniformProjection = glGetUniformLocation(shader, "projection"); // Get the projection variable and place the ID of the location intp the variable uniformProjection
}
int main()
{
//Initialize GLFW
if (!glfwInit()) {
std::cout << "GLFW Initialization failed!";
glfwTerminate();
}
//Setup GLFW window properties
//OpenGL version
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
//Core Profile = No Backwards Compatibility
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
//Allow forward compatibility
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
GLFWwindow* mainWindow = glfwCreateWindow(WIDTH, HEIGHT, "Test Window", NULL, NULL);
if (!mainWindow) {
std::cout << " GLFW window creation failed!";
glfwTerminate();
}
// Get Buffer size information
int bufferWidth, bufferHeight;
glfwGetFramebufferSize(mainWindow, &bufferWidth, &bufferHeight);
//Set the context for GLEW to use
glfwMakeContextCurrent(mainWindow);
//Allow modern extension features
glewExperimental = GL_TRUE;
if (glewInit() != GLEW_OK) {
std::cout << "GLEW Initialization failed!";
glfwDestroyWindow(mainWindow);
glfwTerminate();
}
glEnable(GL_DEPTH_TEST);
//Setup viewport size
glViewport(0, 0, bufferWidth, bufferHeight);
CreateTriangle();
CompileShaders();
glm::mat4 projection = glm::perspective(45.0f, (GLfloat)bufferWidth / (GLfloat)bufferHeight, 0.1f, 100.0f); //(field of view, aspect(width/height), zNear, ZFar
// LLop until window close
while (!glfwWindowShouldClose(mainWindow)) {
// Get + Handle user input events
glfwPollEvents();
// Checking the direction
if (direction)
{
triOffset += triIncrement;
}
else {
triOffset -= triIncrement;
}
// Check if the location is between max and min offset
if (abs(triOffset) >= triMaxoffset) {
direction = !direction; // Easy way of doing a bool
}
curAngle += 1.0f;
if (curAngle >= 360)
{
curAngle -= 360;
}
if (sizeDirection)
{
curSize += 0.001f;
}
else {
curSize -= 0.001f;
}
if (curSize >= maxSize || curSize <= minSize) {
sizeDirection = !sizeDirection;
}
// Clear window
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear the color bubber bit and also the depth buffer bit, now the colors willbe correct.
// Draw the triangle
glUseProgram(shader); // This will now grab that ID, It will go through the graphics card and tell it to use the one with the ID of shader
glm::mat4 model(1.0f); // Remenber, You have to initialize the model matrix variable glm::mat4 model with (1.0f) for a identity matrix
model = glm::translate(model, glm::vec3(triOffset, 0.0f, -2.5f)); // Applying a translation that will only be changing the x value.
model = glm::rotate(model, curAngle * toRadians, glm::vec3(0.0f, 1.0f, 0.0f));
model = glm::scale(model, glm::vec3(0.4f, 0.4f, 1.0f));
// First is the location, then quantity, then we don't want to transpose
// Last we pass the value pointer, because the model is not exactly in a format that will work with the shader
glUniformMatrix4fv(uniformModel, 1, GL_FALSE, glm::value_ptr(model));
glUniformMatrix4fv(uniformProjection, 1, GL_FALSE, glm::value_ptr(projection));
// Rendering using the class
GLCall(meshList[0]->RenderMesh());
glUseProgram(0); // Unassign the shader
glfwSwapBuffers(mainWindow);
}
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
# Multiple Objects
To include a second object we can do:
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <cmath>
#include <GL\glew.h>
#include <GLFW\glfw3.h>
#include <vector>
#include <glm\glm.hpp>
#include <glm\gtc\matrix_transform.hpp>
#include <glm\gtc\type_ptr.hpp>
#include <fstream>
#include <sstream>
#include "Mesh.h"
//Checking for errors
#define ASSERT(x) if (!(x)) __debugbreak();
#define GLCall(x) GLClearError();\
x;\
ASSERT(GLLogCall(#x, __FILE__, __LINE__))
static void GLClearError()
{
while (glGetError() != GL_NO_ERROR);
}
static bool GLLogCall(const char* function, const char* file, int line)
{
while (GLenum error = glGetError())
{
std::cout << "[OpenGL Error] (" << error << ")" << function <<
" " << file << ":" << line << std::endl;
return false;
}
return true;
}
//Window dimensions
const GLint WIDTH = 800, HEIGHT = 600;
const float toRadians = 3.14159265f / 180.0f;
std::vector<Mesh*> meshList;
GLuint shader, uniformModel, uniformProjection;
bool direction = true;
float triOffset = 0.0f;
float triMaxoffset = 0.7f;
float triIncrement = 0.02f;
float curAngle = 0.0f;
bool sizeDirection = true;
float curSize = 0.4f;
float maxSize = 0.8f;
float minSize = 0.1f;
// Vertex Shader
static const char* vShader = " \n\
#version 410 \n\
\n\
layout (location = 0) in vec3 pos; \n\
\n\
out vec4 vCol; \n\
\n\
uniform mat4 model; \n\
uniform mat4 projection; \n\
\n\
void main() \n\
{ \n\
gl_Position = projection * model * vec4(pos, 1.0); \n\
vCol = vec4(clamp(pos, 0.0f, 1.0f), 1.0f); \n\
}";
// clamp make th values outside 0 and 1 to be inside 0 and 1
static const char* fShader = " \n\
#version 410 \n\
\n\
in vec4 vCol; \n\
\n\
out vec4 colour; \n\
\n\
layout (location = 0) in vec3 pos; \n\
\n\
void main() \n\
{ \n\
colour = vCol; \n\
}";
/*!
Here we are going to create a VAO (Vertex Array Object) and a VBO (Vertex Buffer Object)
The VAO will hold multiples VBOs and other types of buffer to define how a triangle should be drawn,
In this funciton is just the vertices of the triangles.
*/
void CreateTriangle()
{
unsigned int indices[] = {
0, 3, 1,
1, 3, 2,
2, 3, 0,
0, 1, 2
};
// Triangle Points in an array of floats (The center of the screen is 0,0)
GLfloat vertices[] = {
-1.0f, -1.0f, 0.0f,
0.0f, -1.0f, 1.0f,
1.0f, -1.0f, 0.0f,
0.0f, 1.0f, 0.0f
};
Mesh *obj1 = new Mesh(); // Make sure to use pointers
obj1->CreateMesh(vertices, indices, 12, 12);
meshList.push_back(obj1); // add to the end of the list
Mesh* obj2 = new Mesh(); // Make sure to use pointers
obj2->CreateMesh(vertices, indices, 12, 12);
meshList.push_back(obj2); // add to the end of the list
}
/*!
Add the shaders to the program funtion
*/
void AddShader(GLuint theProgram, const char* shaderCode, GLenum shaderType)
{
GLuint theShader = glCreateShader(shaderType); // Creating an empty shader for that type
const GLchar* theCode[1]; // Passing a pointer to the first element of GLchar, so we can pass a pointer directly to it
theCode[0] = shaderCode;
GLint codeLength[1]; // Hold the length of the code
codeLength[0] = strlen(shaderCode);
glShaderSource(theShader, 1, theCode, codeLength); // Now the empty shader is modified to have this code
glCompileShader(theShader); // compile the shader
GLint result = 0;
GLchar eLog[1024] = { 0 };
// Error check before attach the shader to the program
glGetShaderiv(theShader, GL_COMPILE_STATUS, &result);
if (!result)
{
glGetShaderInfoLog(theShader, sizeof(eLog), NULL, eLog);
printf("Error compiling the %d shader: '%s'\n", shaderType, eLog);
return;
}
glAttachShader(theProgram, theShader);
}
/*!
*/
void CompileShaders() {
shader = glCreateProgram(); // Create the program and give shader the ID, then we can modify the program and do something with it
// Make sure the shader was created correctly
if (!shader) {
printf("Error creating shader program!\n");
return;
}
//Add the shaders to the program using the function
AddShader(shader, vShader, GL_VERTEX_SHADER);
AddShader(shader, fShader, GL_FRAGMENT_SHADER);
// Getting the error codes, with the results of the two functions we are about to perform and a place to put the errors
GLint result = 0;
GLchar eLog[1024] = { 0 };
glLinkProgram(shader); // Create the exectables in the graphics card.
// Make sure the program is linked properlly
glGetProgramiv(shader, GL_LINK_STATUS, &result);
if (!result)
{
glGetProgramInfoLog(shader, sizeof(eLog), NULL, eLog); // Getting the InfoLog for this program, passing the eLog char array and then print
printf("Error linking program: '%s'\n", eLog);
return;
}
// Validating if the program is valid on the current context that OpenGL is working
glValidateProgram(shader);
glGetProgramiv(shader, GL_VALIDATE_STATUS, &result);
if (!result)
{
glGetProgramInfoLog(shader, sizeof(eLog), NULL, eLog);
printf("Error validating program: '%s'\n", eLog);
return;
}
// Get the actual ID or location of the uniform variable
uniformModel = glGetUniformLocation(shader, "model"); // uniformModel will be the location of the model matrix
uniformProjection = glGetUniformLocation(shader, "projection"); // Get the projection variable and place the ID of the location intp the variable uniformProjection
}
int main()
{
//Initialize GLFW
if (!glfwInit()) {
std::cout << "GLFW Initialization failed!";
glfwTerminate();
}
//Setup GLFW window properties
//OpenGL version
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
//Core Profile = No Backwards Compatibility
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
//Allow forward compatibility
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
GLFWwindow* mainWindow = glfwCreateWindow(WIDTH, HEIGHT, "Test Window", NULL, NULL);
if (!mainWindow) {
std::cout << " GLFW window creation failed!";
glfwTerminate();
}
// Get Buffer size information
int bufferWidth, bufferHeight;
glfwGetFramebufferSize(mainWindow, &bufferWidth, &bufferHeight);
//Set the context for GLEW to use
glfwMakeContextCurrent(mainWindow);
//Allow modern extension features
glewExperimental = GL_TRUE;
if (glewInit() != GLEW_OK) {
std::cout << "GLEW Initialization failed!";
glfwDestroyWindow(mainWindow);
glfwTerminate();
}
glEnable(GL_DEPTH_TEST);
//Setup viewport size
glViewport(0, 0, bufferWidth, bufferHeight);
CreateTriangle();
CompileShaders();
glm::mat4 projection = glm::perspective(45.0f, (GLfloat)bufferWidth / (GLfloat)bufferHeight, 0.1f, 100.0f); //(field of view, aspect(width/height), zNear, ZFar
// LLop until window close
while (!glfwWindowShouldClose(mainWindow)) {
// Get + Handle user input events
glfwPollEvents();
// Checking the direction
if (direction)
{
triOffset += triIncrement;
}
else {
triOffset -= triIncrement;
}
// Check if the location is between max and min offset
if (abs(triOffset) >= triMaxoffset) {
direction = !direction; // Easy way of doing a bool
}
curAngle += 1.0f;
if (curAngle >= 360)
{
curAngle -= 360;
}
if (sizeDirection)
{
curSize += 0.001f;
}
else {
curSize -= 0.001f;
}
if (curSize >= maxSize || curSize <= minSize) {
sizeDirection = !sizeDirection;
}
// Clear window
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear the color bubber bit and also the depth buffer bit, now the colors willbe correct.
// Draw the triangle
glUseProgram(shader); // This will now grab that ID, It will go through the graphics card and tell it to use the one with the ID of shader
glm::mat4 model(1.0f); // Remenber, You have to initialize the model matrix variable glm::mat4 model with (1.0f) for a identity matrix
model = glm::translate(model, glm::vec3(triOffset, 0.0f, -2.5f)); // Applying a translation that will only be changing the x value.
//model = glm::rotate(model, curAngle * toRadians, glm::vec3(0.0f, 1.0f, 0.0f));
model = glm::scale(model, glm::vec3(0.4f, 0.4f, 1.0f));
// First is the location, then quantity, then we don't want to transpose
// Last we pass the value pointer, because the model is not exactly in a format that will work with the shader
glUniformMatrix4fv(uniformModel, 1, GL_FALSE, glm::value_ptr(model));
glUniformMatrix4fv(uniformProjection, 1, GL_FALSE, glm::value_ptr(projection));
// Rendering using the class
meshList[0]->RenderMesh();
model = glm::mat4(1.0f);
model = glm::translate(model, glm::vec3(-triOffset, 1.0f, -2.5f));
model = glm::scale(model, glm::vec3(0.4f, 0.4f, 1.0f));
glUniformMatrix4fv(uniformModel, 1, GL_FALSE, glm::value_ptr(model));
meshList[1]->RenderMesh();
glUseProgram(0); // Unassign the shader
glfwSwapBuffers(mainWindow);
}
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
# Shaders Class - Moving Shaders to External Files
First we make the Shaders
Class.
Shader.h
#pragma once
#include <stdio.h>
#include <string>
#include <iostream>
#include <fstream>
#include <GL/glew.h>
class Shader
{
public:
Shader();
void CreateFromString(const char* vertexCode, const char* fragmentCode);
GLuint GetProjectionLocation();
GLuint GetModelLocation();
void UseShader();
void clearShader();
~Shader();
private:
GLuint shaderID, uniformProjection, unifomModel;
void CompileShader(const char* vertexCode, const char* fragmentCode);
void AddShader(GLuint theProgram, const char* shaderCode, GLenum shaderType);
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Shader.cpp
#include "Shader.h"
Shader::Shader()
{
shaderID = 0;
uniformModel = 0;
uniformProjection = 0;
}
void Shader::CreateFromString(const char* vertexCode, const char* fragmentCode)
{
CompileShader(vertexCode, fragmentCode);
}
void Shader::CompileShader(const char* vertexCode, const char* fragmentCode)
{
shaderID = glCreateProgram(); // Create the program and give shader the ID, then we can modify the program and do something with it
// Make sure the shader was created correctly
if (!shaderID) {
printf("Error creating shader program!\n");
return;
}
//Add the shaders to the program using the function
AddShader(shaderID, vertexCode, GL_VERTEX_SHADER);
AddShader(shaderID, fragmentCode, GL_FRAGMENT_SHADER);
// Getting the error codes, with the results of the two functions we are about to perform and a place to put the errors
GLint result = 0;
GLchar eLog[1024] = { 0 };
glLinkProgram(shaderID); // Create the exectables in the graphics card.
// Make sure the program is linked properlly
glGetProgramiv(shaderID, GL_LINK_STATUS, &result);
if (!result)
{
glGetProgramInfoLog(shaderID, sizeof(eLog), NULL, eLog); // Getting the InfoLog for this program, passing the eLog char array and then print
printf("Error linking program: '%s'\n", eLog);
return;
}
// Validating if the program is valid on the current context that OpenGL is working
glValidateProgram(shaderID);
glGetProgramiv(shaderID, GL_VALIDATE_STATUS, &result);
if (!result)
{
glGetProgramInfoLog(shaderID, sizeof(eLog), NULL, eLog);
printf("Error validating program: '%s'\n", eLog);
return;
}
// Get the actual ID or location of the uniform variable
uniformModel = glGetUniformLocation(shaderID, "model"); // uniformModel will be the location of the model matrix
uniformProjection = glGetUniformLocation(shaderID, "projection"); // Get the projection variable and place the ID of the location intp the variable uniformProjection
}
//Getters of uniformProjectin and uniformModel
GLuint Shader::GetProjectionLocation()
{
return uniformProjection;
}
GLuint Shader::GetModelLocation()
{
return uniformModel;
}
void Shader::UseShader()
{
glUseProgram(shaderID);
}
void Shader::clearShader()
{
if (shaderID != 0)
{
glDeleteProgram(shaderID); //This is deleting the program of the graphics card
shaderID = 0;
}
uniformModel = 0;
uniformProjection = 0;
}
void Shader::AddShader(GLuint theProgram, const char* shaderCode, GLenum shaderType)
{
GLuint theShader = glCreateShader(shaderType); // Creating an empty shader for that type
const GLchar* theCode[1]; // Passing a pointer to the first element of GLchar, so we can pass a pointer directly to it
theCode[0] = shaderCode;
GLint codeLength[1]; // Hold the length of the code
codeLength[0] = strlen(shaderCode);
glShaderSource(theShader, 1, theCode, codeLength); // Now the empty shader is modified to have this code
glCompileShader(theShader); // compile the shader
GLint result = 0;
GLchar eLog[1024] = { 0 };
// Error check before attach the shader to the program
glGetShaderiv(theShader, GL_COMPILE_STATUS, &result);
if (!result)
{
glGetShaderInfoLog(theShader, sizeof(eLog), NULL, eLog);
printf("Error compiling the %d shader: '%s'\n", shaderType, eLog);
return;
}
glAttachShader(theProgram, theShader);
}
Shader::~Shader()
{
clearShader();
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
Using it on the program
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <cmath>
#include <GL\glew.h>
#include <GLFW\glfw3.h>
#include <vector>
#include <glm\glm.hpp>
#include <glm\gtc\matrix_transform.hpp>
#include <glm\gtc\type_ptr.hpp>
#include <fstream>
#include <sstream>
#include "Mesh.h"
#include "Shader.h"
//Checking for errors
#define ASSERT(x) if (!(x)) __debugbreak();
#define GLCall(x) GLClearError();\
x;\
ASSERT(GLLogCall(#x, __FILE__, __LINE__))
static void GLClearError()
{
while (glGetError() != GL_NO_ERROR);
}
static bool GLLogCall(const char* function, const char* file, int line)
{
while (GLenum error = glGetError())
{
std::cout << "[OpenGL Error] (" << error << ")" << function <<
" " << file << ":" << line << std::endl;
return false;
}
return true;
}
//Window dimensions
const GLint WIDTH = 800, HEIGHT = 600;
const float toRadians = 3.14159265f / 180.0f;
std::vector<Mesh*> meshList;
std::vector<Shader*> shaderList;
bool direction = true;
float triOffset = 0.0f;
float triMaxoffset = 0.7f;
float triIncrement = 0.02f;
float curAngle = 0.0f;
bool sizeDirection = true;
float curSize = 0.4f;
float maxSize = 0.8f;
float minSize = 0.1f;
// Vertex Shader
static const char* vShader = " \n\
#version 410 \n\
\n\
layout (location = 0) in vec3 pos; \n\
\n\
out vec4 vCol; \n\
\n\
uniform mat4 model; \n\
uniform mat4 projection; \n\
\n\
void main() \n\
{ \n\
gl_Position = projection * model * vec4(pos, 1.0); \n\
vCol = vec4(clamp(pos, 0.0f, 1.0f), 1.0f); \n\
}";
// clamp make th values outside 0 and 1 to be inside 0 and 1
static const char* fShader = " \n\
#version 410 \n\
\n\
in vec4 vCol; \n\
\n\
out vec4 colour; \n\
\n\
layout (location = 0) in vec3 pos; \n\
\n\
void main() \n\
{ \n\
colour = vCol; \n\
}";
/*!
Here we are going to create a VAO (Vertex Array Object) and a VBO (Vertex Buffer Object)
The VAO will hold multiples VBOs and other types of buffer to define how a triangle should be drawn,
In this funciton is just the vertices of the triangles.
*/
void CreateObjects()
{
unsigned int indices[] = {
0, 3, 1,
1, 3, 2,
2, 3, 0,
0, 1, 2
};
// Triangle Points in an array of floats (The center of the screen is 0,0)
GLfloat vertices[] = {
-1.0f, -1.0f, 0.0f,
0.0f, -1.0f, 1.0f,
1.0f, -1.0f, 0.0f,
0.0f, 1.0f, 0.0f
};
Mesh *obj1 = new Mesh(); // Make sure to use pointers
obj1->CreateMesh(vertices, indices, 12, 12);
meshList.push_back(obj1); // add to the end of the list
Mesh* obj2 = new Mesh(); // Make sure to use pointers
obj2->CreateMesh(vertices, indices, 12, 12);
meshList.push_back(obj2); // add to the end of the list
}
void CreateShaders()
{
Shader *shader1 = new Shader();
shader1->CreateFromString(vShader, fShader);
shaderList.push_back(shader1);
}
int main()
{
//Initialize GLFW
if (!glfwInit()) {
std::cout << "GLFW Initialization failed!";
glfwTerminate();
}
//Setup GLFW window properties
//OpenGL version
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
//Core Profile = No Backwards Compatibility
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
//Allow forward compatibility
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
GLFWwindow* mainWindow = glfwCreateWindow(WIDTH, HEIGHT, "Test Window", NULL, NULL);
if (!mainWindow) {
std::cout << " GLFW window creation failed!";
glfwTerminate();
}
// Get Buffer size information
int bufferWidth, bufferHeight;
glfwGetFramebufferSize(mainWindow, &bufferWidth, &bufferHeight);
//Set the context for GLEW to use
glfwMakeContextCurrent(mainWindow);
//Allow modern extension features
glewExperimental = GL_TRUE;
if (glewInit() != GLEW_OK) {
std::cout << "GLEW Initialization failed!";
glfwDestroyWindow(mainWindow);
glfwTerminate();
}
glEnable(GL_DEPTH_TEST);
//Setup viewport size
glViewport(0, 0, bufferWidth, bufferHeight);
CreateObjects();
CreateShaders();
GLuint uniformProjection = 0, uniformModel = 0;
glm::mat4 projection = glm::perspective(45.0f, (GLfloat)bufferWidth / (GLfloat)bufferHeight, 0.1f, 100.0f); //(field of view, aspect(width/height), zNear, ZFar
// LLop until window close
while (!glfwWindowShouldClose(mainWindow)) {
// Get + Handle user input events
glfwPollEvents();
// Checking the direction
if (direction)
{
triOffset += triIncrement;
}
else {
triOffset -= triIncrement;
}
// Check if the location is between max and min offset
if (abs(triOffset) >= triMaxoffset) {
direction = !direction; // Easy way of doing a bool
}
curAngle += 1.0f;
if (curAngle >= 360)
{
curAngle -= 360;
}
if (sizeDirection)
{
curSize += 0.001f;
}
else {
curSize -= 0.001f;
}
if (curSize >= maxSize || curSize <= minSize) {
sizeDirection = !sizeDirection;
}
// Clear window
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear the color bubber bit and also the depth buffer bit, now the colors willbe correct.
// Draw the triangle
shaderList[0]->UseShader();
uniformModel = shaderList[0]->GetModelLocation();
uniformProjection = shaderList[0]->GetProjectionLocation();
glm::mat4 model(1.0f); // Remenber, You have to initialize the model matrix variable glm::mat4 model with (1.0f) for a identity matrix
model = glm::translate(model, glm::vec3(triOffset, 0.0f, -2.5f)); // Applying a translation that will only be changing the x value.
//model = glm::rotate(model, curAngle * toRadians, glm::vec3(0.0f, 1.0f, 0.0f));
model = glm::scale(model, glm::vec3(0.4f, 0.4f, 1.0f));
// First is the location, then quantity, then we don't want to transpose
// Last we pass the value pointer, because the model is not exactly in a format that will work with the shader
glUniformMatrix4fv(uniformModel, 1, GL_FALSE, glm::value_ptr(model));
glUniformMatrix4fv(uniformProjection, 1, GL_FALSE, glm::value_ptr(projection));
// Rendering using the class
meshList[0]->RenderMesh();
model = glm::mat4(1.0f);
model = glm::translate(model, glm::vec3(-triOffset, 1.0f, -2.5f));
model = glm::scale(model, glm::vec3(0.4f, 0.4f, 1.0f));
glUniformMatrix4fv(uniformModel, 1, GL_FALSE, glm::value_ptr(model));
meshList[1]->RenderMesh();
glUseProgram(0); // Unassign the shader
glfwSwapBuffers(mainWindow);
}
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
Now Let's put the Shaders in a folder and adjust a little bit of the code:
First we now point to the location of the files.
static const char* vShader = "Shaders/shader.vert";
static const char* fShader = "Shaders/shader.frag";
2
Then we change the Shader.h to include new functions CreateFromFiles
and ReadFile
.
#pragma once
#include <stdio.h>
#include <string>
#include <iostream>
#include <fstream>
#include <GL/glew.h>
class Shader
{
public:
Shader();
void CreateFromString(const char* vertexCode, const char* fragmentCode);
void CreateFromFiles(const char* vertexLocation, const char* fragmentLocation);
std::string ReadFile(const char* fileLocation);
GLuint GetProjectionLocation();
GLuint GetModelLocation();
void UseShader();
void clearShader();
~Shader();
private:
GLuint shaderID, uniformProjection, uniformModel;
void CompileShader(const char* vertexCode, const char* fragmentCode);
void AddShader(GLuint theProgram, const char* shaderCode, GLenum shaderType);
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
And now we implement the functions
void Shader::CreateFromString(const char* vertexCode, const char* fragmentCode)
{
CompileShader(vertexCode, fragmentCode);
}
void Shader::CreateFromFiles(const char* vertexLocation, const char* fragmentLocation)
{
std::string vertexString = ReadFile(vertexLocation);
std::string fragmentString = ReadFile(fragmentLocation);
const char* vertexCode = vertexString.c_str();
const char* fragmentCode = fragmentString.c_str();
CompileShader(vertexCode, fragmentCode);
}
std::string Shader::ReadFile(const char* fileLocation)
{
std::string content;
std::ifstream fileStream(fileLocation, std::ios::in);
if (!fileStream.is_open()) {
printf("Failed to read %s File doesn't exist.", fileLocation);
return "";
}
std::string line = "";
while (!fileStream.eof())
{
std::getline(fileStream, line);
content.append(line + "\n");
}
fileStream.close();
return content;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
And a little change on the main program at the CreateShaders
function:
void CreateShaders()
{
Shader *shader1 = new Shader();
shader1->CreateFromFiles(vShader, fShader);
shaderList.push_back(shader1);
}
2
3
4
5
6
# Create Window Class
First we create the Header:
Window.h
#pragma once
#include <stdio.h>
#include <GL/glew.h>
#include <GLFW/glfw3.h>
class Window
{
public:
Window();
Window(GLint windowWidth, GLint windowHeight);
int Initialize();
GLfloat getBufferWidth() { return bufferWidth; }
GLfloat getBufferHeight() { return bufferHeight; }
bool getShouldClose() { return glfwWindowShouldClose(mainWindow); }
void swapBuffers() { glfwSwapBuffers(mainWindow); }
~Window();
private:
GLFWwindow *mainWindow;
GLint width, height;
GLint bufferWidth, bufferHeight;
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
Window.cpp
#include "Window.h"
#include <iostream>
Window::Window()
{
width = 800;
height = 600;
}
Window::Window(GLint windowWidth, GLint windowHeight)
{
width = windowWidth;
height = windowHeight;
}
int Window::Initialize()
{
//Initialize GLFW
if (!glfwInit()) {
std::cout << "GLFW Initialization failed!";
glfwTerminate();
}
//Setup GLFW window properties
//OpenGL version
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
//Core Profile = No Backwards Compatibility
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
//Allow forward compatibility
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
mainWindow = glfwCreateWindow(width, height, "OpenGL Window", NULL, NULL);
if (!mainWindow) {
std::cout << " GLFW window creation failed!";
glfwTerminate();
return 1;
}
// Get Buffer size information
glfwGetFramebufferSize(mainWindow, &bufferWidth, &bufferHeight);
//Set the context for GLEW to use
glfwMakeContextCurrent(mainWindow);
//Allow modern extension features
glewExperimental = GL_TRUE;
GLenum error = glewInit();
if (error != GLEW_OK) {
std::cout << "GLEW Initialization failed!";
glfwDestroyWindow(mainWindow);
glfwTerminate();
return 1;
}
glEnable(GL_DEPTH_TEST);
//Setup viewport size
glViewport(0, 0, bufferWidth, bufferHeight);
}
Window::~Window()
{
glfwDestroyWindow(mainWindow);
glfwTerminate();
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
and change the main file:
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <cmath>
#include <GL\glew.h>
#include <GLFW\glfw3.h>
#include <vector>
#include <glm\glm.hpp>
#include <glm\gtc\matrix_transform.hpp>
#include <glm\gtc\type_ptr.hpp>
#include <fstream>
#include <sstream>
#include "Window.h"
#include "Mesh.h"
#include "Shader.h"
//Checking for errors
#define ASSERT(x) if (!(x)) __debugbreak();
#define GLCall(x) GLClearError();\
x;\
ASSERT(GLLogCall(#x, __FILE__, __LINE__))
static void GLClearError()
{
while (glGetError() != GL_NO_ERROR);
}
static bool GLLogCall(const char* function, const char* file, int line)
{
while (GLenum error = glGetError())
{
std::cout << "[OpenGL Error] (" << error << ")" << function <<
" " << file << ":" << line << std::endl;
return false;
}
return true;
}
//Window dimensions
const GLint WIDTH = 800, HEIGHT = 600;
const float toRadians = 3.14159265f / 180.0f;
Window mainWindow;
std::vector<Mesh*> meshList;
std::vector<Shader*> shaderList;
bool direction = true;
float triOffset = 0.0f;
float triMaxoffset = 0.7f;
float triIncrement = 0.02f;
float curAngle = 0.0f;
bool sizeDirection = true;
float curSize = 0.4f;
float maxSize = 0.8f;
float minSize = 0.1f;
static const char* vShader = "Shaders/shader.vert";
static const char* fShader = "Shaders/shader.frag";
/*!
Here we are going to create a VAO (Vertex Array Object) and a VBO (Vertex Buffer Object)
The VAO will hold multiples VBOs and other types of buffer to define how a triangle should be drawn,
In this funciton is just the vertices of the triangles.
*/
void CreateObjects()
{
unsigned int indices[] = {
0, 3, 1,
1, 3, 2,
2, 3, 0,
0, 1, 2
};
// Triangle Points in an array of floats (The center of the screen is 0,0)
GLfloat vertices[] = {
-1.0f, -1.0f, 0.0f,
0.0f, -1.0f, 1.0f,
1.0f, -1.0f, 0.0f,
0.0f, 1.0f, 0.0f
};
Mesh *obj1 = new Mesh(); // Make sure to use pointers
obj1->CreateMesh(vertices, indices, 12, 12);
meshList.push_back(obj1); // add to the end of the list
Mesh* obj2 = new Mesh(); // Make sure to use pointers
obj2->CreateMesh(vertices, indices, 12, 12);
meshList.push_back(obj2); // add to the end of the list
}
void CreateShaders()
{
Shader *shader1 = new Shader();
shader1->CreateFromFiles(vShader, fShader);
shaderList.push_back(shader1);
}
int main()
{
mainWindow = Window(800, 600);
mainWindow.Initialize();
CreateObjects();
CreateShaders();
GLuint uniformProjection = 0, uniformModel = 0;
glm::mat4 projection = glm::perspective(45.0f, mainWindow.getBufferWidth() / mainWindow.getBufferHeight(), 0.1f, 100.0f); //(field of view, aspect(width/height), zNear, ZFar
// LLop until window close
while (!mainWindow.getShouldClose()) {
// Get + Handle user input events
glfwPollEvents();
// Checking the direction
if (direction)
{
triOffset += triIncrement;
}
else {
triOffset -= triIncrement;
}
// Check if the location is between max and min offset
if (abs(triOffset) >= triMaxoffset) {
direction = !direction; // Easy way of doing a bool
}
curAngle += 1.0f;
if (curAngle >= 360)
{
curAngle -= 360;
}
if (sizeDirection)
{
curSize += 0.001f;
}
else {
curSize -= 0.001f;
}
if (curSize >= maxSize || curSize <= minSize) {
sizeDirection = !sizeDirection;
}
// Clear window
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear the color bubber bit and also the depth buffer bit, now the colors willbe correct.
// Draw the triangle
shaderList[0]->UseShader();
uniformModel = shaderList[0]->GetModelLocation();
uniformProjection = shaderList[0]->GetProjectionLocation();
glm::mat4 model(1.0f); // Remenber, You have to initialize the model matrix variable glm::mat4 model with (1.0f) for a identity matrix
model = glm::translate(model, glm::vec3(triOffset, 0.0f, -2.5f)); // Applying a translation that will only be changing the x value.
//model = glm::rotate(model, curAngle * toRadians, glm::vec3(0.0f, 1.0f, 0.0f));
model = glm::scale(model, glm::vec3(0.4f, 0.4f, 1.0f));
// First is the location, then quantity, then we don't want to transpose
// Last we pass the value pointer, because the model is not exactly in a format that will work with the shader
glUniformMatrix4fv(uniformModel, 1, GL_FALSE, glm::value_ptr(model));
glUniformMatrix4fv(uniformProjection, 1, GL_FALSE, glm::value_ptr(projection));
// Rendering using the class
meshList[0]->RenderMesh();
model = glm::mat4(1.0f);
model = glm::translate(model, glm::vec3(-triOffset, 1.0f, -2.5f));
model = glm::scale(model, glm::vec3(0.4f, 0.4f, 1.0f));
glUniformMatrix4fv(uniformModel, 1, GL_FALSE, glm::value_ptr(model));
meshList[1]->RenderMesh();
glUseProgram(0); // Unassign the shader
mainWindow.swapBuffers();
}
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
# Cleanup final code
Finish the cleanup and excluding the moving features, final code:
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <cmath>
#include <GL\glew.h>
#include <GLFW\glfw3.h>
#include <vector>
#include <glm\glm.hpp>
#include <glm\gtc\matrix_transform.hpp>
#include <glm\gtc\type_ptr.hpp>
#include "Window.h"
#include "Mesh.h"
#include "Shader.h"
//Checking for errors
#define ASSERT(x) if (!(x)) __debugbreak();
#define GLCall(x) GLClearError();\
x;\
ASSERT(GLLogCall(#x, __FILE__, __LINE__))
static void GLClearError()
{
while (glGetError() != GL_NO_ERROR);
}
static bool GLLogCall(const char* function, const char* file, int line)
{
while (GLenum error = glGetError())
{
std::cout << "[OpenGL Error] (" << error << ")" << function <<
" " << file << ":" << line << std::endl;
return false;
}
return true;
}
const float toRadians = 3.14159265f / 180.0f;
Window mainWindow;
std::vector<Mesh*> meshList;
std::vector<Shader*> shaderList;
static const char* vShader = "Shaders/shader.vert";
static const char* fShader = "Shaders/shader.frag";
/*!
Here we are going to create a VAO (Vertex Array Object) and a VBO (Vertex Buffer Object)
The VAO will hold multiples VBOs and other types of buffer to define how a triangle should be drawn,
In this funciton is just the vertices of the triangles.
*/
void CreateObjects()
{
unsigned int indices[] = {
0, 3, 1,
1, 3, 2,
2, 3, 0,
0, 1, 2
};
// Triangle Points in an array of floats (The center of the screen is 0,0)
GLfloat vertices[] = {
-1.0f, -1.0f, 0.0f,
0.0f, -1.0f, 1.0f,
1.0f, -1.0f, 0.0f,
0.0f, 1.0f, 0.0f
};
Mesh *obj1 = new Mesh(); // Make sure to use pointers
obj1->CreateMesh(vertices, indices, 12, 12);
meshList.push_back(obj1); // add to the end of the list
Mesh* obj2 = new Mesh(); // Make sure to use pointers
obj2->CreateMesh(vertices, indices, 12, 12);
meshList.push_back(obj2); // add to the end of the list
}
void CreateShaders()
{
Shader *shader1 = new Shader();
shader1->CreateFromFiles(vShader, fShader);
shaderList.push_back(shader1);
}
int main()
{
mainWindow = Window(800, 600);
mainWindow.Initialize();
CreateObjects();
CreateShaders();
GLuint uniformProjection = 0, uniformModel = 0;
glm::mat4 projection = glm::perspective(45.0f, mainWindow.getBufferWidth() / mainWindow.getBufferHeight(), 0.1f, 100.0f); //(field of view, aspect(width/height), zNear, ZFar
// LLop until window close
while (!mainWindow.getShouldClose()) {
// Get + Handle user input events
glfwPollEvents();
// Clear window
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear the color bubber bit and also the depth buffer bit, now the colors willbe correct.
// Draw the triangle
shaderList[0]->UseShader();
uniformModel = shaderList[0]->GetModelLocation();
uniformProjection = shaderList[0]->GetProjectionLocation();
glm::mat4 model(1.0f); // Remenber, You have to initialize the model matrix variable glm::mat4 model with (1.0f) for a identity matrix
model = glm::translate(model, glm::vec3(0.0f, 0.0f, -2.5f)); // Applying a translation that will only be changing the x value.
//model = glm::rotate(model, curAngle * toRadians, glm::vec3(0.0f, 1.0f, 0.0f));
model = glm::scale(model, glm::vec3(0.4f, 0.4f, 1.0f));
// First is the location, then quantity, then we don't want to transpose
// Last we pass the value pointer, because the model is not exactly in a format that will work with the shader
glUniformMatrix4fv(uniformModel, 1, GL_FALSE, glm::value_ptr(model));
glUniformMatrix4fv(uniformProjection, 1, GL_FALSE, glm::value_ptr(projection));
// Rendering using the class
meshList[0]->RenderMesh();
model = glm::mat4(1.0f);
model = glm::translate(model, glm::vec3(0.0f, 1.0f, -2.5f));
model = glm::scale(model, glm::vec3(0.4f, 0.4f, 1.0f));
glUniformMatrix4fv(uniformModel, 1, GL_FALSE, glm::value_ptr(model));
meshList[1]->RenderMesh();
glUseProgram(0); // Unassign the shader
mainWindow.swapBuffers();
}
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
Window.h
#pragma once
#include <stdio.h>
#include <GL/glew.h>
#include <GLFW/glfw3.h>
class Window
{
public:
Window();
Window(GLint windowWidth, GLint windowHeight);
int Initialize();
GLfloat getBufferWidth() { return bufferWidth; }
GLfloat getBufferHeight() { return bufferHeight; }
bool getShouldClose() { return glfwWindowShouldClose(mainWindow); }
void swapBuffers() { glfwSwapBuffers(mainWindow); }
~Window();
private:
GLFWwindow *mainWindow;
GLint width, height;
GLint bufferWidth, bufferHeight;
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
Window.cpp
#include "Window.h"
#include <iostream>
Window::Window()
{
width = 800;
height = 600;
}
Window::Window(GLint windowWidth, GLint windowHeight)
{
width = windowWidth;
height = windowHeight;
}
int Window::Initialize()
{
//Initialize GLFW
if (!glfwInit()) {
std::cout << "GLFW Initialization failed!";
glfwTerminate();
}
//Setup GLFW window properties
//OpenGL version
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
//Core Profile = No Backwards Compatibility
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
//Allow forward compatibility
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
mainWindow = glfwCreateWindow(width, height, "OpenGL Window", NULL, NULL);
if (!mainWindow) {
std::cout << " GLFW window creation failed!";
glfwTerminate();
return 1;
}
// Get Buffer size information
glfwGetFramebufferSize(mainWindow, &bufferWidth, &bufferHeight);
//Set the context for GLEW to use
glfwMakeContextCurrent(mainWindow);
//Allow modern extension features
glewExperimental = GL_TRUE;
GLenum error = glewInit();
if (error != GLEW_OK) {
std::cout << "GLEW Initialization failed!";
glfwDestroyWindow(mainWindow);
glfwTerminate();
return 1;
}
glEnable(GL_DEPTH_TEST);
//Setup viewport size
glViewport(0, 0, bufferWidth, bufferHeight);
}
Window::~Window()
{
glfwDestroyWindow(mainWindow);
glfwTerminate();
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
Mesh.h
#pragma once
#include <GL\glew.h>
class Mesh
{
public:
Mesh();
void CreateMesh(GLfloat *vertices, unsigned int *indices, unsigned int numofVertices, unsigned int numofIndices);
void RenderMesh();
void ClearMesh();
~Mesh();
private:
GLuint VAO, VBO, IBO;
GLsizei indexCount; // to say how many indeces we have to draw like here, with 12: glDrawElements(GL_TRIANGLES, 12, GL_UNSIGNED_INT, 0);
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Mesh.cpp
#include "Mesh.h"
Mesh::Mesh()
{
VAO = 0;
VBO = 0;
IBO = 0;
indexCount = 0;
}
void Mesh::CreateMesh(GLfloat *vertices, unsigned int *indices, unsigned int numofVertices, unsigned int numofIndices)
{
indexCount = numofIndices;
/*
Creating a VAO
Takes the amount of arrays we want to create and where to store the ID of the array.
When we are calling this function on the graphics card is now creating a vertex array, its defining some space in the memory for some vertex array.
So if you want to access that space in the memory you just pass the ID(VAO)).
*/
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
// Creating another buffer for the indices;
glGenBuffers(1, &IBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices[0]) * numofIndices, indices, GL_STATIC_DRAW);
// Now that we have a VAO we need to create a buffer object to actually go inside of that.
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO); //We have to enter which buffer we want to bind, as VBO has multiple targets it can bind to.
// Now we have to connect the buffer data, the vertices that we created to the GL_BUFFER for this VBO.
// GL_STATIC_DRAW means we are not going to be changing the values we are putting in there, GL_DYNAMIC_DRAW is more complex.
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices[0]) * numofVertices, vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); // First attribute is location of the attribute, then amount of values(x,y,z), then type, then if normalizing(we are not normalizing), then striding value(to jump 3 or whatever to read next value), then offset)
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0); // undoing the binding in the VBO
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindVertexArray(0); //undoing the binding in the VAO
}
void Mesh::RenderMesh()
{
glBindVertexArray(VAO); // We saying now work with this VAO
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);
glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, 0); //draw by the elements IDs
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindVertexArray(0); // Unassign the VAO
}
void Mesh::ClearMesh()
{
if (IBO != 0)
{
glDeleteBuffers(1, &IBO);
IBO = 0;
}
if (VBO != 0)
{
glDeleteBuffers(1, &VBO);
VBO = 0;
}
if (VAO != 0)
{
glDeleteVertexArrays(1, &VAO);
VAO = 0;
}
indexCount = 0;
}
Mesh::~Mesh()
{
ClearMesh();
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
Shader.h
#pragma once
#include <stdio.h>
#include <string>
#include <iostream>
#include <fstream>
#include <GL/glew.h>
class Shader
{
public:
Shader();
void CreateFromString(const char* vertexCode, const char* fragmentCode);
void CreateFromFiles(const char* vertexLocation, const char* fragmentLocation);
std::string ReadFile(const char* fileLocation);
GLuint GetProjectionLocation();
GLuint GetModelLocation();
void UseShader();
void clearShader();
~Shader();
private:
GLuint shaderID, uniformProjection, uniformModel;
void CompileShader(const char* vertexCode, const char* fragmentCode);
void AddShader(GLuint theProgram, const char* shaderCode, GLenum shaderType);
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
Shader.cpp
#include "Shader.h"
Shader::Shader()
{
shaderID = 0;
uniformModel = 0;
uniformProjection = 0;
}
void Shader::CreateFromString(const char* vertexCode, const char* fragmentCode)
{
CompileShader(vertexCode, fragmentCode);
}
void Shader::CreateFromFiles(const char* vertexLocation, const char* fragmentLocation)
{
std::string vertexString = ReadFile(vertexLocation);
std::string fragmentString = ReadFile(fragmentLocation);
const char* vertexCode = vertexString.c_str();
const char* fragmentCode = fragmentString.c_str();
CompileShader(vertexCode, fragmentCode);
}
std::string Shader::ReadFile(const char* fileLocation)
{
std::string content;
std::ifstream fileStream(fileLocation, std::ios::in);
if (!fileStream.is_open()) {
printf("Failed to read %s File doesn't exist.", fileLocation);
return "";
}
std::string line = "";
while (!fileStream.eof())
{
std::getline(fileStream, line);
content.append(line + "\n");
}
fileStream.close();
return content;
}
void Shader::CompileShader(const char* vertexCode, const char* fragmentCode)
{
shaderID = glCreateProgram(); // Create the program and give shader the ID, then we can modify the program and do something with it
// Make sure the shader was created correctly
if (!shaderID) {
printf("Error creating shader program!\n");
return;
}
//Add the shaders to the program using the function
AddShader(shaderID, vertexCode, GL_VERTEX_SHADER);
AddShader(shaderID, fragmentCode, GL_FRAGMENT_SHADER);
// Getting the error codes, with the results of the two functions we are about to perform and a place to put the errors
GLint result = 0;
GLchar eLog[1024] = { 0 };
glLinkProgram(shaderID); // Create the exectables in the graphics card.
// Make sure the program is linked properlly
glGetProgramiv(shaderID, GL_LINK_STATUS, &result);
if (!result)
{
glGetProgramInfoLog(shaderID, sizeof(eLog), NULL, eLog); // Getting the InfoLog for this program, passing the eLog char array and then print
printf("Error linking program: '%s'\n", eLog);
return;
}
// Validating if the program is valid on the current context that OpenGL is working
glValidateProgram(shaderID);
glGetProgramiv(shaderID, GL_VALIDATE_STATUS, &result);
if (!result)
{
glGetProgramInfoLog(shaderID, sizeof(eLog), NULL, eLog);
printf("Error validating program: '%s'\n", eLog);
return;
}
// Get the actual ID or location of the uniform variable
uniformModel = glGetUniformLocation(shaderID, "model"); // uniformModel will be the location of the model matrix
uniformProjection = glGetUniformLocation(shaderID, "projection"); // Get the projection variable and place the ID of the location intp the variable uniformProjection
}
//Getters of uniformProjectin and uniformModel
GLuint Shader::GetProjectionLocation()
{
return uniformProjection;
}
GLuint Shader::GetModelLocation()
{
return uniformModel;
}
void Shader::UseShader()
{
glUseProgram(shaderID);
}
void Shader::clearShader()
{
if (shaderID != 0)
{
glDeleteProgram(shaderID); //This is deleting the program of the graphics card
shaderID = 0;
}
uniformModel = 0;
uniformProjection = 0;
}
void Shader::AddShader(GLuint theProgram, const char* shaderCode, GLenum shaderType)
{
GLuint theShader = glCreateShader(shaderType); // Creating an empty shader for that type
const GLchar* theCode[1]; // Passing a pointer to the first element of GLchar, so we can pass a pointer directly to it
theCode[0] = shaderCode;
GLint codeLength[1]; // Hold the length of the code
codeLength[0] = strlen(shaderCode);
glShaderSource(theShader, 1, theCode, codeLength); // Now the empty shader is modified to have this code
glCompileShader(theShader); // compile the shader
GLint result = 0;
GLchar eLog[1024] = { 0 };
// Error check before attach the shader to the program
glGetShaderiv(theShader, GL_COMPILE_STATUS, &result);
if (!result)
{
glGetShaderInfoLog(theShader, sizeof(eLog), NULL, eLog);
printf("Error compiling the %d shader: '%s'\n", shaderType, eLog);
return;
}
glAttachShader(theProgram, theShader);
}
Shader::~Shader()
{
clearShader();
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157