在iOS中破碎的纹理

时间:2013-01-15 06:54:48

标签: ios opengl-es xamarin.ios core-graphics opengl-es-2.0

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

broken texture

我没有做任何奇怪的事情。我正在使用CGImage - > CGBitmapContext - > GL.TexImage2D从半透明PNG加载纹理。我用两个三角形渲染每个精灵,我的片段着色器只用texture2D()从采样器中读取纹素,并将其乘以uniform 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是一个简单的ortographic投影矩阵。

正如你所看到的,我不想在这里做任何想象。这都是非常标准的代码,它适用于一些纹理,所以我认为通常代码是可以的。我也在Mono for Android中做了同样的事情,它运行良好,没有任何纹理损坏。

瑕疵的颜色就像在某处闻到未初始化的变量一样,并且看到它只发生在透明的部分上,这让我相信我在某处有未初始化的alpha值。但是,GL.Clear(ClearBufferMask.ColorBufferBit)应该清除我的alpha值,即使如此,背景纹理的alpha值为1,并且使用当前BlendFunc,应将这些像素的alpha设置为1.之后,透明纹理的alpha值范围从0到1,因此它们应该正确混合。我在任何地方都看不到未初始化的变量。

...或......这是CGBitmapContext的全部错误。也许通过做DrawImage,我不是blitting源图像,而是用混合绘制它,而垃圾数据来自我AllocGlobal时。这并不能解释为什么它只是在这两种纹理中始终如一......(我将其标记为核心图形,所以也许石英人可以提供帮助)

如果您想查看更多代码,请与我们联系。

1 个答案:

答案 0 :(得分:1)

好的,就像我预期的那样。我使用Marshal.AllocHGlobal获得的内存未初始化为任何内容,而CGBitmapContext.DrawImage只是将图像呈现在上下文中的任何内容之上,这是垃圾。

所以解决这个问题的方法就是在致电context.ClearRect()之前插入context.DrawImage()来电。

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

如果在使用调试堆时将内存分配给0xBAADF00D之类的内容会很好,就像{API} LocalAlloc一样。

另外两个要记住的相关事项:

  1. 在我发布的代码中,我没有释放AllocHGlobal请求的内存。这是一个错误。 GL.TexImage2D将纹理复制到VRAM,因此可以安全地将其释放到那里。

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