简体   繁体   English

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

[英]Creating a Sphere (using osg::Geometry) in OpenSceneGraph

I spent quite some time to get this working, but my Sphere just won't display. 我花了很长时间才使这个工作,但我的Sphere不会显示。
Used the following code to make my function: 使用以下代码来实现我的功能:
Creating a 3D sphere in Opengl using Visual C++ 使用Visual C ++在Opengl中创建3D球体

And the rest is simple OSG with osg::Geometry . 其余的是OSGosg :: Geometry
(Note: Not ShapeDrawable, as you can't implement custom shapes using that.) (注意:不是ShapeDrawable,因为您无法使用它实现自定义形状。)
Added the vertices, normals, texcoords into VecArrays. 将顶点,法线,texcoords添加到VecArrays中。

For one, I suspect something misbehaving, as my saved object is half empty. 首先,我怀疑行为不端,因为我保存的物品是半空的。
Is there a way to convert the existing description into OSG? 有没有办法将现有描述转换为OSG?
Reason? 原因? I want to understand how to create objects later on. 我想了解如何在以后创建对象。
Indeed, it is linked with a later assignment, but currently I'm just prepairing beforehand. 事实上,它与后来的任务有关,但目前我只是预先准备。

Sidenote : Since I have to make it without indices, I left them out. 旁注 :因为我必须在没有索引的情况下制作它,所以我把它们排除了。
But my cylinder displays just fine without them. 但是没有它们,我的圆筒显示得很好。

Caveat: I'm not an OSG expert. 警告:我不是OSG专家。 But, I did do some research. 但是,我确实做了一些研究。

OSG requires all of the faces to be defined in counter-clockwise order, so that backface culling can reject faces that are "facing away". OSG要求以逆时针顺序定义所有面,以便背面剔除可以拒绝“面向”的面。 The code you're using to generate the sphere does not generate all the faces in counter-clockwise order. 您用于生成球体的代码不会以逆时针顺序生成所有面。

You can approach this a couple ways: 你可以通过几种方式解决这个问题:

  1. Adjust how the code generates the faces, by inserting the faces CCW order. 通过插入面CCW顺序调整代码生成面的方式。
  2. Double up your model and insert each face twice, once with the vertices on each face in their current order and once with the vertices in reverse order. 将模型加倍并将每个面插入两次,一次以每个面上的顶点按当前顺序排列,一次以相反的顺序插入顶点。

Option 1 above will limit your total polygon count to what's needed. 上面的选项1会将您的多边形总数限制为所需的数量。 Option 2 will give you a sphere that's visible from outside the sphere as well as within. 选项2将为您提供从球体外部以及内部可见的球体。

To implement Option 2, you merely need to modify this loop from the code you linked to: 要实现选项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;
        }

Double up the set of quads like so: 将四元组加倍,如下所示:

    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;
        }

That really is the "bigger hammer" solution, though. 不过,这确实是“更大的锤子”解决方案。

Personally, I'm having a hard time figuring out why the original loop isn't sufficient; 就个人而言,我很难弄清楚为什么原始循环不够; intuiting my way through the geometry, it feels like it's already generating CCW faces, because each successive ring is above the previous, and each successive sector is CCW around the surface of the sphere from the previous. 通过几何学直观地感知它,它感觉它已经产生了CCW面,因为每个连续的环都在前面,并且每个连续的扇区在前面的球体表面周围是CCW。 So, the original order itself should be CCW with respect to the face nearest the viewer. 因此,原始顺序本身应该是最接近观察者的面部的CCW。


EDIT Using the OpenGL code you linked before and the OSG tutorial you linked today, I put together what I think is a correct program to generate the osg::Geometry / osg::Geode for the sphere. 编辑使用您之前链接的OpenGL代码和您今天链接的OSG教程,我将我认为正确的程序放在一起,为球体生成osg::Geometry / osg::Geode I have no way to test the following code, but desk-checking it, it looks correct or at least largely correct. 我无法测试以下代码,但是检查它,看起来是正确的,或者至少在很大程度上是正确的。

#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; }

};

You can use the getXXX methods to get the various pieces. 您可以使用getXXX方法获取各种部分。 I didn't see how to hook the surface normals to anything, but I do store them in a Vec2Array. 我没有看到如何将表面法线挂钩到任何东西,但我将它们存储在Vec2Array中。 If you have a use for them, they're computed and stored and waiting to be hooked to something. 如果您对它们有用,那么它们就会被计算和存储,并等待某些东西被挂钩。

That code calls glutSolidSphere() to draw a sphere, but it doesn't make sense to call it if your application is not using GLUT to display a window with 3D context. 该代码调用glutSolidSphere()来绘制球体,但如果您的应用程序没有使用GLUT来显示带有3D上下文的窗口,则调用它是没有意义的。

There is another way to draw a sphere easily , which is by invoking gluSphere() (you probably have GLU installed): 还有另一种方法可以轻松绘制球体 ,即通过调用gluSphere() (您可能安装了GLU):

void gluSphere (GLUquadric* quad , GLdouble radius , GLint slices , GLint stacks ); void gluSphere (GLUquadric * quad ,GLdouble radius ,GLint slice ,GLint stacks );

Parameters 参数

quad - Specifies the quadrics object (created with gluNewQuadric). quad - 指定quadrics对象(使用gluNewQuadric创建)。

radius - Specifies the radius of the sphere. radius - 指定球体的半径。

slices - Specifies the number of subdivisions around the z axis (similar to lines of longitude). 切片 - 指定z轴周围的细分数(类似于经度线)。

stacks - Specifies the number of subdivisions along the z axis (similar to lines of latitude). stacks - 指定沿z轴的细分数(类似于纬度线)。

Usage: 用法:

// 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);

It's probably wiser to move the gluNewQuadric() call to the constructor of your class since it needs to be allocated only once, and move the call to gluDeleteQuadric() to the destructor of the class. gluNewQuadric()调用移动到类的构造函数可能更明智,因为它只需要分配一次,并将调用gluDeleteQuadric()移动到类的析构函数。

@JoeZ's answer is excellent, but the OSG code has some errors/bad practices. @ JoeZ的答案非常好,但OSG代码有一些错误/不良做法。 Here's the updated code. 这是更新的代码。 It's been tested and it shows a very nice sphere. 它已经过测试,它显示了一个非常漂亮的球体。

    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;
    }

Changes: 变化:

  • The OSG elements used in the code now are smart pointers 1 . 代码中使用的OSG元素现在是智能指针1 Moreover, classes like Geode and Geometry have their destructors protected, so the only way to instantiate them are via dynamic allocation. 此外, GeodeGeometry等类的析构函数受到保护,因此实例化它们的唯一方法是通过动态分配。

  • Removed spherePrimitiveSets as it isn't needed in the current version of the code. 删除了spherePrimitiveSets因为在当前版本的代码中不需要它。

  • I put the code in a free function, as I don't need a Sphere class in my code. 我把代码放在一个自由函数中,因为我的代码中不需要Sphere类。 I omitted the getters and the protected attributes. 我省略了getters和protected属性。 They aren't needed: if you need to access, say, the geometry, you can get it via: sphereGeode->getDrawable(...) . 它们不是必需的:如果你需要访问几何体,你可以通过: sphereGeode->getDrawable(...)来获取它。 The same goes for the rest of the attributes. 其余属性也是如此。

[1] See Rule of thumb #1 here . [1] 这里参见经验法则#1 It's a bit old but the advice maintains. 这有点旧,但建议仍然坚持。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM