jjreilly's picture

Point Cloud "Wrapping"

I'm new to OpenGL; very experienced with C#. And I'm out of ideas.

I have 56640 points (x,y,z) in a CSV file created from an image of a corroded metal plate. x,y are actual coordinates, z is the plate surface height, FWIW. I'm using an OpenTK GLControl to show the data in a WinForms application. VS 2010 Express, .NET Framework 4 Client Profile on Windows 7 64-bit. My machine is a Lenovo T520, 4 GB RAM with an Intel HD 3000 integrated adapter. According to the "realtech OpenGL Extension Viewer", the chip supports OpenGL 3.1 fully.

I read the whole set into a Vector3 array, calculating a grayscale color based upon the z value. I create a VBO large enough to hold axis endpoints, data points and a Vector3 color for each, organized by all points first, and then all colors (i.e. consecutive, not interleaved). Then I copy the data from the managed arrays into my VBO:

            int isize = Marshal.SizeOf(axes[0]);
 
            IntPtr axesBytes = new IntPtr(isize * axes.Length);
            IntPtr verticesBytes = new IntPtr(isize * disk1bVertices.Length);
            IntPtr colorsBytes = new IntPtr(isize * colors.Length);
            IntPtr totalBytes = new IntPtr(axesBytes.ToInt32() + verticesBytes.ToInt32() + colorsBytes.ToInt32());
 
            GL.BufferData(BufferTarget.ArrayBuffer, totalBytes, IntPtr.Zero, BufferUsageHint.StaticDraw);
 
            GCHandle haxes = GCHandle.Alloc(axes, GCHandleType.Pinned);
            GL.BufferSubData(BufferTarget.ArrayBuffer, IntPtr.Zero, axesBytes, haxes.AddrOfPinnedObject());
            haxes.Free();
 
            GCHandle hvertices = GCHandle.Alloc(disk1bVertices, GCHandleType.Pinned);
            GL.BufferSubData(BufferTarget.ArrayBuffer, axesBytes, verticesBytes, hvertices.AddrOfPinnedObject());
            hvertices.Free();
 
            IntPtr colorOffset = new IntPtr(axesBytes.ToInt32() + verticesBytes.ToInt32());
            GCHandle hcolors = GCHandle.Alloc(colors, GCHandleType.Pinned);
            GL.BufferSubData(BufferTarget.ArrayBuffer, colorOffset, colorsBytes, hcolors.AddrOfPinnedObject());
            hcolors.Free();
 
            // since we're using VBO and OpenGL 3+, the vertex pointer (last parameter)
            // is actually the offset into the currently bound ArrayBuffer
            GL.VertexPointer(3, VertexPointerType.Float, 0, IntPtr.Zero);
 
            // so do the same for colors
            GL.ColorPointer(3, ColorPointerType.Float, 0, colorOffset.ToInt32());

I set up an identity projection matrix, an orthographic projection to show the whole volume, and use the whole window:

         GL.MatrixMode(MatrixMode.Projection);
         GL.LoadIdentity();
         GL.Ortho(_xmin, _xmax, _ymin, _ymax, _zmax, _zmin);
         GL.Viewport(0, 0, w, h);

My render just draws the axes and the data:

         GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
 
         GL.MatrixMode(MatrixMode.Modelview);
         GL.LoadIdentity();
 
         GL.EnableClientState(ArrayCap.VertexArray);
 
         // number of axis vertices
         int nAxisVert = naxes * 2;
 
         // draw world axis using identity model transformation, so it stays fixed
         GL.Color3(Color.AntiqueWhite);
         GL.DrawArrays(BeginMode.Lines, 0, nAxisVert);
 
         GL.EnableClientState(ArrayCap.ColorArray);
 
         // _npoints contains the number of points
         GL.DrawArrays(BeginMode.Points, nAxisVert, _npoints);
 
         GL.DisableClientState(ArrayCap.ColorArray);
 
         GL.DisableClientState(ArrayCap.VertexArray);
 
         glControl31.SwapBuffers();

It seems to work, but doesn't, with the oddest failure. Imagine that you have the correct image, but you cut a narrow strip from the left edge and paste it over on the right. I can draw lines using the same x coordinates, and the lines appear in the correct place. I even modified the grayscale calculation, dropping the blue channel to create a yellow-tinted color, only for the lesser x values, and that strip on the right (ostensibly higher x values) turns yellow. The CSV file is organized such that the x values change faster than the y (i.e. by rows), so it isn't like the first n points are messed up.

I added a clipped inline image. The yellow strip on the right should be on the left. The magenta line is drawn using the same coordinates as the points in the yellow strip; notice the line is in the right place. The blue is my clear color.

It is worse: I can change my CSV file such that y values change faster, and the wrapping moves from the right to the bottom!

I got no ideas what to try. I can send the code for anyone who wants to help.

Addendum: I co-worker suggested that I try immediate mode, and it worked. So something is far wrong with my VBO implementation.

Thanks a lot.

-Reilly.

Inline Images
Image wrapping

Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
the Fiddler's picture

As a sanity check, try using GL.BufferData instead of GL.BufferSubData. My wild guess is that the VBOs contain left-over information that isn't updated (you draw the whole array but only update values between offset and offset+bytes - something going wrong in these values is the most plausible mistake in the above code snippets).

As an aside, you do not need to allocate and pin GCHandles for VBOs. When using a non-pointer OpenGL call, OpenTK guarantees that memory is pinned for the duration of that call - no need to duplicate this code.

jjreilly's picture

Thanks, Fiddler. I failed the sanity check. (No surprise to my friends.)

After I posted, I realized that I didn't need the GCHandles, so I removed them.

I tried BufferData and that didn't work. This program is "static draw". I created the VBOs once at startup and never change the contents.

So I punted and used GL.Vertex3. That worked.

When I get back to the project, I may revisit VBOs.

Regards,

-reilly.