Creating a Sphere (using osg::Geometry) in OpenSceneGraph

后端 未结 3 768
遥遥无期
遥遥无期 2021-02-05 20:40

I spent quite some time to get this working, but my Sphere just won\'t display.
Used the following code to make my function:
Creating a 3D sphere in Opengl using Visual

3条回答
  •  长发绾君心
    2021-02-05 21:05

    Caveat: I'm not an OSG expert. 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". 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.
    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. Option 2 will give you a sphere that's visible from outside the sphere as well as within.

    To implement Option 2, you merely need to modify this loop from the code you linked to:

        indices.resize(rings * sectors * 4);
        std::vector::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::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. So, the original order itself should be CCW with respect to the face nearest the viewer.


    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. I have no way to test the following code, but desk-checking it, it looks correct or at least largely correct.

    #include 
    
    class SolidSphere
    {
    protected:
    
        osg::Geode      sphereGeode;
        osg::Geometry   sphereGeometry;
        osg::Vec3Array  sphereVertices;
        osg::Vec3Array  sphereNormals;
        osg::Vec2Array  sphereTexCoords;
    
        std::vector 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. I didn't see how to hook the surface normals to anything, but I do store them in a Vec2Array. If you have a use for them, they're computed and stored and waiting to be hooked to something.

提交回复
热议问题