在OpenSceneGraph中创建一个Sphere(使用osg :: Geometry)

时间:2013-12-06 20:56:57

标签: c++ opengl openscenegraph

我花了很长时间才开始工作,但是我的Sphere不会显示 使用以下代码来实现我的功能:
Creating a 3D sphere in Opengl using Visual C++

其余的简单的OSG osg :: Geometry
(注意:不是ShapeDrawable,因为您无法使用它实现自定义形状。)
将顶点,法线,texcoords添加到VecArrays中。

一个人我怀疑行为不端,因为我保存的物品是半空的。
有没有办法将现有描述转换为OSG?
原因?我想了解如何在以后创建对象。
事实上,它与后来的任务有关,但目前我只是预先准备。

Sidenote :由于我必须在没有索引的情况下制作它,所以我把它们排除了 但是没有它我的圆筒显示得很好。

3 个答案:

答案 0 :(得分:6)

警告:我不是OSG专家。但是,我确实做了一些研究。

OSG要求以逆时针顺序定义所有面,以便背面剔除可以拒绝“面向”的面。您用于生成球体的代码不会以逆时针顺序生成所有面。

你可以通过几种方式解决这个问题:

  1. 通过插入面CCW顺序,调整代码生成面的方式。
  2. 将模型加倍并将每个面插入两次,每个面上的顶点按当前顺序排列一次,顶点顺序相反。
  3. 上面的选项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。


    编辑使用您之前链接的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中。如果您对它们有用,那么它们就会被计算和存储,并等待被它们挂钩。

答案 1 :(得分:3)

该代码调用glutSolidSphere()来绘制球体,但如果您的应用程序未使用GLUT来显示带有3D上下文的窗口,则调用它是没有意义的。

还有另一种方法可以轻松绘制球体,即通过调用gluSphere()(您可能安装了GLU):

  

无效 gluSphere (GLUquadric * quad ,                   GLdouble radius ,                   GLint 切片,                   GLint 堆栈);

     

<强>参数

     

quad - 指定quadrics对象(使用gluNewQuadric创建)。

     

radius - 指定球体的半径。

     

slices - 指定z轴周围的细分数(类似   到经度线。)

     

stacks - 指定沿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()到类的析构函数。

答案 2 :(得分:0)

@ 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 。此外,像GeodeGeometry这样的类的析构函数受到保护,因此实例化它们的唯一方法是通过动态分配。

  • 删除了spherePrimitiveSets,因为当前版本的代码中不需要它。

  • 我把代码放在一个自由函数中,因为我的代码中不需要Sphere类。我省略了getters和受保护的属性。它们不是必需的:如果您需要访问几何体,您可以通过以下方式获取它:sphereGeode->getDrawable(...)。其余属性也是如此。

[1]参见经验法则#1 here。这有点旧,但建议仍然存在。