Photo Blog 107

23 05 2010

My Birthday was last weekend and for it I was given a brand new Speedlight for my camera. I’m still very much learning how to use it but took it along to my brother’s 21st last night ( we aren’t really born a week apart but this is when he chose to celebrate it). Played around a bit and got a few OK shots.

The two I decided to post are for rather different reasons; the first was a bit of a test to see what it could *really* do and come out quite artistic. I angled the flash straight up and then shot into a mirror on one side of the bar. The result is a cool reflection of the partygoers with myself in the middle behind the camera.

Reflections (f4.0 1/30s 24mm)

It really is quite amazing that it can produce that much light and get a correct exposure at 1/30s. The camera was otherwise saying it would need 4 seconds! This was setting the flash to Manual at 1/1 power so really is a demonstration of what it can do…

The second photo is more personal and is a scene replayed at every 21st the world over. Stories from Mum & Dad to embarrass the birthday boy :-)

Typical 21st (f4.0 1/30s 45mm)

I still need to learn a lot more, and to start with I think I need to fiddle with the white balance a little and work out how not to get over-saturated pictures ( the above has a slight de-saturation applied in post).





Graphics – MGP – Tessellation

23 05 2010

Now that we have all the points necessary we want to draw these as a surface and the first step in this is to generate a set of vertices to get them displayed.

Before we create the vertices we need to know the draw command that we will be using with OpenGL as this will determine the order in which we need them. For our purposes it makes the most sense to use GL_TRIANGLE_STRIP, which needs an order of vertices alternating between two parallel tracks. Remembering that in GL_TRIANGLE_STRIP we use the last 2 vertices and the next supplied one to define a triangle this is equivalent to needing vertex co-ordinates in the order of the diagram below.

Triangle Strip Ordering

With this in mind, and given a particular resolution we can use the following to get the set we need:

float vertices_l[(resT+1)*resS*6];
int vertexCount = (resT+1)*resS*2;
	
memset(&vertices_l,0,sizeof(float)*vertexCount*3);
	
int index = 0;
NSPoint3 vertex, normal;
for(int lt=0; lt < resT; lt++)
{
	for(int ls=0; ls < resS; ls++)
	{
		index = (lt*(resS) + ls)*6;
		vertex = [self pointOnSurfaceT: (lt+1)/(double)resT S: ls/(double)resS];
		vertices_l[index + 0] = vertex.x;
		vertices_l[index + 1] = vertex.y;
		vertices_l[index + 2] = vertex.z;
		index += 3;

		vertex = [self pointOnSurfaceT: (lt)/(double)resT S: ls/(double)resS];
		vertices_l[index + 0] = vertex.x;
		vertices_l[index + 1] = vertex.y;
		vertices_l[index + 2] = vertex.z;
		index += 3;
	}
}

Now of course we could keep this as an array and use immediate mode (the ol’ glBegin() / glEnd() combination) and iterate over it each frame but this would be terribly inefficient. To be a bit smarter we will use vertex arrays and load the data into the graphics card, which is not only faster but also sets us up for the final stage in the project where we transform the vertex data on the GPU.

Using vertex arrays is almost as easy as using immediate mode and follows 3 basic steps:

  1. Create the array reference
  2. Load the vertex data into the array
  3. Use the array whenever we want to draw the object

The first two steps are done only once with the reference being used whenever we need it. The code to perform these two steps is:

// generate the buffer
glGenBuffers(1,&drawRef); 
// fill the buffer
glBindBuffer(GL_ARRAY_BUFFER, drawRef);
glBufferData(GL_ARRAY_BUFFER, numberVerts * 3 * sizeof(float), &vertices, GL_STATIC_DRAW);

Whenever we want to use this buffer to draw any object we can do this as follows:

// let it know which buffer we are talking about
glEnableClientState(GL_VERTEX_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, drawRef);
// let it know offsets
glVertexPointer(3, GL_FLOAT, 0, 0);
// tell it to draw the specified number of vertices
glDrawArrays(GL_TRIANGLE_STRIP, 0, numberVerts);
// turn stuff off again
glDisableClientState(GL_VERTEX_ARRAY); 

Which is dropped in wherever you would normally do the glBegin()/glEnd() bit.

You can see where the GL_TRIANGLE_STRIP bit comes in and how many fewer instructions need to be sent to the graphics driver each draw. And I do say graphics driver because it is up to it where it stores the stuff. You can give it a hint, however, which is what the GL_STATIC_DRAW is doing in the first code snippet. The STATIC means we intend to modify it once and will be drawing it many times and strongly suggest that the data should be kept in VRAM.

With all this work we get the following image:

Tessellation


Which isn’t really that impressive but there is a ways to go yet… next time: normals and lighting





Graphics – MGP – Create a surface

19 05 2010

Now that we have the points needed for the curve we can code the function that will give as any point and then that which defines the surface of revolution.

The first step towards this is to understand the Bezier Curve functions and how to code them.

Calculating points on a Bezier Curve

The equation for a point on a bezier curve of arbitrary order (i.e. an arbitrary number of control points in the curve) is:

where P(i) is the ith point. B(i,n) is the Bernstein polynomial and is given by

and

Equation images taken from Wikipedia’s article here: Bezier Curve

We have Bezier curves of order-4 (they have 4 control points) and we could be all clever, just calculate our coefficients and hard-code them but that isn’t as fun! Performance also isn’t an issue for us as this will only be used at the start of the program for generating assets and then used no more…

In fact the code for the general case is fairly easy to implement provided you don’t try to do it all at once and so we get some helper functions for the second two functions.

int factorial(int n)
{
    for(int i = n-1; i > 1; i--)
        n *= i;
    return n;
}
int coefficient(int i, int n)
{
    if( i==0 || i == n )
        return 1;
    return factorial(n) / (double)(factorial(i)*factorial(n-i));
}
double Basis(int n,int i,float t)
{
    return coefficient(i, n) * pow(t,i) * pow(1.0 - t, n-i);
}

And when it comes to actually using these to compute a point on the curve we get something like this:

-(NSPoint) pointOnCurve:(double) t
{
    NSPoint ret = NSMakePoint(0,0);
    double bern;
    for( int i=0; i < [mControlPoints count]; i++ )
    {
        bern = Basis([mControlPoints count]-1, i, t);
        ret.x += [[mControlPoints objectAtIndex: i] pointValue].x * bern;
        ret.y += [[mControlPoints objectAtIndex: i] pointValue].y * bern;
    }
    return ret;
}

I should have mention that I’m doing all this in Objective-C / Cocoa where possible. Of course for small helper functions I just fall back on my friend: C.

Creating a Surface

Given the methods above (and a few other very simple ones to aggregate multiple curves into one) we can now create our surface of revolution.

This is also pretty easy and will give us a new function that takes 2 parameters (therefore a parametric surface) and spits out a point in 3D.

-(NSPoint3) pointOnSurfaceT:(double) t S:(double) s
{
    NSPoint3 ret;
    NSPoint onPath = [mPath pointOnCurve: t];
    ret.x = onPath.y * cos( 2 * pi * s );
    ret.y = onPath.y * sin( 2 * pi * s );
    ret.z = onPath.x;
    return ret;
}

Fairly easy to follow but broken down into steps:

  1. Calculate a point on our bezier curve
  2. Use the ‘x’ component of the 2D point as our ‘z’ value
  3. Rotate the ‘y’ component around the ‘z’ axis

I tend to use the ‘z up’ coordinate system but it doesn’t really matter.

This function can then be used to create as many vertices as we need at whatever resolution.

For the purposes of this installment we will just treat these vertices as points and next time we’ll deal with normals and tessellation to create a solid surface. In the mean time this is what our code has given us so far:

Surface of Points

Read the rest of this entry »





Graphics – MGP – Create a profile

10 05 2010

The first thing we need to create our melting glass is … a glass … and the easiest way to get this is to use a Surface of Revolution with a profile curve. I don’t really intend to go into heaps of detail about this but basically you get a curve and rotate it around an axis, creating a surface. If anyone is interested the wikipedia article can be found over here: Surface of Revolution

To make the profile curve I used Adobe Illustrator and just drew what I thought looked like half the cross-section of a glass. The only limitation here is that to get a profile we can use one has to use the curve function. The image below shows the profile I drew:

The Profile Curve

By then opening the Illustrator file in a text editor we can find the bit we are looking for which, for the profile above, looks like this:

0.50 158.14 m
0.50 158.14 2.33 38.16 0.83 36.50 c
1.67 35.16 14.83 62.99 15.16 78.66 c
15.31 85.59 12.50 130.14 30.00 137.64 c
36.37 140.37 76.12 143.63 125.47 145.85 c
157.28 147.29 193.07 148.29 226.47 148.44 c
283.60 148.70 333.72 146.46 344.96 139.64 c
360.48 130.22 370.78 98.65 386.59 69.85 c
399.29 46.70 415.56 25.34 440.94 18.66 c
464.22 12.53 498.01 8.23 530.38 5.36 c
577.27 1.19 626.42 -0.92 626.42 6.50 c
626.42 10.83 577.96 12.20 532.32 16.33 c
488.25 20.31 443.14 25.75 432.95 32.66 c
416.74 43.63 387.09 90.07 387.02 90.53 c
386.95 90.99 367.43 137.96 366.95 158.48 c

What this is is the postscript description of the profile we made. You can read & understand this in the following way:

A line ending in:

  • m moves the pen to that location
  • c creates a curve using the current location and 3 points defined on the same line. The pen ends up at the last point

Of course this is all well and good but for our purposes we would much rather have these as sets of curves. So with a bit of re-arranging we have this:

0.50 158.14 0.50 158.14 2.33 38.16 0.83 36.50
0.83 36.50 1.67 35.16 14.83 62.99 15.16 78.66
15.16 78.66 15.31 85.59 12.50 130.14 30.00 137.64
30.00 137.64 36.37 140.37 76.12 143.63 125.47 145.85
125.47 145.85 157.28 147.29 193.07 148.29 226.47 148.44
226.47 148.44 283.60 148.70 333.72 146.46 344.96 139.64
344.96 139.64 360.48 130.22 370.78 98.65 386.59 69.85
386.59 69.85 399.29 46.70 415.56 25.34 440.94 18.66
440.94 18.66 464.22 12.53 498.01 8.23 530.38 5.36
530.38 5.36 577.27 1.19 626.42 -0.92 626.42 6.50
626.42 6.50 626.42 10.83 577.96 12.20 532.32 16.33
532.32 16.33 488.25 20.31 443.14 25.75 432.95 32.66
432.95 32.66 416.74 43.63 387.09 90.07 387.02 90.53
387.02 90.53 386.95 90.99 367.43 137.96 366.95 158.48

Each line of the above output defines a single ‘order-4’ bezier curve. And each curve is designed to flow into the next to create the profile we drew in Illustrator. With this information we can programatically recreate the curve and from there the surface of the wine glass… which is a job for next time.

NB: Unfortunately you can’t use these points because they aren’t normalized properly. If you do want to then each ‘y’ var needs to be made y = 160-y





Graphics – The Melty Glass Project Intro

7 05 2010

So the subtitle of this blog is ‘Graphics, Photography and whatever’ yet all but 2 of the posts have been about Photography. The main reason I have been telling myself is that most of the graphics stuff I do is rather proprietary and it is hard to separate what I do for work and which parts of that I’m allowed to talk about.

That said I do occasionally engage in my own little projects but they rarely pass the ‘play around’ stage and never get to the ‘show people the code’ stage. Which is a bit stupid because you never get better by not putting stuff out there. And to this end I hope to put some of my experiments up here.

Enter the Melty Glass Project.

In 2008 I had the opportunity to attend WWDC in San Francisco and at the ‘Progresses in OpenGL’ session the man from Apple showed a demonstration of a wine glass that would melt when ‘heat’ from the mouse was applied. And all this on the graphics card.

I was rather taken with this and spent quite a while replicating the effect without access to any source materials. It was fun and the result was cool. And then I lost the source through a series of harddrive failures.

So a month or so ago when I wanted to kick this idea of making little projects off I figured re-doing this WWDC project would be a good starting point. I have since found the original source I wrote so have been able to re-use parts, which is a god-send given how fiddly some of the stuff is.

The basic steps necessary:

  • You create a glass using a surface of revolution with a bezier curve as the profile curve.
  • This needs environment mapping.
  • A nice little shader to give the effect of reflection and refraction.
  • Finally a shader working on the vertex data itself to ‘melt’ the glass.

Thus the point of this post: I’m hoping over the next month to document the work I’ve already done and to finish off the project entirely (it’s already close).

I can’t show you anything from the original demonstration by Apple but here is a screenshot from the original, formely lost, source so you get a sneak peek at the end result:

Preview of Melty Glass





Photo Blog 106

7 05 2010

Another update from the Easter Jazz Train. Nothing really ‘wow’ about this shot but I do like the perspective and natural smiles. Had fun in Photoshop correcting for an off-center perspective but I think it still looks alright.

Train Window (f4.0 1/1000 24mm)





Photo Blog 105

5 05 2010

Experimenting with the same techniques as the previous post I’ve done up one of the shots from the Easter Jazz Train. Near the end of the journey one of the engineers came in and began jamming on the piano. He was very cool and fit the theme perfectly.

Jazzman (f4.0 1/60s 24mm)