天下武功,唯快不破
最近网友问了关于点云、倾斜摄影数据的性能优化问题。本来想刀枪剑戟、斧钺勾叉给弄了,但是后来想性能其实是个系统问题,要在第22节分成数小节扎扎实实的讲一讲。
鸣谢
非常感谢王锐王大神的cookbook,我准备主要参考它里面关于性能的一章。也就是第8章。
本节资源
本文集包括本节所有资源包括模型代码都在此下载,按节的序号有文件或文件夹:
注意: 务必使用浏览器打开:
【击此打开网盘资源链接】
本节目标
本节要渲染64233个点,存在data.txt中。一行存一个,一行五个数,分别是位置XYZ,以及密度、亮度。
采用技法
1、 本来我们直接在一个Geometry中渲染64233个点就可以了,位置有,亮度有。但是这样会很慢。
2、 我们采用多实例的渲染方法,把64233个点使用纹理存起来,只需要存位置XYZ和亮度就可以了,正好使用RGBA四个通道。一个512×512可以代表262144个顶点,真是足够了。我们来推一推,一张2048×2048的纹理可以存4194304个顶点。
具体实现
1、 使用多实例非常简单,只需要在addPrimitiveSet的时候,把DrawArrays的最后一个参数设置成实例的个数,如下:
osg::ref_ptr<osg::Geometry> geom = new osg::Geometry;
geom->setUseDisplayList(false);
geom->setUseVertexBufferObjects(true);
geom->setVertexArray(new osg::Vec3Array(1));
geom->addPrimitiveSet(new osg::DrawArrays(GL_POINTS, 0, 1, numInstances));
上面是建立了一个多实例的geometry,假如最后一句不要numInstances或将其置成默认值0,则就是一个普通的绘制一个点的geometry,加上numInstances就会绘制numInstances个点。这里numInstances是512×512。那么每个顶点的位置颜色该怎么确定呢? 我们要先把其存在纹理当中。
2. 申请一个纹理,把data.txt中的数据存里面:
std::ifstream is(file.c_str());
if (!is) return NULL;
osg::ref_ptr<osg::Image> image = new osg::Image;
image->allocateImage(w, h, 1, GL_RGBA, GL_FLOAT);
unsigned int density, brightness;
osg::BoundingBox boundBox;
float* data = (float*)image->data();
while (!is.eof())
{
osg::Vec3 pos;
is >> pos[0] >> pos[1] >> pos[2] >> density >> brightness;
boundBox.expandBy(pos);
*(data++) = pos[0];
*(data++) = pos[1];
*(data++) = pos[2];
*(data++) = brightness / 255.0;
}
3、 最后一步,在shader中,每个顶点按照gl_InstanceID内置变量进行区分,也就是根据gl_InstanceID来取纹理中的像素,转成位置和亮度,赋给点的位置即可:
"uniform sampler2D defaultTex;\\n"
"uniform int width;\\n"
"uniform int height;\\n"
"varying float brightness;\\n"
"void main()\\n"
"{\\n"
" float r = float(gl_InstanceID) / float(width);\\n"
" vec2 uv = vec2(fract(r), floor(r) / float(height));\\n"
" vec4 texValue = texture2D(defaultTex, uv);\\n"
" vec4 pos = gl_Vertex + vec4(texValue.xyz, 1.0);\\n"
" brightness = texValue.a;\\n"
" gl_Position = gl_ModelViewProjectionMatrix * pos;\\n"
"}\\n"
上面的width和height都是512, 512, gl_InstanceID是从0到512×512,使用
" float r = float(gl_InstanceID) / float(width);\\n"
" vec2 uv = vec2(fract( r ), floor( r ) / float(height));\\n"
就得到了当前点的位置在纹理中的uv,取出来给gl_Position用来计算即可。
这种渲染点云的方法是比较快的,假如点云数量再大,比如上万亿级别的点云,则要再想办法。这种处理个百万级别的,问题是不大的。
以下是所有代码
/* -*-c++-*- OpenSceneGraph Cookbook
* Chapter 8 Recipe 8
* Author: Wang Rui <wangray84 at gmail dot com>
*/
#include <osg/Point>
#include <osg/Group>
#include <osgDB/ReadFile>
#include <osgViewer/ViewerEventHandlers>
#include <osgViewer/Viewer>
#include <fstream>
#include <iostream>
const char* vertCode = {
"uniform sampler2D defaultTex;\\n"
"uniform int width;\\n"
"uniform int height;\\n"
"varying float brightness;\\n"
"void main()\\n"
"{\\n"
" float r = float(gl_InstanceID) / float(width);\\n"
" vec2 uv = vec2(fract(r), floor(r) / float(height));\\n"
" vec4 texValue = texture2D(defaultTex, uv);\\n"
" vec4 pos = gl_Vertex + vec4(texValue.xyz, 1.0);\\n"
" brightness = texValue.a;\\n"
" gl_Position = gl_ModelViewProjectionMatrix * pos;\\n"
"}\\n"
};
const char* fragCode = {
"varying float brightness;\\n"
"void main()\\n"
"{\\n"
" gl_FragColor = vec4(brightness, brightness, brightness, 1.0);\\n"
"}\\n"
};
osg::Geometry* createInstancedGeometry(osg::Image* img, unsigned int numInstances)
{
osg::ref_ptr<osg::Geometry> geom = new osg::Geometry;
geom->setUseDisplayList(false);
geom->setUseVertexBufferObjects(true);
geom->setVertexArray(new osg::Vec3Array(1));
geom->addPrimitiveSet(new osg::DrawArrays(GL_POINTS, 0, 1, numInstances));
osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D;
texture->setImage(img);
texture->setInternalFormat(GL_RGBA32F_ARB);
texture->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::LINEAR);
texture->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::LINEAR);
geom->getOrCreateStateSet()->setTextureAttributeAndModes(0, texture.get());
geom->getOrCreateStateSet()->addUniform(new osg::Uniform("defaultTex", 0));
geom->getOrCreateStateSet()->addUniform(new osg::Uniform("width", (int)img->s()));
geom->getOrCreateStateSet()->addUniform(new osg::Uniform("height", (int)img->t()));
osg::ref_ptr<osg::Program> program = new osg::Program;
program->addShader(new osg::Shader(osg::Shader::VERTEX, vertCode));
program->addShader(new osg::Shader(osg::Shader::FRAGMENT, fragCode));
geom->getOrCreateStateSet()->setAttributeAndModes(program.get());
return geom.release();
}
osg::Geometry* readPointData(const std::string& file, unsigned int w, unsigned int h)
{
std::ifstream is(file.c_str());
if (!is) return NULL;
osg::ref_ptr<osg::Image> image = new osg::Image;
image->allocateImage(w, h, 1, GL_RGBA, GL_FLOAT);
unsigned int density, brightness;
osg::BoundingBox boundBox;
float* data = (float*)image->data();
while (!is.eof())
{
osg::Vec3 pos;
is >> pos[0] >> pos[1] >> pos[2] >> density >> brightness;
boundBox.expandBy(pos);
*(data++) = pos[0];
*(data++) = pos[1];
*(data++) = pos[2];
*(data++) = brightness / 255.0;
}
osg::ref_ptr<osg::Geometry> geom = createInstancedGeometry(image.get(), w * h);
geom->setInitialBound(boundBox);
geom->getOrCreateStateSet()->setAttributeAndModes(new osg::Point(5.0f));
return geom.release();
}
int main(int argc, char** argv)
{
osg::ref_ptr<osg::Geode> geode = new osg::Geode;
geode->addDrawable(readPointData("data.txt", 512, 512));
osg::ref_ptr<osg::Group> root = new osg::Group;
root->addChild(geode.get());
osgViewer::Viewer viewer;
viewer.setSceneData(root.get());
viewer.addEventHandler(new osgViewer::StatsHandler);
return viewer.run();
}
暂无评论内容