Created
August 7, 2016 17:55
-
-
Save tchayen/bf6479e447b6aab667eb6a1cfd2096f5 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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