Android OpenGL ES 2纹理象限旋转

时间:2013-08-17 05:14:00

标签: android opengl-es-2.0

我正在尝试将简单的2D纹理加载到正方形上并将其显示在我的GLSurfaceView背面。我复制并修复了Draw a 2D Image using OpenGL ES 2.0中的代码,但是当我安装并运行应用程序时,图像被分成4个象限,然后重新排列。

这是我实现的Sprite类:

public class Sprite {
//Reference to Activity Context
private final Context mActivityContext;

//Added for Textures
private final FloatBuffer mCubeTextureCoordinates;
private int mTextureUniformHandle;
private int mTextureCoordinateHandle;
private final int mTextureCoordinateDataSize = 2;
private int mTextureDataHandle;

private final String vertexShaderCode =
    //Test
    "attribute vec2 a_TexCoordinate;" +
    "varying vec2 v_TexCoordinate;" +
    //End Test
    "uniform mat4 uMVPMatrix;" +
    "attribute vec4 vPosition;" +
    "void main() {" +
    "  gl_Position = uMVPMatrix * vPosition;" +
    //Test
    "v_TexCoordinate = a_TexCoordinate;" +
    //End Test
    "}";

private final String fragmentShaderCode =
    "precision mediump float;" +
    "uniform vec4 v_Color;" +
    //Test
    "uniform sampler2D u_Texture;" +
    "varying vec2 v_TexCoordinate;" +
    //End Test
    "void main() {" +
    //"gl_FragColor = v_Color;" +
    //"gl_FragColor = (v_Color * texture2D(u_Texture, v_TexCoordinate));" +
    // Just draw the texture, don't apply a color
    "gl_FragColor = texture2D(u_Texture, v_TexCoordinate);" +
    "}";

private final int shaderProgram;
private final FloatBuffer vertexBuffer;
private final ShortBuffer drawListBuffer;
private int mPositionHandle;
private int mColorHandle;
private int mMVPMatrixHandle;

// number of coordinates per vertex in this array
static final int COORDS_PER_VERTEX = 2;

static float spriteCoords[] = {
    -0.5f, 0.5f,   // top left
    -0.5f, -0.5f,   // bottom left
    0.5f, -0.5f,   // bottom right
    0.5f,  0.5f  //top right
};

private short drawOrder[] = { 0, 1, 2, 0, 2, 3 }; //Order to draw vertices
private final int vertexStride = COORDS_PER_VERTEX * 4; //Bytes per vertex

// Set color with red, green, blue and alpha (opacity) values
float color[] = { 0.63671875f, 0.76953125f, 0.22265625f, 1.0f };

// Image to draw as a texture
final int textureID = R.raw.quadrants;

public Sprite(final Context activityContext) {
    mActivityContext = activityContext;

    //Initialize Vertex Byte Buffer for Shape Coordinates / # of coordinate values * 4 bytes per float
    ByteBuffer bb = ByteBuffer.allocateDirect(spriteCoords.length * 4);
    //Use the Device's Native Byte Order
    bb.order(ByteOrder.nativeOrder());
    //Create a floating point buffer from the ByteBuffer
    vertexBuffer = bb.asFloatBuffer();
    //Add the coordinates to the FloatBuffer
    vertexBuffer.put(spriteCoords);
    //Set the Buffer to Read the first coordinate
    vertexBuffer.position(0);

    // S, T (or X, Y)
    // Texture coordinate data.
    // Because images have a Y axis pointing downward (values increase as you move down the image) while
    // OpenGL has a Y axis pointing upward, we adjust for that here by flipping the Y axis.
    // What's more is that the texture coordinates are the same for every face.
    final float[] cubeTextureCoordinateData =
        {
            //Front face
            /*0.0f, 0.0f,
              0.0f, 1.0f,
              1.0f, 0.0f,
              0.0f, 1.0f,
              1.0f, 1.0f,
              1.0f, 0.0f*/
            /*
             This was in the code in the aforementioned StackOverflow post,
             but doesn't work either
            -0.5f,  0.5f,
            -0.5f, -0.5f,
            0.5f, -0.5f,
            0.5f,  0.5f
            */
            0.5f, 0.5f,
            0.5f, -0.5f,
            -0.5f, -0.5f,
            -0.5f, 0.5f

        };

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

    //Initialize byte buffer for the draw list
    ByteBuffer dlb = ByteBuffer.allocateDirect(spriteCoords.length * 2);
    dlb.order(ByteOrder.nativeOrder());
    drawListBuffer = dlb.asShortBuffer();
    drawListBuffer.put(drawOrder);
    drawListBuffer.position(0);

    int vertexShader = MyGLRenderer.loadShader(GLES20.GL_VERTEX_SHADER,
                                               vertexShaderCode);
    int fragmentShader = MyGLRenderer.loadShader(GLES20.GL_FRAGMENT_SHADER,
                                                 fragmentShaderCode);

    shaderProgram = GLES20.glCreateProgram();
    GLES20.glAttachShader(shaderProgram, vertexShader);
    GLES20.glAttachShader(shaderProgram, fragmentShader);

    //Texture Code
    GLES20.glBindAttribLocation(shaderProgram, 0, "a_TexCoordinate");

    GLES20.glLinkProgram(shaderProgram);

    //Load the texture
    mTextureDataHandle = loadTexture(mActivityContext, textureID);
}

public void draw(float[] mvpMatrix) {
    //Add program to OpenGL ES Environment
    GLES20.glUseProgram(shaderProgram);

    //Get handle to vertex shader's vPosition member
    mPositionHandle = GLES20.glGetAttribLocation(shaderProgram, "vPosition");

    //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);

    //Get Handle to Fragment Shader's v_Color member
    mColorHandle = GLES20.glGetUniformLocation(shaderProgram, "v_Color");

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

    //Set Texture Handles and bind Texture
    mTextureUniformHandle = GLES20.glGetAttribLocation(shaderProgram, "u_Texture");
    mTextureCoordinateHandle = GLES20.glGetAttribLocation(shaderProgram, "a_TexCoordinate");

    //Set the active texture unit to texture unit 0.
    GLES20.glActiveTexture(GLES20.GL_TEXTURE0);

    //Bind the texture to this unit.
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureDataHandle);

    //Tell the texture uniform sampler to use this texture in the shader by binding to texture unit 0.
    GLES20.glUniform1i(mTextureUniformHandle, 0);

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

    //Get Handle to Shape's Transformation Matrix
    mMVPMatrixHandle = GLES20.glGetUniformLocation(shaderProgram, "uMVPMatrix");

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

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

    //Disable Vertex Array
    GLES20.glDisableVertexAttribArray(mPositionHandle);
}

public static int loadTexture(final Context context, final int resourceId)
{
    final int[] textureHandle = new int[1];

    GLES20.glGenTextures(1, textureHandle, 0);

    if (textureHandle[0] != 0)
        {
            final BitmapFactory.Options options = new BitmapFactory.Options();
            options.inScaled = false;   // No pre-scaling

            // Read in the resource
            final Bitmap bitmap = BitmapFactory
                .decodeResource(context.getResources(), resourceId, options);

            // Bind to the texture in OpenGL
            GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle[0]);

            // 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);

            // 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.");
        }

    return textureHandle[0];
    }
}

例如,http://changesuk.net/wp-content/uploads/2009/05/4-quadrants.jpg处的图像被更改,以便数字都正确定​​向,但是处于模式中

4 | 3
-----
2 | 1

如果我改变添加正方形坐标的顺序(例如从“左下角”开始),图像的象限会移动,但也围绕各自的中心旋转(在上图的情况下) ,数字都将在他们的身边或头上)。我查看了每一行代码,无法理解这里发生了什么。有没有人以前遇到过这种行为,或者有人可以解释可能导致这种行为的原因?

2 个答案:

答案 0 :(得分:2)

我已经看了一段时间了,但我从来没有机会回答问题。我知道这个问题已经有5个月了,但无论如何我都会尝试回答它。

基本上,你正在做的是你没有正确地将纹理的角与顶点的角对齐。你得到了

4 | 3
-----
2 | 1

因为纹理的中心位于正方形的右下方,纹理会重复出现,因此4和2位于右侧,3位于顶部。

解决此问题的方法是在顶点着色器中添加新的vec4

    uniform vec4 pos;

并用x,y,宽度和高度填充

    int pos = GLES20.glGetUniformLocation(mProgram, "pos");
    GLES20.glUniform4f(pos, .5f, .5f, 1, 1); // x, y, width, height

现在改变这个

    v_TexCoordinate = a_TexCoordinate;

到这个

    v_TexCoordinate = vec2((a_TexCoordinate.x - pos.x)/pos.z, (a_TexCoordinate.y - pos.y)/pos.w);

这应该可以解决问题。

答案 1 :(得分:0)

我尝试过P0rter解决方案,但它对我没用。

要明确的是,问题不在于象限的排列,而是在中心开始。其他方面只是默认重复。 如果你打开它,当加载纹理时,它将只绘制右下象限。

    GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
        GLES20.GL_TEXTURE_WRAP_S,
        GLES20.GL_CLAMP_TO_EDGE
    );
    GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
        GLES20.GL_TEXTURE_WRAP_T,
        GLES20.GL_CLAMP_TO_EDGE
    );

我有另一个解决方案。我不知道我在做什么,所以我只是粘贴,对我有用。

public class Sprite extends GLObject {

    final static float COORDS_SQUARE[] = {
        -0.5f, 0.5f, 0.0f,
        -0.5f, -0.5f, 0.0f,
        0.5f, -0.5f, 0.0f,
        0.5f, 0.5f, 0.0f
    };


    /**
     * Number of coordinates per point in this array.
     */
    protected static final int COORDINATES_PER_VERTEX = 3;

    final static float TEXTURE_COORDS[] = {
        0.0f, 1.0f,
        0.0f, 0.0f,
        1.0f, 0.0f,
        1.0f, 1.0f
    };

    /**
     * Number of coordinates per point in this array.
     */
    protected static final int COORDINATES_PER_TEXTURE_VERTEX = 2;

    /**
     * Order to draw COORDINATES_VERTICES
     */
    protected static final short DRAW_ORDER[] = {
        0, 1, 2, 0, 3, 2
    };

    private static final String VERTEX_SHADER_CODE =
        "attribute vec2 aTexCoordinate;" +
            "varying vec2 vTexCoordinate;" +
            "uniform mat4 uMVPMatrix;" +
            "attribute vec4 vPosition;" +
            "void main() {" +
            "  gl_Position = uMVPMatrix * vPosition;" +
            "  vTexCoordinate = aTexCoordinate;" +
            "}";

    private static final String FRAGMENT_SHADER_CODE =
        "precision mediump float;" +
            "uniform sampler2D uTexture;" +
            "varying vec2 vTexCoordinate;" +
            "void main() {" +
            "  gl_FragColor = texture2D(uTexture, vTexCoordinate);" +
            "}";

    private final int mProgram;

    private final FloatBuffer vertexBuffer;
    private final ShortBuffer drawListBuffer;
    private final FloatBuffer mTextureCoordinates;
    private int mTextureDataHandle;

    public Sprite(final Context context, @DrawableRes int resID) {

        //Initialize Vertex Byte Buffer for Shape Coordinates / # of coordinate values * 4 bytes per float
        ByteBuffer vb = ByteBuffer.allocateDirect(COORDS_SQUARE.length * 4);
        //Use the Device's Native Byte Order
        vb.order(ByteOrder.nativeOrder());
        //Create a floating point buffer from the ByteBuffer
        vertexBuffer = vb.asFloatBuffer();
        //Add the coordinates to the FloatBuffer
        vertexBuffer.put(COORDS_SQUARE);
        //Set the Buffer to Read the first coordinate
        vertexBuffer.position(0);

        ByteBuffer tcb = ByteBuffer.allocateDirect(TEXTURE_COORDS.length * 4);
        tcb.order(ByteOrder.nativeOrder());
        mTextureCoordinates = tcb.asFloatBuffer();
        mTextureCoordinates.put(TEXTURE_COORDS);
        mTextureCoordinates.position(0);

        //Initialize byte buffer for the draw list
        ByteBuffer dlb = ByteBuffer.allocateDirect(DRAW_ORDER.length * 2);
        dlb.order(ByteOrder.nativeOrder());
        drawListBuffer = dlb.asShortBuffer();
        drawListBuffer.put(DRAW_ORDER);
        drawListBuffer.position(0);

        int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, VERTEX_SHADER_CODE);
        int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, FRAGMENT_SHADER_CODE);

        mProgram = GLES20.glCreateProgram();
        GLES20.glAttachShader(mProgram, vertexShader);
        GLES20.glAttachShader(mProgram, fragmentShader);

        //Texture Code
        GLES20.glBindAttribLocation(mProgram, 0, "aTexCoordinate");

        GLES20.glLinkProgram(mProgram);

        //Load the texture
        mTextureDataHandle = loadTexture(context, resID);
    }

    public void draw(float[] mvpMatrix) {
        //Add program to OpenGL ES Environment
        GLES20.glUseProgram(mProgram);

        //Get handle to vertex shader's vPosition member
        int positionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
        int textureUniformHandle = GLES20.glGetAttribLocation(mProgram, "uTexture");
        int textureCoordinateHandle = GLES20.glGetAttribLocation(mProgram, "aTexCoordinate");
        int MVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");

        //Enable a handle to the triangle COORDINATES_VERTICES
        GLES20.glEnableVertexAttribArray(positionHandle);

        //Prepare the triangle coordinate data
        vertexBuffer.position(0);
        GLES20.glVertexAttribPointer(
            positionHandle, COORDINATES_PER_VERTEX,
            GLES20.GL_FLOAT, false,
            0, vertexBuffer
        );

        //Set the active texture unit to texture unit 0.
        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);

        //Bind the texture to this unit.
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureDataHandle);

        //Tell the texture uniform sampler to use this texture in the shader by binding to texture unit 0.
        GLES20.glUniform1i(textureUniformHandle, 0);

        //Pass in the texture coordinate information
        mTextureCoordinates.position(0);
        GLES20.glVertexAttribPointer(
            textureCoordinateHandle, COORDINATES_PER_TEXTURE_VERTEX,
            GLES20.GL_FLOAT, false,
            0, mTextureCoordinates
        );
        GLES20.glEnableVertexAttribArray(textureCoordinateHandle);

        //Apply the projection and view transformation
        GLES20.glUniformMatrix4fv(MVPMatrixHandle, 1, false, mvpMatrix, 0);

        //Draw the triangle
        drawListBuffer.position(0);
        GLES20.glDrawElements(GLES20.GL_TRIANGLES, DRAW_ORDER.length, GLES20.GL_UNSIGNED_SHORT,
            drawListBuffer);

        //Disable Vertex Array
        GLES20.glDisableVertexAttribArray(positionHandle);
    }
}

在我的GLObject类中有一些用于加载纹理等的共享方法,没什么重要的。