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)
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:
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:
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:
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:
If there's something you do not understand, feel free to ask.
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)
Code Select
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:
Code Select
#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:
QuoteWe 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:
Code Select
#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:
Code Select
#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.

).
We're in OpenGL FOUR now and the GPU comes with OPENGL 1.4!!?