avic's picture

VBO Screen Updating Problem

Hello.

I'm having some issues using VBOs to display a changing image. I'm writing a program that will read data from a location, convert that data to a color value and then display the resulting value on the screen as the data is being collected. So basically I'm creating a 2d height map with OpenGL as I read the data (I then render this to 3d with an offline program). The problem is that when I use VBOs the viewport doesn't seem to update. I fill an array with every 2d vertex in the viewport (0,0,0,1,0,2,...j,0,j,1,...j,k) and hook that to the vertex array, and I create another array into which I put my color data which is dynamically updated and hook that to the color array.

Now when I do this without using VBOs it works just fine, I see my image being built in real time. In my data acquisition loop I raise an event after each line of data is collected and put into my color data array that is handled by this function:

    Private Sub updateviewport()
        GL.ColorPointer(4, ColorPointerType.Double, 0, colorArray)
        GlControl1.Invalidate()
     End Sub

colorArray is where I store my color data.

When I do the equivalent thing with VBOs, the viewport remains remains black.

GL.BindBuffer(BufferTarget.ArrayBuffer, buffer(1))
GL.BufferData(BufferTarget.ArrayBuffer, IntPtr.Zero, IntPtr.Zero, BufferUsageHint.StreamDraw)
GL.BufferData(BufferTarget.ArrayBuffer, colorArray.Length * 8, colorArray, BufferUsageHint.StreamDraw)
GL.ColorPointer(4, ColorPointerType.Double, 0, IntPtr.Zero)
GlControl1.Invalidate()

buffer is my unsigned integer array where I keep vertex and color data. Using the BufferSubData() function doesn't work either. While debugging, I found that when Invalidate() is called when using VBOs, the Paint subroutine isn't called and I don't understand why.
Also, I tried just filling the entire buffer beforehand with my color data and then rendering at the very end that worked just fine. But I want to be able to show the image being constructed as data is collected. I've resigned to not using VBOs for now so I can continue on my project but it seems that everyone suggests VBOs as the best way to use OpenGL. Any suggestions? Thanks.


Comments

Comment viewing options

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

I suggest you should post more code you are using.
Just guessing:
1) where do you perform drawing?
2) do you reassign VertexPointer each time you refill the buffer and draw?
3) if manual filling the buffer went successfully, what is the difference with the variant that doesn't work?

avic's picture

1. Drawing is done in the paint handler:

    Private Sub GlControl1_Paint(ByVal sender As System.Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles GlControl1.Paint
        If Not loaded Then
            Return
        End If
        GL.Clear(ClearBufferMask.ColorBufferBit)
 
        GL.MatrixMode(MatrixMode.Modelview)
        GL.LoadIdentity()
 
        GL.DrawArrays(BeginMode.Points, 0, picH * picW)
 
 
        GlControl1.SwapBuffers()
    End Sub

picH and picW are my height and width dimensions respectively. loaded is the flag to see whether the control has been loaded as per the windows forms tutorial on this site.
2. I don't reassign the vertex pointer every time I draw...should I? I tried doing that and it didn't seem to make a difference. When I write the program without using buffers and just attach the color pointer directly to my color data array, this isn't a problem. I only need to set the vertex pointer once.
3. So what I meant was that when I fill my color array completely and then hook to a buffer and only render once, the picture shows up just fine. But when I fill only a part of my color array with data and try to render multiple times (each time hooking it to the buffer like I do in my second block of code) as the array is being filled, the screen won't update at all. The array with the color data is initially filled with 0's since vb .net automatically allocates that space you say dim array(arraysize) as double.
So I guess the variant is that rather than rendering just once, I'm rendering several times. I'm not sure if this is what you were asking for...Is there more code that you'd need to see?

nythrix's picture

Interesting. Why do you need a screen full of points?

If you're looking into displaying a 2D image you should stream to a texture and copy the texture to the framebuffer.

avic's picture

I hadn't thought of that. Mainly because I didn't know that you could do that (I'm new to all of this). So after reading a little about texture maps, it seems to me that you have to specify them pixel by pixel which means that you can't really resize them on the fly. The image will be a fixed size. Am I right? Or am I missing something?

Using vertices was nice because no matter how large or small my data set, the image size would be the same on the screen (with the possibility some lines if the screen resolution is too high relative the number of vertices). This is my thinking anyway.

Would you happen to know of a good place where I can find an example of programmatically creating a texture map without using an image file? Or to use a texture map would I need to use an image file?

nythrix's picture

I didn't know it either two weeks ago. Anyway I'm posting something.
1) Texture setup. Not sure if you need to specify all of this. Try removing GL.TexParameters until something goes wrong :) On the fly resizing of the texture is not possible. You'd have to recreate it and rebind it to the framebuffer. However you might be able to play with GL.BufferData and GL.TexSubImage2D so that you move only parts of the image around. That might fake resizing.

            texture = GL.GenTexture();
            GL.BindTexture( TextureTarget.Texture2D, texture );
            // this allocates memory for the texture, no image needed
            GL.TexImage2D( TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba32f,
               colorArrayWidth, colorArrayHeight, 0, PixelFormat.Rgba, PixelType.Float, IntPtr.Zero );
            GL.TexParameter( TextureTarget.Texture2D, TextureParameterName.TextureMinFilter,
                ( int )TextureMinFilter.Linear );
            GL.TexParameter( TextureTarget.Texture2D, TextureParameterName.TextureMagFilter,
                ( int )TextureMagFilter.Linear );
            GL.TexParameter( TextureTarget.Texture2D, TextureParameterName.TextureWrapS,
                ( int )TextureWrapMode.ClampToBorder );
            GL.TexParameter( TextureTarget.Texture2D, TextureParameterName.TextureWrapT,
                ( int )TextureWrapMode.ClampToBorder );

Pixel Buffer Object:

            GL.GenBuffers( 1, out pbo );

Framebuffer Object:

            GL.GenFramebuffers( 1, out framebuffer );
            GL.BindFramebuffer( FramebufferTarget.Framebuffer, framebuffer );
            // attach the texture to the framebuffer so we can copy from it afterwards
            GL.FramebufferTexture2D(
                FramebufferTarget.Framebuffer,
                FramebufferAttachment.ColorAttachment0,
                TextureTarget.Texture2D,
                texture, 0 );

Rendering:

            GL.BindTexture( TextureTarget.Texture2D, texture );
            GL.BindBuffer( BufferTarget.PixelUnpackBuffer, pbo );
 
            // clear the previous data and send the new one
            GL.BufferData( BufferTarget.PixelUnpackBuffer, IntPtr.Zero, IntPtr.Zero, BufferUsageHint.StreamDraw );
            GL.BufferData<Color4>( BufferTarget.PixelUnpackBuffer,
                new IntPtr( colorArraySizeInBytes ),
                colorArray, BufferUsageHint.StreamDraw );
 
            // move the data to the texture. This runs on the GPU so don't worry about performance
            GL.TexSubImage2D( TextureTarget.Texture2D,
                0, 0, 0, colorArrayWidth, colorArrayHeight,
                PixelFormat.Rgba, PixelType.Float, IntPtr.Zero );
 
            // set the buffer to read from
            GL.BindFramebuffer( FramebufferTarget.ReadFramebuffer, framebuffer );                        
            // copy to the default framebuffer (the screen)
            GL.BlitFramebuffer(
                0, 0, colorArrayWidth, colorArrayHeight,
                0, 0, controlWidth, controlHeight,
                ClearBufferMask.ColorBufferBit,
                // control and texture don't have to be of the same size. if the areas don't match, this will enable interpolation of the values
                BlitFramebufferFilter.Linear );

Sorry I don't know VB. But I guess you can find your way through. The code relies on recent OpenGL so I'm not sure how far backward you can push it. Update the drivers if anything.

avic's picture

Cool thanks. I'll give this a shot.