11/17/07

Kaleidoscope shader











The images above are created by rendering some graphics to the back buffer, capturing this as a texture and then using that as input for a kaleidoscope shader. The kaleidoscope shader is trivially. I'm assuming you have drawn your scene and captured it to texture (see previous articles).

First the vertex shader.

varying vec4 vertexcoord;
void main(){
vertexcoord = gl_Vertex;
gl_Position = gl_Vertex;
}

First line 2. It sets the position to be the input vertex (not the more usual ftransform()). This ensures whatever object we draw ignores the transformation matrix. It just makes it easy to draw an object covering the screen. The important line is : vertexcoord = gl_Vertex; Vertexcoord will be used as texture coordinates in the fragment shader.

Now the fragment shader...

uniform sampler2D tex;
varying vec4 vertexcoord;
void main() {
gl_FragColor = texture2D ( tex, abs(vertexcoord.xy) );
}
So an input image is drawn on the screen using the vertexcoord set in the vertex shader. Theres a trick here: abs. This is the kaleidoscope effect - this simple! Now in the main code we just draw a rect...

glRecti(-1,-1,1,1);
A Kaleidoscope shader in very few bytes.

11/12/07

New Layout

Site quickly redesigned to allow for wider and more readable code segments and fit the minimalist theme, so useless information has been removed. Comments (due to some trolling) removed. Most people seem to contact me by email anyway. Hope its cleaner and more useful for you.

11/4/07

Read shader from file without stdlib

Over at Lighthouse there are some useful tutorials and some example code. One piece of code I've found all over is the "read a shader from a file" code. Unfortunately its not much use for size coders as it uses stdlib. Essentially it allows you to write shaders in a normal text file and load them as a string.So here I've done a conversion using win32 equivalent functions. It doesn't check error codes (wasted bytes) so be careful to pass in the right filename. I'm not sure this is the smallest way, its just a translation of Lighthouse code.

Essentially adding this code to your intro allows you to develop the shader without constant recompiles.
#include "windows.h"

char *textFileRead(char *fn) {

char *content=NULL;
DWORD dummy;

HANDLE fp = CreateFile (fn, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
DWORD count=SetFilePointer (fp, 0 ,NULL, FILE_END);
SetFilePointer (fp, 0 ,NULL, FILE_BEGIN);
content = (char *)GlobalAlloc(0,(SIZE_T)(count+1));
ReadFile(fp, (LPVOID)content, count, &dummy, NULL);
content[count] = '\0';
CloseHandle(fp);
return content;
}

11/3/07

Wada basin in real time



My raytracer works well enough to set up something never seen before (I think). This is a shot of a real time animation of a Wada Basin fractal. It runs around 30 fps with no attempt to optimise. Its early days yet but I'm hoping this will really pay off. The slightly wrong shape is due to a severe perspective distortion. I'll fix this and publish better images soon. As usual bigger images are available by clicking on small images.

If you are unsure what a Wada Basin is and why a real time one is quite a novelty, have a look here at Paul Bourkes superb web pages.




I'm not happy with colours or shape yet but in motion, it looks great.


10/30/07

Real Time Raytracing

















I've decided to make a tiny PS3.0 raytracer. It will take some time to get it working on lots of platforms and size optimised but here are the first two buggy but pleasing images. I'll add updates and later publish the code. Click images for larger versions. It runs at about 50-60fps on my x1900 at 1024x768 resolution with six levels of reflection.

10/27/07

Shaders to render normals : NOT!


A useful debugging tool is to get a colour representation of the normals in a scene. With a bit of maths, it can also produce some nice, free colouring for an intro. Above is a correct image. It shows a whole bunch of spheres. Red is the x component of the normal, green the y and blue is z. Study it and you'll see its right. Only it took me a few hours to get this right. I made a very simple error, which maybe others do too so I thought I'd document it here.

Hmmm, this is what I actually got:

Pretty,yes. Wrong, definitely. Here is the WRONG CODE. This code has a serious bug:

const GLchar *normalsvsh="\
varying vec3 n;\
void main(){\
n = normalize(gl_Normal*gl_NormalMatrix);\
gl_Position=ftransform();\
}";

const GLchar *normalsfsh="\
varying vec3 n;\
void main(){\
gl_FragColor = vec4((n+1.0)*0.5,1);\
}";
Can you spot the error? Heres a clue, the spheres are rotating about their centres at different speeds.

The solution is...


n = normalize(gl_NormalMatrix*gl_Normal);\

10/25/07

Tiny OpenGL Windowing Code

Time for another framework update. Most of the framework stuff I have posted over on in4k was developed and optimised for dropper/apack. Last year hitchhiker released the first 1k using crinkler which is now the standard for 1ks (until Mentor releases a better compressor). So its time to update the OpenGL in a window framework code. Here it is. It opens an OGL window and also handles DEVMODE. I've added the DEVMODE bytes because shader performance varies so much on cards currently that its critical to control the screen size of the PC your into runs on. Besides, at 4k who would ever not use DEVMODE?

Tested under XP...Vista I don't know yet. If you try it, let me know OK?

So, Full screen, any resolution, ultra cheap GL window:


#include < windows.h >
#include < GL/gl.h >

static PIXELFORMATDESCRIPTOR pfd={
0, // Size Of This Pixel Format Descriptor... BAD coding, nothing new, saves 6 bytes
1, PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, 32, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0
};

static DEVMODE dmScreenSettings={
"",0,0,sizeof(dmScreenSettings),0,DM_PELSWIDTH|DM_PELSHEIGHT,
0,0,0,0,0,0,0,0,0,0,0,0,0,"",0,0,1024,768,0,0,0,0,0,0,0,0,0,0
};

void WINAPI WinMainCRTStartup()
{
ChangeDisplaySettings (&dmScreenSettings,CDS_FULLSCREEN);
HDC hDC = GetDC( CreateWindow("edit", 0, WS_POPUP|WS_VISIBLE|WS_MAXIMIZE, 0, 0, 0, 0, 0, 0, 0, 0) );
SetPixelFormat ( hDC, ChoosePixelFormat ( hDC, &pfd) , &pfd );
wglMakeCurrent ( hDC, wglCreateContext(hDC) );
ShowCursor(FALSE);
do {
// insert breakpoint winning 1k here
// pohar insert breakpoint winning 5k :-)
SwapBuffers(hDC);
} while ( !GetAsyncKeyState(VK_ESCAPE) );
}

10/24/07

Tiny code to Setup GLSL Shaders

I posted code some time ago on in4k to load GLSL shaders. The code was small but it could get smaller. Using crinkler 1.0a, this code is about 20 bytes smaller than before after compression. Not much, but enough for one more line of shader code.
OpenGL still doesn't compete with DX at this level (1k) of shader coding but I noticed that loonies recently used the framework so I thought it was worth an update.


static void setShaders(){
GLuint p,s;
p = ((PFNGLCREATEPROGRAMPROC)wglGetProcAddress("glCreateProgram"))();
s = ((PFNGLCREATESHADERPROC)(wglGetProcAddress("glCreateShader")))(GL_VERTEX_SHADER);
((PFNGLSHADERSOURCEPROC)wglGetProcAddress("glShaderSource")) (s, 1, &vsh, NULL);
((PFNGLCOMPILESHADERPROC)wglGetProcAddress("glCompileShader"))(s);
((PFNGLATTACHSHADERPROC)wglGetProcAddress("glAttachShader")) (p,s);

s = ((PFNGLCREATESHADERPROC)wglGetProcAddress("glCreateShader"))(GL_FRAGMENT_SHADER);
((PFNGLSHADERSOURCEPROC)wglGetProcAddress("glShaderSource")) (s, 1, &fsh, NULL);
((PFNGLCOMPILESHADERPROC)wglGetProcAddress("glCompileShader"))(s);
((PFNGLATTACHSHADERPROC)wglGetProcAddress("glAttachShader")) (p,s);

((PFNGLLINKPROGRAMPROC)wglGetProcAddress("glLinkProgram"))(p);
((PFNGLUSEPROGRAMPROC) wglGetProcAddress("glUseProgram"))(p);
}

10/17/07

Real Time Ambient Occlusion of Spheres

Well, with SSAO (see article below), this trick is a little redundant. However, I'll put it here for my own records as much as anything else.

I was looking for a trick to squeeze ambient occlusion in real time into 1k. It turns out that the occlusion of a sphere on another sphere can be calculated analytically. Similarly a sphere and plane can occlude each other and the result is analytic. This means it can be calculated using a simple equation for every pixel on the screen.

Fine, so a fragment shader would do?
Yes. And no. I couldnt get it to below 1k. 4k yes, but 1k no. So I started to examine the equations. To my amazement, traditional OpenGL lighting can be set up to model ambient occlusion of spheres on spheres and planes on spheres!

Inside each sphere we place a dark light, a light with negative colour! This is a point light source. Then we set the light attenuation, how the light fades with respect to distance, to exactly that required by the ambient occlusion equations. Lastly we assume all spheres have radius of 1, which simplifies the setup and equations.

Now draw all the spheres cast darkness onto each other correctly. Oh one trick to watch for is to switch off the light inside a sphere when drawing that sphere - a sphere does not occlude itself.

Here is the image:



Not bad. Notice all the shadows. We need one light per sphere and OpenGL allows us 8 lights so we can have 8 spheres. The spheres can also occlude a plane:


Lastly, the plane can also occlude the spheres. This is trickier. In essence one light source is used for the plane and as each sphere is drawn, this light source is moved onto the plane directly under the sphere. I used a point light source which is incorrect (the plane is infinite and requires a directional light but this is not then attenuated correctly). Here is the final result:



Notice the dark patches at the bottom of spheres now from the plane below them. Lastly here is an image where I've used the technique for real. The image was created for Rebels demogroup, but I dont think they will use it. M:ET gave permission to use the Rebels logo here, originally by H2O. The background image is a greeble image created by alien^pdx. I used two passes, the first to draw ambient occlusion, the second to draw the glossy surface. It could be done in one pass, but really 7 or 8 spheres are not hard for PCs now.



(Click any image to see a larger version)

Finally some code to implement the dynamic sphere occlusion...


const float white[] = { 0.75, 0.75, 0.75, 1 };
const float black[] = { 0, 0, 0, 1 }; //doubles as position of lights
const float darklight[] = { -2.0f, -2.0f, -2.0f, 2.0f };

void drawAOSpheres(){
int i;

// we skip the first light as this has its own initial rules
// by ignoring it, the code gets smaller...half size infact
glDisable(GL_LIGHT0);
// everything in the scene will receive white ambient light
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, white);
// set the lights in position and switch them on
for (i=1; i < 8; i++) {
moveSphere(i);
// lights are set with NEGATIVE colour!
glLightfv(GL_LIGHT0+i,GL_DIFFUSE,darklight);
// black is 0,0,0,1, which are the values we need for
// a point light at the origin.
glLightfv (GL_LIGHT0+i, GL_POSITION, black);
// a quadratic falloff of light power is needed
// attenuation is 1/(constant + linear*dist + quadratic*dist)
// we want 1,0,1, default is 1,0,0
glLightf(GL_LIGHT0+i, GL_QUADRATIC_ATTENUATION, 1.0f);
glEnable (GL_LIGHT0+i);
}
// draw the spheres being careful not to draw a sphere occluding itself
for (i=1; i < 8; i++) {
//glColor4f (1.0f-(i/5.0f),0.7f+(i/20.0f),1.0f,1.0f);
// a sphere should not occlude itself so switch off its own light
glDisable (GL_LIGHT0+i);
moveSphere(i);
mySphere(1,60); //gluSphere(q,1,60,60);
glEnable (GL_LIGHT0+i);
}
}

13 lines of code and some constants!!

10/14/07

Screen Space Shading

One new technique in size coding is screen space shading. The idea is to avoid lots of shader code or even host code and produce shading code purely in screen space in the fragment shader.

For example, suppose we wish to do phong shading. Normally we would need to do the following:

1. Submit vertex normals
2. Write a vertex shader to output normals at vertices and the eye position
3. Write a fragment shader to normalize these
4. Calculate lighting

Instead though, steps 1,2 and 3 can be avoided and the normal recovered just from the pixels in the screen. GLSL offers a couple of functions dFdx and dFdy. These tend to work on cards supporting PS3.0 and later. In essence they give us the rate of change of a value across pixels. So recovering a normal in screen space can be written in GLSL as:


vec3 n=normalize(vec3(dFdx(gl_FragCoord.z),dFdy(gl_FragCoord.z),1));


If you are familiar with height maps and deriving normals, thats exactly this equation. There are issues with borders of objects which can result in a black border round the object, but this can also be exploited as a special effect.

Another effect that can be done in screen space is Ambient Occlusion. Iq of rgba has published a few articles on this and the technique seems to have been first used by Crytek. Essentially, in the fragment shader, a number of points are generated around the surface normal in world space. These are samples for the AO. These are then projected into screen space again and their depths tested against the pixels they project to. If the point is further away in z, that sample is occluded. The results are summed as you might expect.

Here is an image from Kindernoiser by iq of rgba using this technique. It is amazing just how well it works! Its fast, dynamic and less code than traditional AO techniques.



There are drawbacks of screen space shading. For example, take a sphere subdivided into a number of flat polygons. With standard normal interpolation the sphere will look smooth as the normal is interpolated across the pixels as if the surface is curved. In screen space, we have only local information so the flat surfaces still look flat. Nontheless advantages in size can be worth it.

Slight update:
Here are some more images of screen space (or image space) illumination. Quite extraordinary. I must get an image space renderer written.

http://www.gamedev.net/community/forums/topic.asp?topic_id=465715

10/11/07

Organic Art










Back at college I had a few heroes of computer graphics. One of them, Professor William Latham was creating fascinating organic art. The three images above are his work and are reproduced with permission. I strongly encourage you to see these images in higher resolution at Williams web site: www.williamlatham1.com

It recently occurred to me that such organic art may be possible in real time and in very few bytes using OpenGL. Whilst I can't hope to match the beauty of the images in very few bytes, I can get close in form.

Here are some images I'm working on. Each image is taken from a real-time program which animates the structure in a more or less organic way.












The structures above are mine. Aside fom the colours and lighting which needs work, I think the code is beginning to show promise. The "latham" routine to generate the shapes is less than 300 bytes after compression.

The code is a recursive, iterative and randomised. I think there is loads of potential here.


void latham (uint32 depth, uint32 n, uint32 seed) {
uint32 i;
float s;

#define OPENNESS 40.0f // lower is more open
#define SPACING 2.0f // distance between generated points...higher, more
#define TWISTINESS 10.0f // how weird the structure gets...lower, less

if (depth==0) return;
for (i=0; i < n; i++) {
mirand = seed;
s= my_sin(sfrand()*(seed+i)*0.04f);
glRotatef ( s*OPENNESS, s*sfrand(),s*sfrand(),s*sfrand() );
s= my_sin(s*TWISTINESS)*SPACING;
glTranslatef ( s*sfrand(), s*sfrand(), s*sfrand() );
if (i%(n/4)==0) {
glPushMatrix();
glScalef(0.5f,0.5f,0.5f);
latham (depth-1, n/2, seed+1);
glPopMatrix();
}
drawSphere(SPACING); // SPACING=radius of sphere
}
}



sfrand() is a function that returns a psuedo-random number between -1 and 1. Mirand is used to set the seed for random number generation. Dirty: but this is size coding. The code iterates applying a random (but repeatable by seed) rotation and translation and drawing a sphere. At a certain iteration, a new random seed is passed recursively into the function in order to draw "branches" off the main creature.

I have removed the animation code here (which is tiny) but you can figure that out yourself.

Overall, through fortuitous randomness we can come close to resembling Lathams structures and animate them in real time. Now if only I had some artistic ability I could light them and colour them better!

10/10/07

Tiny Distortion Shader

Whilst working on Spheres Dream, Pasy of Rebels gave me some code for a distortion shader. I used it to quite good effect but completely size reduced and rewritten. I'll present my distortion shader here but first what is a distortion shader? Well, imagine a lens floating infront of your graphics, it would distort the image behind. To do this in OpenGL we would:
  • Draw the image to be distorted to an offscreen buffer
  • Capture this to texture (see NPOT article)
  • Clear the screen
  • Enable a lens distortion shader
  • Draw a textured quad
The shader would distort the texture co-ordinates as if looking through a lens. Easy. However the lens can be highly complex, have a bumpy surface or even be a second texture.

The first image is a very flat image before distortion. The second image is applying a distortion shader and adds complexity and interest to the image.




I did not want to use a second texture to distort the image. Instead I used a simple trick. The image is drawn and the distort is calculated from the orginal image alone. Infact the distortion is a function of colour in the original image.

Here is the vertex shader:
const char *vsh="varying vec4 p;\
void main(){\
gl_Position=ftransform();\
p=ftransform();\
}";
So p is set to the co-ordinate of the transformed vertex. Which ranges between -1 and 1 in my screen space.

Here is the fragment shader:
const char *distortScreenfsh="\
uniform sampler2D s;\
varying vec4 p;\
void main(){\
vec4 t=p/2.0+0.5;\
float d=length(texture2D(s,t.xy).xyz);\
gl_FragColor=texture2D(s,t.xy+d*p.xy*0.3);\
}";
So t maps to between 0..1 and corresponds exactly to (0,0) in one corner of the screen and (1,1) in the opposite corner. This avoids having to define texture co-ordinates in the main OpenGL code and also, infact means one big trick. In the main code now we can use a gluDisk rather than define a real quad! This is far fewer bytes. If I had tried to use real texture co-ordinates in the shader I couldn't use a Disk as the texture co-ordinates would be wrong.

The real magic then is here:
float d=length(texture2D(s,t.xy).rgb);\
gl_FragColor=texture2D(s,t.xy+d*p.xy*0.3);\
We do a texture lookup in the original image using t as the texture co-ordinates. We read the rgb values and then take the length of this as a vector. So somehow d is a measure of the intensity of colour in the original image.

Then we finally do a texture lookup in the original image, adjusted by d and p. This is the distort right here (+d*p.xy*0.3).

The shader is tiny and produces a much richer feel to the image.

10/9/07

Quick NPOTs

Most cards today support Non Power of Two Textures (NPOTS). In the old days textures had to be 64x32 or 256x512 or some such but screen resolutions are typically 800x600 or 1024x768 - non power of two. I know my card (x600) supports NPOTs but I had trouble getting them to work fast so I guess others may too. I wanted to
  • render the scene to the back buffer (800x600)
  • copy into a texture (also 800x600)
  • Use the texture in a distortion shader
The third step could be anything of course. The key is I needed this in real time. The problem is although NPOTs are supported on cards, they can be incredibly slow. The trick is setting them up correctly. First here is the code to read the screen and copy it into a texture:


void grabScreen(unsigned int tnum, GLubyte *p){
createTexture(tnum,p);
glCopyTexImage2D (GL_TEXTURE_2D, 0, GL_RGB, 0,0,800,600,0);
}


So I call a function to create a texture and then copy the whole back buffer into it. Nothing surprising here, its here for completeness. The real trick comes in the createTexture() routine. Here is where you can foul up pretty easily.


void createTexture (unsigned int tnum, GLubyte *p) {
glBindTexture (GL_TEXTURE_2D, tnum);
//commenting out any of these means it runs slow as hell
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,800,600,0,GL_RGB,GL_UNSIGNED_BYTE,p);
}


You can see the whole trick is explicitly telling OpenGL that it never needs to worry about wrapping of texels across the border. Instead, explicitly clamp the edge of the texture. Its obvious why if you think about it. If the texture wrapped, a modulo maths step is required to find the right texel. For powers of two this is a mask and a shift but is far more complex for non-power of two.

So now its possible to capture the whole screen at real-time rates and use that for...next article.


10/8/07

The Beginning


A quick note to say this Blog is open for business. Over the coming months I'll be posting bizarre and obscure ideas for intro coding: super small graphics programs. The example image is from "Spheres Dream" which is just 4k ..4096 bytes. 4 scenes, music and synthesiser in 4k. It requires pixel shaders 2.0 and a fast graphics card (series 6 Nvidia or x600 onwards from ATi). The graphics are mine and the synth is by Pohar of the demogroup Rebels.