2016-06-14 83 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