SharpGL 2D Texturing issue - c#

I am trying to texture triangle with runtime generated texture using SharpGL wrapper.
I can't figure out why triangle remains not textured.
gl.Error() put in draw loop returns 0 which means GL_NO_ERROR.
private void openGLControl_OpenGLDraw(object sender, OpenGLEventArgs args)
{
OpenGL gl = openGLControl.OpenGL;
gl.Clear(OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT );
gl.LoadIdentity();
gl.Color(1.0f,1.0f,1.0f,1.0f);
gl.Begin(OpenGL.GL_TRIANGLES);
gl.TexCoord(0, 1.0f);
gl.Vertex(0.0f, 0.0f);
gl.TexCoord(1.0f, 0f);
gl.Vertex(1.0f, 0f);
gl.TexCoord(1.0f, 1.0f);
gl.Vertex(1.0f, 1.0f);
gl.End();
}
private void openGLControl_OpenGLInitialized(object sender, OpenGLEventArgs args)
{
Random rnd = new Random();
OpenGL gl = openGLControl.OpenGL;
gl.ClearColor(0, 0, 0, 0);
gl.Enable(OpenGL.GL_TEXTURE_2D);
byte[] colors = new byte[256 * 256 * 4];
for (int i = 0; i < 256 * 256 * 4; i++)
{
colors[i] = (byte)rnd.Next(256);
}
uint[] textureID = new uint[1];
gl.GenTextures(1, textureID);
gl.TexImage2D(OpenGL.GL_TEXTURE_2D, 0, (int)OpenGL.GL_RGBA, 256, 256, 0, OpenGL.GL_RGBA, OpenGL.GL_BYTE, colors);
gl.BindTexture(OpenGL.GL_TEXTURE_2D, textureID[0]);
}

You have to call gl.BindTexture before calling gl.TexImage2D.
The reason for this is in the first argument of both functions. OpenGL has a state machine that keeps track of what is bound. When you call gl.TexImage2D, you are telling GL to upload the pixels to the texture currently bound to OpenGL.GL_TEXTURE_2D. gl.BindTexture binds the texture you generated to OpenGL.GL_TEXTURE_2D.

The reason why texture wasn't visible was lacking setting:
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);
By default SharpGL is using MipMap which weren't generated.

Related

OpenGL/SharpGL: Rendering 2D image is stretched horizontally

I'm playing around with OpenGL in C# (using SharpGL) and I come from a DirectX background... I figured OpenGL would be pretty similar, and it kinda is, except I'm having an issue with my projection matrices which causes my 2D image to be stretched horizontally a little bit (making Lena look VERY bloated).
I think my issue is stemming from my ortho projection, but I'm not positive - I stuffed a projection into the draw function, to make sure it was working as expected. The texture is rendering correctly to the vertices, the image is 512:512 as a BMP (verified when pulled into C#).
Perhaps someone could take a look at my code and help me out? I've copied most of this code from other examples, and have fiddled with it a bit, so this is my latest incarnation:
private bool TexturesInitialised = false;
private Bitmap gImage1;
private System.Drawing.Imaging.BitmapData gbitmapdata;
private uint[] gtexture = new uint[1];
private void InitialiseTexture(ref OpenGL gl)
{
gImage1 = new Bitmap(#"C:\Users\SJ\Pictures\lenaV.bmp");
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_RGBA, gImage1.Width, gImage1.Height, 0, OpenGL.GL_BGR_EXT, OpenGL.GL_UNSIGNED_BYTE, gbitmapdata.Scan0);
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);
TexturesInitialised = true;
}
private void openGLControl_OpenGLDraw(object sender, RenderEventArgs e)
{
// Get the OpenGL object.
OpenGL gl = openGLControl.OpenGL;
// Clear the color and depth buffer.
gl.Clear(OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT);
// Load the identity matrix.
gl.LoadIdentity();
if (!TexturesInitialised)
{
InitialiseTexture(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);
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);
gl.MatrixMode(OpenGL.GL_PROJECTION);
gl.LoadIdentity();
gl.Ortho(0.0, (double)gImage1.Width, (double)gImage1.Height, 0.0, -1.0, 1.0);
gl.MatrixMode(OpenGL.GL_MODELVIEW);
gl.Disable(OpenGL.GL_DEPTH_TEST);
}
private void openGLControl_OpenGLInitialized(object sender, EventArgs e)
{
// Get the OpenGL object.
OpenGL gl = openGLControl.OpenGL;
// Set the clear color.
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();
if (!TexturesInitialised)
{
gl.Ortho(-1, 1, -1, 1, -1, 1);
}
else
{
gl.Ortho(0, gImage1.Width, gImage1.Height, 0, -1, 1);
}
gl.MatrixMode(OpenGL.GL_MODELVIEW);
gl.Disable(OpenGL.GL_DEPTH_TEST);
}
Full credit to GeirGrusom for this one. The change was as simple as setting:
gl.Ortho(0.0, (double)gImage1.Width, (double)gImage1.Height, 0.0, -1.0, 1.0);
to:
gl.Ortho(0.0, (double)openGLControl.Width, (double)openGLControl.Height, 0.0, -1.0, 1.0);

OpenGL texture rendering - only upper left pixel is displayed

I working with C# and OpenTK.
Currently I only want to map a texture on a triangle.
It seems to be working but on nearest texture filter, the whole triangle is only colored with the upper left pixel color of the bmp image and if I set the texture filter to linear the triangle shows still only one color, but it seems whether it is now mixed with the other pixels.
Can someone find the error in the code ?
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
GL.Enable(EnableCap.Texture2D);
GL.ClearColor(0.5F, 0.5F, 0.5F, 1.0F);
int vertexShaderHandle = GL.CreateShader(ShaderType.VertexShader);
int fragmentShaderHandle = GL.CreateShader(ShaderType.FragmentShader);
string vertexShaderSource = #"#version 400
layout(location = 0) in vec3 position;
layout(location = 1) in vec2 uv;
out vec2 texture_uv;
void main()
{
gl_Position = vec4(inPosition.xyz, 1);
texture_uv = uv;
}";
string fragmentShaderSource = #"#version 400
in vec2 texture_uv;
out vec3 outColor;
uniform sampler2D uniSampler;
void main()
{
outColor = texture( uniSampler, texture_uv ).rgb;
}";
GL.ShaderSource(vertexShaderHandle, vertexShaderSource);
GL.ShaderSource(fragmentShaderHandle, fragmentShaderSource);
GL.CompileShader(vertexShaderHandle);
GL.CompileShader(fragmentShaderHandle);
prgHandle = GL.CreateProgram();
GL.AttachShader(prgHandle, vertexShaderHandle);
GL.AttachShader(prgHandle, fragmentShaderHandle);
GL.LinkProgram(prgHandle);
GL.DetachShader(prgHandle, vertexShaderHandle);
GL.DetachShader(prgHandle, fragmentShaderHandle);
GL.DeleteShader(vertexShaderHandle);
GL.DeleteShader(fragmentShaderHandle);
uniSamplerLoc = GL.GetUniformLocation(prgHandle, "uniSampler");
texHandle = GL.GenTexture();
GL.BindTexture(TextureTarget.Texture2D, texHandle);
Bitmap bmp = new Bitmap("C:/Users/Michael/Desktop/Test.bmp");
BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, bmpData.Width, bmpData.Height, 0,
OpenTK.Graphics.OpenGL4.PixelFormat.Bgra, PixelType.UnsignedByte, bmpData.Scan0);
bmp.UnlockBits(bmpData);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Nearest);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Nearest);
vaoHandle = GL.GenVertexArray();
GL.BindVertexArray(vaoHandle);
vboHandle = GL.GenBuffer();
GL.BindBuffer(BufferTarget.ArrayBuffer, vboHandle);
float[] bufferData = { 0.5F, 1, 0, 1, 1,
0, 0, 0, 0, 0,
1, 0, 0, 1, 0 };
GL.BufferData<float>(BufferTarget.ArrayBuffer, (IntPtr) (15 * sizeof(float)), bufferData, BufferUsageHint.StaticDraw);
GL.EnableVertexAttribArray(0);
GL.EnableVertexAttribArray(1);
GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, 5 * sizeof(float), 0);
GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, 5 * sizeof(float), 3 * sizeof(float));
}
protected override void OnUnload(EventArgs e)
{
base.OnUnload(e);
GL.DeleteTexture(texHandle);
GL.DeleteProgram(prgHandle);
GL.DeleteBuffer(vboHandle);
GL.DeleteVertexArray(vaoHandle);
}
protected override void OnRenderFrame(FrameEventArgs e)
{
base.OnRenderFrame(e);
GL.Clear(ClearBufferMask.ColorBufferBit);
GL.UseProgram(prgHandle);
GL.Uniform1(uniSamplerLoc, texHandle);
GL.BindVertexArray(vaoHandle);
GL.DrawArrays(PrimitiveType.Triangles, 0, 3);
SwapBuffers();
}
EDIT:
I tried this:
protected override void OnRenderFrame(FrameEventArgs e)
{
base.OnRenderFrame(e);
GL.Clear(ClearBufferMask.ColorBufferBit);
GL.UseProgram(prgHandle);
GL.BindVertexArray(vaoHandle);
GL.ActiveTexture(TextureUnit.Texture3);
GL.BindTexture(TextureTarget.Texture2D, texHandle);
GL.Uniform1(uniSamplerLoc, 3);
GL.DrawArrays(PrimitiveType.Triangles, 0, 3);
SwapBuffers();
}
But nothing changed :(
The value of a sampler uniform variable needs to be the texture unit it should sample from. In your code, it is set to the texture name (aka texture id, aka texture handle) instead:
GL.Uniform1(uniSamplerLoc, texHandle);
The texture unit can be set with ActiveTexture(). When glBindTexture() is called, the value of the currently active texture unit determines which unit the texture is bound to. The default for the active texture unit is 0. So if you never called ActiveTexture(), the uniform should be set as:
GL.Uniform1(uniSamplerLoc, 0);
Just as a heads-up, another related source of errors is that the value of the uniform is a 0-based index of the texture unit, while the glActiveTexture() call takes an enum starting with GL_TEXTURE0. For example with the C bindings (not sure how exactly this looks with C# and OpenTK, but it should be similar enough), this would bind a texture to texture unit 3, and set a uniform sampler variable to use it:
glActiveTexture(GL_TEXTURE3);
glBindTexture(GL_TEXTURE_2D, texId);
glUniform1i(texUniformLoc, 3);
Note how GL_TEXTURE3 is used in the argument for glActiveTexture(), but a plain 3 in glUniform1i().

OpenTK triangle is always white

I want to draw a colored triangle in OpenTK using a simple fragment shader with a fixed output color. However, the triangle always stays white. What causes this problem?
Here is the init and render code:
game.Load += (sender, e) =>
{
game.VSync = VSyncMode.On;
float[] vertexPositions = {
0.75f, 0.75f, 0.0f, 1.0f,
0.75f, -0.75f, 0.0f, 1.0f,
-0.75f, -0.75f, 0.0f, 1.0f,
};
GL.GenBuffers(1, out vbo);
GL.BindBuffer(BufferTarget.ArrayBuffer, vbo);
GL.BufferData(BufferTarget.ArrayBuffer, new IntPtr(
vertexPositions.Length * sizeof(float)), vertexPositions, BufferUsageHint.StaticDraw);
GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
// load shaders, create and link shader program
string shaderPath = #"K:\VisualProjects\ArcSynthesis\Tut2\Tut2\shaders\";
shVertex = new VertexShader(new System.IO.FileInfo(shaderPath + "vertex.glsl"));
shFragment = new FragShader(new System.IO.FileInfo(shaderPath + "fragment.glsl"));
spMain = new ShaderProgram(shVertex, shFragment);
spMain.Link();
};
and
game.RenderFrame += (sender, e) =>
{
GL.ClearColor(0f, 0f, 0f, 0f);
GL.Clear(ClearBufferMask.ColorBufferBit);
spMain.Use();
GL.BindBuffer(BufferTarget.ArrayBuffer, vbo);
GL.EnableVertexAttribArray(0);
GL.VertexAttribPointer(0, 4, VertexAttribPointerType.Float, false, 0, 0);
GL.DrawArrays(PrimitiveType.Triangles, 0, 3);
GL.DisableVertexAttribArray(0);
GL.UseProgram(0);
game.SwapBuffers();
};
Vertex shader:
#version 130
in vec4 position;
void main()
{
gl_Position = position;
}
Fragment shader:
#version 130
out vec4 outputColor;
void main()
{
outputColor = vec4(0.2f, 0.5f, 0.8f, 1.0f);
}
So I found out that the problem was the line
GL.UseProgram(0);
The shader program class I use (found in some forum) implements the glUseProgram() function like this:
public IDisposable Use()
{
IDisposable r = new ShaderProgram.Handle(curr_program);
if (curr_program != ID)
{
GL.UseProgram(ID);
curr_program = ID;
}
return r;
}
It only calls glUseProgram() if another ShaderProgram has previously set the static int curr_program to its own ID. It does nothing if the GL shader program has been set to 0 manually by calling GL.UseProgram(0).
I simply removed the line and everything is working now.

Can't Draw SharpGL(openGL wrapper) object in EMGU CV

I've been working with emgu cv(opencv wrapper to capture images from a webcam and using its functions to proccess this images.
I also detect the hand and track the hand movement...
Now, I need to draw a kind of earth or just an object according to the hand position, for which sharpGL is perfect for perspective transformation and so on. My problem is that I can't achieve that.
I don't know how to say to sharpGL "you guy, draw that object within this hand tracking window"
Is it impossible what I want to do? I am desperate... any help would be great. Thanks in advance
see this video if you're still confused about what I meant (http://www.youtube.com/watch?v=ccL4t36sVvg)
so far, I've just translated this code http://blog.damiles.com/2008/10/opencv-opengl/ into C#
and here's code snippet
private void openGLControl_OpenGLInitialized(object sender, EventArgs e)
{
// TODO: Initialise OpenGL here.
// The texture identifier.
uint[] textures = new uint[1];
// Get the OpenGL object.
OpenGL gl = openGLControl1.OpenGL;
//texture.Create(gl);
// Get one texture id, and stick it into the textures array.
gl.GenTextures(1, textures);
// Bind the texture.
gl.BindTexture(OpenGL.GL_TEXTURE_2D, textures[0]);
// A bit of extra initialisation here, we have to enable textures.
gl.Enable(OpenGL.GL_TEXTURE_2D);
// Specify linear filtering.
gl.TexParameter(OpenGL.GL_TEXTURE_2D, OpenGL.GL_TEXTURE_MIN_FILTER, OpenGL.GL_NEAREST);
gl.TexParameter(OpenGL.GL_TEXTURE_2D, OpenGL.GL_TEXTURE_MAG_FILTER, OpenGL.GL_NEAREST);
gl.PixelStore(OpenGL.GL_UNPACK_ALIGNMENT, 1);
// Set the clear color.
gl.ClearColor(1.0f, 1.0f, 1.0f, 1.0f);
}
private void openGLControl_Resized(object sender, EventArgs e)
{
// TODO: Set the projection matrix here.
// Get the OpenGL object.
OpenGL gl = openGLControl1.OpenGL;
// Set the projection matrix.
gl.MatrixMode(OpenGL.GL_PROJECTION);
// Load the identity.
gl.LoadIdentity();
// Create a perspective transformation.
gl.Perspective(60.0f, (double)Width / (double)Height, 0.01, 100.0);
// Use the 'look at' helper function to position and aim the camera.
gl.LookAt(-5, 5, -5, 0, 0, 0, 0, 1, 0);
// Set the modelview matrix.
gl.MatrixMode(OpenGL.GL_MODELVIEW);
}
and finally draw an 3D object
private void openGLControl_OpenGLDraw(object sender, PaintEventArgs e)
{
// Get the OpenGL object.
OpenGL gl = openGLControl1.OpenGL;
if (capture == null)
{
this.start_capture();
}
if (capture != null)
{
Image<Bgr, Byte> ImageFrame = capture.QueryFrame();
//I'm trying to use some algorithm using the code from sample (sharpGLTextureExample)
//first, I make an Bitmap object that I take from queryframe(convert it to bitmap first)
Bitmap image = new Bitmap(ImageFrame.ToBitmap());
// Clear the color and depth buffer.
gl.Clear(OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT);
//ImageFrame.Draw(new Rectangle(2, 2, 2, 2), new Bgr(Color.Aqua), 2);
// Load the identity matrix.
gl.LoadIdentity();
//then, 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);
gl.BindTexture(OpenGL.GL_TEXTURE_2D, textures[0]);
//gl.TexImage2D(OpenGL.GL_TEXTURE_2D, 0, (int)OpenGL.GL_RGBA, ImageFrame.Width, ImageFrame.Height, 0, OpenGL.GL_RGBA, OpenGL.GL_UNSIGNED_BYTE, ImageFrame);
gl.TexImage2D(OpenGL.GL_TEXTURE_2D, 0, (int)OpenGL.GL_RGBA, ImageFrame.Width, ImageFrame.Height, 0, OpenGL.GL_RGBA, OpenGL.GL_UNSIGNED_BYTE, bitmapData.Scan0);
//gl.Begin(OpenGL.GL_QUADS);
//gl.TexCoord(0, 0); gl.Vertex(-1, -1, 0);
//gl.TexCoord(1, 0); gl.Vertex(1, -1, 0);
//gl.TexCoord(1, 5); gl.Vertex(1, 1, 0);
//gl.TexCoord(0, 1); gl.Vertex(-1, 1, 0);
//gl.End();
//gl.Flush();
//texture.Bind(gl);
//
//CamImageBox.Image = ImageFrame;
}
}
but the output always return an white, no texture on it...
I've also consindering to use Texture class, but it's no use..because there's no method which the input parameter is the frame...
From your code and help from http://basic4gl.wikispaces.com/2D+Drawing+in+OpenGL, I got SharpGL displaying a video from EmguCV
public partial class FormSharpGLTexturesSample : Form
{
Capture capture;
public FormSharpGLTexturesSample()
{
InitializeComponent();
// Get the OpenGL object, for quick access.
SharpGL.OpenGL gl = this.openGLControl1.OpenGL;
// A bit of extra initialisation here, we have to enable textures.
gl.Enable(OpenGL.GL_TEXTURE_2D);
gl.Disable(OpenGL.GL_DEPTH_TEST);
// Create our texture object from a file. This creates the texture for OpenGL.
capture = new Capture(#"Video file here");
}
private void openGLControl1_OpenGLDraw(object sender, RenderEventArgs e)
{
// Get the OpenGL object, for quick access.
SharpGL.OpenGL gl = this.openGLControl1.OpenGL;
int Width = openGLControl1.Width;
int Height = openGLControl1.Height;
gl.Clear(OpenGL.GL_COLOR_BUFFER_BIT);
gl.LoadIdentity();
var frame = capture.QueryFrame();
texture.Destroy(gl);
texture.Create(gl, frame.Bitmap);
// Bind the texture.
texture.Bind(gl);
gl.Begin(OpenGL.GL_QUADS);
gl.TexCoord(0.0f, 0.0f); gl.Vertex(0, 0, 0);
gl.TexCoord(1.0f, 0.0f); gl.Vertex(Width, 0, 0);
gl.TexCoord(1.0f, 1.0f); gl.Vertex(Width, Height, 0);
gl.TexCoord(0.0f, 1.0f); gl.Vertex(0, Height, 0);
gl.End();
gl.Flush();
}
// The texture identifier.
Texture texture = new Texture();
private void openGLControl1_Resized(object sender, EventArgs e)
{
SharpGL.OpenGL gl = this.openGLControl1.OpenGL;
// Create an orthographic projection.
gl.MatrixMode(MatrixMode.Projection);
gl.LoadIdentity();
// NOTE: Basically no matter what I do, the only points I see are those at
// the "near" surface (with z = -zNear)--in this case, I only see green points
gl.Ortho(0, openGLControl1.Width, openGLControl1.Height, 0, 0, 1);
// Back to the modelview.
gl.MatrixMode(MatrixMode.Modelview);
}
}
I hope it helps.
After some experiments, I could use TexImage2D but only with images having a width and height be a power of two
Instead of:
var frame = capture.QueryFrame();
texture.Destroy(gl);
texture.Create(gl, frame.Bitmap);
It can be replaced by the following block to update the data of the picture. I would like to know how to remove the need to call Resize.
var frame = capture.QueryFrame();
frame = frame.Resize(256, 256, Emgu.CV.CvEnum.INTER.CV_INTER_NN);
Bitmap Bitmap = frame.Bitmap;
BitmapData bitmapData = Bitmap.LockBits(new Rectangle(0, 0, Bitmap.Width, Bitmap.Height),
ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
gl.BindTexture(OpenGL.GL_TEXTURE_2D, textures[0]);
gl.TexImage2D(OpenGL.GL_TEXTURE_2D, 0, OpenGL.GL_RGBA, Bitmap.Width, Bitmap.Height, 0, OpenGL.GL_BGR, OpenGL.GL_UNSIGNED_BYTE, bitmapData.Scan0);
gl.TexParameter(OpenGL.GL_TEXTURE_2D, OpenGL.GL_TEXTURE_MIN_FILTER, OpenGL.GL_LINEAR); // Required for TexImage2D
gl.TexParameter(OpenGL.GL_TEXTURE_2D, OpenGL.GL_TEXTURE_MAG_FILTER, OpenGL.GL_LINEAR); // Required for TexImage2D

SharpGL & Kinect - Rendering ColorImageFrame Bitmap to OpenGL Texture

I want to draw a simple 2D quad with a texture from the Kinect's video input but my code isn't working.
Here is my ColorFrameReady event:
void Sensor_ColorFrameReady(object sender, ColorImageFrameReadyEventArgs e)
{
ColorImageFrame cif = e.OpenColorImageFrame();
if (cif != null)
{
middleBitmapSource = cif.ToBitmapSource();
cif.Dispose();
}
}
This is my OpenGL draw method:
private void OpenGLControl_OpenGLDraw(object sender, OpenGLEventArgs args)
{
// Get the OpenGL instance being pushed to us.
gl = args.OpenGL;
// Clear the colour and depth buffers.
gl.Clear(OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT);
// Enable textures
gl.Enable(OpenGL.GL_TEXTURE_2D);
// Reset the modelview matrix.
gl.LoadIdentity();
// Load textures
if (middleBitmapSource != null)
{
middleBitmap = getBitmap(middleBitmapSource, quadMiddleWidth, quadMiddleHeight);
//middleBitmap = new System.Drawing.Bitmap("C:\\Users\\Bobble\\Pictures\\Crate.bmp"); // When I use this texture, it works fine
gl.GenTextures(1, textures);
gl.BindTexture(OpenGL.GL_TEXTURE_2D, textures[0]);
gl.TexImage2D(OpenGL.GL_TEXTURE_2D, 0, 3, middleBitmap.Width, middleBitmap.Height, 0, OpenGL.GL_BGR, OpenGL.GL_UNSIGNED_BYTE,
middleBitmap.LockBits(new System.Drawing.Rectangle(0, 0, middleBitmap.Width, middleBitmap.Height),
System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb).Scan0);
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);
}
// Move back a bit
gl.Translate(0.0f, 0.0f, -25.0f);
gl.BindTexture(OpenGL.GL_TEXTURE_2D, textures[0]);
// Draw a quad.
gl.Begin(OpenGL.GL_QUADS);
// Draw centre quad
gl.TexCoord(0.0f, 0.0f); gl.Vertex(-(quadMiddleWidth / 2), quadMiddleHeight / 2, quadDepthFar);
gl.TexCoord(0.0f, 1.0f); gl.Vertex(quadMiddleWidth / 2, quadMiddleHeight / 2, quadDepthFar);
gl.TexCoord(1.0f, 1.0f); gl.Vertex(quadMiddleWidth / 2, -(quadMiddleHeight / 2), quadDepthFar);
gl.TexCoord(1.0f, 0.0f); gl.Vertex(-(quadMiddleWidth / 2), -(quadMiddleHeight / 2), quadDepthFar);
gl.End();
gl.Flush();
}
And here is a helper method which converts a BitmapSource to a Bitmap of a given size:
private System.Drawing.Bitmap getBitmap(BitmapSource bitmapSource, double rectangleWidth, double rectangleHeight)
{
double newWidthRatio = rectangleWidth / (double)bitmapSource.PixelWidth;
double newHeightRatio = ((rectangleWidth * bitmapSource.PixelHeight) / (double)bitmapSource.PixelWidth) / (double)bitmapSource.PixelHeight;
BitmapSource transformedBitmapSource = new TransformedBitmap(bitmapSource, new ScaleTransform(newWidthRatio, newHeightRatio));
int width = transformedBitmapSource.PixelWidth;
int height = transformedBitmapSource.PixelHeight;
//int stride = width * ((transformedBitmapSource.Format.BitsPerPixel + 7) / 8);
int stride = ((width * transformedBitmapSource.Format.BitsPerPixel + 31) & ~31) / 8; // See: http://stackoverflow.com/questions/1983781/why-does-bitmapsource-create-throw-an-argumentexception/1983886#1983886
byte[] bits = new byte[height * stride];
transformedBitmapSource.CopyPixels(bits, stride, 0);
unsafe
{
fixed (byte* pBits = bits)
{
IntPtr ptr = new IntPtr(pBits);
System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(
width, height, stride, System.Drawing.Imaging.PixelFormat.Format32bppArgb, ptr);
return bitmap;
}
}
}
The quad renders but it is just white, no texture on it.
This turned out to be an issue with SharpGL - the OpenGL library I was using.
http://sharpgl.codeplex.com/discussions/348278 - discussion on CodePlex which led to the issue being resolved by the SharpGL developers.
I was having a white picture too, but when I changed the input of the function TexImage2D to use width and heights which are multiples of 2, it worked. According to the documentation:
The width of the texture image (must be a power of 2, e.g 64).
The height of the texture image (must be a power of 2, e.g 32).

Categories