c++openscenegraph

Rendering point cloud data with draw instancing from OSG Cookbook not working


I am rendering a point cloud using OSG. I followed the example in the OSG cookbook titled "Rendering point cloud data with draw instancing" that shows how to make one point with many instances and then transfer the point locations to the graphics card via a texture. It then uses a shader to pull the points out of the texture and move each instance to the right location. There appear to be two problems with what is getting rendered.

First, the points aren't in the right location compared to a more straight forward, working approach to rendering. It looks like they are roughly scaled from zero wrong, some kind of multiplicative factor on position.

Second, the imagery is blurry. Points tend to be generally in the right place; there are many points in the place where a large object should be. However, I can't tell what the object. Data rendered with my working (but slower) rendering method looks sharp.

I have verified that I have the same input data going into the texture and draw list in both methods so it seems it has to be something with the rendering.

Here is the code to set up the Geometry which is nearly directly copied from the text book.

osg::Geometry* geo = new osg::Geometry;

osg::ref_ptr<osg::Image> img = new osg::Image;
img->allocateImage(w,h, 1, GL_RGBA, GL_FLOAT);

osg::BoundingBox box;
float* data = (float*)img->data();
for (unsigned long int k=0; k<NPoints; k++)
{
    *(data++) = cloud->x[k];
    *(data++) = cloud->y[k];
    *(data++) = cloud->z[k];
    *(data++) = cloud->meta[0][k];
    box.expandBy(cloud->x[k],cloud->y[k],cloud->z[k]);
}

geo->setUseDisplayList(false);
geo->setUseVertexBufferObjects(true);
geo->setVertexArray( new osg::Vec3Array(1));
geo->addPrimitiveSet( new osg::DrawArrays(GL_POINTS, 0, 1, stop) );
geo->setInitialBound(box);

osg::ref_ptr<osg::Texture2D> tex = new osg::Texture2D;
tex->setImage( img);
tex->setInternalFormat( GL_RGBA32F_ARB );
tex->setFilter( osg::Texture2D::MIN_FILTER, osg::Texture2D::LINEAR);
tex->setFilter( osg::Texture2D::MAG_FILTER, osg::Texture2D::LINEAR);

And here is the shader code.

void main () {
    float row;
    row = float(gl_InstanceID) / float(width);
    vec2 uv = vec2( fract(row), floor(row) / float(height) );
    vec4 texValue = texture2D(defaultTex,uv);
    vec4 pos = gl_Vertex + vec4(texValue.xyz, 1.0);
    gl_Position = gl_ModelViewProjectionMatrix * pos;
}

Solution

  • After a bunch of experimenting, I found that the example code from the OSG Cookbook has some problems.

    The scale issue (the first problem) is in the shader.

    vec4 pos = gl_Vertex + vec4(texValue.xyz, 1.0);
    

    Should be

    vec4 pos = gl_Vertex + vec4(texValue.xyz, 0.0);
    

    This is because the gl_Vertex is a 3-vector with an extra 1 element to aide with matrix transformation. That element should always be 1. The example created another 3+1 vector and added it to gl_Vertex making it a 2. Replace the 1 with a zero and the scale problem goes away.

    The blurriness (the second problem) was caused by texture interpolation.

    tex->setFilter( osg::Texture2D::MIN_FILTER, osg::Texture2D::LINEAR);
    tex->setFilter( osg::Texture2D::MAG_FILTER, osg::Texture2D::LINEAR);
    

    needs to be

    tex->setFilter( osg::Texture2D::MIN_FILTER, osg::Texture2D::NEAREST);
    tex->setFilter( osg::Texture2D::MAG_FILTER, osg::Texture2D::NEAREST);
    

    so that the interpolator will just take the values from the texture instead of interpolating them from neighboring texture pixels which may be points on the other side of the point cloud. After fixing these two issues, the example works as advertised and seems to be a bit faster in my limited testing.