OSG 渲染剖析 之 Geometry中使用VBO、VAO

在OSG 渲染剖析 之 Geometry VBO 生成中,我们分析了OSG 的几何数据如何转换成OpenGL 对应的相数据, 及相关实现类,这一节我们看看,Geometry中的几何数据是怎样被渲染出来的 。

我们先认识一下VBO,VAO的数据派发及绑定状态管理工具类:

class OSG_EXPORT VertexArrayState : public osg::Referenced
{
    public:
        VertexArrayState(osg::State* state);
        struct ArrayDispatch : public osg::Referenced
        {
            ArrayDispatch():
                array(0),
                modifiedCount(0xffffffff),
                active(false) {}
            virtual bool isVertexAttribDispatch() const { return false; }
            virtual const char* className() const = 0; // { return "ArrayDispatch"; }
            virtual void enable_and_dispatch(osg::State& /*state*/, const osg::Array* /*new_array*/) {} // = 0;
            virtual void enable_and_dispatch(osg::State& /*state*/, const osg::Array* /*new_array*/, const osg::GLBufferObject* /*vbo*/) {} // = 0;
            virtual void enable_and_dispatch(osg::State& /*state*/, GLint /*size*/, GLenum /*type*/, GLsizei /*stride*/, const GLvoid * /*ptr*/, GLboolean /*normalized*/) {} // = 0;
            virtual void dispatch(osg::State& /*state*/, const osg::Array* /*new_array*/) {} // = 0;
            virtual void dispatch(osg::State& /*state*/, const osg::Array* /*new_array*/, const osg::GLBufferObject* /*vbo*/) {} // = 0;
            virtual void dispatch(osg::State& /*state*/, GLint /*size*/, GLenum /*type*/, GLsizei /*stride*/, const GLvoid * /*ptr*/, GLboolean /*normalized*/) {} // = 0;
            virtual void disable(osg::State& /*state*/) {} // = 0;
            const osg::Array*   array;
            unsigned int        modifiedCount;
            bool                active;
        };
        typedef std::vector< ref_ptr<ArrayDispatch> >       ArrayDispatchList;
        void setCurrentVertexBufferObject(osg::GLBufferObject* vbo) { _currentVBO = vbo; }
        GLBufferObject* getCurrentVertexBufferObject() { return _currentVBO; }
        inline void bindVertexBufferObject(osg::GLBufferObject* vbo)
        {
            if (vbo->isDirty())
            {
                vbo->compileBuffer();
                _currentVBO = vbo;
            }
            else if (vbo != _currentVBO)
            {
                vbo->bindBuffer();
                _currentVBO = vbo;
            }
        }
        inline void unbindVertexBufferObject()
        {
            if (!_currentVBO) return;
            _ext->glBindBuffer(GL_ARRAY_BUFFER_ARB,0);
            _currentVBO = 0;
        }
        void setCurrentElementBufferObject(osg::GLBufferObject* ebo) { _currentEBO = ebo; }
        GLBufferObject* getCurrentElementBufferObject() { return _currentEBO; }
        inline void bindElementBufferObject(osg::GLBufferObject* ebo)
        {
            if (ebo->isDirty())
            {
                ebo->compileBuffer();
                _currentEBO = ebo;
            }
            else if (ebo != _currentEBO)
            {
                ebo->bindBuffer();
                _currentEBO = ebo;
            }
        }
        inline void unbindElementBufferObject()
        {
            if (!_currentEBO) return;
            _ext->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER_ARB,0);
            _currentEBO = 0;
        }
        void resetBufferObjectPointers() { _currentVBO = 0; _currentEBO = 0; }
        bool correctArrayDispatchAssigned(const ArrayDispatch* ad);
        void assignAllDispatchers();
        void assignVertexArrayDispatcher();
        void assignNormalArrayDispatcher();
        void assignColorArrayDispatcher();
        void assignSecondaryColorArrayDispatcher();
        void assignFogCoordArrayDispatcher();
        void assignTexCoordArrayDispatcher(unsigned int numUnits);
        void assignVertexAttribArrayDispatcher(unsigned int numUnits);
        inline void setVertexBufferObjectSupported(bool flag) { _isVertexBufferObjectSupported = flag; }
        inline bool isVertexBufferObjectSupported() const { return _isVertexBufferObjectSupported; }
        void setArray(ArrayDispatch* vad, osg::State& state, const osg::Array* new_array);
        void setArray(ArrayDispatch* vad, osg::State& state, GLint size, GLenum type, GLsizei stride, const GLvoid *ptr, GLboolean normalized);
        inline void disable(ArrayDispatch* vad, osg::State& state) { vad->disable(state); vad->array=0; vad->modifiedCount=0xffffffff; vad->active=false; }
        void setInterleavedArrays( osg::State& state, GLenum format, GLsizei stride, const GLvoid* pointer);
        inline void setVertexArray(osg::State& state, const osg::Array* array) { setArray(_vertexArray.get(), state, array); }
        inline void setVertexArray(osg::State& state, GLint size, GLenum type, GLsizei stride, const GLvoid *ptr, GLboolean normalized=GL_FALSE) { setArray(_vertexArray.get(), state, size, type, stride, ptr, normalized); }
        inline void disableVertexArray(osg::State& state) { disable(_vertexArray.get(), state); }
        inline void setNormalArray(osg::State& state, const osg::Array* array) { setArray(_normalArray.get(), state, array); }
        inline void setNormalArray(osg::State& state, GLenum type, GLsizei stride, const GLvoid *ptr, GLboolean normalized=GL_FALSE ) { setArray(_normalArray.get(), state, 3, type, stride, ptr, normalized); }
        inline void disableNormalArray(osg::State& state) { disable(_normalArray.get(), state); }
        inline void setColorArray(osg::State& state, const osg::Array* array) { setArray(_colorArray.get(), state, array); }
        inline void setColorArray(osg::State& state, GLint size, GLenum type, GLsizei stride, const GLvoid *ptr, GLboolean normalized=GL_TRUE ) { setArray(_colorArray.get(), state, size, type, stride, ptr, normalized); }
        inline void disableColorArray(osg::State& state) { disable(_colorArray.get(), state); }
        inline void setSecondaryColorArray(osg::State& state, const osg::Array* array) { setArray(_secondaryColorArray.get(), state, array); }
        inline void disableSecondaryColorArray(osg::State& state) { disable(_secondaryColorArray.get(), state); }
        inline void setFogCoordArray(osg::State& state, const osg::Array* array) { setArray(_fogCoordArray.get(), state, array); }
        inline void disableFogCoordArray(osg::State& state) { disable(_fogCoordArray.get(), state); }
        inline void setTexCoordArray(osg::State& state, unsigned int unit, const osg::Array* array) { setArray(_texCoordArrays[unit].get(), state, array); }
        inline void setTexCoordArray(osg::State& state, unsigned int unit, GLint size, GLenum type, GLsizei stride, const GLvoid *ptr, GLboolean normalized=GL_FALSE ) { setArray(_texCoordArrays[unit].get(), state, size, type, stride, ptr, normalized); }
        inline void disableTexCoordArray(osg::State& state, unsigned int unit) { disable(_texCoordArrays[unit].get(),state); }
        inline void disableTexCoordArrayAboveAndIncluding(osg::State& state, unsigned int index);
        inline void setVertexAttribArray(osg::State& state, unsigned int unit, const osg::Array* array) { setArray(_vertexAttribArrays[unit].get(), state, array); }
        inline void disableVertexAttribArray(osg::State& state, unsigned int unit) { disable(_vertexAttribArrays[unit].get(), state); }
        inline void disableVertexAttribArrayAboveAndIncluding(osg::State& state, unsigned int index);
        /** Mark all the vertex attributes as being disabled but leave the disabling till a later call to applyDisablingOfVertexAttributes.*/
        inline void lazyDisablingOfVertexAttributes();
        /** Disable all the vertex attributes that have been marked as to be disabled.*/
        inline void applyDisablingOfVertexAttributes(osg::State& state);
        // Verex Array Object methods.
        void generateVertexArrayObject();
        void deleteVertexArrayObject();
        GLuint getVertexArrayObject() const { return _vertexArrayObject; }
        void setRequiresSetArrays(bool flag) { _requiresSetArrays = flag; }
        bool getRequiresSetArrays() const { return _requiresSetArrays; }
        void dirty();
        void release();
    public:
        // osg::GLBufferObject* getGLBufferObject(osg::Array* array);
        osg::State*                     _state;
        osg::ref_ptr<osg::GLExtensions> _ext;
        bool                            _isVertexBufferObjectSupported;
        GLuint                          _vertexArrayObject;
        osg::ref_ptr<ArrayDispatch>     _vertexArray;
        osg::ref_ptr<ArrayDispatch>     _normalArray;
        osg::ref_ptr<ArrayDispatch>     _colorArray;
        osg::ref_ptr<ArrayDispatch>     _secondaryColorArray;
        osg::ref_ptr<ArrayDispatch>     _fogCoordArray;
        ArrayDispatchList               _texCoordArrays;
        ArrayDispatchList               _vertexAttribArrays;
        typedef std::vector<ArrayDispatch*> ActiveDispatchers;
        ActiveDispatchers               _activeDispatchers;
        ActiveDispatchers               _previous_activeDispatchers;
        GLBufferObject*                 _currentVBO;
        GLBufferObject*                 _currentEBO;
        bool                            _requiresSetArrays;
};

晕晕乎乎的,啥又bind,又dispatch,又是ArrayDispatcher ,又是ArrayAtrributeDispatcher的, Array, ArrayAtribute难道不是一个东西?OpenGL 中顶点相关的不都是作为属性嘛。小弟研究了一阵子,到最后终于整明白了。如果不了解OpenGL 的过去,还是有点晕。所以现在学OpenGL或实现新引擎,还是忘了OpenGL比较老的规范,脑袋里也不至于装太多杂碎的东西,伤脑细胞,把精力用在刀刃上还是好哇。

先捯饬下,OpenGL 顶点, 顶点数组, 顶点属性,静态顶点属性的关系。OpenGL规范比较早的1.0-1.5,绘制实体大致是这个样子:

glBegin (GL_POLYGON)
    glNormal3fv(n0);
    glVertex3fv(v0);
    glNormal3fv(n1);
    glVertex3fv(v1);
    glNormal3fv(n2);
    glVertex3fv(v2);
    glNormal3fv(n3);
    glVertex3fv(v3);
glEnd()

效率那个低啊,每个顶点都得压一次法线、顶点数据到客户端,执行一次OpenGL 调用。OpenGL需要执行大量的函数调用才能完成对几何图元的渲染。为了减少函数调用,OpenGL提供了个方法,批量传递数据,这个想法是在OpenGL 1.1中形成了标准,那就是 顶点数组,顶点数据还是存储在客户端,OpenGL 图元调用变成了这(zhei 读三声)个样子:

glEnableClientState(GL_COLOR_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY);
 
glColorPointer(3, GL_FLOAT, 0, colors);
glVertexPointer(2, GL_INT, 0, vertices);
 
glBegin(GL_TRIANGLES);
    glArrayElement(2);
    glArrayElement(3);
    glArrayElement(5);
glEnd();

效率高了不少,但是数据还是在客户端,没有送到服务器端(OpenGL CS模式),也就是推到显卡内存上。这个效率还是有点低,咋进一步减少OpenGL 客户、服务器端数据传递与函数调用呢? 大法就是 VBO,TBO, EBO, 乃至后面的VAO,这些我就省点精力不说了,说我也说不清楚,(说多了我就不会了)。

OSG的 ArrayDispather 是顶点数据分发的接口类,其两个派生类体系实现了OpenGL旧规范中顶点数组的派发和OpenGL比较新的规范 顶点属性的派发。

我下边列一下它们的后代:

顶点数组派发器:

struct VertexArrayDispatch : public VertexArrayState::ArrayDispatch;

struct ColorArrayDispatch : public VertexArrayState::ArrayDispatch;

struct NormalArrayDispatch : public VertexArrayState::ArrayDispatch;

struct SecondaryColorArrayDispatch : public VertexArrayState::ArrayDispatch;

struct FogCoordArrayDispatch : public VertexArrayState::ArrayDispatch;

struct TexCoordArrayDispatch : public VertexArrayState::ArrayDispatch;

我们先看下顶点数组的派发器:

 virtual void enable_and_dispatch(osg::State&, const osg::Array* new_array)
    {
        VAS_NOTICE<<"    VertexArrayDispatch::enable_and_dispatch("<<new_array->getNumElements()<<")"<<std::endl;
        glEnableClientState(GL_VERTEX_ARRAY);
        glVertexPointer(new_array->getDataSize(), new_array->getDataType(), 0, new_array->getDataPointer());
    }

再看下顶点属性派发器:

struct VertexAttribArrayDispatch : public VertexArrayState::ArrayDispatch

这(zhei 三声)个函数是现在OpenGL 规范中最常用的函数,顶点,法向,纹理坐标,颜色都看成是顶点属性, 上面那些顶点相关的函数已经没啥意思了,但也不是很过时,如果自己做显示相关的,我自己琢磨着也没人会用了,除了俺所在的公司整个CAD的产品搞兼容性,啥都要顾及(真的很郁闷)。

我们看看顶点属性派发器做事的函数:

    virtual void enable_and_dispatch(osg::State& state, const osg::Array* new_array, const osg::GLBufferObject* vbo)
    {
        GLExtensions* ext = state.get<GLExtensions>();

        ext->glEnableVertexAttribArray( unit );
        callVertexAttribPointer(ext, new_array, (const GLvoid *)(vbo->getOffset(new_array->getBufferIndex())));
    }

 inline void callVertexAttribPointer(GLExtensions* ext, const osg::Array* new_array, const GLvoid * ptr)
    {
        if (new_array->getPreserveDataType())
        {
            if (new_array->getDataType()==GL_FLOAT)
                ext->glVertexAttribPointer(static_cast<GLuint>(unit), new_array->getDataSize(), new_array->getDataType(), new_array->getNormalize(), 0, ptr);
            else if (new_array->getDataType()==GL_DOUBLE)
                ext->glVertexAttribLPointer(static_cast<GLuint>(unit), new_array->getDataSize(), new_array->getDataType(), 0, ptr);
            else
                ext->glVertexAttribIPointer(static_cast<GLuint>(unit), new_array->getDataSize(), new_array->getDataType(), 0, ptr);
        }
        else
        {
            ext->glVertexAttribPointer(static_cast<GLuint>(unit), new_array->getDataSize(), new_array->getDataType(), new_array->getNormalize(), 0, ptr);
        }
    }

上面已经很明了了,enable_dispatch* 接口启用顶点属性,再设置顶点属性相关的格式。

有了顶点属性的更新管理器,那么它用在哪里呢?我们再看看Geometry 绘制接口:

void Geometry::drawImplementation(RenderInfo& renderInfo) const
{
    // OSG_NOTICE<<"Geometry::drawImplementation() "<<this<<std::endl;
 
    if (_containsDeprecatedData)
    {
        OSG_WARN<<"Geometry::drawImplementation() unable to render due to deprecated data, call geometry->fixDeprecatedData();"<<std::endl;
        return;
    }
 
    State& state = *renderInfo.getState();
 
    bool usingVertexBufferObjects = state.useVertexBufferObject(_supportsVertexBufferObjects && _useVertexBufferObjects);
    bool usingVertexArrayObjects = usingVertexBufferObjects && state.useVertexArrayObject(_useVertexArrayObject);
 
    osg::VertexArrayState* vas = state.getCurrentVertexArrayState();
    vas->setVertexBufferObjectSupported(usingVertexBufferObjects);
 
    bool checkForGLErrors = state.getCheckForGLErrors()==osg::State::ONCE_PER_ATTRIBUTE;
    if (checkForGLErrors) state.checkGLErrors("start of Geometry::drawImplementation()");
 
 
    drawVertexArraysImplementation(renderInfo);
 
    if (checkForGLErrors) state.checkGLErrors("Geometry::drawImplementation() after vertex arrays setup.");
 
    ///
    //
    // draw the primitives themselves.
    //
 
    drawPrimitivesImplementation(renderInfo);
 
    if (usingVertexBufferObjects && !usingVertexArrayObjects)
    {
        // unbind the VBO's if any are used.
        vas->unbindVertexBufferObject();
        vas->unbindElementBufferObject();
    }
 
    if (checkForGLErrors) state.checkGLErrors("end of Geometry::drawImplementation().");
}
void Geometry::drawVertexArraysImplementation(RenderInfo& renderInfo) const
{
    State& state = *renderInfo.getState();
    VertexArrayState* vas = state.getCurrentVertexArrayState();
 
    bool handleVertexAttributes = !_vertexAttribList.empty();
 
    AttributeDispatchers& attributeDispatchers = state.getAttributeDispatchers();
 
    attributeDispatchers.reset();
    attributeDispatchers.setUseVertexAttribAlias(state.getUseVertexAttributeAliasing());
 
    if (handleVertexAttributes)
    {
        for(unsigned int unit=0;unit<_vertexAttribList.size();++unit)
        {
            attributeDispatchers.activateVertexAttribArray(unit, _vertexAttribList[unit].get());
        }
    }
 
    // activate or dispatch any attributes that are bound overall
    attributeDispatchers.activateNormalArray(_normalArray.get());
    attributeDispatchers.activateColorArray(_colorArray.get());
    attributeDispatchers.activateSecondaryColorArray(_secondaryColorArray.get());
    attributeDispatchers.activateFogCoordArray(_fogCoordArray.get());
 
    if (state.useVertexArrayObject(_useVertexArrayObject))
    {
        if (!vas->getRequiresSetArrays()) return;
    }
 
    vas->lazyDisablingOfVertexAttributes();
 
    // set up arrays
    if( _vertexArray.valid() )
        vas->setVertexArray(state, _vertexArray.get());
 
    if (_normalArray.valid() && _normalArray->getBinding()==osg::Array::BIND_PER_VERTEX)
        vas->setNormalArray(state, _normalArray.get());
 
    if (_colorArray.valid() && _colorArray->getBinding()==osg::Array::BIND_PER_VERTEX)
        vas->setColorArray(state, _colorArray.get());
 
    if (_secondaryColorArray.valid() && _secondaryColorArray->getBinding()==osg::Array::BIND_PER_VERTEX)
        vas->setSecondaryColorArray(state, _secondaryColorArray.get());
 
    if (_fogCoordArray.valid() && _fogCoordArray->getBinding()==osg::Array::BIND_PER_VERTEX)
        vas->setFogCoordArray(state, _fogCoordArray.get());
 
    for(unsigned int unit=0;unit<_texCoordList.size();++unit)
    {
        const Array* array = _texCoordList[unit].get();
        if (array)
        {
            vas->setTexCoordArray(state, unit,array);
        }
    }
 
    if ( handleVertexAttributes )
    {
        for(unsigned int index = 0; index < _vertexAttribList.size(); ++index)
        {
            const Array* array = _vertexAttribList[index].get();
            if (array && array->getBinding()==osg::Array::BIND_PER_VERTEX)
            {
                vas->setVertexAttribArray(state, index, array);
            }
        }
    }
 
    vas->applyDisablingOfVertexAttributes(state);
}
void Geometry::drawVertexArraysImplementation(RenderInfo& renderInfo) const

这个函数就是具体的顶点数据更新过程,我们先不看索引数据的更新与绘制函数的调用,顶点的看完了,索引数据更新与绘制函数的调用大同小异。

 AttributeDispatchers& attributeDispatchers = state.getAttributeDispatchers();
 
    attributeDispatchers.reset();
    attributeDispatchers.setUseVertexAttribAlias(state.getUseVertexAttributeAliasing());
 
    if (handleVertexAttributes)
    {
        for(unsigned int unit=0;unit<_vertexAttribList.size();++unit)
        {
            attributeDispatchers.activateVertexAttribArray(unit, _vertexAttribList[unit].get());
        }
    }
 
    // activate or dispatch any attributes that are bound overall
    attributeDispatchers.activateNormalArray(_normalArray.get());
    attributeDispatchers.activateColorArray(_colorArray.get());
    attributeDispatchers.activateSecondaryColorArray(_secondaryColorArray.get());
    attributeDispatchers.activateFogCoordArray(_fogCoordArray.get());
 

初看上面的函数在这里比较奇怪,咋又一堆dispatchers,让VAS更新顶点数据不就成了?很显然,人家加这个代码肯定不是毫无缘由,记得我们创建Geometry 时要设置数据bind状态吗? 一会PER_VERTEX, 一会PER_Primitive, 还有什么OVERALL的。这些属性派发器就是处理per_primitive, overall 绑定的。在OpenGL 编程指南中不是提到当顶点没有设置相关属性时,用静态绑定设置的属性来作为默认属性,这些属性派发器就是干这事的。

class OSG_EXPORT AttributeDispatchers : public osg::Referenced
{
#define DISPATCH_OR_ACTIVATE(array, dispatcher) \
            if (array) { \
                unsigned int binding = array->getBinding(); \
                if (binding==osg::Array::BIND_OVERALL) \
                { \
                    AttributeDispatch* at = dispatcher; \
                    if (at) (*at)(0); \
                } \
                else if (binding==osg::Array::  BIND_PER_PRIMITIVE_SET) \
                { \
                    AttributeDispatch* at = dispatcher; \
                    if (at) _activeDispatchList.push_back(at); \
                } \
            }
        void activateColorArray(osg::Array* array) { DISPATCH_OR_ACTIVATE(array, colorDispatcher(array)); }
}

if(at) (*at)(0) 是啥意思?其实就是根据数组数据类型调用的OpenGL相关函数的指针,只是取数组第一个元素值对OpenGL 进行属性设置。

void AttributeDispatchers::assignVertexAttribDispatchers(unsigned int unit)
{
 GLExtensions* extensions = _state->get<GLExtensions>();

 for(unsigned int i=_vertexAttribDispatchers.size(); i<=unit; ++i)
 {
     _vertexAttribDispatchers.push_back(new AttributeDispatchMap());
     AttributeDispatchMap& vertexAttribDispatcher = *_vertexAttribDispatchers[i];
     vertexAttribDispatcher.targetAssign<GLuint, GLfloat>(i, Array::FloatArrayType, extensions->glVertexAttrib1fv, 1);
     vertexAttribDispatcher.targetAssign<GLuint, GLfloat>(i, Array::Vec2ArrayType, extensions->glVertexAttrib2fv, 2);
     vertexAttribDispatcher.targetAssign<GLuint, GLfloat>(i, Array::Vec3ArrayType, extensions->glVertexAttrib3fv, 3);
     vertexAttribDispatcher.targetAssign<GLuint, GLfloat>(i, Array::Vec4ArrayType, extensions->glVertexAttrib4fv, 4);
 }
}

这么多的派发器,名字也相近,大家记清属性派发器 attributeDispatcher与数组派发器 arrayDispatcher是实现两个作用就成了,而且 arrayDispatcher 是封装在VertexArrayState() 类中。attributeDispatcher 更新OpenGL 数据的时机在attributeDispatchers.activateNormalArray(_normalArray.get())这样函数中, 而顶点属性更新在vas->setVertexArray(state, _vertexArray.get()) 这样的函数中。

总结下:

attributeDispatchers 设置顶点静态属性值,或以图元为单位设置静态属性值,数据更新操作发生在:

attributeDispatchers.activateNormalArray(_normalArray.get());

VertexArrayState 更新VBO数据,并设置顶点属性,更新操作发生在:
if( _vertexArray.valid()) vas->setVertexArray(state, _vertexArray.get());

俺是觉得设置全局顶点属性的过程完全可以放在VAS中去实现,代码及实现逻辑更好一些。

那 VAO 在哪里生成及设置的呢?

一是编译时:

void Geometry::compileGLObjects(RenderInfo& renderInfo) const
{
 
     if (state.useVertexArrayObject(_useVertexArrayObject))
        {
            VertexArrayState* vas = 0;
 
            _vertexArrayStateList[contextID] = vas = createVertexArrayState(renderInfo);
 
            State::SetCurrentVertexArrayStateProxy setVASProxy(state, vas);
 
            state.bindVertexArrayObject(vas);
 
            drawVertexArraysImplementation(renderInfo);
 
            state.unbindVertexArrayObject();
        }
}

另一处在 Drawable::Draw()的时候:

void Drawable::draw(RenderInfo& renderInfo) const
{
     State& state = *renderInfo.getState();
    bool useVertexArrayObject = state.useVertexArrayObject(_useVertexArrayObject);
    if (useVertexArrayObject)
    {
        unsigned int contextID = renderInfo.getContextID();
 
        VertexArrayState* vas = _vertexArrayStateList[contextID].get();
        if (!vas)
        {
            _vertexArrayStateList[contextID] = vas = createVertexArrayState(renderInfo);
        }
        else
        {
            // vas->setRequiresSetArrays(getDataVariance()==osg::Object::DYNAMIC);
        }
 
        State::SetCurrentVertexArrayStateProxy setVASProxy(state, vas);
 
        state.bindVertexArrayObject(vas);
 
        drawInner(renderInfo);
 
        vas->setRequiresSetArrays(getDataVariance()==osg::Object::DYNAMIC);
 
        return;
    }
}

createVertexArrayState() 调用 CreateVertexArrayStateImplementation() 实现接口:

VertexArrayState* Geometry::createVertexArrayStateImplementation(RenderInfo& renderInfo) const
{
    State& state = *renderInfo.getState();
 
    VertexArrayState* vas = new osg::VertexArrayState(&state);
 
    // OSG_NOTICE<<"Creating new osg::VertexArrayState "<< vas<<std::endl;
 
    if (_vertexArray.valid()) vas->assignVertexArrayDispatcher();
    if (_colorArray.valid()) vas->assignColorArrayDispatcher();
    if (_normalArray.valid()) vas->assignNormalArrayDispatcher();
    if (_secondaryColorArray.valid()) vas->assignSecondaryColorArrayDispatcher();
    if (_fogCoordArray.valid()) vas->assignFogCoordArrayDispatcher();
 
    if (!_texCoordList.empty()) vas->assignTexCoordArrayDispatcher(_texCoordList.size());
    if (!_vertexAttribList.empty()) vas->assignVertexAttribArrayDispatcher(_vertexAttribList.size());
 
    if (state.useVertexArrayObject(_useVertexArrayObject))
    {
        // OSG_NOTICE<<"  Setup VertexArrayState to use VAO "<<vas<<std::endl;
 
        vas->generateVertexArrayObject();
    }
    else
    {
        // OSG_NOTICE<<"  Setup VertexArrayState to without using VAO "<<vas<<std::endl;
    }
 
    return vas;
}
 vas->generateVertexArrayObject() 生成VAO 对象。

如果没有设置使用VAO,VAS 对象不会创建, 顶点等相关属性不会更新, 那么这个VAS在哪里创建的?

void State::initializeExtensionProcs()
{
_globalVertexArrayState = new VertexArrayState(this);
setCurrentToGlobalVertexArrayState();
}

原来在State初始化的时候,创建了个默认的VAS对象,这个VAS由于没有创建OpenGL VAO对象,跟上下文就无关了,各上下文共用也没有问题,只是纯粹调用VBO 相关的API。

以上只是大略的摘了摘OSG 实体绘制及OpenGL 相关函数的封装与调用,写的不是很细,有OSG 基础的可以看看是不是这么回事,如果想了解的更细就需要认真读代码了。

说到这里,我又想到顶点属性在 shader链接后的位置关系, OSG 中的默认别名 osg_Vertex, osg_Normal 这些属性怎么关联到shader 的? 如果这些位置是系统固定的,如何解决shader中的layout(location)位置之间的联系? OSG 怎样做到灵活设置顶点属性? 其实就是Geometry::setVertexAttribArray(unsigned int index, Array* array, osg::Array::Binding binding), 俺下一节再说说。

相关代码:
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用OpenSceneGraph(OSG)库Geometry类生成图像通常需要以下步骤: 1. 创建一个osg::Geode对象,将所有几何图形添加到其。 2. 创建一个osg::Group对象,将osg::Geode对象添加为其子节点。 3. 创建一个osgViewer::Viewer对象,并将osg::Group对象设置为场景数据。 4. 使用osgDB::writeImageFile函数将场景渲染到图像文件。 以下是一个简单的示例代码,演示如何使用OSGGeometry类生成一张图像: ```cpp #include <osg/Geometry> #include <osg/Geode> #include <osg/Group> #include <osgViewer/Viewer> #include <osgDB/WriteFile> int main() { // 创建一个三角形几何图形 osg::ref_ptr<osg::Geometry> triangle = new osg::Geometry(); osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array(); vertices->push_back(osg::Vec3(-1.0f, 0.0f, 0.0f)); vertices->push_back(osg::Vec3(0.0f, 1.0f, 0.0f)); vertices->push_back(osg::Vec3(1.0f, 0.0f, 0.0f)); triangle->setVertexArray(vertices.get()); osg::ref_ptr<osg::DrawElementsUInt> triangleIndices = new osg::DrawElementsUInt(osg::PrimitiveSet::TRIANGLES, 0); triangleIndices->push_back(0); triangleIndices->push_back(1); triangleIndices->push_back(2); triangle->addPrimitiveSet(triangleIndices.get()); // 将三角形几何图形添加到osg::Geode对象 osg::ref_ptr<osg::Geode> geode = new osg::Geode(); geode->addDrawable(triangle.get()); // 创建一个osg::Group对象,并将osg::Geode对象添加为其子节点 osg::ref_ptr<osg::Group> root = new osg::Group(); root->addChild(geode.get()); // 创建一个osgViewer::Viewer对象,并将osg::Group对象设置为场景数据 osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer(); viewer->setSceneData(root.get()); // 渲染场景,并将场景渲染到图像文件 viewer->frame(); osgDB::writeImageFile(*(viewer->getCamera()->captureRenderToImage()), "output.png", osgDB::Registry::instance()->getWriteFileOptions()); return 0; } ``` 运行代码后,将在程序所在目录下生成名为“output.png”的图像文件,其包含了一个三角形几何图形。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值