Just playing around with Geometry Shaders and happened on something simple+eye-fun enough that might be a nice demo for people. The shader itself is pretty simple. The idea is, basically, get a point in space, and generate a triangle for it along with appropriate texture coordinates for each point.
// this will be passed in to use as rotations
uniform float rot;
// this is our max image height/width. could hard coded
uniform float imgWidth, imgHeight;
// this is what we'll give to each vertex that we output: info on which part of the texture it should use
// notice the "out", that means each vertex shader that gets info from this geom shader will get one of these
// and can use it with "varying vec2 txtCoords"
varying out vec2 txtCoords;
void main()
{
// just make the positions a little more interesting
float cosX = (cos(rot) * imgWidth) * 0.5;
float sinY = (sin(rot) * imgHeight) * 0.5;
// this is the position of the particle passed in. I'm drawing this with points, so it only has a length of 1
// b/c a point has only 1 vertex. we're copying the positionIn to the position that we'll output
gl_Position = gl_PositionIn[0];
// now modify our output position a little
gl_Position.x += cosX;
// make good texture coords for it (i.e. bigger than 0 and less than the image height/width and remember that
// each output vertex will need a tex coord
txtCoords.x = imgWidth - clamp(gl_Position.x, 0., imgWidth);
txtCoords.y = imgHeight - clamp(gl_Position.y, 0., imgHeight);
// now spit out the vertex
EmitVertex();
// repeat 2x for the other points of the triangle
gl_Position = gl_PositionIn[0];
gl_Position.x += cosX;
gl_Position.y += sinY;
txtCoords.x = imgWidth - clamp(gl_Position.x, 0., imgWidth);
txtCoords.y = imgHeight - clamp(gl_Position.y, 0., imgHeight);
EmitVertex();
gl_Position = gl_PositionIn[0];
gl_Position.x -= cosX;
gl_Position.y -= sinY;
txtCoords = vec2(gl_Position.x, gl_Position.y);
txtCoords.x = imgWidth - clamp(gl_Position.x, 0., imgWidth);
txtCoords.y = imgHeight - clamp(gl_Position.y, 0., imgHeight);
// finally we're all done, so last vertex
EmitVertex();
// and now finish the primitive. you could think of this like glEnd()
EndPrimitive();
}
The rest is very straightforward. The fragment shader uses the txtCoords to set the pixel color from the texture:
varying vec2 txtCoords;
uniform sampler2DRect tex;
void main()
{
gl_FragColor = texture2DRect(tex, txtCoords.xy);
}
and I initialize the shaders uniforms like so:
glPushMatrix();
glTranslatef(ofGetWidth()/2, ofGetHeight()/2, 0);
glRotatef(rot, 0, 1, 1);
grab.getTextureReference().bind();
fragmentator.begin();
fragmentator.setUniform1f("rot", ofGetElapsedTimef());
fragmentator.setUniform1f("imgWidth", (float) vidWidth);
fragmentator.setUniform1f("imgHeight", (float) vidHeight);
glBegin(GL_POINTS);
vector<coord>::iterator it = coords.begin();
while(it != coords.end()) {
glVertex3f( it->pos->x, it->pos->y, it->pos->z);
glTexCoord2f(it->tx->x, it->tx->y);
++it;
}
glEnd();
grab.getTextureReference().unbind();
fragmentator.end();
glPopMatrix();
Screenshot and source are attached. Have fun!