C++ Demo Project Using OpenGL – Triangular Prism (Interactive)

The code used in the demonstration below was used to create a OpenGL window with a triangular prism object.

The demonstration was created using the resources available at https://learnopengl.com/

The code requires the GLFW, GLAD and glm libraries as dependencies, see the “Hello Triangle” example from the Learn OpenGL website for more details on how to obtain the source files and link the libraries to C++ Compilers in Microsoft Visual Studio.

The code allows for use of the arrow keys to be pressed individually to allow the point of view to be rotated 90 degrees from the initial point of view in the direction the button has been held. However, only one button pressed is registered at a time as part of this code (as of Version 1.0).

The demonstration was created in order to understand and show how to utilise OpenGL using C++ to generate Graphics Windows and Objects with changes to the shape type/colour, introduction of limited interactive actions (Arrow Keys, Escape Key) and the generation of custom shaders for graphics objects.

This code was created to use OpenGL 4.60 hence the use of
#version 460 core

Version 1.0 of the code is included below (Version 2.0: work in progress):

#include <GLAD/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

int WindowWidth = 1920;
int WindowHeight = 1080;

// Vertex Shader Position and Colour using OpenGL version 4.6

const char* vertexShaderSource = "#version 460 core\n"
								 "layout (location=0) in vec3 aPos;\n"
								 "layout (location=1) in vec3 aColor;\n"
								 "out vec3 ourColor;\n"
								 "uniform mat4 projection;\n"
								 "uniform mat4 view;\n"
								 "uniform mat4 model;\n"
								 "void main()\n"
								 "{\n"
								 "gl_Position = projection * view * model * vec4(aPos, 1.0);\n"
								 "ourColor = aColor;\n"
								 "}\0";


// Vertex Shader Passing Colour Data to Fragment Shader using OpenGL version 4.6

const char* fragmentShaderSource = "#version 460 core\n"
								   "out vec4 FragColor;\n"
								   "in vec3 ourColor;\n"
								   "void main()\n"
								   "{\n"
								   "FragColor = vec4(ourColor, 1.0);\n" // Fragment Colour - RGB values and alpha (transparency)
								   "}\0";

void framebuffer_size_callback(GLFWwindow* window, int width, int height) // Function Callback to respond to user
{
	glViewport(0, 0, width, height);
}

void processInput(GLFWwindow* window) // Function to close window when the Escape Key is pressed.
{
	if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) {
		glfwSetWindowShouldClose(window, true);
	}
}

float *camrotation(GLFWwindow* window) // Function to obtain camera rotation and upward direction for lookAt function from User Key Presses.
{
	float camrot[6]{-1,0,0,0,1,0};
    int index = 0;

	if (glfwGetKey(window, GLFW_KEY_UP) == GLFW_PRESS) {
		camrot[index + 1] = 1;
		camrot[index] = 0;
		camrot[index + 4] = 0;
		camrot[index + 3] = 1;
	}
	else {
		if (glfwGetKey(window, GLFW_KEY_DOWN) == GLFW_PRESS) {
			camrot[index + 1] = -1;
			camrot[index] = 0;
			camrot[index + 4] = 0;
			camrot[index + 3] = -1;
		}
		else {
			if (glfwGetKey(window, GLFW_KEY_LEFT) == GLFW_PRESS) {
				camrot[index + 2] = -1;
				camrot[index] = 0;
			}
			else {
				if (glfwGetKey(window, GLFW_KEY_RIGHT) == GLFW_PRESS) {
					camrot[index + 2] = 1;
					camrot[index] = 0;
				}
			}
		}
	}
	

	return camrot;
}

int main()
{
	glfwInit();
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
	//glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT,GL_TRUE); // MAC OS X Only see original Note on Learn OpenGL.

	GLFWwindow* window = glfwCreateWindow(WindowWidth, WindowHeight, "Triangular Prism", NULL, NULL);
	if (window == NULL)
	{
		std::cout << "FailedtocreateGLFW window" << std::endl;
		glfwTerminate();
		return -1;
	}
	glfwMakeContextCurrent(window);

	if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
	{
		std::cout << "FailedtoinitializeGLAD" << std::endl;
		return -1;
	}

	glViewport(0, 0, WindowWidth, WindowHeight);

	glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

	// Initialise Vertex Shader
	unsigned int vertexShader;
	vertexShader = glCreateShader(GL_VERTEX_SHADER);

	// Compile Vertex Shader
	glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
	glCompileShader(vertexShader);

	// Check for successful compile of Vertex Shader
	int success;
	char infoLog[512];
	glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
	if (!success)
	{
		glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
		std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
	}

	// Initialise Fragment Shader
	unsigned int fragmentShader;
	fragmentShader = glCreateShader(GL_FRAGMENT_SHADER)

	// Compile Fragment Shader
	glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
	glCompileShader(fragmentShader);

	// Check for successful compile of Fragment Shader
	glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
	if (!success)
	{
		glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
		std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
	}

	glEnable(GL_DEPTH_TEST);

	// Initialise Shader Program
	unsigned int shaderProgram;
	shaderProgram = glCreateProgram();

	// Link Vertex and Fragment Shaders as Part of Program
	glAttachShader(shaderProgram, vertexShader);
	glAttachShader(shaderProgram, fragmentShader);
	glLinkProgram(shaderProgram);

	// Check for successful Linking of Shader Program
	glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
	if (!success)
	{
		glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
		std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
	}

	// Define the use of new Shader Program
	glUseProgram(shaderProgram);

	// Delete Vertex and Fragment Shaders which are now linked under Shader Program and no longer needed
	glDeleteShader(vertexShader);
	glDeleteShader(fragmentShader);

	glm::mat4 projection = glm::perspective(glm::radians(45.0f), (float)WindowWidth / (float)WindowHeight, 0.1f, 100.0f);
	unsigned int projectionLoc = glGetUniformLocation(shaderProgram, "projection");
	glUniformMatrix4fv(projectionLoc, 1, GL_FALSE, glm::value_ptr(projection));

	// render loop
	while (!glfwWindowShouldClose(window))
	{
		// input
		processInput(window);

		// rendering commands here
		glClearColor(0.2f, 0.3f, 0.3f, 1.0f); // Background Window Colour - RGB values and alpha (transparency)
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

		// Triangular Prism using Element Buffer Object - Position and Colour

		float vertices[] = {
			 0.5f,  0.5f, 0.0f, 1.0f, 1.0f, 0.0f,  // top right - Red and Green set to maximum value.
			 0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 0.0f,  // bottom right - Red and Green set to minimum value.
			-0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 0.0f,  // bottom left - Red and Blue set to minimum value.
			-0.5f,  0.5f, 0.0f, 1.0f, 0.0f, 1.0f,   // top left - Red and Green set to maximum value.
			 0.0f,  0.5f, -0.86603f, 0.0f, 1.0f, 1.0f, // upper back - Green and Blue set to maximum value.
			 0.0f, -0.5f, -0.86603f, 0.0f, 0.0f, 0.0f  // bottom back - Green and Blue set to minimum value.
		};
		unsigned int indices[] = {  // note that we start from 0!
			0, 1, 3,   // first triangle
			1, 2, 3,   // second triangle
			4, 5, 0,   // third triangle
			5, 1, 0,   // fourth triangle
			3, 2, 4,   // fifth triangle
			2, 5, 4,   // sixth triangle
			0, 4, 3,   // seventh triangle
			1, 5, 2    // eighth triangle
		};
		/* Indices describes the triangles created from the vertices.
			First and Second Triangle used to create the first rectangular face of the triangular prism.
			Third and Fourth Triangle used to create the second rectangular face.
			Fifth and Sixth Triangle used to create the third rectangular face.
			Seventh Triangle used to create top triangular face.
			Eighth Triangle used to create bottom triangular face.
		*/

		// Initialise Vertex Buffer Object - VBO
		unsigned int VBO;
		glGenBuffers(1, &VBO);

		// Initialise Vertex Array Object - VAO
		unsigned int VAO;
		glGenVertexArrays(1, &VAO);

		// Initialise Element Buffer Object - EBO
		unsigned int EBO;
		glGenBuffers(1, &EBO);

		// Bind VAO and VBO for attribute changes
		glBindVertexArray(VAO);
		glBindBuffer(GL_ARRAY_BUFFER, VBO);
		glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

		// Bind EBO
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
		glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

		//1. then set the vertex attributes pointers

		//1a. position attribute
		glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
		glEnableVertexAttribArray(0);

		//1a. color attribute
		glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
		glEnableVertexAttribArray(1);

		//2. use our shader program when we want to render an object
		glUseProgram(shaderProgram);

		//2a. call function to determine if key pressed has changed the camera rotation.

		float *campoint;
		campoint = camrotation(window);

		float camrot[6] = { -1,0,0,0,1,0 };

		for (int index = 0; index < 6; index++) {
			camrot[index] = *(campoint + index);
		}

		glm::mat4 view = glm::mat4(1.0f); // make sure to initialize matrix to identity matrix first
		float radius = 5.0f;

		float camX = camrot[0]*radius;
		float camY = camrot[1]*radius;
		float camZ = camrot[2]*radius;
		float upX = camrot[3];
		float upY = camrot[4];
		float upZ = camrot[5];

		view = glm::lookAt(glm::vec3(camX, camY, camZ), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(upX, upY, upZ));
		unsigned int viewLoc = glGetUniformLocation(shaderProgram, "view");
		glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));

		glm::mat4 model = glm::mat4(1.0f);
		unsigned int modelLoc = glGetUniformLocation(shaderProgram, "model");
		glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));

		glBindVertexArray(VAO);

		//3. now draw the object

		//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); // Draw Shape as Line Drawing

		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
		glDrawElements(GL_TRIANGLES, 24, GL_UNSIGNED_INT, 0);

		// check and call events and swap the buffers
		glfwSwapBuffers(window);
		glfwPollEvents();
	}

	glfwTerminate();

	return 0;
}

Leave a comment