0

如何将像素从简单的字节数组上传到 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);
4

3 回答 3

3

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

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

然后画出纹理。

于 2012-04-13T00:27:22.510 回答
1

尽管已经上传了一些东西,但纹理仍然是白色的,这表明不是所有的 mipmap 级别都被正确上传,或者过滤器设置没有正确设置。

那么感兴趣的是

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

使用 GL_NEAREST 或 GL_LINEAR 禁用 mipmapping。默认情况下启用 Mipmapping。

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

于 2012-04-13T07:57:26.030 回答
0

您的 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 内存不应移动。为此,您应使用固定语句或其他与垃圾收集器相关的方法。

例如:

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函数的 object 参数旨在用于任何数据数组(那些实现Array类的对象(即 byte[]、short[]、int[] 等)。

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

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

于 2012-04-13T20:15:40.703 回答