Whilst working on one of my projects this year for uni I was looking for some code to draw a cylinder between two points (using OpenGL). There were a couple of solutions out there, but they weren’t that great.
I couldn’t find one that worked reliably and simply (without lots of different if statements trying to catch different cases). Anyway, after a bit of thought I knocked this one out. It’s actually a lot simpler than you think…which is probably why people haven’t bothered to post it.
Anyway…enjoy some pseudo code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | Vector3D a, b; (the two points you want to draw between) // This is the default direction for the cylinders to face in OpenGL Vector3D z = Vector3D(0,0,1);Â Â Â Â Â Â Â Â // Get diff between two points you want cylinder along Vector3D p = (a - b);Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â // Get CROSS product (the axis of rotation) Vector3D t = CROSS_PRODUCT (z , p); // Get angle. LENGTH is magnitude of the vector double angle = 180 / PI * acos ((DOT_PRODUCT(z, p) / p.LENGTH()); glTranslated(b.x,b.y,b.z); glRotated(angle,t.x,t.y,t.z); gluQuadricOrientation(YourQuadric,GLU_OUTSIDE); gluCylinder(YourQuadric, RADIUS, RADIUS, p.LENGTH(), SEGS1, SEGS2); |
Hope that helps someone out there.
Comments
I stumbled on your post today… And found it very useful.
I use a scene graph library called OpenSceneGraph
Based on your post… here is how to draw a cylinder between two points in Open Scene Graph
///////////////////////////////////////////////////////////////////////////////
// void AddCylinderBetweenPoints(osg::Vec3 StartPoint, osg::Vec3 EndPoint, float radius, osg::Vec4 CylinderColor, osg::Group *pAddToThisGroup)
/*
The following function creates a cylinder between two points with a given radius and color.
*/
void AddCylinderBetweenPoints(osg::Vec3 StartPoint, osg::Vec3 EndPoint, float radius, osg::Vec4 CylinderColor, osg::Group *pAddToThisGroup)
{
osg::ref_ptr geode = new osg::Geode;
osg::Vec3 center;
float height;
osg::ref_ptr cylinder;
osg::ref_ptr cylinderDrawable;
osg::ref_ptr pMaterial;
height = (StartPoint- EndPoint).length();
center = osg::Vec3( (StartPoint.x() + EndPoint.x()) / 2, (StartPoint.y() + EndPoint.y()) / 2, (StartPoint.z() + EndPoint.z()) / 2);
// This is the default direction for the cylinders to face in OpenGL
osg::Vec3 z = osg::Vec3(0,0,1);
// Get diff between two points you want cylinder along
osg::Vec3 p = (StartPoint – EndPoint);
// Get CROSS product (the axis of rotation)
osg::Vec3 t = z ^ p;
// Get angle. length is magnitude of the vector
double angle = acos( (z * p) / p.length());
// Create a cylinder between the two points with the given radius
cylinder = new osg::Cylinder(center,radius,height);
cylinder->setRotation(osg::Quat(angle, osg::Vec3(t.x(), t.y(), t.z())));
cylinderDrawable = new osg::ShapeDrawable(cylinder );
geode->addDrawable(cylinderDrawable);
// Set the color of the cylinder that extends between the two points.
pMaterial = new osg::Material;
pMaterial->setDiffuse( osg::Material::FRONT, CylinderColor);
geode->getOrCreateStateSet()->setAttribute( pMaterial, osg::StateAttribute::OVERRIDE );
// Add the cylinder between the two points to an existing group
pAddToThisGroup->addChild(geode);
}
Awesome, I’m glad it helped. Thanks for sharing more code too.
it works
But could you explain why it works?
[…] 轉自:http://www.thjsmith.com/40/cylinder-between-two-points-opengl-c […]
Sure.
We calculate p as the vector going from b to a (p = a – b).
z is simply the OpenGL default cylinder draw direction. If we did no rotation, it would be drawn in that orientation.
The cross product of z and p gives a vector perpendicular to both p and z, (this is t). This is then the vector the rotation pivots about.
We then use the dot product to obtain the angle needed to rotate (the division by the length is purely because I haven’t normalised p).
Then, we just perform the rotation.
Does that help?
Thx, it helps
Thanks!
Leave a Comment