2

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

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

4

2 回答 2

2

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

基本上,您所做的是您没有正确地将纹理的角与顶点的角对齐。你明白了

4 | 3
-----
2 | 1

因为纹理的中心是正方形的右下角,并且纹理会重复,因此 4 和 2 在右侧,而 3 在顶部。

解决这个问题的方法是在你的顶点着色器中添加一个新的 vec4

    uniform vec4 pos;

并用 x, y, width 和 height 填充它

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

这应该够了吧。

于 2014-01-24T21:16:43.343 回答
1

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

于 2015-10-19T20:10:30.460 回答