用OpenSceneGraph实现的NeHe OpenGL教程 - 第二十八课

52 篇文章 38 订阅
  • 简介

NeHe教程在这节课中向我们介绍了贝塞尔曲面,贝塞尔曲面是一种可以只使用很少的参数就可以描述出复杂曲面的一种数学工具。关于贝塞尔曲线的内容可以参考Bézier surface

  • 实现

本课的实现过程非常简单,在理解了贝塞尔曲面之后利用贝塞尔曲面的计算公式计算出控制点和插值点坐标:

控制点线条的代码如下:

osg::Geode*	createBezierControlLineGeode()
{
	osg::Geode *geode = new osg::Geode;
	osg::Geometry *geometry = new osg::Geometry;
	osg::Vec3Array *vertexArray = new osg::Vec3Array;
	osg::Vec3Array *colorArray = new osg::Vec3Array;
	colorArray->push_back(osg::Vec3(1.0, 0.0, 0.0));
	geometry->setColorArray(colorArray, osg::Array::BIND_OVERALL);
	geometry->setVertexArray(vertexArray);
	geometry->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
	
	for(int i=0;i<4;i++) {								// draw the horizontal lines
		for(int j=0;j<4;j++)
			vertexArray->push_back(osg::Vec3(mybezier.anchors[i][j].x, mybezier.anchors[i][j].y, mybezier.anchors[i][j].z));
	}
	for(int i=0;i<4;i++) {								// draw the vertical lines
		for(int j=0;j<4;j++)
			vertexArray->push_back(osg::Vec3(mybezier.anchors[j][i].x, mybezier.anchors[j][i].y, mybezier.anchors[j][i].z));
	}

	geometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINE_STRIP, 0, vertexArray->size() / 2));
	geometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINE_STRIP, vertexArray->size() / 2, vertexArray->size() / 2));
	geode->addDrawable(geometry);
	return geode;
}
利用控制点插值出曲面中的其他坐标点,绘制曲面:

osg::Geode* createBezierGeode(BEZIER_PATCH patch, int divs)
{
	int			u = 0, v;
	float		py, px, pyold; 
	POINT_3D	temp[4];
	POINT_3D	*last = (POINT_3D*)malloc(sizeof(POINT_3D)*(divs+1));
	// array of points to mark the first line of polys

	temp[0] = patch.anchors[0][3];				// the first derived curve (along x axis)
	temp[1] = patch.anchors[1][3];
	temp[2] = patch.anchors[2][3];
	temp[3] = patch.anchors[3][3];

	for (v=0;v<=divs;v++) {						// create the first line of points
		px = ((float)v)/((float)divs);			// percent along y axis
		// use the 4 points from the derives curve to calculate the points along that curve
		last[v] = Bernstein(px, temp);
	}
	
	osg::Geode *geode = new osg::Geode;
	osg::Geometry *geometry = new osg::Geometry;
	osg::Vec3Array *vertexArray = new osg::Vec3Array;
	osg::Vec2Array *textureArray = new osg::Vec2Array;
	geometry->setVertexArray(vertexArray);
	geometry->setTexCoordArray(0, textureArray);
	osg::Vec3Array *colorArray = new osg::Vec3Array;
	colorArray->push_back(osg::Vec3(1, 1, 1));
	geometry->setColorArray(colorArray, osg::Array::BIND_OVERALL);
	geometry->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF);


	for (u=1;u<=divs;u++) {
		py	  = ((float)u)/((float)divs);			// Percent along Y axis
		pyold = ((float)u-1.0f)/((float)divs);		// Percent along old Y axis

		temp[0] = Bernstein(py, patch.anchors[0]);	// Calculate new bezier points
		temp[1] = Bernstein(py, patch.anchors[1]);
		temp[2] = Bernstein(py, patch.anchors[2]);
		temp[3] = Bernstein(py, patch.anchors[3]);

		for (v=0;v<=divs;v++) {
			px = ((float)v)/((float)divs);			// Percent along the X axis

			textureArray->push_back(osg::Vec2(pyold, px));				// Apply the old texture coords
			vertexArray->push_back(osg::Vec3(last[v].x, last[v].y, last[v].z));	// Old Point

			last[v] = Bernstein(px, temp);			// Generate new point
			textureArray->push_back(osg::Vec2(py, px));					// Apply the new texture coords
			vertexArray->push_back(osg::Vec3(last[v].x, last[v].y, last[v].z));	// New Point
		}
	}

	osg::Texture2D *bezierTex = new osg::Texture2D;
	bezierTex->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
	bezierTex->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
	bezierTex->setImage(osgDB::readImageFile("Data/NeHe.bmp"));

	geometry->getOrCreateStateSet()->setTextureAttributeAndModes(0, bezierTex);
	geometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::TRIANGLE_STRIP, 0, vertexArray->size()));
	geode->addDrawable(geometry);

	return geode;
}
最后将二者添加到Switch节点中,在交互操作中切换控制点线条的显示和隐藏:

	osg::Switch *switchLineAndGeodeSwitch = new osg::Switch;
	g_Swith = switchLineAndGeodeSwitch;
	rotZMT->addChild(switchLineAndGeodeSwitch);

	switchLineAndGeodeSwitch->addChild(createBezierControlLineGeode());
	switchLineAndGeodeSwitch->addChild(createBezierGeode(mybezier, divs));
	switchLineAndGeodeSwitch->setAllChildrenOn();
交互部分代码如下

				if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Space)
				{
					if (!g_Swith)
						return false;
					if (g_Swith->getValue(0)){ 
						g_Swith->setSingleChildOn(1);
					}else{
						g_Swith->setAllChildrenOn();
					}
				}

最后编译运行程序:


附:本课源码(源码中可能存在错误和不足,仅供参考)

#include "../osgNeHe.h"

#include <QtCore/QTimer>
#include <QtGui/QApplication>
#include <QtGui/QVBoxLayout>

#include <osgViewer/Viewer>
#include <osgDB/ReadFile>
#include <osgQt/GraphicsWindowQt>

#include <osg/MatrixTransform>
#include <osg/Texture2D>
#include <osg/Switch>


osg::MatrixTransform *g_ZRotMT = NULL;
osg::Switch					*g_Swith = NULL;

typedef struct point_3d {
	double x, y, z;
} POINT_3D;

typedef struct bpatch {
	POINT_3D	anchors[4][4];
} BEZIER_PATCH;


BEZIER_PATCH	mybezier;	
int				divs = 7;



POINT_3D pointAdd(POINT_3D p, POINT_3D q) {
	p.x += q.x;		p.y += q.y;		p.z += q.z;
	return p;
}

POINT_3D pointTimes(double c, POINT_3D p) {
	p.x *= c;	p.y *= c;	p.z *= c;
	return p;
}

POINT_3D makePoint(double a, double b, double c) {
	POINT_3D p;
	p.x = a;	p.y = b;	p.z = c;
	return p;
}


// Calculates 3rd degree polynomial based on array of 4 points
// and a single variable (u) which is generally between 0 and 1
POINT_3D Bernstein(float u, POINT_3D *p) {
	POINT_3D	a, b, c, d, r;

	a = pointTimes(pow(u,3), p[0]);
	b = pointTimes(3*pow(u,2)*(1-u), p[1]);
	c = pointTimes(3*u*pow((1-u),2), p[2]);
	d = pointTimes(pow((1-u),3), p[3]);

	r = pointAdd(pointAdd(a, b), pointAdd(c, d));

	return r;
}

//
void initBezier(void) {
	mybezier.anchors[0][0] = makePoint(-0.75,	-0.75,	-0.5);
	mybezier.anchors[0][1] = makePoint(-0.25,	-0.75,	0.0);
	mybezier.anchors[0][2] = makePoint(0.25,	-0.75,	0.0);
	mybezier.anchors[0][3] = makePoint(0.75,	-0.75,	-0.5);
	mybezier.anchors[1][0] = makePoint(-0.75,	-0.25,	-0.75);
	mybezier.anchors[1][1] = makePoint(-0.25,	-0.25,	0.5);
	mybezier.anchors[1][2] = makePoint(0.25,	-0.25,	0.5);
	mybezier.anchors[1][3] = makePoint(0.75,	-0.25,	-0.75);
	mybezier.anchors[2][0] = makePoint(-0.75,	0.25,	0.0);
	mybezier.anchors[2][1] = makePoint(-0.25,	0.25,	-0.5);
	mybezier.anchors[2][2] = makePoint(0.25,	0.25,	-0.5);
	mybezier.anchors[2][3] = makePoint(0.75,	0.25,	0.0);
	mybezier.anchors[3][0] = makePoint(-0.75,	0.75,	-0.5);
	mybezier.anchors[3][1] = makePoint(-0.25,	0.75,	-1.0);
	mybezier.anchors[3][2] = makePoint(0.25,	0.75,	-1.0);
	mybezier.anchors[3][3] = makePoint(0.75,	0.75,	-0.5);
}

//
//创建贝塞尔曲线

osg::Geode*	createBezierControlLineGeode()
{
	osg::Geode *geode = new osg::Geode;
	osg::Geometry *geometry = new osg::Geometry;
	osg::Vec3Array *vertexArray = new osg::Vec3Array;
	osg::Vec3Array *colorArray = new osg::Vec3Array;
	colorArray->push_back(osg::Vec3(1.0, 0.0, 0.0));
	geometry->setColorArray(colorArray, osg::Array::BIND_OVERALL);
	geometry->setVertexArray(vertexArray);
	geometry->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
	
	for(int i=0;i<4;i++) {								// draw the horizontal lines
		for(int j=0;j<4;j++)
			vertexArray->push_back(osg::Vec3(mybezier.anchors[i][j].x, mybezier.anchors[i][j].y, mybezier.anchors[i][j].z));
	}
	for(int i=0;i<4;i++) {								// draw the vertical lines
		for(int j=0;j<4;j++)
			vertexArray->push_back(osg::Vec3(mybezier.anchors[j][i].x, mybezier.anchors[j][i].y, mybezier.anchors[j][i].z));
	}

	geometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINE_STRIP, 0, vertexArray->size() / 2));
	geometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINE_STRIP, vertexArray->size() / 2, vertexArray->size() / 2));
	geode->addDrawable(geometry);
	return geode;
}

osg::Geode* createBezierGeode(BEZIER_PATCH patch, int divs)
{
	int			u = 0, v;
	float		py, px, pyold; 
	POINT_3D	temp[4];
	POINT_3D	*last = (POINT_3D*)malloc(sizeof(POINT_3D)*(divs+1));
	// array of points to mark the first line of polys

	temp[0] = patch.anchors[0][3];				// the first derived curve (along x axis)
	temp[1] = patch.anchors[1][3];
	temp[2] = patch.anchors[2][3];
	temp[3] = patch.anchors[3][3];

	for (v=0;v<=divs;v++) {						// create the first line of points
		px = ((float)v)/((float)divs);			// percent along y axis
		// use the 4 points from the derives curve to calculate the points along that curve
		last[v] = Bernstein(px, temp);
	}
	
	osg::Geode *geode = new osg::Geode;
	osg::Geometry *geometry = new osg::Geometry;
	osg::Vec3Array *vertexArray = new osg::Vec3Array;
	osg::Vec2Array *textureArray = new osg::Vec2Array;
	geometry->setVertexArray(vertexArray);
	geometry->setTexCoordArray(0, textureArray);
	osg::Vec3Array *colorArray = new osg::Vec3Array;
	colorArray->push_back(osg::Vec3(1, 1, 1));
	geometry->setColorArray(colorArray, osg::Array::BIND_OVERALL);
	geometry->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF);


	for (u=1;u<=divs;u++) {
		py	  = ((float)u)/((float)divs);			// Percent along Y axis
		pyold = ((float)u-1.0f)/((float)divs);		// Percent along old Y axis

		temp[0] = Bernstein(py, patch.anchors[0]);	// Calculate new bezier points
		temp[1] = Bernstein(py, patch.anchors[1]);
		temp[2] = Bernstein(py, patch.anchors[2]);
		temp[3] = Bernstein(py, patch.anchors[3]);

		for (v=0;v<=divs;v++) {
			px = ((float)v)/((float)divs);			// Percent along the X axis

			textureArray->push_back(osg::Vec2(pyold, px));				// Apply the old texture coords
			vertexArray->push_back(osg::Vec3(last[v].x, last[v].y, last[v].z));	// Old Point

			last[v] = Bernstein(px, temp);			// Generate new point
			textureArray->push_back(osg::Vec2(py, px));					// Apply the new texture coords
			vertexArray->push_back(osg::Vec3(last[v].x, last[v].y, last[v].z));	// New Point
		}
	}

	osg::Texture2D *bezierTex = new osg::Texture2D;
	bezierTex->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
	bezierTex->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
	bezierTex->setImage(osgDB::readImageFile("Data/NeHe.bmp"));

	geometry->getOrCreateStateSet()->setTextureAttributeAndModes(0, bezierTex);
	geometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::TRIANGLE_STRIP, 0, vertexArray->size()));
	geode->addDrawable(geometry);

	return geode;
}


//
场景交互代码
//

class RotAxisCallback : public osg::NodeCallback
{
public:
	RotAxisCallback(const osg::Vec3& axis, double rotSpeed = 0.0, double currentAngle = 0.0)
		: _rotAxis(axis), _rotSpeed(rotSpeed), _currentAngle(currentAngle){ }

	virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
	{
		osg::MatrixTransform *rotMT = dynamic_cast<osg::MatrixTransform*>(node);
		if (!rotMT)
			return;
		rotMT->setMatrix(osg::Matrix::rotate(_currentAngle, _rotAxis));

		traverse(node, nv);
	}

	void setRotateAngle(double speed)
	{
		_currentAngle = speed;
	}

	double getRotateAngle() const
	{
		return _currentAngle;
	}


private:
	osg::Vec3		_rotAxis;
	double			_currentAngle;
	double			_rotSpeed;
};



//
//
class ManipulatorSceneHandler : public osgGA::GUIEventHandler
{
public:
	ManipulatorSceneHandler()
	{
	}

	virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa)
	{
		osgViewer::Viewer *viewer = dynamic_cast<osgViewer::Viewer*>(&aa);
		if (!viewer)
			return false;
		if (!viewer->getSceneData())
			return false;
		if (ea.getHandled()) 
			return false;

		osg::Group *root = viewer->getSceneData()->asGroup();

		switch(ea.getEventType())
		{
		case(osgGA::GUIEventAdapter::KEYDOWN):
			{
				if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Left)
				{
					if (!g_ZRotMT)
						return false;

					RotAxisCallback *rotCallback = dynamic_cast<RotAxisCallback*>(g_ZRotMT->getUpdateCallback());
					if (!rotCallback)
						return false;

					double speed = rotCallback->getRotateAngle();
					speed -= 0.02;
					rotCallback->setRotateAngle(speed);
				}

				if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Right)
				{
					if (!g_ZRotMT)
						return false;

					RotAxisCallback *rotCallback = dynamic_cast<RotAxisCallback*>(g_ZRotMT->getUpdateCallback());
					if (!rotCallback)
						return false;

					double speed = rotCallback->getRotateAngle();
					speed += 0.02;
					rotCallback->setRotateAngle(speed);
				}

				if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Space)
				{
					if (!g_Swith)
						return false;
					if (g_Swith->getValue(0)){ 
						g_Swith->setSingleChildOn(1);
					}else{
						g_Swith->setAllChildrenOn();
					}
				}
			}
		default: break;
		}
		return false;
	}
};



class ViewerWidget : public QWidget, public osgViewer::Viewer
{
public:
	ViewerWidget(osg::Node *scene = NULL)
	{
		QWidget* renderWidget = getRenderWidget( createGraphicsWindow(0,0,100,100), scene);

		QVBoxLayout* layout = new QVBoxLayout;
		layout->addWidget(renderWidget);
		layout->setContentsMargins(0, 0, 0, 1);
		setLayout( layout );

		connect( &_timer, SIGNAL(timeout()), this, SLOT(update()) );
		_timer.start( 10 );
	}

	QWidget* getRenderWidget( osgQt::GraphicsWindowQt* gw, osg::Node* scene )
	{
		osg::Camera* camera = this->getCamera();
		camera->setGraphicsContext( gw );

		const osg::GraphicsContext::Traits* traits = gw->getTraits();

		camera->setClearColor( osg::Vec4(0.0, 0.0, 0.0, 1.0) );
		camera->setViewport( new osg::Viewport(0, 0, traits->width, traits->height) );
		camera->setProjectionMatrixAsPerspective(45.0f, static_cast<double>(traits->width)/static_cast<double>(traits->height), 0.1f, 100.0f );
		camera->setViewMatrixAsLookAt(osg::Vec3d(0, 0, 1), osg::Vec3d(0, 0, 0), osg::Vec3d(0, 1, 0));

		this->setSceneData( scene );
		addEventHandler(new ManipulatorSceneHandler);
		return gw->getGLWidget();
	}

	osgQt::GraphicsWindowQt* createGraphicsWindow( int x, int y, int w, int h, const std::string& name="", bool windowDecoration=false )
	{
		osg::DisplaySettings* ds = osg::DisplaySettings::instance().get();
		osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;
		traits->windowName = name;
		traits->windowDecoration = windowDecoration;
		traits->x = x;
		traits->y = y;
		traits->width = w;
		traits->height = h;
		traits->doubleBuffer = true;
		traits->alpha = ds->getMinimumNumAlphaBits();
		traits->stencil = ds->getMinimumNumStencilBits();
		traits->sampleBuffers = ds->getMultiSamples();
		traits->samples = ds->getNumMultiSamples();

		return new osgQt::GraphicsWindowQt(traits.get());
	}

	virtual void paintEvent( QPaintEvent* event )
	{ 
		frame(); 
	}

protected:

	QTimer _timer;
};



osg::Node*	buildScene()
{
	initBezier();

	osg::Group *root = new osg::Group;

	osg::MatrixTransform *zoomMT = new osg::MatrixTransform;
	zoomMT->setMatrix(osg::Matrix::translate(0.0f,0.0f,-4.0f));

	osg::MatrixTransform *rotXMT = new osg::MatrixTransform;
	rotXMT->setMatrix(osg::Matrix::rotate(osg::DegreesToRadians(-75.0), osg::X_AXIS));
	
	osg::MatrixTransform *rotZMT = new osg::MatrixTransform;
	rotZMT->addUpdateCallback(new RotAxisCallback(osg::Z_AXIS));
	g_ZRotMT = rotZMT;

	osg::Switch *switchLineAndGeodeSwitch = new osg::Switch;
	g_Swith = switchLineAndGeodeSwitch;
	rotZMT->addChild(switchLineAndGeodeSwitch);

	switchLineAndGeodeSwitch->addChild(createBezierControlLineGeode());
	switchLineAndGeodeSwitch->addChild(createBezierGeode(mybezier, divs));
	switchLineAndGeodeSwitch->setAllChildrenOn();

	root->addChild(zoomMT);
	zoomMT->addChild(rotXMT);
	rotXMT->addChild(rotZMT);

	return root;
}


int main( int argc, char** argv )
{
	QApplication app(argc, argv);
	ViewerWidget* viewWidget = new ViewerWidget(buildScene());
	viewWidget->setGeometry( 100, 100, 640, 480 );
	viewWidget->show(); 
	return app.exec();
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值