【问题标题】:Android OpenGL ES 2 Texture Quadrants RotatedAndroid OpenGL ES 2 纹理象限旋转
【发布时间】:2013-08-19 13:43:30
【问题描述】:

我正在尝试将一个简单的 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

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

【问题讨论】:

  • 这里有什么问题?
  • 我已经更新了帖子。

标签: android opengl-es-2.0


【解决方案1】:

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

这应该可以解决问题。

【讨论】:

    【解决方案2】:

    我尝试了 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 类中有一些用于加载纹理等的共享方法,这并不重要。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-10-23
      • 1970-01-01
      • 2012-04-08
      • 1970-01-01
      • 2012-02-16
      • 1970-01-01
      • 2011-04-27
      • 2011-11-12
      相关资源
      最近更新 更多