2013-08-17 218 views
2

我正試圖將一個簡單的2D紋理加載到一個正方形上並將其顯示在我的GLSurfaceView背面。我已經複製並修復了Draw a 2D Image using OpenGL ES 2.0中的代碼,但是當我安裝並運行應用程序時,圖像被分成4個象限,然後重新排列。Android OpenGL ES 2紋理象限旋轉

這裏是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 

如果我改變添加正方形座標的順序(例如從「左下角」開始),圖像的象限會移動,但也會圍繞它們各自的中心旋轉(在上圖中,數字將全部是在他們的側面或頭上)。我瀏覽過每一行代碼,無法理解這裏發生了什麼。有沒有人曾經遇到過這種行爲,或者有人可能會解釋導致它的原因?

+0

這裏有什麼問題? – zyzof

+0

我已更新帖子。 –

回答

2

我一直在查看SO一段時間,但從來沒有機會回答一個問題。我知道這個問題有5個月了,但我會盡力回答。

基本上,你在做什麼是你沒有正確對齊你的紋理的角落與你的頂點的角落。你得到

4 | 3 
----- 
2 | 1 

,因爲你的紋理中心是您正方形的右下角,並且紋理被重複,因此4和2的右側和3是在上面。

解決這個問題的辦法是在你的頂點着色器

uniform vec4 pos; 

添加新vec4與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); 

這應該做的伎倆。

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類是用於裝載質地等,沒有什麼重要的一些共享的方法。