如何在OpenGL 2.0,Android中指示3D可旋转立方体的每个面

时间:2016-06-14 20:14:16

标签: android opengl-es 3d

我使用OpenGL2.0创建了一个3D可旋转立方体。现在我想让它显示每个脸部的名称,如果我们触摸特定的脸部。例如,我们旋转立方体并将底面显示为正面。然后我们触摸或点击这个脸,会出现一个提示,告诉我们它是“底面”。

任何人都知道如何做到这一点?由于我只需要这个简单的功能,如果解决方案很简单,那就更好了。非常感谢!

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

}

1 个答案:

答案 0 :(得分: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时进入Toast:

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