2016-06-14 99 views
0

我使用OpenGL2.0創建了3D旋轉立方體。現在,如果我們觸摸特定的臉部,現在我想讓它顯示每張臉的名稱。例如,我們旋轉立方體並將底面顯示爲正面。然後我們觸摸或點擊這張臉,會出現一個提示,並告訴我們這是「底部」。如何在OpenGL 2.0中表示3D旋轉立方體的每個面,Android

任何人都有如何做到這一點的想法?由於我只需要這個簡單的功能,如果解決方案很簡單,那會更好。非常感謝!

public class MyCube { 
private FloatBuffer vertexBuffer; 
private ShortBuffer drawListBuffer; 
private ShortBuffer[] ArrayDrawListBuffer; 
private FloatBuffer colorBuffer; 

private int mProgram; 

//For Projection and Camera Transformations 
private final String vertexShaderCode = 
     // This matrix member variable provides a hook to manipulate 
     // the coordinates of the objects that use this vertex shader 
     "uniform mat4 uMVPMatrix;" + 
       "attribute vec4 vPosition;" + 
       //"attribute vec4 vColor;" + 
       //"varying vec4 vColorVarying;" + 
       "void main() {" + 
       // the matrix must be included as a modifier of gl_Position 
       // Note that the uMVPMatrix factor *must be first* in order 
       // for the matrix multiplication product to be correct. 
       " gl_Position = uMVPMatrix * vPosition;" + 
       //"vColorVarying = vColor;"+ 
       "}"; 

// Use to access and set the view transformation 
private int mMVPMatrixHandle; 

private final String fragmentShaderCode = 
     "precision mediump float;" + 
       "uniform vec4 vColor;" + 
       //"varying vec4 vColorVarying;"+ 
       "void main() {" + 
       //" gl_FragColor = vColorVarying;" + 
       " gl_FragColor = vColor;" + 
       "}"; 

// number of coordinates per vertex in this array 
static final int COORDS_PER_VERTEX = 3; 
float cubeCoords[] = { 
     -0.5f, 0.5f, 0.5f, // front top left 0 
     -0.5f, -0.5f, 0.5f, // front bottom left 1 
     0.5f, -0.5f, 0.5f, // front bottom right 2 
     0.5f, 0.5f, 0.5f, // front top right 3 
     -0.5f, 0.5f, -0.5f, // back top left 4 
     0.5f, 0.5f, -0.5f, // back top right 5 
     -0.5f, -0.5f, -0.5f, // back bottom left 6 
     0.5f, -0.5f, -0.5f, // back bottom right 7 
     }; 


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

private short drawOrder[][] = { 
           {0, 1, 2, 0, 2, 3},//front 
           {0, 4, 5, 0, 5, 3}, //Top 
           {0, 1, 6, 0, 6, 4}, //left 
           {3, 2, 7, 3, 7 ,5}, //right 
           {1, 2, 7, 1, 7, 6}, //bottom 
           {4, 6, 7, 4, 7, 5} //back 
           }; //(order to draw vertices) 


final float cubeColor3[][] = 
     { 
       // Front face (red) 
       {1.0f, 0.0f, 0.0f, 1.0f, 
       1.0f, 0.0f, 0.0f, 1.0f, 
       1.0f, 0.0f, 0.0f, 1.0f, 
       1.0f, 0.0f, 0.0f, 1.0f, 
       1.0f, 0.0f, 0.0f, 1.0f, 
       1.0f, 0.0f, 0.0f, 1.0f}, 

       // Top face (green) 
       {0.0f, 1.0f, 0.0f, 1.0f, 
       0.0f, 1.0f, 0.0f, 1.0f, 
       0.0f, 1.0f, 0.0f, 1.0f, 
       0.0f, 1.0f, 0.0f, 1.0f, 
       0.0f, 1.0f, 0.0f, 1.0f, 
       0.0f, 1.0f, 0.0f, 1.0f}, 

       // Left face (blue) 
       {0.0f, 0.0f, 1.0f, 1.0f, 
       0.0f, 0.0f, 1.0f, 1.0f, 
       0.0f, 0.0f, 1.0f, 1.0f, 
       0.0f, 0.0f, 1.0f, 1.0f, 
       0.0f, 0.0f, 1.0f, 1.0f, 
       0.0f, 0.0f, 1.0f, 1.0f}, 

       // Right face (yellow) 
       {1.0f, 1.0f, 0.0f, 1.0f, 
       1.0f, 1.0f, 0.0f, 1.0f, 
       1.0f, 1.0f, 0.0f, 1.0f, 
       1.0f, 1.0f, 0.0f, 1.0f, 
       1.0f, 1.0f, 0.0f, 1.0f, 
       1.0f, 1.0f, 0.0f, 1.0f}, 

       // Bottom face (cyan) 
       {0.0f, 1.0f, 1.0f, 1.0f, 
       0.0f, 1.0f, 1.0f, 1.0f, 
       0.0f, 1.0f, 1.0f, 1.0f, 
       0.0f, 1.0f, 1.0f, 1.0f, 
       0.0f, 1.0f, 1.0f, 1.0f, 
       0.0f, 1.0f, 1.0f, 1.0f}, 

       // Back face (magenta) 
       {1.0f, 0.0f, 1.0f, 1.0f, 
       1.0f, 0.0f, 1.0f, 1.0f, 
       1.0f, 0.0f, 1.0f, 1.0f, 
       1.0f, 0.0f, 1.0f, 1.0f, 
       1.0f, 0.0f, 1.0f, 1.0f, 
       1.0f, 0.0f, 1.0f, 1.0f} 
     }; 



public MyCube() { 
    // initialize vertex byte buffer for shape coordinates 
    ByteBuffer bb = ByteBuffer.allocateDirect(
      // (# of coordinate values * 4 bytes per float) 
      cubeCoords.length * 4); 
    bb.order(ByteOrder.nativeOrder()); 
    vertexBuffer = bb.asFloatBuffer(); 
    vertexBuffer.put(cubeCoords); 
    vertexBuffer.position(0); 


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

    // create empty OpenGL ES Program 
    mProgram = GLES20.glCreateProgram(); 

    // add the vertex shader to program 
    GLES20.glAttachShader(mProgram, vertexShader); 

    // add the fragment shader to program 
    GLES20.glAttachShader(mProgram, fragmentShader); 

    // creates OpenGL ES program executables 
    GLES20.glLinkProgram(mProgram); 
} 

private int mPositionHandle; 
private int mColorHandle; 

private final int vertexCount = cubeCoords.length/COORDS_PER_VERTEX; 
private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex 

public void draw(float[] mvpMatrix) { // pass in the calculated transformation matrix 


    // Draw the cube 
    for (int face = 0; face < 6; face++) { 

     // Add program to OpenGL ES environment 
     GLES20.glUseProgram(mProgram); 

     // get handle to vertex shader's vPosition member 
     mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition"); 
     // get handle to fragment shader's vColor member 

     //mColorHandle = GLES20.glGetAttribLocation(mProgram, "vColor"); 
     mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor"); 

     // Enable a handle to the cube vertices 
     GLES20.glEnableVertexAttribArray(mPositionHandle); 
     // Prepare the cube coordinate data 
     GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX, 
       GLES20.GL_FLOAT, false, 
       vertexStride, vertexBuffer); 
     // initialize byte buffer for the draw list 
     ByteBuffer dlb = ByteBuffer.allocateDirect(
       // (# of coordinate values * 2 bytes per short) 
       drawOrder[face].length * 2); 
     dlb.order(ByteOrder.nativeOrder()); 
     drawListBuffer = dlb.asShortBuffer(); 
     drawListBuffer.put(drawOrder[face]); 
     drawListBuffer.position(0); 

     GLES20.glUniform4fv(mColorHandle, 1, cubeColor3[face], 0); 

     // get handle to shape's transformation matrix 
     mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix"); 

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

     GLES20.glDrawElements(GLES20.GL_TRIANGLES, drawOrder[face].length, GLES20.GL_UNSIGNED_SHORT, drawListBuffer); 
    } 


    // Disable vertex array 
    GLES20.glDisableVertexAttribArray(mMVPMatrixHandle); 
    } 
} 

渲染:

public class MyRenderer implements GLSurfaceView.Renderer{ 

private MyCube mCube; 

// mMVPMatrix is an abbreviation for "Model View Projection Matrix" 
private final float[] mMVPMatrix = new float[16]; 
private final float[] mProjectionMatrix = new float[16]; 
private final float[] mViewMatrix = new float[16]; 
//create another transformation matrix (a rotation matrix) 
private float[] mRotationMatrix = new float[16]; 

/** Store the accumulated rotation. */ 
private final float[] mAccumulatedRotation = new float[16]; 
/** Store the current rotation. */ 
private final float[] mCurrentRotation = new float[16]; 
/** Create a temporary matrix. */ 
private final float[] mTemporaryMatrix = new float[16]; 



public void onSurfaceCreated(GL10 unused, EGLConfig config) { 
    // Set the background frame color 
    GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); 
    GLES20.glEnable(GLES20.GL_DEPTH_TEST); 

    // initialize a square 
    mCube = new MyCube(); 

    // Initialize the accumulated rotation matrix 
    Matrix.setIdentityM(mAccumulatedRotation, 0); 
} 

public void onDrawFrame(GL10 unused) { 
    // Redraw background color 
    GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT); 

    float[] scratch = new float[16]; 
    // Create a rotation transformation for the square 
    Matrix.setIdentityM(mRotationMatrix, 0); 
    Matrix.setIdentityM(mCurrentRotation, 0); 
    Matrix.rotateM(mCurrentRotation, 0, mDeltaX, 0.0f, 1.0f, 0.0f); 
    Matrix.rotateM(mCurrentRotation, 0, mDeltaY, 1.0f, 0.0f, 0.0f); 
    mDeltaX = 0.0f; 
    mDeltaY = 0.0f; 

    // Multiply the current rotation by the accumulated rotation, and then set the accumulated 
    // rotation to the result. 
    Matrix.multiplyMM(mTemporaryMatrix, 0, mCurrentRotation, 0, mAccumulatedRotation, 0); 
    System.arraycopy(mTemporaryMatrix, 0, mAccumulatedRotation, 0, 16); 

    // Rotate the cube taking the overall rotation into account. 
    Matrix.multiplyMM(mTemporaryMatrix, 0, mRotationMatrix, 0, mAccumulatedRotation, 0); 
    System.arraycopy(mTemporaryMatrix, 0, mRotationMatrix, 0, 16); 



    // Set the camera position (View matrix) 
    Matrix.setLookAtM(mViewMatrix, 0, -2, 2, 5, 0f, 0f, 0f, 0f, 1.0f, 0.0f); 

    //Calculate the projection and view transformation 
    Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0); 

    // Combine the rotation matrix with the projection and camera view 
    // Note that the mMVPMatrix factor *must be first* in order 
    // for the matrix multiplication product to be correct. 
    Matrix.multiplyMM(scratch, 0, mMVPMatrix, 0, mRotationMatrix, 0); 

    // Draw shape 
    mCube.draw(scratch); 
} 


@Override 
public void onSurfaceChanged(GL10 unused, int width, int height) { 
    GLES20.glViewport(0, 0, width, height); 

    float ratio = (float) width/height; 

    // this projection matrix is applied to object coordinates 
    // in the onDrawFrame() method 
    Matrix.frustumM(mProjectionMatrix, 0, -ratio, ratio, -1, 1, 3, 7); 
} 

public static int loadShader(int type, String shaderCode){ 

    // create a vertex shader type (GLES20.GL_VERTEX_SHADER) 
    // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER) 
    int shader = GLES20.glCreateShader(type); 

    // add the source code to the shader and compile it 
    GLES20.glShaderSource(shader, shaderCode); 
    GLES20.glCompileShader(shader); 

    return shader; 
} 

public volatile float mDeltaX; 
public volatile float mDeltaY; 
} 
} 

MyActivity.java

public class MyActivity extends Activity { 
private MyGLSurfaceView mGLView; 

public void showToast(final String toast) { 
    runOnUiThread(new Runnable() { 
     public void run() { 
      Toast.makeText(MyActivity.this, toast, Toast.LENGTH_SHORT).show(); 
     } 
    }); 
} 



@Override 
public void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 

    // Create a GLSurfaceView instance and set it 
    // as the ContentView for this Activity. 
    mGLView = new MyGLSurfaceView(this); 
    setContentView(mGLView); 

    //LinearLayout ll = new LinearLayout(this); 
    Button b = new Button(this); 
    b.setText("hello world"); 
    //ll.addView(b); 
    //ll.setGravity(Gravity.CENTER_HORIZONTAL | Gravity.CENTER_VERTICAL); 
    this.addContentView(b, 
      new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); 
} 
} 

MyGLSurfaceView.java

public class MyGLSurfaceView extends GLSurfaceView { 

private final MyRenderer mRenderer; 

private final float TOUCH_SCALE_FACTOR = 180.0f/320; 
private float mPreviousX; 
private float mPreviousY; 


float touchedX = 0; 
float touchedY = 0; 


@Override 
public boolean onTouchEvent(MotionEvent event) { 
    // MotionEvent reports input details from the touch screen 
    // and other input controls. In this case, you are only 
    // interested in events where the touch position changed. 

    if (event != null) { 
     float x = event.getX(); 
     float y = event.getY(); 

     if (event.getAction() == MotionEvent.ACTION_MOVE) { 

      if (mRenderer != null) { 
       float deltaX = (x - mPreviousX)/2f; 
       float deltaY = (y - mPreviousY)/2f; 

       mRenderer.mDeltaX += deltaX; 
       mRenderer.mDeltaY += deltaY; 
       mRenderer.mTotalDeltaX += mRenderer.mDeltaX; 
       mRenderer.mTotalDeltaY += mRenderer.mDeltaY; 
       mRenderer.mTotalDeltaX = mRenderer.mTotalDeltaX % 360; 
       mRenderer.mTotalDeltaY = mRenderer.mTotalDeltaY % 360; 

      } 
      requestRender(); 
     } 
     if (event.getAction() == MotionEvent.ACTION_DOWN) { 
      if (event.getX() < 950f && event.getX() > 150f && event.getX() < 1300f && event.getX() > 400f) { 
       Log.d("DEBUG", Float.toString(mRenderer.mTotalDeltaX) + " " + Float.toString(mRenderer.mTotalDeltaY)); 
       Log.d("DEBUG", Float.toString(event.getX()) + " " + Float.toString(event.getY())); 
//***Here is where I want to add toast*** Thank you so much!/// 

      } else { 

      } 
     } 


     mPreviousX = x; 
     mPreviousY = y; 

     return true; 
    } else { 
     return super.onTouchEvent(event); 
    } 
} 






public MyGLSurfaceView(Context context){ 
    super(context); 

    // Create an OpenGL ES 2.0 context 
    setEGLContextClientVersion(2); 

    mRenderer = new MyRenderer(); 

    // Set the Renderer for drawing on the GLSurfaceView 
    setRenderer(mRenderer); 

    // Render the view only when there is a change in the drawing data. 
    // To allow the Square to rotate automatically, this line is commented out: 
    //setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); 
} 

}

+0

請提供您如何實現的你旋轉一些代碼立方體。我可能有一個簡單的答案,但這取決於你如何選擇做旋轉。 – brohan322

+0

嗨,我在那裏添加了我的代碼,請看看。非常感謝。 – user6420993

回答

0

所以它出現在你的代碼,你有,

mDeltaX; 
mDeltaY; 

充當臨時變量用於旋轉立方體(您將它們重新設置爲0.0每一幀)。 除了這些變量,創建兩個變量,

mTotalDeltaX; 
mTotalDeltaY; 

和,而不是將其復位到0.0,讓他們聚集在X和改變整個變化Y.

利用這些變量,你現在知道相對於X軸和Y軸完全旋轉了多少。現在,您只需要使用幾個基於這兩個變量的if語句來確定當前正在查看哪個面。

例如,如果

mTotalDeltaX = 0.0; 
mTotalDeltaY = 0.0; 

你知道你正在尋找在立方體的第一面,因爲它沒有在所有的旋轉。

如果您無法確定mTotalDeltaX和mTotalDeltaY會爲您提供正確的面值,我建議您使用logcat來幫助您,因爲您會找出mTotalDeltaX和mTotalDeltaY顯示哪些面的值。放入onDrawFrame()這一行:

Log.d("DEBUG", Float.toString(mTotalDeltaX) + " " + Float.toString(mTotalDeltaY)); 

當你旋轉你的魔方,看看你的logcat的,看看有什麼mTotalDeltaX和mTotalDeltaY是。記下這些數字,並據此創建一系列if語句。

重要說明:使用模數運算符可防止mTotalDeltaX和mTotalDeltaY爲負數或超過360度。這將允許使用該範圍內的if語句。例如,你已經更新完畢後mTotalDeltaX和mTotalDeltaY,

mTotalDeltaX += mDeltaX; 
mTotalDeltaY += mDeltaY; 

應用模運算符,

mTotalDeltaX = mTotalDeltaX % 360; 
mTotalDeltaY = mTotalDeltaY % 360; 

編輯:在回答下面的新問題,敬酒檢測ACTION_DOWN時:

在MyGLSurfaceView.java的底部,您將上下文作爲參數傳遞。保存此背景下,到另一個上下文變量,

Context myContext; 

public MyGLSurfaceView(Context context){ 
super(context); 
myContext = context; 

// Create an OpenGL ES 2.0 context 
setEGLContextClientVersion(2); 

mRenderer = new MyRenderer(); 

// Set the Renderer for drawing on the GLSurfaceView 
setRenderer(mRenderer); 

// Render the view only when there is a change in the drawing data. 
// To allow the Square to rotate automatically, this line is commented out: 
//setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); 
} 

現在,在ACTION_DOWN,在那裏你有評論說,你希望你的麪包,

Toast.makeText(myContext, "text", Toast.LENGTH_SHORT).show(); 
+0

好主意!非常感謝!另外,如果我想在'MotionEvent.ACTION_DOWN'時使用'Toast.makeText()'來顯示哪個面在前面。我應該在哪裏添加吐司?因爲我有MyActivity.java,MyRenderer.java,MyGLSurfaceView.java。再次感謝。 – user6420993

+0

請參閱https://developer.android.com/reference/android/widget/Toast.html。上下文是必需的,我假設你可以在MyActivity.java中做到這一點,如果這是在場景渲染過程中運行的活動。您只需編寫Toast.makeText(MyActivity.this,「text」,Toast.LENGTH_SHORT).show()。 – brohan322

+0

是的,這就是我所做的。但是,可以檢測運動的onTouchEvent位於MyGLSurfaceView.java下。敬酒可以現在顯示,但'ACTION_DOWN'時不顯示。這可能被認爲是多線程,這就是我的困惑所在。 – user6420993