OpenGL ES 2 - 不显示纹理

OpenGL ES 2 - Textures Are Not Displayed

在我重构代码以显示多个不同的纹理之前,一切都运行良好,但现在我得到的只是比它们应该的尺寸小的黑框,显然它们没有任何纹理!我什至没有触及顶点,我根本不知道为什么尺寸会受到影响。


截图(黑色的大方块应该已经覆盖了整个屏幕,底部的小方块应该更大了。不知道是什么影响了它们的大小) :


着色器:(它们在使用前编译)

const val vertexShader_Image = "uniform mat4 u_MVPMatrix;" +
        "attribute vec4 a_Position;" +
        "attribute vec2 a_texCoord;" +
        "varying vec2 v_texCoord;" +
        "void main() {" +
        "  gl_Position = u_MVPMatrix * a_Position;" +
        "  v_texCoord = a_texCoord;" +
        "}"

const val fragmentShader_Image = "precision mediump float;" +
        "uniform sampler2D u_texture;" +
        "varying vec2 v_texCoord;" +
        "void main() {" +
        "  gl_FragColor = texture2D(u_texture, v_texCoord);" +
        "}" 

TextureLoader:(对象在Kotlin中基本上是静态的class如果你不熟悉的话)

object TextureLoader
{
    var textures: Map<TextureName, Int> = mutableMapOf()



    fun generateTextures(nameBitmapPair: List<Pair<TextureName, Bitmap>>, screenWidth: Int, screenHeight: Int)
    {

        val textureAmount = nameBitmapPair.size

        val mutableTextureMap = mutableMapOf<TextureName, Int>()

        if(textureAmount > 31 || textureAmount< 0)
            {
                throw IllegalStateException("Texture amount is bigger than 31 or smaller than 0. Texture limit for OPEN GL is 31, it can't be bigger than it")
            }
        val textureHandles = IntArray(textureAmount)
        GLES20.glGenTextures(textureAmount, textureHandles, 0)

        for (i in 0 until textureAmount)
        {
            if (textureHandles[i] != 0)
            {
                GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandles[i])

                GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR)
                GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR)

                // Load the bitmap into the bound texture.
                GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, nameBitmapPair.get(i).second, 0)
                nameBitmapPair.get(i).second.recycle()

                mutableTextureMap.put(nameBitmapPair.get(i).first, textureHandles[i])
                Timber.i("created new texture, Name = ${nameBitmapPair.get(i).first}, ID = ${textureHandles[i]}")

            }
            else
            {
                throw RuntimeException("Error loading texture.")
            }
        }
        textures = mutableTextureMap
    }
    
}

Batcher:(处理 OpenGL 样板代码的 class 因此代码库的其余部分不会让任何人眼花缭乱)

class Batcher private constructor()
{
    // Store the model matrix. This matrix is used to move models from object space (where each model can be thought
    // of being located at the center of the universe) to world space.
    private val mtrxModel = FloatArray(16)

    // Allocate storage for the final combined matrix. This will be passed into the shader program.
    private val mtrxMVP = FloatArray(16)

    // Store the projection matrix. This is used to project the scene onto a 2D viewport.
    private val mtrxProjection = FloatArray(16)

/*    This was my UV array before I refactored the code to add animations. Now it's accessed by the 
      sprite.animator.getUvCoordinationForCurrentFrame()

      private var uvArray = floatArrayOf(
            0.0f, 0.0f,
            0.0f, 0.20f,
            0.20f, 0.20f,
            0.20f, 0.0f)
*/

    private var uvBuffer: FloatBuffer? = null
    private var vertexBuffer: FloatBuffer? = null

    private var indices = shortArrayOf(0, 1, 2, 0, 2, 3) // The order of vertexrendering.
    private var indicesBuffer: ShortBuffer? = null

    // NOTE: companion object is the static class of the Kotlin if you are not familiar with it. It's used to create a singleton here.
    companion object
    {
        private var instance: Batcher? = null

        fun getInstance(): Batcher
        {
            if (instance == null)
            {
                instance = Batcher()
            }

            return instance!!
        }
    }

    //Constructor of the Kotlin classes if you aren't familiar with it
    init
    {

        glEnable(GL_BLEND)

        // initialize byte buffer for the draw list
        indicesBuffer = ByteBuffer.allocateDirect(indices.size * 2)
            .order(ByteOrder.nativeOrder())
            .asShortBuffer()
        indicesBuffer!!.put(indices)
            .position(0)
        val vertices = floatArrayOf(
                0f, 0f, 0f,
                0f, 1.0f, 0f,
                1.0f, 1.0f, 0f,
                1.0f, 0f, 0f)

        // The vertex buffer.
        vertexBuffer = ByteBuffer.allocateDirect(vertices.size * 4)
            .order(ByteOrder.nativeOrder())
            .asFloatBuffer()
        vertexBuffer!!.put(vertices)
            .position(0)
    }


    fun render(sprites: Sprites)
    {
        //TODO should these be called on every draw call??

        // Get handle to shape's transformation matrix
        val u_MVPMatrix = glGetUniformLocation(ShaderHelper.programTexture, "u_MVPMatrix")
        val a_Position = glGetAttribLocation(ShaderHelper.programTexture, "a_Position")
        val a_texCoord = glGetAttribLocation(ShaderHelper.programTexture, "a_texCoord")
        val u_texture = glGetUniformLocation(ShaderHelper.programTexture, "u_texture")
        glEnableVertexAttribArray(a_Position)
        glEnableVertexAttribArray(a_texCoord)

        // "it" here is the currently iterated sprite object 
        sprites.forEach{

            val uvArray = it.animator.getUvCoordinationForCurrentFrame()
            if (uvArray != null)
            {
                updateUVBuffer(uvArray)

                glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)

                // Matrix op - start
                Matrix.setIdentityM(mtrxMVP, 0)
                Matrix.setIdentityM(mtrxModel, 0)

                Matrix.translateM(mtrxModel, 0, it.x.toFloat(), it.y.toFloat(), 0f)
                Matrix.scaleM(mtrxModel, 0, it.scaledFrameWidth.toFloat(), it.scaledFrameHeight.toFloat(), 0f)

                if (it.isHorizontallyFlipped)
                {
                    Matrix.translateM(mtrxModel, 0, 1f, 0f, 0f)
                    Matrix.scaleM(mtrxModel, 0, -1f, 1f, 0f)
                }


                Matrix.multiplyMM(mtrxMVP, 0, mtrxModel, 0, mtrxMVP, 0)
                Matrix.multiplyMM(mtrxMVP, 0, mtrxProjection, 0, mtrxMVP, 0)


                // Matrix op - end
                // Pass the data to shaders - start
                // Prepare the triangle coordinate data
                //Binds this vertex's data to a spot in the buffer
                glVertexAttribPointer(a_Position, 3, GL_FLOAT, false, 0, vertexBuffer)

                // Prepare the texture coordinates
                glVertexAttribPointer(a_texCoord, 2, GL_FLOAT, false, 0, uvBuffer)
                glUniformMatrix4fv(u_MVPMatrix, 1, false, mtrxMVP, 0)

                // Set the sampler texture unit to where we have saved the texture.
                // second param is the active texture index
               glUniform1i(u_texture,  TextureLoader.textures.get(it.textureName)!!)

                // Draw the triangles
                glDrawElements(GL_TRIANGLES, indices.size, GL_UNSIGNED_SHORT, indicesBuffer)
            }
            else
            {
                Timber.w("UV array of the sprite is null, skipping rendering. \nSprite: ${it}")
            }
        }
    }

    fun setScreenDimension(screenWidth: Int, screenHeight: Int)
    {
        Matrix.setIdentityM(mtrxProjection, 0)

        Matrix.orthoM(mtrxProjection, 0, 0f, screenWidth.toFloat(), screenHeight.toFloat(), 0f, 0f, 1f)
    }

    private fun updateUVBuffer(uvArray: FloatArray)
    {
        uvBuffer = ByteBuffer.allocateDirect(uvArray.size * 4)
            .order(ByteOrder.nativeOrder())
            .asFloatBuffer()
        uvBuffer!!.put(uvArray)
            .position(0)
    }

要分配给纹理采样器统一的值不是纹理的对象编号。它必须是纹理绑定到的纹理单元。由于您的纹理绑定到纹理单元 0 (GL_TEXTURE0),因此您需要将 0 分配给纹理采样器统一(默认为 0):

glUniform1i(u_texture, TextureLoader.textures.get(it.textureName)!!)

glUniform1i(u_texture, 0)

glBindTexture binds a texture to the specified target and the current texture unit. The texture unit can be set by glActiveTexture.


如果你这样做

glActiveTexture(GLES20.GL_TEXTURE0)
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandles[i])

然后你必须将 0 分配给纹理采样器。但如果你这样做

glActiveTexture(GLES20.GL_TEXTURE1)
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandles[i])

然后你必须将1分配给纹理采样器。


在绘制网格之前必须确保纹理已绑定。由于您有不同的纹理,因此每次要渲染具有纹理的几何体时都应调用 glBindTexture:例如:

glActiveTexture(GLES20.GL_TEXTURE0)
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,  TextureLoader.textures.get(it.textureName)!!)
glUniform1i(u_texture, 0)
glDrawElements(GL_TRIANGLES, indices.size, GL_UNSIGNED_SHORT, indicesBuffer)