Android OpenGL ES 2 纹理象限旋转

标签 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

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

最佳答案

我已经观看 SO 一段时间了,但从未有机会回答问题。我知道这个问题已经有 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);

这应该可以解决问题。

关于Android OpenGL ES 2 纹理象限旋转,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18285373/

相关文章:

android - 设置TableRow的高度

ios - 顶点和法线的单独数组

android - OpenGL ES 2.0 通过共享 C++ 代码在 ios 和 android 上进行抗锯齿或平滑处理

android - 无法从 android sdk 构建 OpenglES2.0 示例

android - 如何从 Android MVVM 架构中的 LiveData 列表中获取第一个或(任何)元素?

java - 与适配器持有者混淆,如何防止 getView 方法修改不适当的 ListView 行?

android - 带有 overridePendingTransition 的 Activity 的滑动动画有奇怪的效果

android - android sha224和python sha224的区别

安卓弹出窗口

opengl - OpenGL/OpenGL ES 中的绑定(bind)点是什么?