 Joined: Jun 2009
Posts: 476
|
I wrote a simple OpenGL/GLSL program to generate a TV-like noise effect. (using SFML to setup the opengl context and for audio purposes). The idea is simple: we use a fragment shader to compute the color of each pixel randomly. We can also force "groups" of pixels to have a single color to give an illusion of a lower resolution. The requirements: basic OpenGL and GLSL knowledge. Some code is taken from Durian Software's OpenGL tutorial. Our source code is made of four files: The Makefile, the C++ code, the fragment shader and the vertex shader. I named them "Makefile", "noise.cpp", "noise.f.glsl" and "noise.v.glsl". Let's start with the Makefile(tested under Linux GCC) CXX:=g++ CXXFLAGS:=-Wall -Wextra -Werror -g -O0 LDFLAGS:=-lsfml-window -lsfml-audio -lsfml-system -lGLEW -lGL OUTFILES:=noise.cpp.o APP:=noise
all: $(OUTFILES) $(CXX) $(CXXFLAGS) $(OUTFILES) $(LDFLAGS) -o $(APP)
clean: rm -f $(OUTFILES) $(APP)
%.cpp.o: %.cpp $(CXX) $(CXXFLAGS) -c $< -o $@
If you're familiar with Makefiles, this should be trivial to understand. Our program basically just uses a shape with four vertexes - the window rectangle. Since we use no matrixes nor any tricks, the vertex shader is super-simple: #version 110
attribute vec2 position;
void main() { gl_Position = vec4(position, 0.0, 1.0); }
Essentially: The position of the vertex is the position the program sends, with Z=0.0 and W=1.0. Our fragment shader is, unfortunately, more complex. Although GLSL does include some noise(random) functions, it seems some(most? all?) implementations - including the proprietary driver for my NVidia GPU - are very xkcd-ish.  (Seriously - they actually do this) So, unfortunately, if we want reliable random, we'll have to get our own. stackoverflow to the rescue!Our second problem is this: We can also force "groups" of pixels to have a single color to give an illusion of a lower resolution. So how do we do this? For this, I used floor. Essentially, I have a variable "pixels_per_block"(which would perhaps be more adequately called "sqrt_pixels_per_block", since the actual number of pixels is the square of that variable). OpenGL provides a variable named gl_FragCoord which gives us the position of the pixel in the screen. By dividing gl_FragCoord.x or y by pixels_per_block and then using floor, we obtain the desired effect. If pixels per block is 3, we get this 0 -> 0 1 -> 0 2 -> 0 3 -> 1 4 -> 1 5 -> 1 6 -> 2 which is exactly what we wanted. Keep in mind that GLSL(at least the version I used) does not allow implicit type casts, and we can't divide a float by an integer, so we'll have to explicitly cast pixels_per_block to float. Finally, we want the noise pattern to change on each frame. To achieve this effect I used something I called "seed", computed on the C++ side. So, let's see what we've got: #version 110
uniform int seed1, seed2; uniform int pixels_per_block;
float rand(vec2 co) { return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453); }
void main() { float xpos = floor(gl_FragCoord.x/float(pixels_per_block)); float ypos = floor(gl_FragCoord.y/float(pixels_per_block)); vec2 src = vec2(xpos, ypos) * vec2(seed1, seed2); float val = rand(src); gl_FragColor = vec4(val, val, val, 1.0); }
To generate audio noise, we also use random. SFML provides us with a class named SoundStream that enables us to compute sounds as they are needed - in this case, using the rand() function If you haven't read the Durian Software OpenGL tutorial, this would be a good time to do so, since I'm going to skip lots of OpenGL explanations and assume you understand it all. I provide the code here, for those who are interested: #include <iostream> #include <vector> #include <cstdio> #include <cstdlib> #include <cstring> #include <ctime> #include <GL/glew.h> #include <SFML/Window.hpp> #include <SFML/Audio.hpp>
#define PIXELS_PER_BLOCK 2 #define NOISE_SOUND_NBSAMPLES 16000
using std::cerr; using std::endl; using std::vector;
class NoiseAudioStream : public sf::SoundStream { public: vector<sf::Int16> myBuffer;
NoiseAudioStream() { myBuffer.reserve(NOISE_SOUND_NBSAMPLES*2); Initialize(2, 32000); }
virtual bool OnStart() { for (int i = 0; i < NOISE_SOUND_NBSAMPLES*2; ++i) { myBuffer[i] = rand(); } return true; }
virtual bool OnGetData(sf::SoundStream::Chunk& data) { data.NbSamples = NOISE_SOUND_NBSAMPLES; data.Samples = &myBuffer[0]; return true; } };
GLuint vertex_buffer, element_buffer; GLuint vertex_shader, fragment_shader, program; GLuint shader_position; GLuint shader_seed1, shader_seed2, shader_pixels_per_block;
static const GLfloat vertex_buffer_data[] = { -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f }; static const GLushort element_buffer_data[] = { 0, 1, 2, 3 };
static GLuint make_buffer(GLenum target, const void* buffer_data, GLsizei buffer_size) { GLuint buffer;
glGenBuffers(1, &buffer); glBindBuffer(target, buffer); glBufferData(target, buffer_size, buffer_data, GL_STATIC_DRAW);
return buffer; }
static GLchar* file_contents(const char* filename, GLint* length) { FILE* f = fopen(filename, "r"); if (!f) { cerr << "Could not open " << filename << endl; return 0; } fseek(f, 0, SEEK_END); *length = ftell(f); fseek(f, 0, SEEK_SET);
void* buffer = malloc(*length + 1); *length = fread(buffer, 1, *length, f); fclose(f); ((char*) buffer)[*length] = '\0';
return (GLchar*) buffer; }
static void show_info_log(GLuint object, PFNGLGETSHADERIVPROC glGet__iv, PFNGLGETSHADERINFOLOGPROC glGet__InfoLog) { GLint log_length; char *log;
glGet__iv(object, GL_INFO_LOG_LENGTH, &log_length); log = (char*) malloc(log_length); glGet__InfoLog(object, log_length, NULL, log); cerr << log << endl; free(log); }
static GLuint make_shader(GLenum type, const char* filename) { GLint length; GLchar* source = file_contents(filename, &length); GLuint shader; GLint shader_ok;
if (!source) return 0;
shader = glCreateShader(type); glShaderSource(shader, 1, (const GLchar**)&source, &length); free(source); glCompileShader(shader);
glGetShaderiv(shader, GL_COMPILE_STATUS, &shader_ok); if (!shader_ok) { cerr << "Failed to compile " << filename << ":" << endl; show_info_log(shader, glGetShaderiv, glGetShaderInfoLog); glDeleteShader(shader); return 0; }
return shader; }
static GLuint make_program(GLuint vertex_shader, GLuint fragment_shader) { GLint program_ok;
GLuint program = glCreateProgram(); glAttachShader(program, vertex_shader); glAttachShader(program, fragment_shader); glLinkProgram(program);
glGetProgramiv(program, GL_LINK_STATUS, &program_ok); if (!program_ok) { cerr << "Failed to link shader program:" << endl; show_info_log(program, glGetProgramiv, glGetProgramInfoLog); glDeleteProgram(program); return 0; } return program; }
static void make_resources() { vertex_buffer = make_buffer(GL_ARRAY_BUFFER, vertex_buffer_data, sizeof(vertex_buffer_data)); element_buffer = make_buffer(GL_ELEMENT_ARRAY_BUFFER, element_buffer_data, sizeof(element_buffer_data));
vertex_shader = make_shader(GL_VERTEX_SHADER, "noise.v.glsl"); fragment_shader = make_shader(GL_FRAGMENT_SHADER, "noise.f.glsl"); program = make_program(vertex_shader, fragment_shader);
shader_seed1 = glGetUniformLocation(program, "seed1"); shader_seed2 = glGetUniformLocation(program, "seed2"); shader_pixels_per_block = glGetUniformLocation(program, "pixels_per_block"); shader_position = glGetAttribLocation(program, "position"); }
static void init() { glewInit();
glClearDepth(1.f); glClearColor(0.f, 0.f, 0.f, 0.f);
make_resources(); }
int main() { srand(time(NULL));
sf::Window App(sf::VideoMode(800, 600, 32), "SFML OpenGL"); App.SetFramerateLimit(60);
NoiseAudioStream noise;
init(); noise.Play();
while (App.IsOpened()) { sf::Event Event; while (App.GetEvent(Event)) { if (Event.Type == sf::Event::Resized) glViewport(0, 0, Event.Size.Width, Event.Size.Height); else if (Event.Type == sf::Event::Closed) App.Close(); else if ((Event.Type == sf::Event::KeyPressed) && (Event.Key.Code == sf::Key::Escape)) App.Close(); }
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUseProgram(program); glUniform1i(shader_seed1, rand()); glUniform1i(shader_seed2, rand()); glUniform1i(shader_pixels_per_block, PIXELS_PER_BLOCK);
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer); glVertexAttribPointer(shader_position, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 2, (void*)0); glEnableVertexAttribArray(shader_position);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, element_buffer); glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, (void*)0);
glDisableVertexAttribArray(shader_position);
App.Display(); }
return 0; }
If there's something you do not understand, feel free to ask.
|