Android 上的 OpenGL ES 2.0:无法加载纹理

OpenGL ES 2.0 on Android: Cannot load Textures

我是一个固定功能管道人员,正在习惯新奇的着色器。

我正在尝试扩展 Android OpenGL ES 示例以制作方形纹理。如果我的着色器的最后一行是 gl_Fragcolor = vColor; 我得到一个白色方块。如果是 gl_FragColor = vColor * texture2D(u_Texture, v_TexCoordinate); 我就变黑了。

奇怪的是:当我开始检查 GL 错误时,我发现 draw() 函数开头的 GLES20.glUseProgram(mProgram); 行给我 GL 错误 1280,GL_INVALID_ENUM .这对我来说毫无意义——没有枚举传递给该函数。

想法?

DemoRenderer.java:

package com.wtracy.executivedemo;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import android.app.Activity;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView.Renderer;
import android.opengl.Matrix;
import android.util.Log;

public class DemoRenderer implements Renderer {
    float ratio;
    // mMVPMatrix is an abbreviation for "Model View Projection Matrix"
    private final float[] mMVPMatrix = new float[16];
    private final float[] mProjectionMatrix = new float[16];
    private final float[] mViewMatrix = new float[16];
    private SplashScreen splash; 
    Activity parent;

    public DemoRenderer(Activity parent) {
        this.parent = parent;
    }

    public void onDrawFrame(GL10 arg0) {
        // Draw background color
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);

        // Set the camera position (View matrix)
        Matrix.setLookAtM(mViewMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);

        // Calculate the projection and view transformation
        Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);

        // Draw square
        splash.draw(mMVPMatrix);
    }

    public void onSurfaceChanged(GL10 arg0, int width, int height) {
        // Adjust the viewport based on geometry changes,
        // such as screen rotation
        GLES20.glViewport(0, 0, width, height);

        ratio = (float) width / height;

        float newWidth;
        float newHeight;

        if (ratio >= 1f) {
            newWidth = ratio;
            newHeight = 1f;
        } else {
            newWidth = 1f;
            newHeight = 1f/ratio;
        }
        // this projection matrix is applied to object coordinates
        // in the onDrawFrame() method
        Matrix.frustumM(mProjectionMatrix, 0, -newWidth, newWidth, -newHeight, newHeight, 3, 7);
    }

    public void onSurfaceCreated(GL10 arg0, EGLConfig arg1) {
        // Set the background frame color
        GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

        splash = new SplashScreen(parent.getResources());
    }

    public static int loadShader(int type, String shaderCode){
        // create a vertex shader type (GLES20.GL_VERTEX_SHADER)
        // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
        int shader = GLES20.glCreateShader(type);
        checkGlError("glCreateShader");

        // add the source code to the shader and compile it
        GLES20.glShaderSource(shader, shaderCode);
        checkGlError("glShaderSource");
        GLES20.glCompileShader(shader);
        checkGlError("glCompileShader");

        return shader;
    }

    public static void checkGlError(String glOperation) {
        int error;
        while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
            Log.e("com.wtracy.executivedemo", glOperation + ": glError " + error);
            throw new RuntimeException(glOperation + ": glError " + error);
        }
    }
}

SplashScreen.java:

package com.wtracy.executivedemo;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;

import com.wtracy.executivedemo.R;

import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLES20;
import android.opengl.GLUtils;

/**
 * A two-dimensional square for use as a drawn object in OpenGL ES 2.0.
 */
public class SplashScreen {

    private final String vertexShaderCode =
            // This matrix member variable provides a hook to manipulate
            // the coordinates of the objects that use this vertex shader
            "uniform mat4 uMVPMatrix;" +
            "attribute vec4 vPosition;" +
            "attribute vec2 a_TexCoordinate; " +// Per-vertex texture coordinate information we will pass in.
            "varying vec2 v_TexCoordinate;" +
            "void main() {" +
            // The matrix must be included as a modifier of gl_Position.
            // Note that the uMVPMatrix factor *must be first* in order
            // for the matrix multiplication product to be correct.
            "  gl_Position = uMVPMatrix * vPosition;" +
            "  v_TexCoordinate = a_TexCoordinate;" +
            "}";

    private final String fragmentShaderCode =
            "precision mediump float; " +
            "uniform vec4 vColor; " +
            "uniform sampler2D u_Texture; " +
            "varying vec2 v_TexCoordinate; " +
            "void main() {" +
            /*"  float diffuse;" +
            "  diffuse = diffuse * (1.0 / (1.0 + (0.10 * distance)));" +
            "  diffuse = diffuse + 0.3;" +*/
            "  gl_FragColor = vColor * texture2D(u_Texture, v_TexCoordinate);" +
            //"  gl_FragColor = vColor;" +
            "}";

    private final FloatBuffer vertexBuffer;
    private final ShortBuffer drawListBuffer;
    private final int mProgram;
    private int mPositionHandle;
    private int mColorHandle;
    private int mMVPMatrixHandle;
    /** Store our model data in a float buffer. */
    private final FloatBuffer mCubeTextureCoordinates;
    /** This will be used to pass in the texture. */
    private int mTextureUniformHandle;
    /** This will be used to pass in model texture coordinate information. */
    private int mTextureCoordinateHandle;
    /** Size of the texture coordinate data in elements. */
    private final int mTextureCoordinateDataSize = 2;
    /** This is a handle to our texture data. */
    private int mTextureDataHandle;

    // number of coordinates per vertex in this array
    static final int COORDS_PER_VERTEX = 3;
    static float squareCoords[] = {
            -1.f,  1.f, 0.0f,   // top left
            -1.f, -1.f, 0.0f,   // bottom left
             1.f, -1.f, 0.0f,   // bottom right
             1.f,  1.f, 0.0f }; // top right

    private final short drawOrder[] = { 0, 1, 2, 0, 2, 3 }; // order to draw vertices

    private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex

    final float color[] = { 1f, 1f, 1f, 1.0f };
    final int[] textureHandle = new int[1];

    /**
     * Sets up the drawing object data for use in an OpenGL ES context.
     */
    public SplashScreen(Resources resources) {
        // initialize vertex byte buffer for shape coordinates
        ByteBuffer bb = ByteBuffer.allocateDirect(
        // (# of coordinate values * 4 bytes per float)
                squareCoords.length * 4);
        bb.order(ByteOrder.nativeOrder());
        vertexBuffer = bb.asFloatBuffer();
        vertexBuffer.put(squareCoords);
        vertexBuffer.position(0);

        bb = ByteBuffer.allocateDirect(squareCoords.length*4);
        bb.order(ByteOrder.nativeOrder());
        mCubeTextureCoordinates = bb.asFloatBuffer();
        mCubeTextureCoordinates.put(squareCoords);
        mCubeTextureCoordinates.position(0);

        // initialize byte buffer for the draw list
        ByteBuffer dlb = ByteBuffer.allocateDirect(
                // (# of coordinate values * 2 bytes per short)
                drawOrder.length * 2);
        dlb.order(ByteOrder.nativeOrder());
        drawListBuffer = dlb.asShortBuffer();
        drawListBuffer.put(drawOrder);
        drawListBuffer.position(0);

        // prepare shaders and OpenGL program
        int vertexShader = DemoRenderer.loadShader(
                GLES20.GL_VERTEX_SHADER,
                vertexShaderCode);
        int fragmentShader = DemoRenderer.loadShader(
                GLES20.GL_FRAGMENT_SHADER,
                fragmentShaderCode);

        mProgram = GLES20.glCreateProgram();             // create empty OpenGL Program
        DemoRenderer.checkGlError("glCreateProgramr");
        GLES20.glAttachShader(mProgram, vertexShader);   // add the vertex shader to program
        DemoRenderer.checkGlError("glAttachShader");
        GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program
        DemoRenderer.checkGlError("glAttachShader");
        GLES20.glLinkProgram(mProgram);                  // create OpenGL program executables
        DemoRenderer.checkGlError("glLinkProgram");

        GLES20.glGenTextures(1, textureHandle, 0);
        DemoRenderer.checkGlError("glGenTextures");

        Bitmap bitmap;
        BitmapFactory.Options bo = new BitmapFactory.Options();
        bo.inScaled = false;
        bitmap = BitmapFactory.decodeResource(
                resources, 
                R.drawable.myfirstdemo,
                bo);
        // Bind to the texture in OpenGL
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle[0]);
        DemoRenderer.checkGlError("glBindTexture");

        // Set filtering
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);
        DemoRenderer.checkGlError("glTexParameter");

        // Load the bitmap into the bound texture.
        GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);

        // Recycle the bitmap, since its data has been loaded into OpenGL.
        bitmap.recycle();

        if (textureHandle[0] == 0)
        {
            throw new RuntimeException("Error loading texture.");
        }
    }

    /**
     * Encapsulates the OpenGL ES instructions for drawing this shape.
     *
     * @param mvpMatrix - The Model View Project matrix in which to draw
     * this shape.
     */
    public void draw(float[] mvpMatrix) {
        // Add program to OpenGL environment
        GLES20.glUseProgram(mProgram);
        DemoRenderer.checkGlError("glUseProgram");

        // get handle to fragment shader's vColor member
        mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
        DemoRenderer.checkGlError("glGetUniformLocation");
        // get handle to vertex shader's vPosition member
        mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
        DemoRenderer.checkGlError("glGetAttribLocation");
        mTextureUniformHandle = GLES20.glGetUniformLocation(mProgram, "u_Texture");
        DemoRenderer.checkGlError("glGetUniformLocation");
        mTextureCoordinateHandle = GLES20.glGetAttribLocation(mProgram, "a_TexCoordinate");
        DemoRenderer.checkGlError("glGetAttribLocation");

        // Set color for drawing the triangle
        GLES20.glUniform4fv(mColorHandle, 1, color, 0);
        //DemoRenderer.checkGlError("glUniform4fv");

        GLES20.glActiveTexture(textureHandle[0]);
        //DemoRenderer.checkGlError("glActiveTexture");
        // Bind the texture to this unit.
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureDataHandle);
        //DemoRenderer.checkGlError("glBindTexture");
        // Tell the texture uniform sampler to use this texture in the shader by binding to texture unit 0.
        GLES20.glUniform1i(mTextureUniformHandle, 0);
        //DemoRenderer.checkGlError("glUniform1i");

        // get handle to shape's transformation matrix
        mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
        //DemoRenderer.checkGlError("glGetUniformLocation");

        // Apply the projection and view transformation
        GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);
        //DemoRenderer.checkGlError("glUniformMatrix4fv");

        // Enable a handle to the triangle vertices
        GLES20.glEnableVertexAttribArray(mPositionHandle);

        // Prepare the triangle coordinate data
        GLES20.glVertexAttribPointer(
                mPositionHandle, COORDS_PER_VERTEX,
                GLES20.GL_FLOAT, false,
                vertexStride, vertexBuffer);

        // Pass in the texture coordinate information
        mCubeTextureCoordinates.position(0);
        GLES20.glVertexAttribPointer(mTextureCoordinateHandle, mTextureCoordinateDataSize, GLES20.GL_FLOAT, false,
                0, mCubeTextureCoordinates);
        GLES20.glEnableVertexAttribArray(mTextureCoordinateHandle);

        // Draw the square
        GLES20.glDrawElements(
                GLES20.GL_TRIANGLES, drawOrder.length,
                GLES20.GL_UNSIGNED_SHORT, drawListBuffer);

        // Disable vertex array
        GLES20.glDisableVertexAttribArray(mPositionHandle);
        GLES20.glDisableVertexAttribArray(mTextureCoordinateHandle);
    }

}

您的 GL_INVALID_ENUM 错误几乎可以肯定来自其他调用。请记住,错误值会一直存在,直到您检查它为止。所以它可能来自自上次调用 glGetError().

以来的任何调用

通过快速扫描代码,我看到两个调用在这里得到了错误的参数:

GLES20.glActiveTexture(textureHandle[0]);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureDataHandle);
  • glActiveTexture() 采用一个枚举来定义要绑定的 纹理单元 ,同时传递纹理名称。
  • 另一方面,
  • glBindTexture() 需要一个纹理名称作为第二个参数,而您传递的变量在发布的代码中的任何地方都没有赋值。

这些调用应该是:

GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle[0]);