glTexImage2D + byte[]

本文关键字:byte glTexImage2D | 更新日期: 2023-09-27 18:31:09

如何将像素从简单的字节数组上传到 OpenGl 纹理?

我正在使用glTexImage2D,我得到的只是一个白色矩形而不是像素化的纹理。第 9 个参数(指向像素数据的 32 位指针)是 IMO 的问题。我在那里尝试了很多参数类型(byte,ref byte,byte[],ref byte[],int&IntPtr + Marshall,out byte,out byte[],byte*)。glGetError() 总是返回GL_NO_ERROR。一定是我做错了什么,因为它从来都不是一些乱码。它总是白色的。glGenTextures工作正确。第一个 id 的值为 1,就像在 OpenGL 中一样。我画彩色线条没有任何问题。所以我的纹理有问题。我控制着DllImport。因此,如有必要,我可以更改参数类型。

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);

glTexImage2D + byte[]

一个常见的错误是不更改 MIN 过滤器,因为默认值是 mipmapped,这使得纹理不完整。这样做:

GL.glBindTexture(GL_TEXTURE_2D, id);
GL.glTexParameteri(GL_TEXTURE_2D, GL_MIN_FILTER, GL_NEAREST);

然后绘制纹理。

尽管

上传了某些内容,但纹理仍然保持白色,这表明并非所有 mipmap 级别都未正确上传,或者过滤器设置不正确。

那么感兴趣的是

glTexParameteri(GL_TEXTURE_..., GL_MIN_FILTER, GL_...);

使用GL_NEAREST或GL_LINEAR禁用 mipmap。默认情况下启用 Mipmap。

另一个重要的事情是在上传之前设置数据的结构,即调用glTexImage。为此,您可以使用函数glPixelStorei来设置GL_UNPACK_...参数。您需要设置对齐方式、步幅等内容。我请您参考文档。

你的 P/Invoke 声明是错误的。

简短的回答是:

[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);

这个 P/Invoke 声明是一个安全的声明(不使用直接指针,而是使用 IntPtr)。

问题是 .NET 内存管理。内存块不是固定在某个内存地址上的:垃圾回收器(GC)可以自由地将内存移动到任何地方(例如,它可以在堆空间中移动堆栈分配的内存,反之亦然)。

实际上,您需要告诉 .NET GC 内存不得移动。为此,应使用 fixed 语句或其他垃圾回收器相关方法。

例如:

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();
     }         
}

TexImage2D 函数的对象参数旨在用于任何数据数组(实现 Array 类的对象(即 byte[]、short[]、int[] 等)。

本质上,上面的代码告诉 GC:获取像素的地址,并且在我在内存句柄上调用 Free() 之前不要移动它。

Usin 固定语句是另一种选择,但需要一个不安全的 P/Invoke 声明,并且在代码中使用它会更详细一些(对于每个调用,您必须定义一个不安全固定的语句)。