VRender User's Manual
How does it work ?
Basically, VRender is called with parameters and a drawing function. When
executed, it turns OpenGL into feedback mode and calls the drawing function,
then turns OpenGL back into its original drawing mode to get the rendered geometric primitives in
window coordinates. After that, the challenging part is to sort everything so as to be able to render
everything back to front in the postscript file.
What should I do ?
Using VRender only needs to call the following method declared in VRender.h
:
void vrender::VectorialRender(RenderCB DrawFunc, void *callback_params, VRenderParams& render_params) ;
DrawFunc
is your drawing function. It should have the
following prototype (defined as RenderCB
type):
void draw(void *param)
callback_params
will be passed as param
to the
draw()
function at the time VRender calls draw()
.
This functionality is provided for your convenience if your drawing function
needs some parameters.
render_params
contains all the rendering parameters (sort method, options, etc.). Using a class to pass these params is a good way to keep the same interface prototype and easily handle default values. See the
table below for the various options and the method to set them. Nothing prevents you from building a graphic user interface to setup these parameters, as done in the provided example.
WARNING:
Your draw()
function should not contain any
operation related to the OpenGL render mode or to the OpenGL context;
otherwise you'll add some nice pieces to your core dump collection. Typical
examples of calls that should not be inside draw()
are: glXSwapBuffers()
,
glXCreateContext()
, and glRenderMode()
, but this list is not exhaustive.
Rendering parameters
All rendering parameters are listed in the table below along with their default value and the method in
class VRenderParams
to set them up:
Parameter/Option | Values | Explaination | VRenderParams Method | Default |
Sorting algorithm | NoSorting BSPSort TopologicalSort AdvancedTopo- logicalSort | This is the sorting algorithm to use. See below for more details about each method. | setSortMethod(value) | BSPSort |
Output format | EPS PS XFIG SVG | Format to output the drawing. Names speek for themseves. SVG is not supported yet, but will soon be. | setFormat(value) | EPS |
Output filename | Any string | File name to output the drawing. | setFilename(value) | NULL |
Progress function | Any function of type void f(float,const std::string&) | The supplied function will be called at regular intervals. The string parameters is describes the currently performed operation, while the float parameter indicates in the in the interval [0,1] the progress of this operation. This can be used to make a progress bar in your own application. | setProgressFunction(value) | NULL |
Hidden face removal | true/false | Cleverly suppresses hidden faces from the output instead of just rendering them back to front. | setOption(CullHiddenFaces,value) | false |
Black and white | true/false | Renders plain polygons in white and lines in black. | setOption(RenderBlackAndWhite,value) | false |
Add background | true/false | Renders a rectangle behind the scene to simulate the effect of the glClear() call. | setOption(AddBackground,value) | false |
Tighten bounding box | true/false | Tighten the bounding box to the rectangular region occupied by primitives. | setOption(TightenBoundingBox,value) | false |
Cull back faces | true/false | Early backface culling applied to the polygons before any sorting operation. This helps reducing the number of polygons if backface culling is not enabled in your drawing function. Otherwise culled polygons do not appear in the feedback buffer anyway. | setOption(OptimizeBackFaceCulling, value) | false |
Sorting algorithms
The different sorting methods are explained below into more details. Each has its own advantage,
so I kept them all in the lib:
Method |
Short explaination |
Example |
No sorting |
Do not sort primitives and keep the original order into which they were sent to OpenGL.
This is usefull for shooting 2D drawings were sorting is not relevant,
or when rendering only black lines. |
|
BSP | Primitives are sorted into a BSP which splitting planes are based on
the primitive themselves. This is a very stable algorithm although it tends to split primitives
much more than necessary. |
|
Topological sort |
Primitives are compared together related to the viewpoint and categorized as upper/below or independent.
Naturally, intersecting primitives may result into both upper and below. From this we build a precedence graph which is then rendered back to front. This is perfect for most meshes were no polygons intersect each other. |
|
Advanced topological sort |
Same as topological sort, except that cycles are properly broken. This results into a correct rendering in
any case, but only splitting primitives when necessary. Output size is usualy much smaller than
that of the BSP method. Computation time is significantly larger. |
|
Example code
This features a very simple example code which calls VRender. The code is inspired to what is done internaly
in the QGLViewer library.
First, define the draw()
and progress
functions (See above):
// This class is responsible for drawing, and lots of other nice stuff.
class QGLViewer ;
// The drawing function passed to VRender.
static void drawVectorial(void* param)
{
static_cast<QGLViewer*>(param)->drawGL();
}
// The progress function passed to VRender.
static void progressFunction(float f, const std::string& info)
{
// Example one: just output the info
fprintf(stdout,"%1.2f completed (%s)",f*100,info.c_str()) ;
// Example two: let's use a progress bar, it's nicer.
static QProgressBar *pbar = NULL ;
if(!pbar)
{
pbar = new QProgressBar(200) ;
pbar->show() ;
}
pbar->setCaption(info) ;
pbar->setProgress((int)(f*200)) ;
}
Then define the main function which first launches an interface to get the parameters set, and then
calls vrender::VectorialRender()
with these parameters.
int QGLViewer::saveVectorialSnapshot()
{
static EPSInterface* interf = NULL;
// Launch an interface with buttons and check-boxes so as to allow the user to choose
// the vector rendering options.
if (!interf)
interf = new EPSInterface(widget);
if(interf->exec() == QDialog::Rejected)
return -1;
// Now, get back the user's choices.
// And store them into the VRenderParams structure defined for that in VRender.h
vrender::VRenderParams vrp ;
vrp.setOption(vrender::VRenderParams::OptimizeBackFaceCulling,interf->backFaceCullingEnabled()) ;
vrp.setOption(vrender::VRenderParams::RenderBlackAndWhite, interf->blackAndWhiteEnabled()) ;
vrp.setOption(vrender::VRenderParams::AddBackground, interf->includeBackgroundEnabled()) ;
vrp.setOption(vrender::VRenderParams::TightenBoundingBox,interf->tightenBBoxEnabled()) ;
vrp.setOption(vrender::VRenderParams::CullHiddenFaces, interf->cullHiddenFacesEnabled()) ;
vrp.setSortMethod(interf->sortMethod()) ;
QString FileName = QFileDialog::getSaveFilename("*.eps") ;
vrp.setFilename(FileName) ;
// Now setup progress display function (defined at top of the file)
vrp.setProgressFunction(progressFunction) ;
// And call the renderer. Note that params is set to be this, so that VectorialRender will pass this
// to drawVectorial which in turns will call the draw() method of this. This is inelegant, but this is the
// only way of passing a non static method as a parameter.
vrender::VectorialRender(drawVectorial,(void *)this,vrp) ;
}
FAQ
Q1: My postscript displays whitish edges inside polygons
- A1: Yep. This is the antialiasing of ghostview which is not exact. Do not worry about this, it does not show on the printer nor in most pdf viewers.
Q2: Polygons are not sorted adequately with respect to lines
- A2: This is because you render polygons and lines -- which should theoretically be in the same plane
-- with too much of an offset. If you look at your OpenGL display, you will most probably see the same artifacts.
Q3: I get unexpected core dumps, whatever the rendering parameters and viewpoint
- A3: You most probably inserted undesired glx calls in your drawing function. See above warning.
Q4: Sometimes, depending on the viewpoint, primitives are not sorted ok
- A4: Sorry. Primitive comparison is not an exact science since
everything must be done up to an ε-precision. This happens quite rarely
and depends on the sorting method, so try changing the sorting method or
the viewpoint.
Q5: The library does not compile under windows
- A5: I now. You have two possibilities: (1) try a real operating system with a real c++ compiler,
or (2) use QGLViewer
which includes VRender
and for which Gilles Debunne has made a real effort
adapting the code so that it compiles under windows
(use Ctrl+S and select eps/ps/xfig format).
Q6: Their is a bug
- A6: Well, send it to me ! Please try to make a small program and make an effort to
over-simplify the geometry while keeping the bug.
Last modified on Thursday, March 3, 2005.