Skip to content

Instantly share code, notes, and snippets.

@tchayen
Created August 7, 2016 17: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 tchayen/bf6479e447b6aab667eb6a1cfd2096f5 to your computer and use it in GitHub Desktop.
Save tchayen/bf6479e447b6aab667eb6a1cfd2096f5 to your computer and use it in GitHub Desktop.
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include "gl3w\gl3w.h"
#include "glfw-3.2\glfw3.h"
#include "soil-1.16\SOIL.h"
// not necessary but makes my life much easier when I can just see the number of
// bits and I dont have to write 'unsigned' as a separate word
typedef char int8;
typedef short int16;
typedef int int32;
typedef unsigned char uint8;
typedef unsigned short uint16;
typedef unsigned int uint32;
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);
GLuint getShaderProgramId(const char *vertexFile, const char *fragmentFile);
GLuint compileShader(const GLchar *source, GLuint shaderType);
GLFWwindow* window;
const uint32
WINDOW_WIDTH = 1440,
WINDOW_HEIGHT = 810;
int32 textureWidth, textureHeight;
GLuint shaderProgramId, vao, vbo, ubo, textureId;
// in order to avoid sending information to shaders screen resolution is hard coded
// (precisely, half of it in each dimension), but it should be unnoticable in when
// extracted to shader uniform
const char *vertexShader =
"#version 330\n"
"layout (location = 0) in vec2 vert;\n"
"layout (location = 1) in vec2 _uv;\n"
"out vec2 uv;\n"
"void main()\n"
"{\n"
" uv = _uv;\n"
" gl_Position = vec4(vert.x / 720.0 - 1.0, vert.y / 405.0 - 1.0, 0.0, 1.0);\n"
"}\n";
const char *fragmentShader =
"#version 330\n"
"out vec4 color;\n"
"in vec2 uv;\n"
"uniform sampler2D tex;\n"
"void main()\n"
"{\n"
" color = texture(tex, uv);\n"
"}\n";
struct Texture { uint16 width, height; float u1, v1, u2, v2; };
struct Object { int16 x, y; Texture texture; };
static int16 vertices[12];
static float uvs[12];
float gauss(float x, float sigma)
{
// inverse square root of two times PI saved in constant for optimization
static const float a = 0.3989422804014327;
float b = x / sigma;
return a / sigma * std::exp(-0.5f * b * b);
}
void gaussianBlur(float sigma, uint32 kernelSize, uint16 width, uint16 height, uint8 *input, uint8 *output)
{
if (kernelSize % 2 == 0)
{
printf("Error, kernel size cannot be even number.\n");
return;
}
float *kernel = new float[kernelSize / 2 + 1];
for (int32 i = 0; i <= kernelSize / 2; i++)
kernel[i] = gauss(i, sigma);
// buffer needed for storing first pass before applying the second one
uint8 *buffer = new uint8[width * height * 4];
float sum = 0.0f;
for (int32 i = 0; i <= kernelSize / 2; i++)
sum += kernel[i];
for (int32 i = 1; i <= kernelSize / 2; i++)
sum += kernel[i];
printf("%f", sum);
// horizontal pass, from input to buffer
for (int32 x = 0; x < width; x++)
{
for (int32 y = 0; y < height; y++)
{
for (int32 i = 0; i < 3; i++)
{
float color = 0.0f;
for (int32 k = -((int32)kernelSize / 2); k <= -1; k++)
color += input[(x + (x + k >= 0 ? k : 0) + y * width) * 4 + i] * kernel[-k];
color += input[(x + y * width) * 4 + i] * kernel[0];
for (int32 k = 1; k <= kernelSize / 2; k++)
color += input[(x + (x + k < width ? k : 0) + y * width) * 4 + i] * kernel[k];
buffer[(x + y * width) * 4 + i] = round(color);
}
}
}
// vertical pass, from buffer to output
for (int32 x = 0; x < width; x++)
{
for (int32 y = 0; y < height; y++)
{
for (int32 i = 0; i < 3; i++)
{
float color = 0.0f;
for (int32 k = -((int32)kernelSize / 2); k <= -1; k++)
color += buffer[(x + (y + (y + k >= 0 ? k : 0)) * width) * 4 + i] * kernel[-k];
color += buffer[(x + y * width) * 4 + i] * kernel[0];
for (int32 k = 1; k <= kernelSize / 2; k++)
color += buffer[(x + (y + (y + k < height ? k : 0)) * width) * 4 + i] * kernel[k];
output[(x + y * width) * 4 + i] = round(color);
}
output[(x + y * width) * 4 + 3] = input[(x + y * width) * 4 + 3]; // preserve alpha
}
}
delete[] buffer;
delete[] kernel;
}
void render()
{
glClear(GL_COLOR_BUFFER_BIT);
glDrawArrays(GL_TRIANGLES, 0, 6);
glfwSwapBuffers(window);
}
int32 main()
{
// initialize GLFW
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "", 0, 0);
glfwMakeContextCurrent(window);
glfwSetKeyCallback(window, key_callback);
// initialize GL3W
if (gl3wInit())
{
fprintf(stderr, "fzailed to initialize OpenGL.\n");
return -1;
}
if (!gl3wIsSupported(3, 2))
{
fprintf(stderr, "OpenGL 3.2 not supported.\n");
return -1;
}
// OpenGL setup
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_CULL_FACE);
glFrontFace(GL_CCW);
glEnable(GL_BLEND);
glDisable(GL_DEPTH_TEST);
glDisable(GL_SCISSOR_TEST);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
// viewport setup
{
int32 width, height;
const GLFWvidmode *mode = glfwGetVideoMode(glfwGetPrimaryMonitor());
glfwSetWindowPos(window, (mode->width - WINDOW_WIDTH) / 2, (mode->height - WINDOW_HEIGHT) / 2);
glfwGetFramebufferSize(window, &width, &height);
glViewport(0, 0, width, height);
}
// initialize shader
shaderProgramId = getShaderProgramId(vertexShader, fragmentShader);
// texture
glGenTextures(1, &textureId);
glBindTexture(GL_TEXTURE_2D, textureId);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// load image using SOIL and store it on GPU
{
uint8 *image = SOIL_load_image("assets/cballs.png", &textureWidth, &textureHeight, 0, SOIL_LOAD_RGBA);
uint8 *imageBlurred = new uint8[textureWidth * textureHeight * 4];
gaussianBlur(1.0f, 5, textureWidth, textureHeight, image, imageBlurred);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, textureWidth, textureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageBlurred);
if (image == 0)
{
printf("Failed to load texture image.\n");
system("pause");
exit(1);
}
else
{
SOIL_free_image_data(image);
}
delete[] imageBlurred;
}
Object obj
{
(WINDOW_WIDTH - textureWidth) / 2,
(WINDOW_HEIGHT - textureHeight) / 2,
{ textureWidth, textureHeight, 0.0f, 0.0f, 1.0f, 1.0f }
};
// vertices
{
// top right
vertices[0] = obj.x + obj.texture.width;
vertices[1] = obj.y;
// bottom right
vertices[2] = obj.x + obj.texture.width;
vertices[3] = obj.y + obj.texture.height;
// top left
vertices[4] = obj.x;
vertices[5] = obj.y;
// bottom right
vertices[6] = obj.x + obj.texture.width;
vertices[7] = obj.y + obj.texture.height;
// bottom left
vertices[8] = obj.x;
vertices[9] = obj.y + obj.texture.height;
// top left
vertices[10] = obj.x;
vertices[11] = obj.y;
}
// uvs
{
// top right
uvs[0] = obj.texture.u2;
uvs[1] = obj.texture.v2;
// bottom right
uvs[2] = obj.texture.u2;
uvs[3] = obj.texture.v1;
// top left
uvs[4] = obj.texture.u1;
uvs[5] = obj.texture.v2;
// bottom right
uvs[6] = obj.texture.u2;
uvs[7] = obj.texture.v1;
// bottom left
uvs[8] = obj.texture.u1;
uvs[9] = obj.texture.v1;
// top left
uvs[10] = obj.texture.u1;
uvs[11] = obj.texture.v2;
}
// initialize OpenGL buffers
glGenVertexArrays(1, &vao);
glGenBuffers(1, &vbo);
glGenBuffers(1, &ubo);
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_DYNAMIC_DRAW);
glVertexAttribPointer(0, 2, GL_SHORT, GL_FALSE, 2 * sizeof(int16), 0);
glBindBuffer(GL_ARRAY_BUFFER, ubo);
glBufferData(GL_ARRAY_BUFFER, sizeof(uvs), uvs, GL_STATIC_DRAW);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_TRUE, 2 * sizeof(GLfloat), 0);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
// those usually go to render(), but because this example uses one shader, vao and texture it is enough to set them once
glUseProgram(shaderProgramId);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, textureId);
glBindVertexArray(vao);
// main loop
double t = 0.0;
const double dt = 0.01;
double currentTime = glfwGetTime();
double accumulator = 0.0;
while (!glfwWindowShouldClose(window))
{
double newTime = glfwGetTime();
double frameTime = newTime - currentTime;
currentTime = newTime;
accumulator += frameTime;
while (accumulator >= dt)
{
glfwPollEvents();
accumulator -= dt;
t += dt;
}
render();
}
// cleanup
glDeleteVertexArrays(1, &vao);
glDeleteBuffers(1, &vbo);
glDeleteBuffers(1, &ubo);
glDeleteTextures(1, &textureId);
glDeleteProgram(shaderProgramId);
glfwTerminate();
return 0;
}
void key_callback(GLFWwindow* window, int key, int32 scancode, int32 action, int32 mode)
{
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
glfwSetWindowShouldClose(window, GL_TRUE);
}
GLuint getShaderProgramId(const char *vertexFile, const char *fragmentFile)
{
GLuint programId, vertexHandler, fragmentHandler;
vertexHandler = compileShader(vertexFile, GL_VERTEX_SHADER);
fragmentHandler = compileShader(fragmentFile, GL_FRAGMENT_SHADER);
programId = glCreateProgram();
glAttachShader(programId, vertexHandler);
glAttachShader(programId, fragmentHandler);
glLinkProgram(programId);
GLint success;
GLchar infoLog[512];
glGetProgramiv(programId, GL_LINK_STATUS, &success);
if (!success)
{
glGetProgramInfoLog(programId, 512, 0, infoLog);
printf("Error in linking of shaders:\n%s\n", infoLog);
system("pause");
exit(1);
}
glDeleteShader(vertexHandler);
glDeleteShader(fragmentHandler);
return programId;
}
GLuint compileShader(const GLchar *source, GLuint shaderType)
{
GLuint shaderHandler;
shaderHandler = glCreateShader(shaderType);
glShaderSource(shaderHandler, 1, &source, 0);
glCompileShader(shaderHandler);
GLint success;
GLchar infoLog[512];
glGetShaderiv(shaderHandler, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(shaderHandler, 512, 0, infoLog);
printf("Error in compilation of shader:\n%s\n", infoLog);
system("pause");
exit(1);
};
return shaderHandler;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment