Make a shader class and add colors to the verticies.

This commit is contained in:
Fries 2024-04-01 15:51:14 -07:00
parent 7215c6aba3
commit fe8a38d624
10 changed files with 245 additions and 93 deletions

2
.clangd Normal file
View file

@ -0,0 +1,2 @@
CompileFlags:
Add: [-std=c++20]

View file

@ -1,4 +1,4 @@
project('opengl-learning', 'cpp', 'c') project('opengl-learning', 'cpp', 'c', default_options: ['cpp_std=c++20'])
glfw = dependency('glfw3') glfw = dependency('glfw3')
libepoxy = dependency('epoxy') libepoxy = dependency('epoxy')

8
src/fragment.glsl Normal file
View file

@ -0,0 +1,8 @@
// Set the GLSL version to 3.3 and use the OpenGL core profile
#version 330 core
out vec4 FragColor;
in vec3 ourColor;
void main() {
FragColor = vec4(ourColor, 1.0);
}

View file

@ -1,35 +1,17 @@
#include <iostream>
#include <epoxy/gl.h> #include <epoxy/gl.h>
#include <GLFW/glfw3.h> #include <GLFW/glfw3.h>
#include <cmath>
#include <iostream>
#include <format>
#include "shader.hh"
#include "utilities.hh"
void framebuffer_size_callback(GLFWwindow* window, int width, int height); void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void process_input(GLFWwindow* window); void process_input(GLFWwindow* window);
void key_pressed(GLFWwindow *window, int key, int scancode, int action, int mods); void key_pressed(GLFWwindow *window, int key, int scancode, int action, int mods);
int check_shader_compilation_status(unsigned int shaderId); int check_shader_compilation_status(unsigned int shaderId);
const char* vertexShaderCode = R"(
// Set the GLSL version to 3.3 and use the OpenGL core profile
#version 330 core
layout (location = 0) in vec3 aPos;
void main() {
gl_Position = vec4(aPos, 1.0);
}
)";
const char* fragmentShaderCode = R"(
// Set the GLSL version to 3.3 and use the OpenGL core profile
#version 330 core
out vec4 FragColor;
uniform vec4 ourColor;
void main() {
FragColor = ourColor;
}
)";
bool wireframe; bool wireframe;
bool rectangleToggled; bool rectangleToggled;
@ -52,9 +34,10 @@ struct GlRenderObjects {
GlRenderObjects* triangle() { GlRenderObjects* triangle() {
// An array of verticies containing data for a triangle. // An array of verticies containing data for a triangle.
float vertices[] = { float vertices[] = {
-0.5f, -0.5f, 0.0f, // positions // colors
0.5f, -0.5f, 0.0f, -0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.5f, 0.0f 0.5f, -0.5f, 0.0f, 0.0f,1.0f, 0.0f,
0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f
}; };
unsigned int vertexBufferObject, vertexArrayObject; unsigned int vertexBufferObject, vertexArrayObject;
@ -71,12 +54,18 @@ GlRenderObjects* triangle() {
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// Tell OpenGL how to process the Vertex attributes so it can pass it to the Vertex shader. // Tell OpenGL how to process the Vertex attributes so it can pass it to the Vertex shader.
// This will pass the vertex to the location "0". We tell OpenGL the size of each vertex is 3 floats.
// This will pass the position attribute to the location "0". We tell OpenGL the size of each position attribute is 3 floats.
// We tell OpenGL that we're using floating point types. // We tell OpenGL that we're using floating point types.
// We tell OpenGL we don't want our data to be normalized as its already normalized. // We tell OpenGL we don't want our data to be normalized as its already normalized.
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0); glEnableVertexAttribArray(0);
// Pass the color attribute data to the location 1.
// We tell it to offset the data per "vertex" by 3 floats to get the color data instead of the position data.
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3*sizeof(float)));
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0); glBindVertexArray(0);
@ -85,10 +74,11 @@ GlRenderObjects* triangle() {
GlRenderObjects* rectangle() { GlRenderObjects* rectangle() {
float vertices[] = { float vertices[] = {
0.5f, 0.5f, 0.0f, // positions // colors
0.5f, -0.5f, 0.0f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f,
-0.5f, -0.5f, 0.0f, 0.5f, -0.5f, 0.0f, 0.0f,1.0f, 0.0f,
-0.5f, 0.5f, 0.0f -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f,
-0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f
}; };
unsigned int indices[] = { unsigned int indices[] = {
@ -115,10 +105,15 @@ GlRenderObjects* rectangle() {
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementBufferObject); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementBufferObject);
// Copy indicies into the GPU's memory. // Copy indicies into the GPU's memory.
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
// Tell OpenGL on how to process the verticies. // Tell OpenGL on how to process the verticies.
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void *)0); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void *)0);
glEnableVertexAttribArray(0); glEnableVertexAttribArray(0);
// Process the color data.
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void *)(3*sizeof(float)));
glEnableVertexAttribArray(1);
// Unbind the current buffers and arrays from the state. // Unbind the current buffers and arrays from the state.
glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0); glBindVertexArray(0);
@ -155,54 +150,10 @@ int main() {
// Register a callback for window size changes. // Register a callback for window size changes.
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
unsigned int vertexShader; std::string currentPath = utilities::getCurrentPath();
// Create a OpenGL shader object.
vertexShader = glCreateShader(GL_VERTEX_SHADER);
// Pass the source code to the state.
glShaderSource(vertexShader, 1, &vertexShaderCode, nullptr);
// Compile the shader
glCompileShader(vertexShader);
int success; // Create a shader class.
char infoLog[512]; Shader* shader = new Shader(std::format("{}/vertex.glsl", currentPath).c_str(), std::format("{}/fragment.glsl", currentPath).c_str());
// Check if the shader successfully compiled.
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
if (!check_shader_compilation_status(vertexShader)) {
glGetShaderInfoLog(vertexShader, 512, nullptr, infoLog);
std::cout << "ERROR::SHADER::VERTEX::COMPILIATION_FAILED\n" << infoLog << std::endl;
}
unsigned int fragmentShader;
// Create a OpenGL Shader object.
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
// Give the object the source code of the shader, sent with a single string.
glShaderSource(fragmentShader, 1, &fragmentShaderCode, NULL);
// Compile the shader.
glCompileShader(fragmentShader);
if (!check_shader_compilation_status(fragmentShader)) {
glGetShaderInfoLog(fragmentShader, 512, nullptr, infoLog);
std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
}
unsigned int shaderProgram;
// Create a OpenGL Shader Program object.
shaderProgram = glCreateProgram();
// Attach the Vertex Shader to the Shader Program.
glAttachShader(shaderProgram, vertexShader);
// Attach the Fragment Shader to the Shader Program.
glAttachShader(shaderProgram, fragmentShader);
// Link the Shader Program.
glLinkProgram(shaderProgram);
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::LINKING::LINKING_FAILED\n" << infoLog << std::endl;
}
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
GlRenderObjects* triangleRenderObjs = triangle(); GlRenderObjects* triangleRenderObjs = triangle();
GlRenderObjects* rectangleRenderObjs = rectangle(); GlRenderObjects* rectangleRenderObjs = rectangle();
@ -219,14 +170,8 @@ int main() {
// Clear the screen and use the color from the state. // Clear the screen and use the color from the state.
glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT);
// Use the shaderProgram shader program for drawing verticies. // activate the shader
glUseProgram(shaderProgram); shader->activate();
// Change the uniform color.
float timeValue = glfwGetTime();
float greenValue = (std::sin(timeValue) / 2.0f) + 0.5f;
int vertexColorLocation = glGetUniformLocation(shaderProgram, "ourColor");
glUniform4f(vertexColorLocation, 0.0f, greenValue, 0.0f, 1.0f);
if (!rectangleToggled) { if (!rectangleToggled) {
// Bind the state to use the vertexArrayObject object so OpenGL knows what to do with the verticies. // Bind the state to use the vertexArrayObject object so OpenGL knows what to do with the verticies.
@ -250,7 +195,7 @@ int main() {
// Clean up objects from memory. // Clean up objects from memory.
delete triangleRenderObjs; delete triangleRenderObjs;
delete rectangleRenderObjs; delete rectangleRenderObjs;
glDeleteProgram(shaderProgram); delete shader;
glfwTerminate(); glfwTerminate();
return 0; return 0;

View file

@ -1,6 +1,14 @@
sources = [ sources = [
'main.cc' 'main.cc',
'shader.cc',
'utilities.cc'
] ]
executable('main', sources, dependencies: [glfw, libepoxy]) copy = find_program('cp')
fragment = custom_target('fragment', output: 'fragment.glsl', input: 'fragment.glsl', command: [copy, '@INPUT@', '@OUTPUT@'])
vertex = custom_target('vertex', output: 'vertex.glsl', input: 'vertex.glsl', command: [copy, '@INPUT@', '@OUTPUT@'])
executable('main', [sources] + [fragment, vertex], dependencies: [glfw, libepoxy])
executable('fixedfunction', ['fixedfunction.cc'], dependencies: [glfw, libepoxy]) executable('fixedfunction', ['fixedfunction.cc'], dependencies: [glfw, libepoxy])

105
src/shader.cc Normal file
View file

@ -0,0 +1,105 @@
#include "shader.hh"
#include <fstream>
#include <sstream>
#include <iostream>
#include <epoxy/gl.h>
#include <format>
Shader::Shader(const char* vertexPath, const char* fragmentPath) {
std::string vertexCode, fragmentCode;
std::ifstream vShaderFile, fShaderFile;
vShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
fShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
try {
// open shader files.
vShaderFile.open(vertexPath);
fShaderFile.open(fragmentPath);
std::stringstream vShaderStream, fShaderStream;
// read files into stringstreams.
vShaderStream << vShaderFile.rdbuf();
fShaderStream << fShaderFile.rdbuf();
// close the file handles.
vShaderFile.close();
fShaderFile.close();
// convert the streams into c++ strings.
vertexCode = vShaderStream.str();
fragmentCode = fShaderStream.str();
} catch (std::ifstream::failure e) {
std::cout << "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ" << std::endl;
}
// compile vertex and fragment shaders.
uint vertex, fragment;
vertex = compileVertexShader(vertexCode);
fragment = compileFragmentShader(fragmentCode);
// link the shader to a shader program.
id = glCreateProgram();
glAttachShader(id, vertex);
glAttachShader(id, fragment);
glLinkProgram(id);
// check if the shader program successfully linked.
int success;
char infoLog[512];
glGetProgramiv(id, GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(id, 512, nullptr, infoLog);
std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED" << std::endl << infoLog << std::endl;
}
// clean up un-needed shader objects.
glDeleteShader(vertex);
glDeleteShader(fragment);
}
Shader::~Shader() {
glDeleteProgram(id);
}
uint Shader::compileShader(std::string code, GLenum type, std::string shaderTypeName) {
int success;
uint shader;
char infoLog[512];
const char* codeCString = code.c_str();
shader = glCreateShader(type);
glShaderSource(shader, 1, &codeCString, nullptr);
glCompileShader(shader);
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
if (!success) {
glGetShaderInfoLog(shader, 512, NULL, infoLog);
std::cout << std::format("ERROR::SHADER::{}::COMPILATION_FAILED", shaderTypeName) << std::endl << infoLog << std::endl;
}
return shader;
}
uint Shader::compileVertexShader(std::string code) {
return compileShader(code, GL_VERTEX_SHADER, "VERTEX");
}
uint Shader::compileFragmentShader(std::string code) {
return compileShader(code, GL_FRAGMENT_SHADER, "FRAGMENT");
}
void Shader::activate() {
glUseProgram(id);
}
void Shader::setBool(const std::string &name, bool value) const {
glUniform1f(glGetUniformLocation(id, name.c_str()), (int)value);
}
void Shader::setInt(const std::string &name, int value) const {
glUniform1f(glGetUniformLocation(id, name.c_str()), value);
}
void Shader::setFloat(const std::string &name, float value) const {
glUniform1f(glGetUniformLocation(id, name.c_str()), value);
}

25
src/shader.hh Normal file
View file

@ -0,0 +1,25 @@
#ifndef SHADER_HH
#define SHADER_HH
#include <string>
#include <epoxy/gl.h>
class Shader {
public:
// The Shader ID.
unsigned int id;
// Constructor that builds the shader from a path of vertex and fragment shaders.
Shader(const char* vertexPath, const char* fragmentPath);
// Destructor that cleans up memory.
~Shader();
// activate the shader.
void activate();
// utility uniform functions
void setBool(const std::string &name, bool value) const;
void setInt(const std::string &name, int value) const;
void setFloat(const std::string &name, float value) const;
private:
uint compileShader(std::string code, GLenum type, std::string shaderTypeName);
uint compileVertexShader(std::string code);
uint compileFragmentShader(std::string code);
};
#endif

38
src/utilities.cc Normal file
View file

@ -0,0 +1,38 @@
#include "utilities.hh"
#include <cstddef>
#include <string>
#include <sstream>
#include <unistd.h>
#include <linux/limits.h>
std::string utilities::getCurrentPath() {
char result[PATH_MAX];
ssize_t count = readlink("/proc/self/exe", result, PATH_MAX);
std::string executablePath = std::string(result, (count > 0) ? count : 0);
std::vector<std::string> stringVector = utilities::splitString(executablePath, '/');
stringVector.pop_back();
return utilities::combineString(stringVector, '/');
}
std::vector<std::string> utilities::splitString(std::string string, char delimiter) {
size_t last = 0;
size_t next = 0;
std::vector<std::string> vector;
while ((next = string.find(delimiter, last)) != std::string::npos) {
vector.push_back(string.substr(last, next-last));
last = next + 1;
}
vector.push_back(string.substr(last));
return vector;
}
std::string utilities::combineString(std::vector<std::string> stringVector, char delimiter) {
std::stringstream stringStream;
for (auto it = stringVector.begin() ; it != stringVector.end(); it++) {
if (it != stringVector.begin()) {
stringStream << delimiter;
}
stringStream << *it;
}
return stringStream.str();
}

11
src/utilities.hh Normal file
View file

@ -0,0 +1,11 @@
#ifndef UTILITIES_HH
#define UTILITIES_HH
#include <string>
#include <vector>
namespace utilities {
std::string getCurrentPath();
std::vector<std::string> splitString(std::string string, char delimiter);
std::string combineString(std::vector<std::string> stringVector, char delimiter);
}
#endif

10
src/vertex.glsl Normal file
View file

@ -0,0 +1,10 @@
// Set the GLSL version to 3.3 and use the OpenGL core profile
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;
out vec3 ourColor;
void main() {
gl_Position = vec4(aPos, 1.0);
ourColor = aColor;
}