在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), 俺下一节再说说。
相关代码: