Planning the Spontaneous

It's more than just a blueprint.

Posts Tagged ‘Nonrectangular’

DrawInside(Stencil);

Posted by Robert Chow on 26/05/2010

So a few things have changed since I last made a post.

My targets for the end of the year have been reduced.  Which I’m very glad about.  No, it’s not because I’m a slacker, but more importantly because there simply isn’t enough time.

What’s new?

As a result, the target is now very focused on getting an end product out to show for Grapher.  This end product will have rather minimal functionalities, yet will still be comprehensible enough to use as a Grapher component.  Of course, this target will not actually be used as a component without further work; like those functionalities.

Unfortunately, there are still hidden complexities.  There are always hidden complexities in any piece of software you write, in particular those which you have no prior knowledge about.  My supervisor said to me that you shouldn’t start coding until you have answered all those question marks.  Yet another downfall to my side of planning.  But I think I’m starting to learn.

Once again, Grapher threw up a hidden complexity in the way that layers would be drawn.  The idea was to draw the plot in its own viewport, eliminating everything outside of the view such as data points that do not lie in the visible range.  Of course, this is all fine with a rectangular plot – the natural OpenGl viewport is rectangular.  However, come to nonrectangular plots such as triplots or spiderplots, the story is rather different.

The first solution, would be to use a mask to hide away the data outside of the plot.  Unfortunately, this mask would also hide away any items lying underneath that has already been renderered there.

As a result, the next solution states that, because of the action caused by the first solution, some layers would have to be fixed; for example, the data plot will always be at the bottom to make sure the mask does not obscure anything underneath it.

At this point in the placement, I never did think that I’d be adding something major to Renderer;  the final solution, and arguably the best would be to use a stencil buffer.

Stencil me this

A stencil buffer allows for a nonrectangular viewport.  Primitvives, acting as the viewport is drawn to the stencil buffer; using this, only items drawn inside the viewport outlined by the stencil buffer is rendered.

Simple right?

Of course, there are hidden complexities.  Again.

The best thing for me to do would be to spike the solution, instead of coding it straight into Renderer.

First point of action I took was to visit the OpenGl documentation pages, and they provided me with a few lines of code.

Gl.glEnable(Gl.GL_STENCIL_TEST);

// This will always write to the corresponding stencil buffer bit 1 for each pixel rendered to
Gl.glStencilFunc(Gl.GL_ALWAYS, 1, 1);

// This will only render pixels if the corresponding stencil buffer bit is 0
Gl.glStencilFunc(Gl.GL_EQUALS, 0, 1);

Using these should result in items declared after the final call, to be only visible if it lies outside of the viewport declared after the second call.  I continue to claim that by all means, I am no expert on OpenGl, but that’s the rough idea I get from these calls.

I tried this, but to no avail, nothing happened.

My worry was that SimpleOpenGlControl, the rendering device provided by the Tao Framework did not support a stencil buffer; an earlier look notified me that it did not support an alpha channel.  Luckily, the stencil buffer bits can be changed without having to change the source code.  This should be done before initialising the control contexts.

SimpleOpenGlControl.StencilBits = 1;
SimpleOpenGlControl.InitializeContexts();

Once again, nothing happened.

I had a look around the Internet to try and find some source code that worked;  I noticed in one of them, one particular call was present, yet in mine, it was not.

// This specifies what action to take when either the stencil test or depth test succeeds or fails.
Gl.glStencilOp(Gl.GL_KEEP, Gl.GL_KEEP, Gl.GL_REPLACE);

Placing this at the start of the code appears to make use of the stencil buffer correctly.  I believe this is because when the stencil test succeeds, it will replace the current pixel with the resultant pixel, instead of keeping the old one.  Again, I am no expert; there is a lot more information on the documentation page (however useful, I cannot tell).

Drawing a conclusion

Below is the code and a screenshot of the application I used to spike using the stencil buffer.

public partial class Client : Form
{
public Client()
{
InitializeComponent();

SimpleOpenGlControl.StencilBits = 1;
SimpleOpenGlControl.InitializeContexts();

Gl.glEnable(Gl.GL_STENCIL_TEST);
Gl.glStencilOp(Gl.GL_KEEP, Gl.GL_KEEP, Gl.GL_REPLACE);
// Enable hinting

}

private void simpleOpenGlControl_Paint(object sender, PaintEventArgs e)
{
Gl.glClearColor(1, 1, 1, 1);
Gl.glClear(Gl.GL_COLOR_BUFFER_BIT | Gl.GL_STENCIL_BUFFER_BIT);

Gl.glMatrixMode(Gl.GL_PROJECTION);
Gl.glLoadIdentity();
Gl.glOrtho(0, SimpleOpenGlControl.Width, 0, SimpleOpenGlControl.Height, 10, –10);

Gl.glMatrixMode(Gl.GL_MODELVIEW);
Gl.glLoadIdentity();

// Draw to the stencil buffer
Gl.glStencilFunc(Gl.GL_ALWAYS, 1, 1);
// Turn off colour
Gl.glColorMask(Gl.GL_FALSE, Gl.GL_FALSE, Gl.GL_FALSE, Gl.GL_FALSE);
// Draw our new viewport
Gl.glBegin(Gl.GL_TRIANGLES);
Gl.glVertex2d(0, 0);
Gl.glVertex2d(SimpleOpenGlControl.Width, 0);
Gl.glVertex2d(SimpleOpenGlControl.Width / 2.0, SimpleOpenGlControl.Height);
Gl.glEnd();

// This has been changed so it will draw to the inside of the viewport
// To draw to the outside, use Gl.glStencilFunc(Gl.GL_EQUAL, 0, 1);

Gl.glStencilFunc(Gl.GL_EQUAL, 1, 1);
// Turn on colour
Gl.glColorMask(Gl.GL_TRUE, Gl.GL_TRUE, Gl.GL_TRUE, Gl.GL_TRUE);
// Draw triangles here

// Disable stencil test to be able to draw anywhere irrespective of the stencil buffer
Gl.glPushAttrib(Gl.GL_ENABLE_BIT);
Gl.glDisable(Gl.GL_STENCIL_TEST);
// Draw border here

Gl.glPopAttrib();

SimpleOpenGlControl.Invalidate();
}
}

Stencil Test.  This is created by first delaring the triangular viewport in the stencil buffer;  only primitives inside the stencil will be rendered.  The border was added on later, after disabling the stencil test.

Advertisements

Posted in Computing, Placement | Tagged: , , , , , , , | Leave a Comment »