简体   繁体   中英

OpenGL ES 2 - Textures Are Not Displayed

Before I refactored the code to display multiple different textures everything was working fine but now all I am getting are black boxes that are smaller than how their size supposed to be and obviously they don't have any textures, I didn't even touch the vertexes. I don't why the size is affected at all.


Screenshot (the black big box should've covered the whole screen and the small square at the bottom should've been way bigger. Have no clue what affected their size) : SS


Shaders: (they are compiled before used)

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: (object is basically a static class in Kotlin if you are not familiar with it)

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: (The class that handles the OpenGL boilerplate code so the rest of the codebase doesn't bleed anyone's eyes)

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)
    }

The value to be assigned to the texture sampler uniform is not the object number of the texture. It must be the texture unit that the texture is bound to. Since your texture is bound to texture unit 0 ( GL_TEXTURE0 ), you need to assign 0 to the texture sampler uniform (0 is default):

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 .


If you do

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

then you have to assign 0 to the texture sampler. But if you do

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

then you have to assign 1 to the texture sampler.


You have to ensure that the texture is bound before drawing the mesh. Since you have different textures, glBindTexture should be called each time when you are going to render a geometry with a texture: For instance:

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)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM