Android Opengl-es加载非2次幂纹理

10

我有一个在android上使用opengl-es的应用程序。

目前,我通过以下方式从位图加载纹理:

//Load up and flip the texture - then dispose the temp
    Bitmap temp = BitmapFactory.decodeResource(Deflecticon.getContext().getResources(), resourceID);
    Bitmap bmp = Bitmap.createBitmap(temp, 0, 0, temp.getWidth(), temp.getHeight(), flip, true);
    temp.recycle();

    //Bind the texture in memory
    gl.glBindTexture(GL10.GL_TEXTURE_2D, id);

    //Set the parameters of the texture.
    gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);
    gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);

    //On to the GPU
    GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bmp, 0);
明显的问题是我使用的纹理必须是2的幂。目前,我正在使用Photoshop预处理纹理,使其成为2的幂,并且只有空边框。但这有点繁琐,我希望能够加载它们,因为它们是什么..识别它们不是2的幂并将它们加载到纹理中。
我知道我可以缩放位图以成为2的幂大小,并简单地拉伸纹理,但我不想拉伸纹理,并且在某些情况下可能需要将几个纹理放入一个“atlas”中。
我知道我可以使用glTexSubImage2D()将我想要的数据粘贴到纹理中,而且可以指定想要的原点。这很好!
但是,我不知道在Android中如何初始化没有数据的纹理?
在这个问题之前问过的建议是调用glTexImage2D()没有数据,然后填充它。
但是,在Android中当您调用“GLUtils.texImage2D(GL10.GL_TEXTURE_2D,0,bmp,0);”时,您没有指定宽度/高度。我认为它从位图中读取这个信息。
最好的方法是什么?我可以创建一个正确大小的新位图,只有空白而没有任何数据,并使用它来初始化纹理,然后使用subImage粘贴到其中吗?或者应该制作一个新的位图,以某种方式将我想要的像素复制(不确定是否可以轻松地实现)到这个新位图中(保留边框),然后只使用此?
5个回答

4
我认为如果您尝试使用2的幂轴大小创建位图,然后添加您的位图,它应该可以正常工作。可能类似于:
Bitmap.createBitmap(notPowerOf2Bitmap, offx, offy, xsize, ysize, bitmapFlag)

除此之外,我会建议您尝试完成Photoshop的处理过程。你有多少张图片?


不要太多,我们希望可能稍后更改它们,如果调整大小做得好,这将使事情变得更容易!该方法表示它采用非POT位图的子集,这是否意味着如果子集位图小于指定的xSize和ySize,则会有空白边缘?还是会拉伸它?(我想尝试一下,但可悲的是我没有我的编码电脑)。 - iexus
位图不应该被拉伸,除非它们被传递到createScaledBitmap方法中。剩余的空间将是空白的。 - ahodder
嗯...那我能想到的唯一办法就是创建一个位图,为其分配画布并将未缩放的位图绘制到画布上,然后使用新的位图。这会占用一定的资源,但这是我所能想到的唯一方法了... - ahodder
我尝试了一下,目前它工作得非常好,因为我是在应用程序的开头做所有这些事情,所以我不介意资源问题。任何进一步的加载/释放都将在任何重要的基于速度的事物之间进行,所以一切都很好! - iexus
1
等一下,哪个有效?上面的编写解决方案还是评论中的?上面的编写解决方案被修改了吗?这已经被接受。 - Kit10
显示剩余3条评论

2

一些GLES平台支持非2的幂次方(NPOT)位图,但您需要检查是否存在适当的扩展。然而,请注意,至少在PowerVR SGX上,即使支持NPOT,仍然存在其他相当任意的限制;例如,如果不是2的幂次方,则纹理宽度必须是2的倍数。此外,许多GPU上的NPOT渲染往往会慢一些。

您可以做的一件事就是创建一个包装器纹理,其大小为2的幂次方大小,然后使用glTexSubimage2D将纹理上传以仅覆盖其中的一部分,然后相应地调整纹理坐标。显然的缺点是,在这种情况下无法使用纹理包裹。如果您绝对必须支持包裹,那么您可以在调用glTexImage2D之前将纹理缩放到最近的2的幂次方大小,尽管通常会引入采样伪影并使事物变得模糊,特别是如果您试图进行像素精确的2D工作。

如果您不需要支持包裹,另一件可以考虑的事情是制作“纹理集”,其中将所有纹理压缩到几个大纹理中,并且让多边形映射到纹理集的某些部分。在生成MIPmaps时必须小心,但除此之外,它通常提供相当不错的性能优势,并且更有效地利用纹理内存,因为您不会浪费太多内存用于填充或缩放图像。


正如我的问题所述,你如何在Android中创建一个包装纹理并对其进行采样?最佳方法是什么? - iexus
请查看 glTexSubimage2D 的文档。 - fluffy

2

我已经采取了两个解决方案来解决这个问题。如果需要的话,我可以更具体地说明,但是从概念上讲,您可以:

  1. Make the image a power of 2, and the section to crop you fill with 100% alpha channel and load the images with alpha enabled.

  2. Tweak your texture vector / buffer so it doesn't load that section. So instead of using the default

    float texture[] = { 
        0.0f, 1.0f, //
        1.0f, 1.0f, //
        0.0f, 0.0f, //
        1.0f, 0.0f, //
    };
    

作为矩阵(显然是为了将图像加载到两个三角形的正方形中),通过比例将面积因素回归到裁剪,例如。

    float texture[] = { 
        0.0f, 0.75f, //
        0.9f, 0.75f, //
        0.0f, 0.0f, //
        0.9f, 0.0f, //
     };

当然,数学计算必须准确,否则可能会出现图像留下的不需要的部分,或者你会剪切掉一些真实的图像。显然,这个数组是在运行时计算而不是像我在这里演示的那样硬编码的。

0

您可以使用GLES20.glTexImage2D()创建指定宽度和高度的空纹理。示例代码如下:

public static int genTexture(int texWidth, int texHeight) {
    int[] textureIds = new int[1];
    GLES20.glGenTextures(1, textureIds, 0);
    assertNoError();
    int textureId = textureIds[0];
    texWidth = Utils.nextPowerOf2(texWidth);
    texHeight = Utils.nextPowerOf2(texHeight);

    GLES20.glBindTexture(GL11.GL_TEXTURE_2D, textureId);
    GLES20.glTexParameteri(
            GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
    GLES20.glTexParameteri(
            GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
    GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D,
            GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
    GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D,
            GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);

    GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA,
            texWidth, texHeight, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null);

    return textureId;
}

0

嗯,为什么不创建两个位图。像你现在这样加载第一个位图,然后使用createBitmapScaled将该位图转换为2的幂次方。就性能而言,我不知道它是否是最快的方法,但它可以工作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接