2

我在 MonoTouch 中使用 OpenTK 在 iOS 中渲染一些纹理,并且一些纹理出现损坏。这是 iPad 屏幕截图的特写,显示了一个正确渲染的纹理(顶部)和下面的两个损坏的纹理:

破碎的纹理

我没有做任何奇怪的事情。我正在使用CGImage-> CGBitmapContext->从半透明 PNG 加载纹理GL.TexImage2D。我用两个三角形渲染每个精灵,我的片段着色器只是从采样器中读取纹素,texture2D()并将其乘以 auniform vec4以着色纹理。

文件本身似乎没问题,使用 Mono for Android 的同一应用程序的 Android 端口,以及完全相同的二进制资源完美呈现它们。如您所见,其他透明纹理也可以正常工作。

如果有帮助,当我在模拟器中运行程序时,几乎所有纹理都会被破坏。即使我重建程序,这个问题也仍然存在。

有关如何找出导致此问题的原因的任何想法?

这是我的顶点着色器:

attribute vec4 spritePosition;
attribute vec2 textureCoords;
uniform mat4 projectionMatrix;
uniform vec4 color;

varying vec4 colorVarying;
varying vec2 textureVarying;

void main()
{
    gl_Position = projectionMatrix * spritePosition;
    textureVarying = textureCoords;

    colorVarying = color;
}

这是我的片段着色器:

varying lowp vec4 colorVarying;
varying lowp vec2 textureVarying;
uniform sampler2D spriteTexture;

void main()
{
    gl_FragColor = texture2D(spriteTexture, textureVarying) * colorVarying;
}

我正在加载这样的图像:

using (var bitmap = UIImage.FromFile(resourcePath).CGImage)
{
    IntPtr pixels = Marshal.AllocHGlobal(bitmap.Width * bitmap.Height * 4);
    using (var context = new CGBitmapContext(pixels, bitmap.Width, bitmap.Height, 8, bitmap.Width * 4, bitmap.ColorSpace, CGImageAlphaInfo.PremultipliedLast))
    {
        context.DrawImage(new RectangleF(0, 0, bitmap.Width, bitmap.Height), bitmap);
        int[] textureNames = new int[1];
        GL.GenTextures(1, textureNames);
        GL.BindTexture(TextureTarget.Texture2D, textureNames[0]);
        GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)All.Linear);
        GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)All.Linear);
        GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)All.ClampToEdge);
        GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)All.ClampToEdge);
        GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, bitmap.Width, bitmap.Height, 0, PixelFormat.Rgba, PixelType.UnsignedByte, pixels);
        CurrentResources.Add(resourceID, new ResourceData(resourcePath, resourceType, 0, new TextureEntry(textureNames[0], bitmap.Width, bitmap.Height)));
    }
}

在我的 onRenderFrame 中,我有这个:

GL.ClearColor(1.0f, 1.0f, 1.0f, 1.0f);
GL.Clear(ClearBufferMask.ColorBufferBit);
GL.Enable(EnableCap.Blend);
GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha);
GL.UseProgram(RenderingProgram);
GL.VertexAttribPointer((int)ShaderAttributes.SpritePosition, 2, VertexAttribPointerType.Float, false, 0, squareVertices);
GL.VertexAttribPointer((int)ShaderAttributes.TextureCoords, 2, VertexAttribPointerType.Float, false, 0, squareTextureCoords);
GL.EnableVertexAttribArray((int)ShaderAttributes.SpritePosition);
GL.EnableVertexAttribArray((int)ShaderAttributes.TextureCoords);
//...
GL.ActiveTexture(TextureUnit.Texture0);
GL.BindTexture(TextureTarget.Texture2D, textureEntry.TextureID);
GL.Uniform1(Uniforms[(int)ShaderUniforms.Texture], 0);
// ...    
GL.DrawArrays(BeginMode.TriangleStrip, 0, 4);

该三角形条由构成纹理的两个三角形组成,顶点和纹理坐标设置为我想要显示我的精灵的位置。projectionMatrix是一个简单的正射投影矩阵。

正如你所看到的,我不想在这里做任何花哨的事情。这都是非常标准的代码,它适用于某些纹理,所以我认为总的来说代码是可以的。我也在 Mono for Android 中做几乎相同的事情,它运行良好,没有任何纹理损坏。

像这样的损坏颜色在某处闻起来像未初始化的变量,并且只在透明部分发生这种情况让我相信我在某处有未初始化的 alpha 值。但是,GL.Clear(ClearBufferMask.ColorBufferBit)应该清除我的 alpha 值,即使这样,背景纹理的 alpha 值也是 1,并且使用 current BlendFunc,应该将这些像素的 alpha 设置为 1。之后,透明纹理的 alpha 值范围为 0 到 1 ,所以它们应该正确混合。我在任何地方都看不到未初始化的变量。

……或者……这都是 的错CGBitmapContext。也许通过这样做DrawImage,我不是在对源图像进行 blitting,而是通过混合来绘制它,而垃圾数据来自我这样做的时候AllocGlobal。这并不能解释为什么它始终只发生在这两种纹理上......(我将其标记为核心图形,所以也许石英人之一可以提供帮助)

如果您想查看更多代码,请告诉我。

4

1 回答 1

1

好吧,和我预料的一样。我得到的内存Marshal.AllocHGlobal没有初始化为任何东西,CGBitmapContext.DrawImage只是将图像呈现在上下文中的任何内容之上,这是垃圾。

所以解决这个问题的方法就是context.ClearRect()在我调用之前插入一个调用context.DrawImage()

我不知道为什么它与其他(更大的)纹理一起工作得很好,但也许是因为在这些情况下,我请求了一大块内存,所以 iOS(或单声道)内存管理器获得了一个新的归零块,而对于较小的纹理,我正在重用之前释放的内存,这些内存尚未归零。

如果您的内存被分配给0xBAADF00D使用调试堆时的东西,就像LocalAlloc在 Windows API 中那样,那就太好了。

另外两件有些相关的事情要记住:

  1. 在我发布的代码中,我没有释放使用AllocHGlobal. 这是一个错误。GL.TexImage2D将纹理复制到 VRAM,因此在此处释放它是安全的。

  2. context.DrawImage正在将图像绘制到新的上下文中(而不是从图像中读取原始像素),而 Core Graphics适用于预乘 alpha(我觉得这很愚蠢)。因此,如果我以这种方式加载,加载的纹理将始终加载预乘 alpha。这意味着我还必须将 alpha 混合函数更改为GL.BlendFunc(BlendingFactorSrc.One, BlendingFactorDest.OneMinusSrcAlpha),并确保所有交叉渐变代码都适用于整个 RGBA,而不仅仅是 alpha 值。

于 2013-01-19T08:33:11.227 回答