Reputation: 33
How can I upload pixels from a simple byte array to an OpenGl texture ?
I'm using glTexImage2D and all I get is a white rectangle instead of a pixelated texture. The 9th parameter (32-bit pointer to the pixel data) is IMO the problem. I tried lots of parameter types there (byte, ref byte, byte[], ref byte[], int & IntPtr + Marshall, out byte, out byte[], byte*). glGetError() always returns GL_NO_ERROR. There must be something I'm doing wrong because it's never some gibberish pixels. It's always white. glGenTextures works correct. The first id has the value 1 like always in OpenGL. And I draw colored lines without any problem. So something is wrong with my texturing. I'm in control of the DllImport. So I can change the parameter types if necessary.
GL.glBindTexture(GL.GL_TEXTURE_2D, id);
int w = 4;
int h = 4;
byte[] bytes = new byte[w * h * 4];
for (int i = 0; i < bytes.Length; i++)
bytes[i] = (byte)Utils.random(256);
GL.glTexImage2D(GL.GL_TEXTURE_2D, 0, GL.GL_RGBA, w, h, 0, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, bytes);
[DllImport(GL_LIBRARY)] public static extern void glTexImage2D(uint what, int level, int internalFormat, int width, int height, int border, int format,
int type, byte[] bytes);
Upvotes: 0
Views: 2521
Reputation: 11961
Your P/Invoke declaration is wrong.
The short answer is:
[System.Runtime.InteropServices.DllImport(Library, EntryPoint = "glTexImage2D", ExactSpelling = true)]
internal extern static void glTexImage2D(int target, int level, int internalformat, Int32 width, Int32 height, int border, int format, int type, IntPtr pixels);
This P/Invoke declaration is a safe one (not using directly pointers, but IntPtr).
The problem is the .NET memory management. Memory blocks are not fixed on a certain memory address: the garbage collector (GC) is free to move memory everywhere (for example, it can move a stack allocated memory in the heap space and viceversa).
Indeed, what you need is to tell to the .NET GC that the memory shall not moved. To do so, you shall use the fixed statement or other garbage collector related method.
For example:
public static void TexImage2D(int target, int level, int internalformat, Int32 width, Int32 height, int border, int format, int type, object pixels) {
GCHandle pp_pixels = GCHandle.Alloc(pixels, GCHandleType.Pinned);
try {
if (Delegates.pglTexImage2D != null)
Delegates.pglTexImage2D(target, level, internalformat, width, height, border, format, type, pp_pixels.AddrOfPinnedObject());
else
throw new InvalidOperationException("binding point TexImage2D cannot be found");
} finally {
pp_pixels.Free();
}
}
The object parameter of the TexImage2D function is meant to be used with any array of data (those objects implementing the Array class (that is, byte[], short[], int[] and so on).
Essentially the code above tell to the GC: take the address of pixels, and don't move it untill I call Free() on the memory handle.
Usin the fixed statement is another option, but requires an unsafe P/Invoke declaration and it's a little more verbose to use it in the code (for each call you have to define an unsafe and fixed statements).
Upvotes: 0
Reputation: 162164
A texture remaining white despite something has been uploaded is a indicator for either not all mipmap levels being uploaded properly, or filter settings not set correctly.
Of interest is then
glTexParameteri(GL_TEXTURE_..., GL_MIN_FILTER, GL_...);
With GL_NEAREST or GL_LINEAR to disable mipmapping. Mipmapping is enabled by default.
The other important thing is to set the structure of the data before uploading, i.e. calling glTexImage. For this you use the function glPixelStorei
to set the GL_UNPACK_... parameters. You need to set things like alignment, stride, and so on. I refer you to the documentation.
Upvotes: 1
Reputation: 56347
A common mistake is not change the MIN filter, since the default is mipmapped, which makes textures incomplete. Do this:
GL.glBindTexture(GL_TEXTURE_2D, id);
GL.glTexParameteri(GL_TEXTURE_2D, GL_MIN_FILTER, GL_NEAREST);
Then draw the texture.
Upvotes: 3