Skip to content

Instantly share code, notes, and snippets.

@tuket
Created October 25, 2022 11:45
Show Gist options
  • Save tuket/034101ef4132dad7005bbb5d6259ab1f to your computer and use it in GitHub Desktop.
Save tuket/034101ef4132dad7005bbb5d6259ab1f to your computer and use it in GitHub Desktop.
OpenGL sRGB experiment
#include "utils.hpp"
#include <GLFW/glfw3.h>
constexpr int W = 800;
constexpr int H = 600;
GLFWwindow* window;
namespace shader_srcs
{
ConstStr vertShad_constant =
R"GLSL(
layout(location = 0)in vec2 a_pos;
void main()
{
gl_Position = vec4(a_pos, 0, 1);
}
)GLSL";
ConstStr fragShad_constant =
R"GLSL(
layout(location = 0) out vec4 o_color;
void main()
{
o_color = vec4(vec3(0.5), 1);
}
)GLSL";
ConstStr vertShad_textured =
R"GLSL(
layout(location = 0)in vec2 a_pos;
layout(location = 1)in vec2 a_tc;
out vec2 v_tc;
void main()
{
gl_Position = vec4(a_pos, 0, 1);
v_tc = a_tc;
}
)GLSL";
ConstStr fragShad_textured =
R"GLSL(
layout(location = 0) out vec4 o_color;
in vec2 v_tc;
uniform sampler2D u_tex;
void main()
{
o_color = vec4(texture(u_tex, v_tc).rgb, 1);
}
)GLSL";
}
static void glfwErrorCallback(int error, const char* description)
{
fprintf(stderr, "Glfw Error %d: %s\n", error, description);
}
int main()
{
glfwSetErrorCallback(glfwErrorCallback);
if (!glfwInit())
return 1;
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
window = glfwCreateWindow(W, H, "test", nullptr, nullptr);
if (window == nullptr)
return 1;
glfwMakeContextCurrent(window);
glfwSwapInterval(1); // Enable vsync
if (gladLoadGL() == 0) {
fprintf(stderr, "Failed to initialize OpenGL loader!\n");
return 1;
}
glad_set_post_callback(glErrorCallback);
glfwSetWindowSizeCallback(window, [](GLFWwindow* window, int w, int h) {} );
glfwSetMouseButtonCallback(window, [](GLFWwindow* window, int button, int action, int mods) {} );
glfwSetCursorPosCallback(window, [](GLFWwindow* window, double x, double y) {} );
glfwSetScrollCallback(window, [](GLFWwindow* window, double dx, double dy) {} );
glfwSetKeyCallback(window, [](GLFWwindow* window, int key, int scancode, int action, int mods)
{
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
});
glClearColor(0.5, 0.5, 0.5, 0);
u32 vbo_big;
u32 vao_big;
{
glGenVertexArrays(1, &vao_big);
glBindVertexArray(vao_big);
glGenBuffers(1, &vbo_big);
struct Vert { vec2 pos;};
const Vert verts[] = {
{{-0.8, -0.8}},
{{+0.8, -0.8}},
{{+0.8, +0.8}},
{{-0.8, +0.8}},
};
glBindBuffer(GL_ARRAY_BUFFER, vbo_big);
glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(Vert), nullptr);
}
u32 vbo_small;
u32 vao_small;
{
glGenVertexArrays(1, &vao_small);
glBindVertexArray(vao_small);
glGenBuffers(1, &vbo_small);
struct Vert { vec2 pos; vec2 tc; };
const Vert verts[] = {
{{-0.5, -0.5}, {0, 0}},
{{+0.5, -0.5}, {1, 0}},
{{+0.5, +0.5}, {1, 1}},
{{-0.5, +0.5}, {0, 1}},
};
glBindBuffer(GL_ARRAY_BUFFER, vbo_small);
glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(Vert), nullptr);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Vert), (void*)offsetof(Vert, tc));
}
u32 tex;
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_2D, tex);
const glm::u8vec3 pixelColor(127);
glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, &pixelColor[0]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
const u32 prog_constant = easyCreateShaderProg("shader0", shader_srcs::vertShad_constant, shader_srcs::fragShad_constant);
const u32 prog_textured = easyCreateShaderProg("shader1", shader_srcs::vertShad_textured, shader_srcs::fragShad_textured);
glUseProgram(prog_textured);
glUniform1i(glGetUniformLocation(prog_textured, "u_tex"), 0);
glViewport(0, 0, W, H);
glScissor(0, 0, W, H);
while (!glfwWindowShouldClose(window))
{
glfwPollEvents();
// clear with 50% grey
glClear(GL_COLOR_BUFFER_BIT);
// uses hard-coded 50% grey in the shader
glUseProgram(prog_constant);
glBindVertexArray(vao_big);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
// uses a sRGB texture with 50% (perceptual) grey
glUseProgram(prog_textured);
glBindVertexArray(vao_small);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glfwSwapBuffers(window);
}
}
#include "utils.hpp"
#include <span>
char buffer[SCRATCH_BUFFER_SIZE];
std::span<u8> bufferU8((u8*)buffer, SCRATCH_BUFFER_SIZE);
namespace shader_srcs
{
ConstStr header =
R"GLSL(
#version 330
#define PI 3.1415926535897932
)GLSL";
}
static const char* geGlErrStr(GLenum const err)
{
switch (err) {
case GL_NO_ERROR: return "GL_NO_ERROR";
case GL_INVALID_ENUM: return "GL_INVALID_ENUM";
case GL_INVALID_VALUE: return "GL_INVALID_VALUE";
case GL_INVALID_OPERATION: return "GL_INVALID_OPERATION";
case GL_INVALID_FRAMEBUFFER_OPERATION: return "GL_INVALID_FRAMEBUFFER_OPERATION";
case GL_OUT_OF_MEMORY: return "GL_OUT_OF_MEMORY";
case GL_STACK_UNDERFLOW: return "GL_STACK_UNDERFLOW";
case GL_STACK_OVERFLOW: return "GL_STACK_OVERFLOW";
default:
assert(!"unknown error");
return nullptr;
}
}
void glErrorCallback(const char* name, void* funcptr, int len_args, ...) {
GLenum error_code;
error_code = glad_glGetError();
if (error_code != GL_NO_ERROR) {
fprintf(stderr, "ERROR %s in %s\n", geGlErrStr(error_code), name);
assert(false);
}
}
// --- shader utils ---
char* checkCompileErrors(u32 shad, std::span<char> buffer)
{
i32 ok;
glGetShaderiv(shad, GL_COMPILE_STATUS, &ok);
if (!ok) {
GLsizei outSize;
glGetShaderInfoLog(shad, buffer.size(), &outSize, buffer.data());
return buffer.data();
}
return nullptr;
}
char* checkLinkErrors(u32 prog, std::span<char> buffer)
{
GLint success;
glGetProgramiv(prog, GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(prog, buffer.size(), nullptr, buffer.data());
return buffer.data();
}
return nullptr;
}
static void printCodeWithLines(std::span<const char*> srcs)
{
printf("%4d| ", 0);
int line = 1;
for (const char* s : srcs)
{
int start = 0;
int end = 0;
while (s[end]) {
if (s[end] == '\n') {
printf("%.*s\n", end - start, s + start);
printf("%4d| ", line);
start = end = end + 1;
line++;
}
else
end++;
}
}
printf("\n");
}
void printShaderCodeWithHeader(const char* src)
{
const char* srcs[2] = { shader_srcs::header, src };
printCodeWithLines(srcs);
}
u32 easyCreateShader(const char* name, const char* src, GLenum type)
{
static ConstStr s_shaderTypeNames[] = { "VERT", "FRAG", "GEOM" };
const char* typeName = nullptr;
switch (type) {
case GL_VERTEX_SHADER:
typeName = s_shaderTypeNames[0]; break;
case GL_FRAGMENT_SHADER:
typeName = s_shaderTypeNames[1]; break;
case GL_GEOMETRY_SHADER:
typeName = s_shaderTypeNames[2]; break;
default:
assert(false);
}
const u32 shad = glCreateShader(type);
ConstStr srcs[] = { shader_srcs::header, src };
glShaderSource(shad, 2, srcs, nullptr);
glCompileShader(shad);
if (const char* errMsg = checkCompileErrors(shad, buffer)) {
printf("Error in '%s'(%s):\n%s", name, typeName, errMsg);
printShaderCodeWithHeader(src);
assert(false);
}
return shad;
}
u32 easyCreateShaderProg(const char* name, const char* vertShadSrc, const char* fragShadSrc, u32 vertShad, u32 fragShad)
{
u32 prog = glCreateProgram();
glAttachShader(prog, vertShad);
glAttachShader(prog, fragShad);
defer(
glDetachShader(prog, vertShad);
glDetachShader(prog, fragShad);
);
glLinkProgram(prog);
if (const char* errMsg = checkLinkErrors(prog, buffer)) {
printf("%s\n", errMsg);
printf("Vertex Shader:\n");
printShaderCodeWithHeader(vertShadSrc);
printf("Fragment Shader:\n");
printShaderCodeWithHeader(fragShadSrc);
assert(false);
}
return prog;
}
u32 easyCreateShaderProg(const char* name, const char* vertShadSrc, const char* fragShadSrc)
{
const u32 vertShad = easyCreateShader(name, vertShadSrc, GL_VERTEX_SHADER);
const u32 fragShad = easyCreateShader(name, fragShadSrc, GL_FRAGMENT_SHADER);
const u32 prog = easyCreateShaderProg(name, vertShadSrc, fragShadSrc, vertShad, fragShad);
glDeleteShader(vertShad);
glDeleteShader(fragShad);
return prog;
}
#pragma once
#include <stdio.h>
#include <assert.h>
#include <string.h>
#define GLAD_DEBUG
#include <glad/glad.h>
#include <glm/glm.hpp>
#include <span>
typedef uint8_t u8;
typedef int32_t i32;
typedef uint32_t u32;
using glm::vec2;
using glm::vec3;
using glm::vec4;
typedef const char* const ConstStr;
constexpr int SCRATCH_BUFFER_SIZE = 4 * 1024 * 1024;
extern char buffer[SCRATCH_BUFFER_SIZE];
extern std::span<u8> bufferU8;
template <typename T> auto bufferSpan(size_t offset = 0) { return std::span<T>((T*)(buffer + offset), (SCRATCH_BUFFER_SIZE - offset) / sizeof(T)); }
typedef const char* const ConstStr;
void glErrorCallback(const char* name, void* funcptr, int len_args, ...);
char* checkCompileErrors(u32 shad, std::span<char> buffer);
char* checkLinkErrors(u32 prog, std::span<char> buffer);
void printShaderCodeWithHeader(const char* src);
u32 easyCreateShader(const char* name, const char* src, GLenum type);
u32 easyCreateShaderProg(const char* name, const char* vertShadSrc, const char* fragShadSrc);
u32 easyCreateShaderProg(const char* name, const char* vertShadSrc, const char* fragShadSrc, u32 vertShad, u32 fragShad);
// -- DEFER --
template <typename F>
struct _Defer {
F f;
_Defer(F f) : f(f) {}
~_Defer() { f(); }
};
template <typename F>
_Defer<F> _defer_func(F f) {
return _Defer<F>(f);
}
#define DEFER_1(x, y) x##y
#define DEFER_2(x, y) DEFER_1(x, y)
#define DEFER_3(x) DEFER_2(x, __COUNTER__)
#define defer(code) auto DEFER_3(_defer_) = _defer_func([&](){code;})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment