Skip to content

Instantly share code, notes, and snippets.

@ramntry
Created May 5, 2019 05:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ramntry/f173de25d061cf57ff944eb788f6475f to your computer and use it in GitHub Desktop.
Save ramntry/f173de25d061cf57ff944eb788f6475f to your computer and use it in GitHub Desktop.
Very pretty triangle (OpenGL)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdocumentation"
#define GLEW_STATIC
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#pragma clang diagnostic pop
#include <iostream>
#include <vector>
#include <cmath>
static constexpr GLint WIDTH = 800, HEIGHT = 600;
struct Vector3f {
GLfloat x;
GLfloat y;
GLfloat z;
};
struct Color3f {
GLfloat r;
GLfloat g;
GLfloat b;
};
struct Vertex {
Vector3f position;
Color3f color;
};
static constexpr char vertexShaderSource[] = R"(
#version 330 core
uniform mat4 transform;
uniform mat4 colorFlow;
layout (location = 0) in vec3 position;
layout (location = 1) in vec3 color;
out vec4 vertexColor;
void main() {
gl_Position = transform * vec4(position, 1.0);
vertexColor = colorFlow * vec4(color, 1.0);
}
)";
static constexpr char fragmentShaderSource[] = R"(
#version 330 core
in vec4 vertexColor;
out vec4 color;
void main() {
color = vertexColor;
}
)";
static const std::vector<Vertex> rgbTriangle = {
{.position={-0.5f, -0.625f, 0.0f}, .color={1.0f, 0.0f, 0.0f}},
{.position={ 0.5f, -0.625f, 0.0f}, .color={0.0f, 1.0f, 0.0f}},
{.position={ 0.0f, 0.375f, 0.0f}, .color={0.0f, 0.0f, 1.0f}},
};
class VertexArray {
GLuint VAO;
GLuint VBO;
const unsigned short numVertices;
VertexArray(const VertexArray &) = delete;
public:
VertexArray(const std::vector<Vertex> &vertices);
~VertexArray();
void bind() const;
void unbind() const;
GLsizei size() const { return numVertices; }
static GLuint currentlyBoundVAO();
};
VertexArray::VertexArray(const std::vector<Vertex> &vertices)
: numVertices(static_cast<decltype(numVertices)>(vertices.size())) {
assert(numVertices == vertices.size() && "Unexpectedly large number of vertices");
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(vertices[0]), &vertices[0], GL_STATIC_DRAW);
glVertexAttribPointer(0, sizeof(vertices[0].position) / sizeof(GLfloat), GL_FLOAT, GL_FALSE, sizeof(vertices[0]), 0);
glVertexAttribPointer(1, sizeof(vertices[0].color) / sizeof(GLfloat), GL_FLOAT, GL_FALSE, sizeof(vertices[0]),
reinterpret_cast<const GLvoid *>(sizeof(vertices[0].position)));
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
void VertexArray::bind() const {
glBindVertexArray(VAO);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
}
void VertexArray::unbind() const {
assert(currentlyBoundVAO() == VAO && "Unbinding an unexpected Vertex Array Object");
glDisableVertexAttribArray(1);
glDisableVertexAttribArray(0);
glBindVertexArray(0);
}
VertexArray::~VertexArray() {
if (currentlyBoundVAO() == VAO) {
unbind();
}
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
}
GLuint VertexArray::currentlyBoundVAO() {
GLint boundVAO;
glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &boundVAO);
assert(boundVAO >= 0 && "Unexpected value of a Vertex Array handle");
return boundVAO;
}
struct GLFWInitTerminate {
GLFWInitTerminate(const GLFWInitTerminate &) = delete;
GLFWInitTerminate() : status(glfwInit()) {
if (status != GL_TRUE) {
std::cerr << "Failed to initialize GLFW" << std::endl;
}
}
~GLFWInitTerminate() {
if (status == GL_TRUE) {
glfwTerminate();
}
}
const int status;
};
bool compileShader(GLuint shaderHandle, const char *shaderSource, const char *shaderName) {
glShaderSource(shaderHandle, 1, &shaderSource, nullptr);
glCompileShader(shaderHandle);
GLint success;
glGetShaderiv(shaderHandle, GL_COMPILE_STATUS, &success);
if (success) {
return true;
}
static GLchar infoLog[1024];
glGetShaderInfoLog(shaderHandle, sizeof(infoLog), nullptr, infoLog);
std::cerr << "Failed to compile the " << shaderName << " shader:\n" << infoLog << std::endl;
return false;
}
struct ShaderCreateDelete {
ShaderCreateDelete(const ShaderCreateDelete &) = delete;
ShaderCreateDelete(GLenum shaderType) : handle(glCreateShader(shaderType)) {
if (!handle) {
std::cerr << "Failed to create a shader object" << std::endl;
}
}
~ShaderCreateDelete() {
glDeleteShader(handle);
}
const GLuint handle;
};
bool buildProgram(GLuint shaderProgram, const char *vertexShaderText, const char *fragmentShaderText) {
ShaderCreateDelete vertexShader(GL_VERTEX_SHADER);
if (!compileShader(vertexShader.handle, vertexShaderText, "vertex")) {
return false;
}
ShaderCreateDelete fragmentShader(GL_FRAGMENT_SHADER);
if (!compileShader(fragmentShader.handle, fragmentShaderText, "fragment")) {
return false;
}
glAttachShader(shaderProgram, vertexShader.handle);
glAttachShader(shaderProgram, fragmentShader.handle);
glLinkProgram(shaderProgram);
GLint success;
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
if (success) {
return true;
}
static GLchar infoLog[1024];
glGetProgramInfoLog(shaderProgram, sizeof(infoLog), nullptr, infoLog);
std::cerr << "Failed to link the shader program:\n" << infoLog << std::endl;
return false;
}
bool validateProgram(GLuint shaderProgram) {
glValidateProgram(shaderProgram);
GLint success;
glGetProgramiv(shaderProgram, GL_VALIDATE_STATUS, &success);
static GLchar infoLog[1024];
glGetProgramInfoLog(shaderProgram, sizeof(infoLog), nullptr, infoLog);
std::string infoLogString(infoLog);
if (!success) {
std::cerr << "Failed shader program validation:\n" << infoLogString << std::endl;
return false;
}
if (!infoLogString.empty()) {
std::clog << "Shader program validation info:\n" << infoLogString << std::endl;
}
return true;
}
class ShaderProgram {
GLuint shaderProgram;
ShaderProgram(const ShaderProgram &) = delete;
public:
ShaderProgram(const char *vertexShaderText, const char *fragmentShaderText);
~ShaderProgram() {
glDeleteProgram(shaderProgram);
}
bool isValid() const { return shaderProgram; }
bool validate();
void use() const {
assert(isValid() && "Can't use an invalid shader program object");
glUseProgram(shaderProgram);
}
GLint getUniformLocation(const char *variableName) const;
};
ShaderProgram::ShaderProgram(const char *vertexShaderText,
const char *fragmentShaderText) : shaderProgram(glCreateProgram()) {
if (!shaderProgram) {
std::cerr << "Failed to create a shader program object" << std::endl;
return;
}
if (!buildProgram(shaderProgram, vertexShaderText, fragmentShaderText)) {
glDeleteProgram(shaderProgram);
shaderProgram = 0;
}
}
bool ShaderProgram::validate() {
if (!validateProgram(shaderProgram)) {
glDeleteProgram(shaderProgram);
shaderProgram = 0;
}
return isValid();
}
GLint ShaderProgram::getUniformLocation(const char *variableName) const {
assert(isValid() && "Can't get a uniform's location in an invalid shader program object");
GLint Location = glGetUniformLocation(shaderProgram, variableName);
if (Location == -1) {
std::clog << "Uniform ``" << variableName << "'' is not used in the shader" << std::endl;
}
return Location;
}
struct Transform {
Transform &initAsScale(GLfloat x = 1.0f, GLfloat y = 1.0f, GLfloat z = 1.0f);
Transform &initAsRotateAroundZ(double angle);
Transform &initAsTranslate(GLfloat x, GLfloat y = 0.0f, GLfloat z = 0.0f);
Transform &multiplyFromLeftBy(const Transform &A);
Transform &addScale(GLfloat x, GLfloat y, GLfloat z) {
return multiplyFromLeftBy(Transform().initAsScale(x, y, z));
}
Transform &addRotateAroundZ(double angle) {
return multiplyFromLeftBy(Transform().initAsRotateAroundZ(angle));
}
Transform &addTranslate(GLfloat x, GLfloat y = 0.0f, GLfloat z = 0.0f) {
return multiplyFromLeftBy(Transform().initAsTranslate(x, y, z));
}
GLfloat mat[4][4];
};
Transform &Transform::initAsScale(GLfloat x, GLfloat y, GLfloat z) {
std::memset(mat, 0, sizeof(mat));
mat[0][0] = x;
mat[1][1] = y;
mat[2][2] = z;
mat[3][3] = 1.0f;
return *this;
}
Transform &Transform::initAsRotateAroundZ(double angle) {
initAsScale();
mat[0][0] = std::cos(angle); mat[0][1] = std::sin(-angle);
mat[1][0] = std::sin(angle); mat[1][1] = std::cos(-angle);
return *this;
}
Transform &Transform::initAsTranslate(GLfloat x, GLfloat y, GLfloat z) {
initAsScale();
mat[0][3] = x;
mat[1][3] = y;
mat[2][3] = z;
return *this;
}
Transform &Transform::multiplyFromLeftBy(const Transform &A) {
Transform B(*this);
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
mat[i][j] = 0.0f;
for (int k = 0; k < 4; ++k) {
mat[i][j] += A.mat[i][k] * B.mat[k][j];
}
}
}
return *this;
}
int main(int argc, const char **argv) {
GLFWInitTerminate glfwScope;
if (glfwScope.status != GL_TRUE) {
return 1;
}
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
GLFWwindow *window = glfwCreateWindow(WIDTH, HEIGHT, "Learn OpenGL", nullptr, nullptr);
if (!window) {
std::cerr << "Failed to create GLFW window" << std::endl;
return 2;
}
int screenWidth, screenHeight;
glfwGetFramebufferSize(window, &screenWidth, &screenHeight);
if (!screenWidth || !screenHeight) {
std::cerr << "Failed to retrieve the framebuffer size" << std::endl;
return 3;
}
glfwMakeContextCurrent(window);
glViewport(0, 0, screenWidth, screenHeight);
glewExperimental = GL_TRUE;
if (glewInit() != GLEW_OK) {
std::cerr << "Failed to initialize GLEW" << std::endl;
return 4;
}
ShaderProgram shaderProgram(vertexShaderSource, fragmentShaderSource);
if (!shaderProgram.isValid()) {
return 5;
}
VertexArray VAO(rgbTriangle);
VAO.bind();
if (!shaderProgram.validate()) {
return 6;
}
shaderProgram.use();
GLint transformUniformLocation = shaderProgram.getUniformLocation("transform");
GLint colorFlowUniformLocation = shaderProgram.getUniformLocation("colorFlow");
Transform transform;
Transform colorFlow;
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
while (!glfwWindowShouldClose(window)) {
glfwPollEvents();
glClear(GL_COLOR_BUFFER_BIT);
const double t = glfwGetTime();
if (transformUniformLocation != -1) {
const double st = std::sin(t);
transform.initAsTranslate(0.5 * st, 0.5 * st * st);
glUniformMatrix4fv(transformUniformLocation, 1, GL_TRUE, &transform.mat[0][0]);
}
if (colorFlowUniformLocation != -1) {
colorFlow.initAsScale(0.5f, 0.5f)
.addRotateAroundZ(12.0 * t)
.addTranslate(0.5f, 0.5f);
glUniformMatrix4fv(colorFlowUniformLocation, 1, GL_TRUE, &colorFlow.mat[0][0]);
}
glDrawArrays(GL_TRIANGLES, 0, VAO.size());
glfwSwapBuffers(window);
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment