-
-
Save amoffat/2f3d836b2235106ec810 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 <iostream> | |
#include <cstdlib> | |
#include <cassert> | |
#include <sstream> | |
#include <fstream> | |
#include <stdexcept> | |
#include <vector> | |
#include <GL/glew.h> | |
#include <GL/gl.h> | |
#include <GL/glu.h> | |
#include <GL/glext.h> | |
#include <SDL/SDL.h> | |
int width = 1024; | |
int height = 768; | |
int fps = 10; | |
#define error_check()\ | |
do {\ | |
std::stringstream msg;\ | |
GLenum error = glGetError();\ | |
if (error != GL_NO_ERROR) {\ | |
msg << "opengl error "\ | |
<< __FILE__ << " " << __LINE__\ | |
<< ": " << gluErrorString(error) << "\n";\ | |
throw std::runtime_error(msg.str());\ | |
}\ | |
} while(0) | |
std::string readFile(const std::string &filename) { | |
std::ifstream shader_file(filename.c_str(), std::ios::in); | |
std::stringstream buffer; | |
buffer << shader_file.rdbuf(); | |
std::string shaderBuffer(buffer.str()); | |
shader_file.close(); | |
return shaderBuffer; | |
} | |
void compileShader(int shader, const std::string &source) { | |
const char *c_str_buffer = source.c_str(); | |
GLint size[1] = {GLint(source.length())}; | |
glShaderSource(shader, 1, &c_str_buffer, size); | |
error_check(); | |
glCompileShader(shader); | |
error_check(); | |
int success; | |
glGetShaderiv(shader, GL_COMPILE_STATUS, &success); | |
error_check(); | |
if (!success) { | |
GLint log_length; | |
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length); | |
error_check(); | |
std::string log(log_length, ' '); | |
glGetShaderInfoLog(shader, log_length, NULL, &log[0]); | |
error_check(); | |
throw std::logic_error(log); | |
} | |
} | |
void linkProgram(int program) { | |
glLinkProgram(program); | |
error_check(); | |
int success; | |
glGetProgramiv(program, GL_LINK_STATUS, &success); | |
error_check(); | |
if (!success) { | |
GLint log_length; | |
glGetShaderiv(program, GL_INFO_LOG_LENGTH, &log_length); | |
error_check(); | |
std::string log(log_length, ' '); | |
glGetShaderInfoLog(program, log_length, NULL, &log[0]); | |
error_check(); | |
throw std::logic_error(log); | |
} | |
} | |
uint createProgram(const std::string &vert, const std::string &frag) { | |
int fragShader = glCreateShader(GL_FRAGMENT_SHADER); | |
error_check(); | |
int vertShader = glCreateShader(GL_VERTEX_SHADER); | |
error_check(); | |
std::string source; | |
source = readFile(frag); | |
compileShader(fragShader, source); | |
source = readFile(vert); | |
compileShader(vertShader, source); | |
uint program = glCreateProgram(); | |
error_check(); | |
glAttachShader(program, fragShader); | |
error_check(); | |
glAttachShader(program, vertShader); | |
error_check(); | |
linkProgram(program); | |
return program; | |
} | |
int writeNumberProgram; | |
int readNumberProgram; | |
uint vbo; | |
uint texId; | |
uint fboId; | |
bool bug = false; | |
void draw() { | |
/* | |
* the bug occurs if blending is enabled, regardless of whether or not | |
* we're blending with the destination pixel (GL_ZERO for glBlendFunc) | |
*/ | |
if (bug) { | |
glEnable(GL_BLEND); | |
error_check(); | |
glBlendEquation(GL_FUNC_ADD); | |
error_check(); | |
glColorMask(true, true, true, true); | |
error_check(); | |
glBlendFunc(GL_ONE, GL_ZERO); | |
error_check(); | |
} | |
/* | |
* here we're binding our framebuffer, attaching our 32-bit float texture | |
* and writing a uint as a float with a specific bit pattern | |
*/ | |
glBindFramebuffer(GL_FRAMEBUFFER, fboId); | |
error_check(); | |
glDrawBuffer(GL_COLOR_ATTACHMENT0); | |
error_check(); | |
GLfloat color[] = {0,0,0,0}; | |
glClearBufferfv(GL_COLOR, 0, color); | |
error_check(); | |
glUseProgram(writeNumberProgram); | |
error_check(); | |
glBindBuffer(GL_ARRAY_BUFFER, vbo); | |
error_check(); | |
glEnableVertexAttribArray(0); | |
error_check(); | |
glVertexAttribPointer(0, 2, GL_INT, GL_FALSE, 0, 0); | |
error_check(); | |
glDrawArrays(GL_QUADS, 0, 4); | |
error_check(); | |
glDisableVertexAttribArray(0); | |
error_check(); | |
glBindBuffer(GL_ARRAY_BUFFER, 0); | |
error_check(); | |
glUseProgram(0); | |
error_check(); | |
glBindFramebuffer(GL_FRAMEBUFFER, 0); | |
error_check(); | |
glDisable(GL_BLEND); | |
error_check(); | |
/* | |
* now we're going to read our uint written as a float from the drawbuffer | |
* texture. if blending was enabled in the previous pass, our screen | |
* will be red, indicating the bug. if it was disabled, our screen will | |
* be green | |
*/ | |
glUseProgram(readNumberProgram); | |
error_check(); | |
glActiveTexture(GL_TEXTURE0); | |
error_check(); | |
glBindTexture(GL_TEXTURE_2D, texId); | |
error_check(); | |
int loc = glGetUniformLocation(readNumberProgram, "tex"); | |
error_check(); | |
assert(loc != -1); | |
glProgramUniform1i(readNumberProgram, loc, 0); | |
error_check(); | |
glBindBuffer(GL_ARRAY_BUFFER, vbo); | |
error_check(); | |
glEnableVertexAttribArray(0); | |
error_check(); | |
glVertexAttribPointer(0, 2, GL_INT, GL_FALSE, 0, 0); | |
error_check(); | |
glDrawArrays(GL_QUADS, 0, 4); | |
error_check(); | |
glDisableVertexAttribArray(0); | |
error_check(); | |
glBindBuffer(GL_ARRAY_BUFFER, 0); | |
error_check(); | |
glUseProgram(0); | |
error_check(); | |
} | |
int main(int argc, char** argv) { | |
/* | |
* we can enable the bug by specifying any argument when running the program | |
*/ | |
bug = argc > 1; | |
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) != 0) { | |
printf("Unable to initialize SDL: %s\n", SDL_GetError()); | |
exit(1); | |
} | |
SDL_Surface *screen = SDL_SetVideoMode(width, height, 32, SDL_OPENGL); | |
if (screen == NULL) { | |
perror(SDL_GetError()); | |
exit(1); | |
} | |
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); | |
GLenum err = glewInit(); | |
if (GLEW_OK != err) { | |
std::cerr << "Error: " << glewGetErrorString(err); | |
std::flush(std::cerr); | |
exit(1); | |
} | |
/* | |
* create a texture for our render buffer. this will hold the uint-as-float | |
* value that we'll be writing to it | |
*/ | |
glGenTextures(1, &texId); | |
error_check(); | |
glTextureImage2DEXT(texId, GL_TEXTURE_2D, 0, GL_RGBA32F, width, height, 0, | |
GL_RGBA, GL_FLOAT, nullptr); | |
error_check(); | |
glTextureParameteriEXT(texId, GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | |
error_check(); | |
glTextureParameteriEXT(texId, GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | |
error_check(); | |
/* | |
* attach our texture to the FBO | |
*/ | |
glGenFramebuffers(1, &fboId); | |
error_check(); | |
glNamedFramebufferTextureEXT(fboId, GL_COLOR_ATTACHMENT0, texId, 0); | |
error_check(); | |
GLenum status = glCheckNamedFramebufferStatus(fboId, GL_FRAMEBUFFER); | |
error_check(); | |
assert(status == GL_FRAMEBUFFER_COMPLETE); | |
/* | |
* create a VBO to hold enough geometry to render a full-screen quad | |
*/ | |
GLint vertices[8] = { 0, 0, 1, 0, 1, 1, 0, 1 }; | |
glGenBuffers(1, &vbo); | |
error_check(); | |
glBindBuffer(GL_ARRAY_BUFFER, vbo); | |
error_check(); | |
glBufferData(GL_ARRAY_BUFFER, sizeof(GLint) * 8, vertices, GL_STATIC_DRAW); | |
error_check(); | |
glBindBuffer(GL_ARRAY_BUFFER, 0); | |
error_check(); | |
glViewport(0, 0, width, height); | |
error_check(); | |
writeNumberProgram = createProgram("write.vert", "write.frag"); | |
readNumberProgram = createProgram("read.vert", "read.frag"); | |
SDL_Event event; | |
memset(&event, 0, sizeof(SDL_Event)); | |
bool done = false; | |
int lastDraw = 0; | |
while (!done) { | |
int newtime = SDL_GetTicks(); | |
if (!lastDraw) lastDraw = newtime - (1/(float)fps); | |
int elapsed = newtime - lastDraw; | |
if (elapsed < 1000/(float)fps) continue; | |
while (SDL_PollEvent(&event)) { | |
switch (event.type) { | |
case SDL_KEYDOWN: | |
case SDL_QUIT: | |
done = true; | |
break; | |
} | |
} | |
draw(); | |
lastDraw = newtime; | |
SDL_GL_SwapBuffers(); | |
} | |
return 0; | |
} |
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
#version 450 | |
in vec2 texcoord; | |
out vec4 texel; | |
uniform sampler2D tex; | |
void main(void) { | |
float f = texture(tex, texcoord).r; | |
uint u = floatBitsToUint(f); | |
if (u == 4292171093) { | |
texel = vec4(0,1,0,1); | |
} | |
else { | |
texel = vec4(1,0,0,1); | |
} | |
} |
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
#version 450 | |
layout(location=0) in vec4 position; | |
layout(location=0) out vec2 texcoord; | |
void main(void) { | |
texcoord = position.xy; | |
gl_Position = position * 2.0 - 1.0; | |
} |
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
#version 450 | |
out vec4 texel; | |
void main(void) { | |
float f = uintBitsToFloat(4292171093); | |
texel = vec4(f,0,0,1); | |
} |
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
#version 450 | |
layout(location = 0) in vec4 position; | |
void main(void) { | |
gl_Position = position * 2.0 - 1.0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment