Giantbucket’s Blog

February 15, 2009

Drawing cirlcles with shaders

Filed under: Uncategorized — GiantBucket @ 12:17 am

In one of my recent (but misguided) attempts to improve efficiency, I’ve improved my circle rendering to take advantage of the wonders of shaders. Previously, I’ve rendered circles with a triangle fan with about 20 or so segments. I could make that number dynamic and have them adaptively subdivide based on the distance to the camera, but you have to think, why are we trying to draw curved shapes with triangles anyway?

Queue the shader. Here’s the solution: render a quad with texture coordinates (-1, -1 for top left corner, 1, 1 for bottom right etc.) and make a shader that uses those coordinates to calculate the distance the pixel is from the center of the quad, drawing an alpha of zero when it is outside the radius of the circle.

Here’s the shader code:

void main()
{
if((gl_TexCoord[1][0] * gl_TexCoord[1][0]) + (gl_TexCoord[1][1] * gl_TexCoord[1][1]) < 1.0)
gl_FragColor = gl_Color;
else
gl_FragColor[3] = 0.0;
}

It’s fairly simple: if the squared distance from texture coords 0, 0 is below the squared distance to the edge of the quad (1.0*1.0 = 1.0) then draw the usual color, otherwise set the alpha to zero. You use the squared distance to avoid an expensive square root operation. To adjust the radius of the circle, just adjust the size of the quad.

What if we want just the outline of a circle (or an inner radius)?

uniform float InnerRadiusSquared;

void main()
{
float v = (gl_TexCoord[1][0] * gl_TexCoord[1][0]) + (gl_TexCoord[1][1] * gl_TexCoord[1][1]);
if((v InnerRadiusSquared))
gl_FragColor = gl_Color;
else
gl_FragColor[3] = 0.0;
}

Now we can use the uniform InnerRadiusSquared to set the inner radius. It’ll need to be the ratio of the inner radius to the outer radius (InnerRadius / OuterRadius) squared (again for performance). Awesome. But what if we want it textured? The texture coordinates are being messed with, it wouldn’t appear correctly? Notice that we’re using texture unit 1 (not 0) e.g. gl_TexCoord[1][]. So when drawing the quad we need to specify the texture unit we are passing the coords on is unit 1. We can pass normal texture coordinates for our texture on unit 0. So the quad drawing code would be:

//Bind you shader here

glBegin(GL_TRIANGLE_FAN);

glMultiTexCoord2i(GL_TEXTURE0, 0, 1); //For the texture
glMultiTexCoord2i(GL_TEXTURE1, -1, 1); //For the shader
glVertex2f(-2.f, 2.f); //Radius of 2

glMultiTexCoord2i(GL_TEXTURE0, 1, 1);
glMultiTexCoord2i(GL_TEXTURE1, 1, 1);
glVertex2f(2.f, 2.f);

glMultiTexCoord2i(GL_TEXTURE0, 1, 0);
glMultiTexCoord2i(GL_TEXTURE1, 1, -1);
glVertex2f(2.f, -2.f);

glMultiTexCoord2i(GL_TEXTURE0, 0, 0);
glMultiTexCoord2i(GL_TEXTURE1, -1, -1);
glVertex2f(-2.f, -2.f);

glEnd();

And the shader:

uniform sampler2D tex; //Automatically set to texture unit 0

void main()
{
if((gl_TexCoord[1][0] * gl_TexCoord[1][0]) + (gl_TexCoord[1][1] * gl_TexCoord[1][1]) < 1.0)
gl_FragColor = texture2D(tex, gl_TexCoord[0].st);
else
gl_FragColor[3] = 0.0;
}

I haven’t covered the vertex shader, here it is:

void main()
{
gl_Position = ftransform();
gl_TexCoord[0] = gl_MultiTexCoord0;
gl_TexCoord[1] = gl_MultiTexCoord1;
gl_FrontColor = gl_Color;
}

It just passes the values through, nothing special. Anyway, you could add the inner radius code to this and have a textured, outlined circle with only two triangles. Yay! So how does it perform? I’d imagine much better, depending on the number of segments you would be using otherwise. LiquidEngine seems to be CPU bound at the moment, and it only made 2-3 frames difference (with about 200-300 circles). I’m sure this will pay off in the end though, when the CPU side is optimized more. Obviously you can optimize further by batching many circles together into VBOs etc. rather than using the horribly slow quad drawing code here.

One major issue though is anti-aliasing. I don’t think multisampling takes extra samples into surfaces covered by shaders (at least not by default). So either this feature must be somehow turned on, or the shader must anti-alias the circle edge. I’m not really sure at this point, but I’ll hopefully post a solution someway down the track.

February 10, 2009

Unprojecting the cursor

Filed under: Uncategorized — GiantBucket @ 8:35 am
LiquidEngine's Cursor

LiquidEngine's Cursor

I’ve finally converted my unprojecting code to a more efficent method. It took alot of screwing around and I’m quite glad to blog the solution. But first, how was I doing it before?

Well, the objective is to translate the cursor coordinates 0nto the XY plane in 3D. We then have 2d coordinates relative to the work plane, and can render a cursor there with a scale based on the distance to the camera (so the cursor always appears the same size on the screen).

GLUT provides the very usefull gluUnProject function. When given the cursor coords, the projection matrix, the modelview matrix and the depth of the pixel below the cursor, will ouput translated 3D coordinates. The matricies are easy to get, but how do you get the depth? My (somewhat dodgey) solution was to draw a quad on the XY plane that covers the screen. I can then use the glReadPixels function to get the depth of the work plane under the cursor.

Anyway that’s a fairly convoluted way to get the depth value, surely there’s a way to directly calculate it?     Yes, yes there is.

The new way to do it is this: Unproject the cursor position with a depth value of 0 (near clip plane) and 1 (far clip plane). We can then calculate the intersection point between the line these two points make and the XY plane. So those two positions can be computed with gluUnPoject, but what about the intersection? Heres the code:

bool LineXYPlaneIntersect3D(Vector3& LinePoint1, Vector3& LinePoint2, vect2d& retIntersectPoint)
{
//Is the line parallel to axis?
if(LinePoint1.z == LinePoint2.z)
return false;

vect2d LinePoint12d(LinePoint1.x, LinePoint1.y);
vect2d LinePoint22d(LinePoint2.x, LinePoint2.y);

retIntersectPoint = LinePoint12d + (LinePoint22d – LinePoint12d) * (-LinePoint1.z / (LinePoint2.z – LinePoint1.z));
return true;
}

So there you have it. Or is it? What if we want to do the unprojecting ourselves? Well with quite a bit of screwing around (and Ogre’s matrix and vector classes), this is how you do it:

Get the normalized device coordinates from the cursor’s window coordinates. Device coordinates are in the range -1.0 to 1.0 for the x, y, and z axis. So,

float DeviceCoordX = ((WindowCoordX / WindowWidth) * 2.f) - 1.f;

float DeviceCoordY = (((WindowHeight – WindowCoordY) / WindowHeight) * 2.f) – 1.f; //Window coord Y goes from the top, needs to be reversed

float DeviceCoordZNear = -1.f;

float DeviceCoordZFar = 1.f;

We then put those coordinates into Vector4s (thanks Ogre). We need the fourth component for homogeonous coordinates (make this 1.f). We multiply this with the projection matrix, like this:

CursorPosNear = ProjectionMatrix * CursorPosNear;

CursorPosFar = ProjectionMatrix * CursorPosFar;

Order of operation look weird? Well I think so, but I’m a math noob. Now it’s time to multiply the x, y, and z components of the Vector4s with their w component. Like this:

CursorPosNear.x *= CursorPosNear.w;
CursorPosNear.y *= CursorPosNear.w;
CursorPosNear.z *= CursorPosNear.w;
CursorPosNear.w = 0.f;

//And the far pos as well…

Now we can multiply the vectors with the modelview matrix, but I ran into trouble here. I ended up pulling the rotation and translation out of the matrix and appling them directly to the vectors. But either way, it works fine. I guess I’ll update this post as my solution becomes better.

February 6, 2009

Fucking Hot

Filed under: Uncategorized — Tags: , , — GiantBucket @ 10:18 am

Yes, people talk about the weather when they have nothing else to talk about, and this is the case at the moment. I cannot program because it is too damn hot to think properly. I’m in Canberra and temperatures are reaching record highs, and me and my computer are mostly naked. I’ve been staring at this unprojection code for a while, I’m really just guessing until it works. I’ve borrowed (ok, stolen) Ogre’s wonderful Vector and Matrix classes to assist me here, and this is what I have so far:

Vector4 CursorPos((float) pWindow->Mouse.X / (float) pWindow->GetWidth(),
(float) pWindow->Mouse.Y / (float) pWindow->GetHeight(),
-Camera.Pos.z,
0.f);
CursorPos = Matrix4::CLIPSPACE2DTOIMAGESPACE.inverse() * CursorPos;
CursorPos = ProjectionMatrix.inverse() * CursorPos;
CursorPos = ModelviewMatrix.inverse() * CursorPos;

See, I make a Vector4 out of the window coords (fourth component for homogenous coordinate system), and some kind of z value. I then translate that into clipspace with Ogre’s handy matrix. Then I multiply it with the inverse of the Projection matrix, then the inverse of the modelview matrix. I should now have usable coordinates.

But I don’t, because I don’t know what I’m doing. What z value do i pass? Am I multiplying the matricies right (this is the only way it seems to work), should both the projection and modelview matricies be inversed like that? And I haven’t even got to projecting this coordinate onto the UI plane. What a perilous journey the cursor’s coordinates must take…

The heat isn’t helping much; I’m considering asking the folks at GameDev.net to point me in the right direction.

February 3, 2009

Writing a profiler

Filed under: Uncategorized — Tags: , , , — GiantBucket @ 8:19 am

Lately I’ve been writing a profiler into LiquidEngine. You see, performance in LE is crap, and until now I had no idea why. The reason: getting the 3d cursor position by getting depth info from the framebuffer and unprojecting. Pulling pixels from the framebuffer with OpenGL seems to be insanely slow. I’m only doing it once per frame, and it’s taking up most of the time.

But! How do I know this? Because of awesome profiler as previously mentioned. How does it work? Well it’s not the most amazing solution, in fact it’s probably a very unoriginal approach, but it’s fun to work on. I’m using timer objects (which time from when they are constructed to when they are destructed). The timing is done with QueryPerformanceCounter(), which has it’s share of issues (including being windows specific). It’s good enough for my purposes though.

I’m using a macro which uses the wonders of static to fetch an ID for the string tag you pass, which is then used to construct the timer object on every subsequent call. My macro looks like this:

#define LE_SCOPE_TIMER(Tag) static int ScopeTimerID = mProfilerData.GetIDForTag(string("<") + string(__FUNCTION__) + string("> ") + Tag); ScopeTimer mScopeTimer(ScopeTimerID);

So you’d put the macro at the start of every function, or every code block, that you want to profile. This is a tad inconvenient, but hey, it’s the best I can do.

It uses the (also windows dependent) __FUNCTION__ macro in addition to the tag you pass. All the profiling data is sent to mProfilerData, a global instance. The call stack can be worked out by keeping a pointer to the last created timer.

This is all fairly boring, but things get much more interesting when I go to visualize the data. I’ve got OpenGL to pull off whatever crazy shit I want. Here’s what I’ve come up with so far:

Some profiler data visualized using LiquidEngine

Some profiler data visualized using LiquidEngine

The big red block is my bottleneck, where I’m getting my cursor position. Hooray for the profiler, for finding the stupid problem with style. So, what do all the colors mean? well the top graph is the functions call’s times, and the graph below that is the proportions of time taken by functions called within this function. Those child timers are listed and color-coded on the left, with the green buttons spawning a profiler data entry viewers for them. I spawned one of the children out and moved it below the first entry hence the second set of graphs.

Anyway I’m still working on it, it’s so damned fun…

Hello world!

Filed under: Uncategorized — GiantBucket @ 7:08 am

I’ve decided to keep the default first post title as this will be a programming blog and hello world is a very programmery thing to say. I plan to post about interesting programming things which will mostly be about my personal project LiquidEngine.

I’m still trying to figure out how to describe LE, but my latest attempt is this: An OpenGL powered object-orientated software UI tool. If this is a little vague or confusing, you could have a look for yourself at my ISP webspace. Comments/suggestions are welcome on my youtube videos (where silly or obnoxious comments are totally acceptable), or here (where silly or obnoxious comments are still accepted, but may look out of place).

Theme: Silver is the New Black. Blog at WordPress.com.

Follow

Get every new post delivered to your Inbox.