在OpenSceneGraph中创建球体(使用osg::Geometry)
Creating a Sphere (using osg::Geometry) in OpenSceneGraph
我花了相当长的时间来实现这一点,但我的Sphere无法显示
使用以下代码生成我的函数:
使用Visual C++在Opengl中创建三维球体
其余的是简单OSG,带有OSG::Geometry
(注意:不可ShapeDrawable,因为您无法使用它实现自定义形状。)
将顶点、法线和纹理坐标添加到VecArray中。
首先,我怀疑有什么不正常的地方,因为我保存的对象有一半是空的
有没有办法将现有描述转换为OSG
原因我想了解以后如何创建对象。
事实上,这与以后的任务有关,但目前我只是提前做好准备。
旁注:由于我必须在没有索引的情况下制作,所以我将它们排除在外
但我的圆柱体在没有它们的情况下显示得很好。
注意:我不是OSG专家。但是,我确实做了一些研究。
OSG要求以逆时针顺序定义所有面,以便背面剔除可以拒绝"面朝外"的面。用于生成球体的代码不会按逆时针顺序生成所有面。
你可以通过两种方式来解决这个问题:
- 通过按CCW顺序插入面,调整代码生成面的方式
- 将模型加倍,并将每个面插入两次,一次是每个面上的顶点按当前顺序插入,另一次是顶点按相反顺序插入
上面的选项1将限制您的多边形总数。选项2将为您提供一个从球体外部和内部都可见的球体。
要实现选项2,您只需要从链接到的代码中修改此循环:
indices.resize(rings * sectors * 4);
std::vector<GLushort>::iterator i = indices.begin();
for(r = 0; r < rings-1; r++)
for(s = 0; s < sectors-1; s++) {
*i++ = r * sectors + s;
*i++ = r * sectors + (s+1);
*i++ = (r+1) * sectors + (s+1);
*i++ = (r+1) * sectors + s;
}
将四边形集合加倍,如下所示:
indices.resize(rings * sectors * 8);
std::vector<GLushort>::iterator i = indices.begin();
for(r = 0; r < rings-1; r++)
for(s = 0; s < sectors-1; s++) {
*i++ = r * sectors + s;
*i++ = r * sectors + (s+1);
*i++ = (r+1) * sectors + (s+1);
*i++ = (r+1) * sectors + s;
*i++ = (r+1) * sectors + s;
*i++ = (r+1) * sectors + (s+1);
*i++ = r * sectors + (s+1);
*i++ = r * sectors + s;
}
不过,这确实是"更大的锤子"解决方案。
就我个人而言,我很难弄清楚为什么最初的循环不够;通过我对几何体的直觉,感觉它已经生成了CCW面,因为每个连续的环都在前一个环的上方,而每个连续的扇区都是围绕前一个球面的CCW面。因此,原始顺序本身应该是相对于离观看者最近的面的CCW。
EDIT使用您之前链接的OpenGL代码和今天链接的OSG教程,我编写了一个我认为正确的程序来生成球体的osg::Geometry
/osg::Geode
。我没有办法测试下面的代码,但通过桌面检查,它看起来是正确的,或者至少基本上是正确的。
#include <vector>
class SolidSphere
{
protected:
osg::Geode sphereGeode;
osg::Geometry sphereGeometry;
osg::Vec3Array sphereVertices;
osg::Vec3Array sphereNormals;
osg::Vec2Array sphereTexCoords;
std::vector<osg::DrawElementsUInt> spherePrimitiveSets;
public:
SolidSphere(float radius, unsigned int rings, unsigned int sectors)
{
float const R = 1./(float)(rings-1);
float const S = 1./(float)(sectors-1);
int r, s;
sphereGeode.addDrawable( &sphereGeometry );
// Establish texture coordinates, vertex list, and normals
for(r = 0; r < rings; r++)
for(s = 0; s < sectors; s++)
{
float const y = sin( -M_PI_2 + M_PI * r * R );
float const x = cos(2*M_PI * s * S) * sin( M_PI * r * R );
float const z = sin(2*M_PI * s * S) * sin( M_PI * r * R );
sphereTexCoords.push_back( osg::Vec2(s*R, r*R) );
sphereVertices.push_back ( osg::Vec3(x * radius,
y * radius,
z * radius) );
sphereNormals.push_back ( osg::Vec3(x, y, z) );
}
sphereGeometry.setVertexArray ( &spehreVertices );
sphereGeometry.setTexCoordArray( &sphereTexCoords );
// Generate quads for each face.
for(r = 0; r < rings-1; r++)
for(s = 0; s < sectors-1; s++)
{
spherePrimitiveSets.push_back(
DrawElementUint( osg::PrimitiveSet::QUADS, 0 )
);
osg::DrawElementsUInt& face = spherePrimitiveSets.back();
// Corners of quads should be in CCW order.
face.push_back( (r + 0) * sectors + (s + 0) );
face.push_back( (r + 0) * sectors + (s + 1) );
face.push_back( (r + 1) * sectors + (s + 1) );
face.push_back( (r + 1) * sectors + (s + 0) );
sphereGeometry.addPrimitveSet( &face );
}
}
osg::Geode *getGeode() const { return &sphereGeode; }
osg::Geometry *getGeometry() const { return &sphereGeometry; }
osg::Vec3Array *getVertices() const { return &sphereVertices; }
osg::Vec3Array *getNormals() const { return &sphereNormals; }
osg::Vec2Array *getTexCoords() const { return &sphereTexCoords; }
};
您可以使用getXXX
方法来获得各种工件。我没有看到如何将曲面法线挂接到任何东西,但我确实将它们存储在Vec2Array中。如果你有它们的用途,它们会被计算和存储,等待被钩住。
该代码调用glutSolidSphere()
来绘制球体,但如果您的应用程序没有使用GLUT来显示具有3D上下文的窗口,则调用它是没有意义的。
还有另一种简单绘制球体的方法,那就是调用gluSphere()
(您可能安装了GLU):
voidgluSphere(GLUquadri*quad,GLdouble半径,GLint切片,GLint堆栈);
参数
quad-指定二次曲面对象(使用gluNewQuadric创建)。
radius-指定球体的半径。
切片-指定z轴周围的细分数目(类似到经线)。
堆栈-指定沿z轴的细分数目(类似到纬度线)。
用法:
// If you also need to include glew.h, do it before glu.h
#include <glu.h>
GLUquadric* _quadratic = gluNewQuadric();
if (_quadratic == NULL)
{
std::cerr << "!!! Failed gluNewQuadric" << std::endl;
return;
}
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0.0, 0.0, -5.0);
glColor3ub(255, 97, 3);
gluSphere(_quadratic, 1.4f, 64, 64);
glFlush();
gluDeleteQuadric(_quadratic);
将gluNewQuadric()
调用移动到类的构造函数可能更明智,因为它只需要分配一次,并将对gluDeleteQuadric()
的调用移动到该类的析构函数。
@JoeZ的回答很好,但OSG代码存在一些错误/不良做法。这是更新后的代码。经过测试,它显示出一个非常漂亮的球体。
osg::ref_ptr<osg::Geode> buildSphere( const double radius,
const unsigned int rings,
const unsigned int sectors )
{
osg::ref_ptr<osg::Geode> sphereGeode = new osg::Geode;
osg::ref_ptr<osg::Geometry> sphereGeometry = new osg::Geometry;
osg::ref_ptr<osg::Vec3Array> sphereVertices = new osg::Vec3Array;
osg::ref_ptr<osg::Vec3Array> sphereNormals = new osg::Vec3Array;
osg::ref_ptr<osg::Vec2Array> sphereTexCoords = new osg::Vec2Array;
float const R = 1. / static_cast<float>( rings - 1 );
float const S = 1. / static_cast<float>( sectors - 1 );
sphereGeode->addDrawable( sphereGeometry );
// Establish texture coordinates, vertex list, and normals
for( unsigned int r( 0 ); r < rings; ++r ) {
for( unsigned int s( 0) ; s < sectors; ++s ) {
float const y = sin( -M_PI_2 + M_PI * r * R );
float const x = cos( 2 * M_PI * s * S) * sin( M_PI * r * R );
float const z = sin( 2 * M_PI * s * S) * sin( M_PI * r * R );
sphereTexCoords->push_back( osg::Vec2( s * R, r * R ) );
sphereVertices->push_back ( osg::Vec3( x * radius,
y * radius,
z * radius) )
;
sphereNormals->push_back ( osg::Vec3( x, y, z ) );
}
}
sphereGeometry->setVertexArray ( sphereVertices );
sphereGeometry->setTexCoordArray( 0, sphereTexCoords );
// Generate quads for each face.
for( unsigned int r( 0 ); r < rings - 1; ++r ) {
for( unsigned int s( 0 ); s < sectors - 1; ++s ) {
osg::ref_ptr<osg::DrawElementsUInt> face =
new osg::DrawElementsUInt( osg::PrimitiveSet::QUADS,
4 )
;
// Corners of quads should be in CCW order.
face->push_back( ( r + 0 ) * sectors + ( s + 0 ) );
face->push_back( ( r + 0 ) * sectors + ( s + 1 ) );
face->push_back( ( r + 1 ) * sectors + ( s + 1 ) );
face->push_back( ( r + 1 ) * sectors + ( s + 0 ) );
sphereGeometry->addPrimitiveSet( face );
}
}
return sphereGeode;
}
更改:
代码中使用的OSG元素现在是智能指针1。此外,像
Geode
和Geometry
这样的类的析构函数受到保护,因此实例化它们的唯一方法是通过动态分配。删除了
spherePrimitiveSets
,因为在当前版本的代码中不需要它。我把代码放在一个免费函数中,因为我的代码中不需要
Sphere
类。我省略了getters
和受保护的属性。它们是不需要的:如果你需要访问,比如说几何体,你可以通过:sphereGeode->getDrawable(...)
获取。其余属性也是如此。
[1] 请参阅此处的经验法则#1。它有点旧,但建议仍然有效。
- 如何创建一个CMake变量,除非显式重写,否则使用默认值
- C++:TypeDef使用元组
- 使用std::multimap迭代器创建std::list
- 从不同线程使用int64的不同字节安全吗
- 比较并显示使用最小值(a,b)和最大值(a、b)升序排列的4个数字
- 为什么在全局范围内使用"extern int a"似乎不行?
- 在C#中处理C++指针而不使用unsafe的最佳方法
- 使用C++库在Android项目中修改gradle中的cmake参数,用于插入指令的测试
- 如何使用Google Mock来模拟gettimeofday()
- 如何使用默认参数等选择模板专业化
- 为什么使用 "this" 指针调用派生成员函数?
- 使用新行和不使用新行读取文件
- 无法在 Windows 上将 OSG 与 GDAL 一起使用
- 如何使用OSG API在Osgearth上绘制三角形
- 在OpenSceneGraph中创建球体(使用osg::Geometry)
- 如何从文件预加载 3D 模型并在 OSG 中多次使用它
- OSG:使用AnimationPathManipulator的摄影机飞行
- 将OSG的osgViewerQt示例与Qt信号/插槽一起使用
- 如何从Python中使用c++ OSG对象
- 由于GDAL中的ODBC链接错误,无法使用Visual Studio 2013构建OSG Earth