Visualization LibraryA lightweight C++ OpenGL middleware for 2D/3D graphics |
[Home] [Tutorials] [All Classes] [Grouped Classes] |
|
|
|
| A Bézier surface defined by multiple concatenated patches rendered with an ascending (from left to right) tessellation level. The dots and the white lines represent the control points of the patches, the red dots represent the corner control points that are guaranteed to touch the Bézier surface. | ||
From Wikipedia [http://en.wikipedia.org/wiki/Bézier_surface]:
"Bézier surfaces are a species of mathematical spline used in computer graphics, computer-aided design, and finite element modelling. As with the Bézier curve, a Bézier surface is defined by a set of control points. Similar to interpolation in many respects, a key difference is that the surface does not, in general, pass through the central control points; rather, it is "stretched" toward them as though each were an attractive force. They are visually intuitive, and for many applications, mathematically convenient."
"Bézier surfaces were first described in 1972 by the French engineer Pierre Bézier who used them to design automobile bodies. Bézier surfaces can be of any degree, but bicubic Bézier surfaces generally provide enough degrees of freedom for most applications."
As you will se the actual code dealing with the Bezier patches is less than ten lines of code! The majority of the code is dedicated to generate the patch data, to setup the effects, to manage the user input and so on.
[From App_BezierSurfaces.hpp]
class App_BezierSurfaces: public BaseDemo { public: App_BezierSurfaces(): mDetail(10) {} void initEvent() { BaseDemo::initEvent(); /* 2 pass shader: 1 = solid, 2 = wireframe */ vl::ref<vl::Effect> fx = new vl::Effect; fx->lod(0)->push_back( new vl::Shader); fx->shader()->enable(vl::EN_LIGHTING); fx->shader()->gocLightModel()->setTwoSide(true); fx->shader()->enable(vl::EN_DEPTH_TEST); fx->shader()->gocLight(0)->setLinearAttenuation(0.025f); fx->shader(0,1)->enable(vl::EN_DEPTH_TEST); fx->shader(0,1)->gocPolygonOffset()->set(-0.5f, -0.5f); fx->shader(0,1)->enable(vl::EN_POLYGON_OFFSET_LINE); fx->shader(0,1)->gocPolygonMode()->set(vl::PM_LINE, vl::PM_LINE); fx->shader(0,1)->gocDepthMask()->set(false); /* Generate random Bézier patches - The simplest bicubic Bézier patch requires 4x4 = 16 control points: A, B, C, D, E, F, G, H, I, L, M, N, O, P, Q, R - The Bézier surface is guaranteed to touch only the 4 corner control points A, D, O and R. A---B---C---D | | | | E---F---G---H | | | | I---L---M---N | | | | O---P---Q---R - You can concatenate two bicubic Bézier patches to form a larger suface by sharing their control points like this: patch 1 patch 2 A---+---+---B---+---+---G | | | | | | | +---+---+---C---+---+---+ | | | | | | | +---+---+---D---+---+---+ | | | | | | | F---+---+---E---+---+---H - In this case the two patches share the control points B, C, D and E. - As we can see the total control points needed are 28 = (2 (patches along x) * 3 + 1) * (1 (patches along y) * 3 + 1) - Also in this case the Bézier surface is guaranteed to touch only the 6 corner control points A, B, E, F, G and H. */ // This concatenated patch will actually contain 3*2 = 6 bicubic Bézier patches for a total of 70 control points! int x = 3; int y = 2; // We use the formula seen above to compute the number of control points required in each direction. vl::ref<vl::BezierPatch> patch1 = new vl::BezierPatch(x*3+1,y*3+1); for(int y=0;y<patch1->y();++y) for(int x=0;x<patch1->x();++x) patch1->points()[x][y] = vl::dvec3(x,vl::randomMinMax(-2.0,+2.0),y); /* Add Bézier patches to our Bézier surface */ // Instance our Bézier surface mBezier = new vl::BezierSurface; // We can actually add multiple Bézier patches mBezier->patches().push_back(patch1.get()); // Define the subdivision detail mBezier->setDetail(mDetail); // Generate the actual geometry using the current patches and detail mBezier->updateBezierSurface(false); // Compute the normals as we have lighting activated mBezier->computeNormals(); // Used by the line rendering mBezier->setColorArray(vlut::blue); // Add the Bézier surface to our scene sceneManager()->tree()->addActor(mBezier.get(), fx.get(), NULL); /* Show the control points */ showPatchControlPoints(mBezier.get()); } /* up/down arrow = increase/decrease the Bézier surface tessellation detail space = toggle control points visibility */ void keyPressEvent(unsigned short ch, vl::EKey key) { BaseDemo::keyPressEvent(ch,key); if (key == vl::Key_Up) { ++mDetail; mDetail = vl::clamp(mDetail, 2, 64); mBezier->setDetail(mDetail); mBezier->updateBezierSurface(false); mBezier->computeNormals(); mBezier->setVBODirty(true); } else if (key == vl::Key_Down) { --mDetail; mDetail = vl::clamp(mDetail, 2, 64); mBezier->setDetail(mDetail); mBezier->updateBezierSurface(false); mBezier->computeNormals(); mBezier->setVBODirty(true); } else if(key == vl::Key_Space) { if (sceneManager()->tree()->actors()->find(mCtrlPoints_Actor.get()) == -1) sceneManager()->tree()->actors()->push_back(mCtrlPoints_Actor.get()); else sceneManager()->tree()->actors()->erase(mCtrlPoints_Actor.get()); } } /* Generates the geometry to render the control points */ void showPatchControlPoints(vl::BezierSurface* bezier) { vl::ref<vl::Effect> fx = new vl::Effect; fx->shader()->enable(vl::EN_DEPTH_TEST); fx->shader()->gocPolygonMode()->set(vl::PM_LINE, vl::PM_LINE); fx->shader()->gocLineWidth()->set(1.0f); fx->shader()->gocPointSize()->set(5.0f); vl::ref<vl::Geometry> geom = new vl::Geometry; int istart = 0; std::vector<vl::fvec3> verts; std::vector<vl::fvec4> colos; for(unsigned ipatch=0; ipatch<bezier->patches().size(); ++ipatch) { const vl::BezierPatch::Points& p = bezier->patches()[ipatch]->points(); for(unsigned ix=0; ix<p.size()-3; ix+=3) for(unsigned iy=0; iy<p[ix].size()-3; iy+=3, istart+=16) { verts.push_back((vl::fvec3)p[ix+0][iy+0]); colos.push_back(vlut::red); verts.push_back((vl::fvec3)p[ix+0][iy+1]); colos.push_back(vlut::white); verts.push_back((vl::fvec3)p[ix+0][iy+2]); colos.push_back(vlut::white); verts.push_back((vl::fvec3)p[ix+0][iy+3]); colos.push_back(vlut::red); verts.push_back((vl::fvec3)p[ix+1][iy+0]); colos.push_back(vlut::white); verts.push_back((vl::fvec3)p[ix+1][iy+1]); colos.push_back(vlut::white); verts.push_back((vl::fvec3)p[ix+1][iy+2]); colos.push_back(vlut::white); verts.push_back((vl::fvec3)p[ix+1][iy+3]); colos.push_back(vlut::white); verts.push_back((vl::fvec3)p[ix+2][iy+0]); colos.push_back(vlut::white); verts.push_back((vl::fvec3)p[ix+2][iy+1]); colos.push_back(vlut::white); verts.push_back((vl::fvec3)p[ix+2][iy+2]); colos.push_back(vlut::white); verts.push_back((vl::fvec3)p[ix+2][iy+3]); colos.push_back(vlut::white); verts.push_back((vl::fvec3)p[ix+3][iy+0]); colos.push_back(vlut::red); verts.push_back((vl::fvec3)p[ix+3][iy+1]); colos.push_back(vlut::white); verts.push_back((vl::fvec3)p[ix+3][iy+2]); colos.push_back(vlut::white); verts.push_back((vl::fvec3)p[ix+3][iy+3]); colos.push_back(vlut::red); vl::ref<vl::DrawArrays> da = new vl::DrawArrays(vl::PT_POINTS,istart,16); vl::ref<vl::DrawElementsUInt> de = new vl::DrawElementsUInt(vl::PT_QUADS); de->indices()->resize(4*9); unsigned int quads[] = { 0,1,5,4, 4,5,9,8, 8,9,13,12, 1,2,6,5, 5,6,10,9, 9,10,14,13, 2,3,7,6, 6,7,11,10, 10,11,15,14 }; for(int q=0; q<4*9; ++q) quads[q] += istart; memcpy(de->indices()->ptr(), quads, sizeof(quads)); geom->primitives()->push_back(de.get()); geom->primitives()->push_back(da.get()); } } vl::ref<vl::ArrayFVec3> vert_array = new vl::ArrayFVec3; geom->setVertexArray(vert_array.get()); *vert_array = verts; vl::ref<vl::ArrayFVec4> cols_array = new vl::ArrayFVec4; geom->setColorArray(cols_array.get()); *cols_array = colos; mCtrlPoints_Actor = sceneManager()->tree()->addActor(geom.get(), fx.get(), NULL); } protected: vl::ref<vl::BezierSurface> mBezier; vl::ref<vl::Actor> mCtrlPoints_Actor; int mDetail; }; // Have fun!