1.OpenGL图元重启动
图元重启功能,是OpenGL3.1版本以后支持的一项,快速绘制重复图元的技术。
当需要处理较大的顶点数据集的时候,我们可能会*执行大量的OpenGL绘制操作,并且每次绘制的内容总是与前一次图元的类型相同(例如GL_TRIANGLE_STRIP)。当然,我们可以使用glMultiDraw*()形式的函数,但是这样需要额外去管理图元的起始索引位置和长度的数组。
OpenGL支持在同一个渲染命令中进行图元重启动的功能,此时需要指定一个特殊的值,叫做图元重启动索引(primitive restart index),OpenGL内部会对它做特殊的处理。如果绘制调用过程中遇到了这个重启动索引,那么就会从这个索引之后的顶点开始,重新开始进行相同图元类型的渲染。图元重启动索引的定义是通过glPrimitiveRestartIndex()函数来完成的(引用《OpenGL编程指南8》)
使用到的主要函数:
void glPrimitiveRestartIndex(GLuint index);
同时,开启状图:
glDisable(GL_PRIMITIVE_RESTART);
如,需要绘制一个立方体的八个面,不使用图元重启动只需调用两次绘制命令,使用图元重启动,则一次就可完成:
// 设置使用glDrawElements绘制
glBindVertexArray(vao[0]);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER<ebo[0]);
#if USE_PRIMITIVE_RESTART
// 如果启用了图元重启动,那么就需要调用一次绘制命令
glEnable(GL_PRIMITIVE_RESTART);
glPrimitiveRestartIndex(0xFFFF);
glDrawElements(GL_TRIANGLE_STRIP,17,GL_UNSIGNED_SHORT,NULL);
#else
// 如果没有开启图元重启动,需要调用两次绘制命令
glDrawElements(GL_TRIANGLE_STRIP,8,GL_UNSIGNED_SHORT,NULL);
glDrawElements(GL_TRIANGLE_STRIP,8,GL_UNSIGNED_SHORT,
(const GLvoid*)(9*sizeof(GLushort)));
#endif
以上代码的关键,就是要在定义索引时,将0xFFFF插入到正常的索引值中,作为特殊标记来使用。
static const GLushort cube_indices[] =
{
0, 1, 2, 3, 6, 7, 4, 5, // 第一组条带
0xFFFF, // 图元重启标识符
2, 6, 0, 4, 1, 5, 3, 7 // 第二组条带
};
2. OSG 图元重启动绘制
对于OSG程序,osg库中有对glPrimitiveRestartIndex的封装,即:PrimitiveRestartIndex
但osg中确不能向OpenGL中那像使用,原因是osg要使用索引来访问节点,计算包围盒。如果使用0xFFFF作为标记,则程序出现访问越界。
为此,使用一个绘制地形网格线为例子,说明怎么使用图元重启:
- 程序使用W:切换线框模式和填充模式,
- left:增加x方向网格密度
- right:减少x方向网格密度
- up:增加y方向网格密度
- down:减少y方向网格密度
效果如图:
#include <osgViewer/viewer>
#include <osg/PrimitiveSet>
#include <osg/StateSet>
#include <osg/LineWidth>
#include <osg/PolygonMode>
#include <osg/StateAttribute>
#include <osg/PrimitiveRestartIndex>
#include <osg/Geometry>
#include <osg/Geode>
#include <osg/array>
#include <osg/Hint>
#include <osg/Texture2D>
#include <osgDB/ReadFile>
#include <osgDB/FileUtils>
#include <osgGA/TrackballManipulator>
#include <osgGA/GUIEventHandler>
#include <iostream>
// 使用图元重启绘制网格
// dimessionX: X方向上细分多少个点
// dimessionY: Y方向上细分多少个点
// width: X方总向长度
// height: Y方向总长度
osg::Node* CreateGround(int dimensionX = 5, int dimensionY= 5, float width = 80, float height = 80)
{
// 网格绘制,按照列序进行绘制,每一列为一组Quad_Strip,然后插入重启绘制标识符。
float dx = width / (dimensionX - 1); // x方向,每段线的长度
float dy = height / (dimensionY - 1); // y方向,每段线的长度
float startX = - width / 2;
float startY = - height / 2;
float endX = - startX;
float endY = - startY;
osg::ref_ptr<osg::Geode> node = new osg::Geode;
osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array;
osg::ref_ptr<osg::Vec3Array> normals = new osg::Vec3Array;
osg::ref_ptr<osg::Vec2Array> coords = new osg::Vec2Array;
// 索引数组个数
// 2*dimessionX * dimessionY 标识所有顶点都重复使用
// - dimensionY * 2 减掉 网格开始和结束的两条边界
// (dimessionX -2) 重启索引个数
int indicesSize = 2 * dimensionX * dimensionY - dimensionY * 2 + (dimensionX - 2);
osg::ref_ptr<osg::DrawElementsUInt> indices = new osg::DrawElementsUInt(osg::PrimitiveSet::QUAD_STRIP, indicesSize);
unsigned int restartFlag = 0xFFFF;
// 使用图元重启绘制网格
/*
X1 Y1 ---- X2,Y1
| |
| |
X1,Y2 ----- X2,Y2
*/
// calculate vertices
for(int i = 0; i < dimensionX; ++i)
{
for(int j = 0; j < dimensionY; ++j)
{
float x1 = startX + dx * i;
float y1 = startY + dy * j;
osg::Vec3 pos1(x1, y1, 0);
vertices->push_back(pos1);
osg::Vec2 coord(float(i)/(dimensionX - 1),float(j)/(dimensionY - 1));
coords->push_back(coord);
}
}
vertices->push_back(osg::Vec3(endX,endY,0));// 多存储一个顶点,用作因使用图元重启,而导致计算包围盒访问越界。
// calculate indices
// fist quad strip
for (int j=0;j<dimensionY;j++)
{
(*indices)[j*2] = j;
(*indices)[j*2+1] = dimensionY+j;
std::cout << j*2 <<"," << j*2+1 << "-->"<< j <<"," << dimensionY +j <<std::endl;
}
std::cout << std::endl;
unsigned int restartIndex = 0;
for (int i = 1; i < dimensionX - 1 ; ++i)
{
restartIndex += 2 * dimensionY;
// 重启索引所设置的标识符, 此处不能这么设置,必须设置为一个特殊的点,比如,最后一个顶点,因为osg在计算包围盒时,
// 会通过索引去访问顶点,若为0xFFFF,则出现访问越界,所以使用最后一个顶点
// (*indices)[restartIndex] = restartFlag;
(*indices)[restartIndex] = dimensionX * dimensionY;
restartIndex++; // 存储下一个有效的索引
for (int j = 0; j < dimensionY; ++j) // 每次绘制一列的quad_strip
{
(*indices)[restartIndex + j*2] = dimensionY*i + j; // 计算当前顶点索引
(*indices)[restartIndex + j*2+1] = dimensionY*(i+1) + j;
std::cout << restartIndex + j*2 <<"," << restartIndex + j*2+1 << "-->"<< dimensionY*i + j <<"," << dimensionY*(i+1) + j<<std::endl;
}
std::cout << std::endl;
}
osg::ref_ptr<osg::Geometry> geometry = new osg::Geometry;
geometry->addPrimitiveSet(indices);
//geometry->addPrimitiveSet(new osg::DrawArrays(GL_QUAD_STRIP,0,dimensionX * dimensionY));
geometry->setVertexArray(vertices);
geometry->setTexCoordArray(0, coords, osg::Array::BIND_PER_PRIMITIVE_SET);
normals->push_back(osg::Vec3d(0, 0, 1));
geometry->setNormalArray(normals, osg::Array::BIND_PER_PRIMITIVE_SET);
osg::StateSet* stateset = new osg::StateSet();
osg::PolygonMode* pm = new osg::PolygonMode(osg::PolygonMode::Face::FRONT_AND_BACK, osg::PolygonMode::Mode::LINE);
stateset->setAttributeAndModes(pm, osg::StateAttribute::ON);
stateset->setAttributeAndModes(new osg::LineWidth(1.5f), osg::StateAttribute::ON);
stateset->setMode(GL_LINE_SMOOTH, osg::StateAttribute::ON);
stateset->setMode(GL_POLYGON_SMOOTH, osg::StateAttribute::ON);
stateset->setAttributeAndModes(new osg::Hint(GL_LINE_SMOOTH_HINT, GL_NICEST), osg::StateAttribute::ON);
stateset->setAttributeAndModes(new osg::Hint(GL_POLYGON_SMOOTH_HINT, GL_NICEST), osg::StateAttribute::ON);
stateset->setAttributeAndModes(new osg::PrimitiveRestartIndex(dimensionX * dimensionY), osg::StateAttribute::ON);
stateset->setMode(GL_PRIMITIVE_RESTART, osg::StateAttribute::ON);
std::string fileName = "Images/primitives.gif";
osg::Image* image = osgDB::readImageFile(fileName);
osg::Texture2D* tex = new osg::Texture2D;
tex->setImage(image);
stateset->setTextureAttributeAndModes(0, tex,osg::StateAttribute::ON);
geometry->setStateSet(stateset);
geometry->setUseVertexBufferObjects(true);
node->addDrawable(geometry);
return node.release();
}
class keyBoardHandler :public osgGA::GUIEventHandler
{
public:
keyBoardHandler(osg::Group* groundRoot):m_ground(groundRoot),m_dimessionX(2),m_dimessionY(2)
{
}
virtual bool handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter& aa, osg::Object*, osg::NodeVisitor*)
{
if(ea.getEventType() == osgGA::GUIEventAdapter::KEYDOWN)
{
if(ea.getKey() == osgGA::GUIEventAdapter::KEY_W)
{
osg::Geode* geode = m_ground->getChild(0)->asGeode();
if(geode)
{
osg::Geometry* geom = geode->getDrawable(0)->asGeometry();
if(geom)
{
osg::StateAttribute* attrib = geom->getOrCreateStateSet()->getAttribute(osg::StateAttribute::POLYGONMODE);
osg::PolygonMode* pm = dynamic_cast<osg::PolygonMode*>(attrib);
if(pm)
{
osg::PolygonMode::Mode mode = pm->getMode(osg::PolygonMode::Face::FRONT_AND_BACK);
if(mode == osg::PolygonMode::Mode::LINE)
{
pm->setMode(osg::PolygonMode::Face::FRONT_AND_BACK, osg::PolygonMode::Mode::FILL);
}
else
{
pm->setMode(osg::PolygonMode::Face::FRONT_AND_BACK, osg::PolygonMode::Mode::LINE);
}
}
}
}
return true;
}
else if(ea.getKey() == osgGA::GUIEventAdapter::KEY_Up)
{
m_dimessionY++;
ChangeLines(m_dimessionX,m_dimessionY);
return true;
}
else if(ea.getKey() == osgGA::GUIEventAdapter::KEY_Down)
{
m_dimessionY--;
ChangeLines(m_dimessionX,m_dimessionY);
return true;
}
else if(ea.getKey() == osgGA::GUIEventAdapter::KEY_Left)
{
m_dimessionX++;
ChangeLines(m_dimessionX,m_dimessionY);
return true;
}
else if(ea.getKey() == osgGA::GUIEventAdapter::KEY_Right)
{
m_dimessionX--;
ChangeLines(m_dimessionX,m_dimessionY);
}
}
return false;
}
void ChangeLines(int x, int y)
{
if(x <2) x = 2;
if(y < 2) y = 2;
m_ground->removeChild(0,1);
m_ground->addChild(CreateGround(x,y));
}
private:
osg::ref_ptr<osg::Group> m_ground;
int m_dimessionX;
int m_dimessionY;
};
void main()
{
osgViewer::Viewer viewer;
osg::Group* root = new osg::Group;
osg::Node* pNode = osgDB::readNodeFile("cow.osg");
root->addChild(pNode);
osg::Group* ground = new osg::Group;
osg::Node* groundMesh = CreateGround(13,3);
ground->addChild(groundMesh);
root->addChild(ground);
viewer.setSceneData(root);
viewer.setUpViewInWindow(10,10,800,600);
//viewer.addEventHandler(new osgGA::TrackballManipulator);
viewer.addEventHandler(new keyBoardHandler(ground));
viewer.run();
}
referenced:
https://blog.****.net/yulinxx/article/details/77896541
https://blog.****.net/qq_36665171/article/details/81459338