Hello,
I was wondering what the best method of drawing shapes or images that are rotated. Currently, the method I use is the following code:
// Translates the screen so that the point of rotation
// is at 0, 0 (pixel coordinates)
gl::translate(rotPt.x, rotPt.y);
// Rotates the screen to the specified amount, if the amount is non-zero
if(rotation != 0)
{
gl::rotate(-rotation);
}
// Draws the rectangle
gl::drawSolidRect(Rectf(pointA.x - rotPt.x, pointA.y - rotPt.y, pointB.x - rotPt.x, pointB.y - rotPt.y));
// Rotates the screen back so that the screen is now level,
// leaving the rectangle rotated to the specified angle,
// if the amount is non-zero
if(rotation != 0)
{
gl::rotate(rotation);
}
// Translates the screen back so that the origin
// is in the correct place again (undoes the above translation)
gl::translate((-rotPt.x), (-rotPt.y));
However, I would think that this isn’t the best way to produce a rotated object, as it seems rather clunky, and the calculations for translations and rotations* would become very intensive once there are many objects on the screen. Is there a better way to do this?
*This is assuming that the gl::translate() and gl::rotate() functions translate and rotate the entire screen, not just the object. As far as I know, this is how they function, but I could be wrong, so let me know if I am.
Thanks, Zack
Hi,
there are a few improvements you can make, which I’ll address briefly:
- Instead of undoing your transformations by applying the reverse translation or rotation, use
gl::pushModelMatrix()
and gl::popModelMatrix()
. See this article for more general information about the matrix stack. Note that Cinder uses slightly different function names, but the idea is the same.
{
gl::pushModelMatrix();
gl::translate( ... );
gl::rotate( ... );
gl::draw( ... );
gl::popModelMatrix();
}
- If you don’t want to remember to call
gl::popModelMatrix()
for each gl::pushModelMatrix()
, you can use the scoped version which will do it for you. In the following example, we will create a variable named scpModel
which pushes the current matrix. When it goes out of scope, it automatically pops the matrix for you. Very powerful. There’s many more scoped functions for you to take advantage of. Learn to use them.
{
// #include "cinder/gl/scoped.h"
gl::ScopedModelMatrix scpModel;
gl::translate( ... );
gl::rotate( ... );
gl::draw( ... );
}
- Store the combined transformations in a
ci::mat4
variable. That way you don’t have to recalculate them every frame. See e.g. glm::translate()
, glm::rotate()
, glm::angleAxis()
and glm::scale()
. You can then apply the transformation by calling gl::setModelMatrix()
.
- Pushing and popping a matrix isn’t very expensive, but if you want to draw thousands of objects it’s better to avoid it. That’s another advantage of using a
ci::mat4
to store the matrix, as you can call gl::pushModelMatrix()
once, then call gl::setModelMatrix()
for each object and finally call gl::popModelMatrix()
once you’re done.
{
gl::ScopedModelMatrix scpModel;
// mMatrices is of type std::vector<ci::mat4>
for( auto &modelMatrix : mMatrices ) {
gl::setModelMatrix( modelMatrix );
gl::draw( ... );
}
}
- The
gl::drawSolidRect()
function is slow. Consider creating a gl::Batch
from a geom::Rect
instead and use it over and over.
- Speaking of which: if you want to draw thousands of rectangles, you better use instanced rendering. This will greatly reduce the number of draw calls (from 1000’s to just 1), hugely increasing render performance. For each instance, you’d store a
mat4
in a Vbo
, attach it to a VboMesh
, store it in a Batch
and render it by calling Batch::drawInstanced()
. See the Cinder samples for more information. Or have a look at my Depth-Of-Field sample, which uses exactly this technique to render hundreds of teapots.
-Paul
1 Like
Thanks!
Does this still apply to things that change position/angle every frame?
Yes, just update its transformation matrix.