This project is read-only.

SharpGL FBO and High Res Texture Question

Nov 17, 2014 at 3:09 PM
I am attempting to create an OpenGL Spherical Viewer using SharpGL and Visual Studio 2010. I used the SharpGL WinForms template to start my code. I am using equrectangular jpeg images that are 8000x4000 pixels in size. I am using the SharpGL.SceneGraph.Texture object to load my images into a texture. I am binding the texture to a sphere created using the SharpGL.SceneGraph.Quadrics.Sphere object.

My first attempt yielded my sphere however the ScheneGraph.Texture code down-sampled my texture to 1024x1024 pixels. OK, at least I got something. After some browsing of this forum I determined that I should change my RenderContextType from DIBSection to FBO. This yielded a sphere with a texture, but again the ScheneGraph.Texture down-sampled my image/texture to 4096x2048. I figure great, I'm half way home.

The next thing I do to remedy my situation is change the OpenGLVersion from OpenGL2_1 to OpenGL4_4. Only now nothing gets rendered and my form remains black. So, I figure I've done something wrong. In an effort to remove my code from the equation I open the SharpGL Examples Solution and debug the "RenderContextsSample" code. It opens and runs with no complaint and displays four tabs, each with a SharpGL control on it and each control us using a different RenderContextType and all are using an OpenGLVersion of OpenGL2_1.

Then I change the OpenGLVersion of the control on the FBO tab from OpenGL2_1 to OpenGL3_0. I debug the project. Low and behold the FBO tab does not work. I modify all the other tabs and none of them work with an OpenGLVersion any higher than OpenGL2_1.

My Video Card is an NVIDIA Quatro 2000 and it is capable of rendering these images at full resolution as I have tested other viewers. I just need to write my own viewer so I can control it and pass information and/or control back and forth between my viewer and my GIS software.

Does anyone have an example using FBO, SharpGL, OpenGL newer than 2.1 and a textured quadric? I know I am asking a lot and will gladly accept guidance over code as long as I am lead closer to my goal.

I believe it was Pascal who said "We are usually convinced more easily by reasons we have found ourselves, than by those which have occurred to others." But that doesn't mean I won't listen to others.

Respectfully;

Windknot
Nov 18, 2014 at 9:18 PM
Edited Nov 19, 2014 at 6:35 PM
So, I think I found the code that limits me to OpenGLVersion.OpenGL2_1. It is in a portion of the RenderContextProviders module of the SharpGL project. The code follows:
    protected void UpdateContextVersion(OpenGL gl)
    {
        //  If the request version number is anything up to and including 2.1, standard render contexts
        //  will provide what we need (as long as the graphics card drivers are up to date).
        var requestedVersionNumber = VersionAttribute.GetVersionAttribute(requestedOpenGLVersion);
        if (requestedVersionNumber.IsAtLeastVersion(3, 0) == false)
            {
            createdOpenGLVersion = requestedOpenGLVersion;
            return;
        }

        //  Now the none-trivial case. We must use the WGL_ARB_create_context extension to 
        //  attempt to create a 3.0+ context.
        try
        {
            int[] attributes = 
            {
                OpenGL.WGL_CONTEXT_MAJOR_VERSION_ARB, requestedVersionNumber.Major,  
                OpenGL.WGL_CONTEXT_MINOR_VERSION_ARB, requestedVersionNumber.Minor,
                OpenGL.WGL_CONTEXT_FLAGS_ARB, OpenGL.WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
                0
            };
            var hrc = gl.CreateContextAttribsARB(IntPtr.Zero, attributes);
            Win32.wglMakeCurrent(IntPtr.Zero, IntPtr.Zero);
            Win32.wglDeleteContext(renderContextHandle);
            Win32.wglMakeCurrent(deviceContextHandle, hrc);
            renderContextHandle = hrc; 
        }
        catch(Exception)
        {
            //  TODO: can we actually get the real version?
            createdOpenGLVersion = OpenGLVersion.OpenGL2_1;
        }
    }
So, according to the line " if (requestedVersionNumber.IsAtLeastVersion(3, 0) == false)" when I request version 2.1 or lower the created version gets set to the requested version. However if the version is 3.0 or greater it becomes a "none-trivial case." I gather from the code that it attempts to generate a rendering context handle and should it fail it falls back on OpenGLVersion.OpenGL2_1. Considering that I get a blank screen when I change the controls context version to something higher to 2.1 a new "hrc" and "renderContexHandle" is created, but somehow not used or not used properly. If the "try" code has an exception the fallback it 2.1 which has been demonstrated to work, so I don't think an exception is being raised there. I suppose I could create a break point near this code to check for an exception.

In order to create a work around and try newer versions of OpenGL, I modified the "if...IsAtLeast..." line to pass on contexts < 4.4 because I am confident my video card uses version 4.4. This work around did change the behavior of the form and it successfully rendered my sphere and texture when I set the OpenGLVersion of the control to 3.0 and up to 4.3. However this work around did not solve my initial problem with the down-sampling of the texture "resolution." I am definitely going to have to take another look at the "Texture" class of the SharpGL.SceneGraph Project.

Respectfully;

Windknot
Nov 19, 2014 at 6:34 PM
Edited Nov 19, 2014 at 6:40 PM
First and foremost I would like to thank Dave Kerr for all his work on the SharpGL project. I would also like to thank him for posting his source code so that novices such as myself can learn. My initial question was why can't I use a texture where the initial image was 8000x4000 pixels. I've been dancing around the solution for a few days now and finally pinned it down to code in the "Texture" class of the SharpGL.SceneGraph project. More specifically in "Create" method of the SharpGL.SceneGraph.Assets.Texture code.
        /// <summary>
        /// This function creates the texture from an image.
        /// </summary>
        /// <param name="gl">The OpenGL object.</param>
        /// <param name="image">The image.</param>
        /// <returns>True if the texture was successfully loaded.</returns>
        public virtual bool Create(OpenGL gl, Bitmap image)
        {
            //  Create the underlying OpenGL object.
            Create(gl);

            //  Get the maximum texture size supported by OpenGL.
            int[] textureMaxSize = { 0 };
            gl.GetInteger(OpenGL.GL_MAX_TEXTURE_SIZE, textureMaxSize);

            //  Find the target width and height sizes, which is just the highest
            //  posible power of two that'll fit into the image.

            int targetWidth = textureMaxSize[0];
            int targetHeight = textureMaxSize[0];

            for (int size = 1; size <= textureMaxSize[0]; size *= 2)
            {
                if (image.Width < size)
                {
                    targetWidth = size / 2;
                    break;
                }
                if (image.Width == size)
                    targetWidth = size;

            }

            for (int size = 1; size <= textureMaxSize[0]; size *= 2)
            {
                if (image.Height < size)
                {
                    targetHeight = size / 2;
                    break;
                }
                if (image.Height == size)
                    targetHeight = size;
            }

            //  If need to scale, do so now.
            if (image.Width != targetWidth || image.Height != targetHeight)
            {
                System.Windows.Forms.MessageBox.Show("IWidth: " + image.Width.ToString() + "  IHeight: " + image.Height.ToString() + " TWidth: " + targetWidth.ToString() + " THeight: " + targetHeight.ToString());

                //  Resize the image.
                Image newImage = image.GetThumbnailImage(targetWidth, targetHeight, null, IntPtr.Zero);

                //  Destory the old image, and reset.
                image.Dispose();
                image = (Bitmap)newImage;
            }

            //  Lock the image bits (so that we can pass them to OGL).
            BitmapData bitmapData = image.LockBits(new Rectangle(0, 0, image.Width, image.Height),
                ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);

            //  Set the width and height.
            width = image.Width;
            height = image.Height;

            //  Bind our texture object (make it the current texture).
            gl.BindTexture(OpenGL.GL_TEXTURE_2D, TextureName);

            //  Set the image data.
            gl.TexImage2D(OpenGL.GL_TEXTURE_2D, 0, (int)OpenGL.GL_RGBA,
                width, height, 0, OpenGL.GL_BGRA, OpenGL.GL_UNSIGNED_BYTE,
                bitmapData.Scan0);

            //  Unlock the image.
            image.UnlockBits(bitmapData);

            //  Dispose of the image file.
            image.Dispose();

            //  Set linear filtering mode.
            gl.TexParameter(OpenGL.GL_TEXTURE_2D, OpenGL.GL_TEXTURE_MIN_FILTER, OpenGL.GL_LINEAR);
            gl.TexParameter(OpenGL.GL_TEXTURE_2D, OpenGL.GL_TEXTURE_MAG_FILTER, OpenGL.GL_LINEAR);

            //  We're done!
            return true;
        }
The first thing this code does is to get the Max Texture Size form the current OpenGL context. In my case that is 16384. Then it runs through two loops calculating powers of 2 (stored in the "size" variable) from 1 to the Max Texture Size (1, 2, 4, 8, 16, ... 1024, 2048, 4096, 8192, ...) till the power of 2 was greater than the image height or width (depending on the loop). When the power of 2 jumps beyond the image size, it divides the power of 2 or "size" by 2 to get the largest power of 2 not greater than the image size. Where this hoses me for my width is the loop counts up to 8192 which is greater than my width of 8000 and then divides that by 2 which ends up being 4096. The same thing happens for my height of 4000. The code loops through to 4096 which is greater than 4000, so it divides by two and returns 2048. 4096x2048 is just over half the resolution of my image.

The code then goes on to compare the "targetWidth" and "targetHeight" to the image width and height. If they don't match, the code then re-samples the image to the "targetWidth" and "targetHeight" which ends giving me a texture at a little over 1/2 my initial resolution. My work around is to let it do its thing and then just set the "targetWidth" and "targetHeight" to my image's width and height prior to the code that ordinarily would re-scale the image / texture.

I have learned a lot from Mr. Kerr's code. However the most important thing I've learned is that I still have a lot to learn.

Respectfully;


Windknot
Nov 19, 2014 at 8:43 PM
Edited Nov 20, 2014 at 12:29 AM
I tested this and the same thing happens, image disappears, when changing to version "OpenGL3_0" (and greater), in the openGLControl Properties window.
I tried with bmp and jpg big and small with same problem.

From https://github.com/dwmkerr/sharpgl/wiki/Specifying-OpenGL-Version
"The desired OpenGL version of a render context can be specified by using the OpenGLVersion of the OpenGL control. This is required to use some features of OpenGL 2.1 onwards. If an OpenGL 2.1 context is not available, then SharpGL will revert to using a 2.1 context." Is the last sentence wrong?

Why do you need to use OpenGL2_1 or greater?

Attached is code I used:
public partial class SharpGLForm : Form
    {
        private bool TexturesInitialised = false;

        private Bitmap gImage1;
        private System.Drawing.Imaging.BitmapData gbitmapdata;
        private uint[] gtexture = new uint[1];

        public SharpGLForm()
        {
            InitializeComponent();
        }

        private void IntialiseTexture(ref OpenGL gl)
        {
            gImage1 = new Bitmap(@"C:\Ruapehu 8000x4000 super big.jpg");  //Environment.GetFolderPath(Environment.SpecialFolder.MyPictures)

            Rectangle rect = new Rectangle(0, 0, gImage1.Width, gImage1.Height);
            gbitmapdata = gImage1.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
            gImage1.UnlockBits(gbitmapdata);
            gl.GenTextures(1, gtexture);
            gl.BindTexture(OpenGL.GL_TEXTURE_2D, gtexture[0]);
            gl.TexImage2D(OpenGL.GL_TEXTURE_2D, 0, (int)OpenGL.GL_RGB8, gImage1.Width, gImage1.Height, 0, OpenGL.GL_BGR_EXT, OpenGL.GL_UNSIGNED_BYTE, gbitmapdata.Scan0);
            uint[] array = new uint[] { OpenGL.GL_NEAREST };
            gl.TexParameterI(OpenGL.GL_TEXTURE_2D, OpenGL.GL_TEXTURE_MIN_FILTER, array);
            gl.TexParameterI(OpenGL.GL_TEXTURE_2D, OpenGL.GL_TEXTURE_MAG_FILTER, array);

            TexturesInitialised = true;
        }

      
        private void openGLControl_OpenGLDraw(object sender, RenderEventArgs e)
        {  
            OpenGL gl = openGLControl.OpenGL;
            gl.Clear(OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT);

            gl.LoadIdentity();

            if (!TexturesInitialised)
                IntialiseTexture(ref gl);

            gl.Enable(OpenGL.GL_TEXTURE_2D);
           
            gl.BindTexture(OpenGL.GL_TEXTURE_2D, gtexture[0]);
           
            gl.Color(1.0f, 1.0f, 1.0f, 0.1f); //Must have, weirdness!
            gl.Begin(OpenGL.GL_QUADS);
            gl.TexCoord(1.0f, 1.0f);            
            gl.Vertex(gImage1.Width, gImage1.Height, 1.0f);     
            gl.TexCoord(0.0f, 1.0f);            
            gl.Vertex(0.0f, gImage1.Height, 1.0f);      
            gl.TexCoord(0.0f, 0.0f);        
            gl.Vertex(0.0f, 0.0f, 1.0f);
            gl.TexCoord(1.0f, 0.0f);        
            gl.Vertex(gImage1.Width, 0.0f, 1.0f);       
            gl.End();
            gl.Disable(OpenGL.GL_TEXTURE_2D); 
        }

        private void openGLControl_OpenGLInitialized(object sender, EventArgs e)
        {
            OpenGL gl = openGLControl.OpenGL;
            gl.ClearColor(0, 0, 0, 0);
        }

        private void openGLControl_Resized(object sender, EventArgs e)
        { 
            OpenGL gl = openGLControl.OpenGL;
            gl.MatrixMode(OpenGL.GL_PROJECTION);
            gl.LoadIdentity();
            gl.Perspective(60.0f, (double)Width / (double)Height, 100, 100000.0); //Changed
            gl.LookAt(0, 0, -20000, 0, 0, 0, 0, 1, 0);//Changed
            gl.MatrixMode(OpenGL.GL_MODELVIEW);
        }
    }
Nov 20, 2014 at 2:11 AM
The following Sphere with Texture code works for me:

However, I'm not 100% sure if the image is scaled. It looks ok to me. How could you tell, did it look f-ugly?
I'm just calling gl.BindTexture before the sphere.Render to texture map.
I'm not sure how the texture mapping lines up to the sphere or how to change it.

Settings:
8000x4000 pixels
OpenGLVersion = OpenGL2_1
RenderContextType = FBO or NativeWindow

It is a hack together (not checked for bugs).
       public partial class SharpGLForm : Form
    {
        private bool TexturesInitialised = false;
        private float rotation = 0.0f;
        private float Scale = 1;

        private Bitmap gImage1;
        private System.Drawing.Imaging.BitmapData gbitmapdata;
        private uint[] gtexture = new uint[1];

        SharpGL.SceneGraph.Quadrics.Sphere sphere = new SharpGL.SceneGraph.Quadrics.Sphere();

        public SharpGLForm()
        {
            InitializeComponent();
        }

        private void InitialiseTexture(ref OpenGL gl)
        {
            gImage1 = new Bitmap(@"C:\Ruapehu 8000x4000 super big.bmp");//Environment.GetFolderPath(Environment.SpecialFolder.MyPictures)

            Rectangle rect = new Rectangle(0, 0, gImage1.Width, gImage1.Height);
            gbitmapdata = gImage1.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
            gImage1.UnlockBits(gbitmapdata);
            gl.GenTextures(1, gtexture);
            gl.BindTexture(OpenGL.GL_TEXTURE_2D, gtexture[0]);
            gl.TexImage2D(OpenGL.GL_TEXTURE_2D, 0, (int)OpenGL.GL_RGB8, gImage1.Width, gImage1.Height, 0, OpenGL.GL_BGR_EXT, OpenGL.GL_UNSIGNED_BYTE, gbitmapdata.Scan0);
            uint[] array = new uint[] { OpenGL.GL_NEAREST };
            gl.TexParameterI(OpenGL.GL_TEXTURE_2D, OpenGL.GL_TEXTURE_MIN_FILTER, array);
            gl.TexParameterI(OpenGL.GL_TEXTURE_2D, OpenGL.GL_TEXTURE_MAG_FILTER, array);

            TexturesInitialised = true;
        }

        private void InitialiseSphere(ref OpenGL gl)
        {
            sphere.QuadricDrawStyle = SharpGL.SceneGraph.Quadrics.DrawStyle.Fill;
            sphere.CreateInContext(gl);
            sphere.Slices = 100;
            sphere.Stacks = 100;
            sphere.TextureCoords = false;
            sphere.Radius = 4000;
            sphere.TextureCoords = true;
        }
      
        private void openGLControl_OpenGLDraw(object sender, RenderEventArgs e)
        {  
            OpenGL gl = openGLControl.OpenGL;
            gl.Clear(OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT);

            gl.LoadIdentity();

            gl.Rotate(rotation, 0.0f, 1.0f, 0.0f);
            gl.Scale(Scale, Scale, Scale);

            if (!TexturesInitialised)
            {
                InitialiseTexture(ref gl);
                InitialiseSphere(ref gl);
            }

            gl.Enable(OpenGL.GL_TEXTURE_2D);
           
            gl.BindTexture(OpenGL.GL_TEXTURE_2D, gtexture[0]);

            gl.Color(1.0f, 1.0f, 1.0f, 0.1f); //Must have, weirdness!
            sphere.Render(gl, SharpGL.SceneGraph.Core.RenderMode.Render);

           // gl.Color(1.0f, 1.0f, 1.0f, 0.1f); //Must have, weirdness!
            gl.Begin(OpenGL.GL_QUADS);
            gl.TexCoord(1.0f, 1.0f);            
            gl.Vertex(gImage1.Width, gImage1.Height, 1.0f);     
            gl.TexCoord(0.0f, 1.0f);            
            gl.Vertex(0.0f, gImage1.Height, 1.0f);      
            gl.TexCoord(0.0f, 0.0f);        
            gl.Vertex(0.0f, 0.0f, 1.0f);
            gl.TexCoord(1.0f, 0.0f);        
            gl.Vertex(gImage1.Width, 0.0f, 1.0f);       
            gl.End();
            gl.Disable(OpenGL.GL_TEXTURE_2D);
        }

        private void openGLControl_OpenGLInitialized(object sender, EventArgs e)
        {
            OpenGL gl = openGLControl.OpenGL;
            gl.ClearColor(0, 0, 0, 0);
        }

        private void openGLControl_Resized(object sender, EventArgs e)
        { 
            OpenGL gl = openGLControl.OpenGL;
            gl.MatrixMode(OpenGL.GL_PROJECTION);
            gl.LoadIdentity();
            gl.Perspective(60.0f, (double)Width / (double)Height, 100, 100000.0); //Changed
            gl.LookAt(0, 0, -20000, 0, 0, 0, 0, 1, 0);//Changed
            gl.MatrixMode(OpenGL.GL_MODELVIEW);
        }

        private void buttonRotate_Click(object sender, EventArgs e)
        {
           rotation += 3.0f;
        }

        private void buttonZoomIn_Click(object sender, EventArgs e)
        {
            Scale -= 0.5f;        
        }

        private void buttonZoomOut_Click(object sender, EventArgs e)
        {
            Scale += 0.5f;
        }
    }
Nov 21, 2014 at 11:49 AM
AORD;

Thanks for your reply. I did not know if I needed a version greater than 2.1 or not. I was having a problem and thought that perhaps version 2.1 was limiting the resolution. I thought "older version = less memory = smaller bitmaps." It turns out I was wrong, but I wanted to change one variable in an ocean of unknowns. Even so, I also thought that what does work in version 2.1 should also work in version 3.0 and greater. However, the SharpGL code uses version 2.1 as a default.

When a user requests that SharpGL use a higher version the SharpGL code "attempts" to create a rendering context to the requested version. This "attempt" is within a "Try" and "Catch" block that rolls the rendering context back to version 2.1 "if" there is an exception. The problem with this "attempt" is that it appears to successfully create a rendering context by making calls to Windows and OpenGL and appears to never throw the exception, at least on my computer. However, the context that is created does not seem to make its way back to the control and your screen remains blank.

Respectfully;


Windknot
Nov 21, 2014 at 11:55 AM
AORD;

I wish to publicly thank you for posting your examples. I will contact you outside of this discussion to let you know how I can be reached and set up a means by which you can send me your VS 2010 projects.

Sincerely;


Windknot
Nov 25, 2014 at 12:35 AM
Perhaps the reason that OpenGL2_1 works and OpenGL4_4 doesn't, is the code perhaps uses deprecated commands.
In my code I found TexCoord appears to be deprecated (works in OpenGL2_1, but not OpenGL4_4).

https://www.opengl.org/registry/oldspecs/gl.spec - search this document using "deprecated" to find old commands.

Support is still there:
https://developer.nvidia.com/opengl-driver
Jan 14, 2015 at 12:33 PM
Hi,

I know this problem might be solved by now. But if anyone has the same problem:

If deprecated commands are used, you need to initialize the rendercontext differently.

I wrote a post about this problem here: https://sharpgl.codeplex.com/discussions/567675

Cheers

Thomas