gpt4 book ai didi

java - OpenGL ES 2.0 Android - 立方体旋转错误

转载 作者:行者123 更新时间:2023-11-30 05:54:51 24 4
gpt4 key购买 nike

我使用用于 Android 应用程序的 OpenGL ES 2.0 实现了彩色 3D 立方体的可视化。我的目标:立方体应该对滑动事件(左、右、上、下)使用react,然后立方体应该在相应的方向旋转

旋转:

  • 如果 (current_angle % 90 == 0) -> 则停止,以便您从一张脸滑动到另一张脸
  • 一次仅旋转一圈(且仅绕 x 轴或 y 轴)
  • 也应该分步完成(例如:5 度),因此不会立即完成 -> 用户可以看到

我的代码:

public class Cube20 {

private volatile int angleX;
private volatile int angleY;

private volatile Cube.RotateDirection rotateDirection;

public Cube.RotateDirection getRotateDirection() {
return rotateDirection;
}


public void setRotation(Cube.RotateDirection rotateDirection) {
this.rotateDirection = Cube.RotateDirection.getDirectionForID(rotateDirection.getId());
}

public int getAngleX() {
return angleX;
}

public void setAngleX(int angleX) {
this.angleX = angleX;
}

public int getAngleY() {
return angleY;
}

public void setAngleY(int angleY) {
this.angleY = angleY;
}

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;" +
"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;" +
"}";

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

private final FloatBuffer vertexBuffer;
//private final ShortBuffer drawListBuffer;
private final int mProgram;
private final ShortBuffer indexBuffer;
private int mPositionHandle;
private int mColorHandle;
private int mMVPMatrixHandle;

// number of coordinates per vertex in this array
static final int COORDS_PER_VERTEX = 3;

private float[] vertices = { // Vertices of the 6 faces
// FRONT
-1.0f, -1.0f, 1.0f, // 0. left-bottom-front (0)
1.0f, -1.0f, 1.0f, // 1. right-bottom-front
-1.0f, 1.0f, 1.0f, // 2. left-top-front
1.0f, 1.0f, 1.0f, // 3. right-top-front
// BACK
1.0f, -1.0f, -1.0f, // 6. right-bottom-back (4)
-1.0f, -1.0f, -1.0f, // 4. left-bottom-back
1.0f, 1.0f, -1.0f, // 7. right-top-back
-1.0f, 1.0f, -1.0f, // 5. left-top-back
// LEFT
-1.0f, -1.0f, -1.0f, // 4. left-bottom-back (8)
-1.0f, -1.0f, 1.0f, // 0. left-bottom-front
-1.0f, 1.0f, -1.0f, // 5. left-top-back
-1.0f, 1.0f, 1.0f, // 2. left-top-front
// RIGHT
1.0f, -1.0f, 1.0f, // 1. right-bottom-front (12)
1.0f, -1.0f, -1.0f, // 6. right-bottom-back
1.0f, 1.0f, 1.0f, // 3. right-top-front
1.0f, 1.0f, -1.0f, // 7. right-top-back
// TOP
-1.0f, 1.0f, 1.0f, // 2. left-top-front
1.0f, 1.0f, 1.0f, // 3. right-top-front
-1.0f, 1.0f, -1.0f, // 5. left-top-back
1.0f, 1.0f, -1.0f, // 7. right-top-back
// BOTTOM
-1.0f, -1.0f, -1.0f, // 4. left-bottom-back
1.0f, -1.0f, -1.0f, // 6. right-bottom-back
-1.0f, -1.0f, 1.0f, // 0. left-bottom-front
1.0f, -1.0f, 1.0f // 1. right-bottom-front
};

private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex

private float[][] colors = { // Colors of the 6 faces
{1.0f, 0.5f, 0.0f, 1.0f}, // 0. orange
{1.0f, 0.0f, 1.0f, 1.0f}, // 1. violet
{0.0f, 1.0f, 0.0f, 1.0f}, // 2. green
{0.0f, 0.0f, 1.0f, 1.0f}, // 3. blue
{1.0f, 0.0f, 0.0f, 1.0f}, // 4. red
{1.0f, 1.0f, 0.0f, 1.0f} // 5. yellow
};

short[] indices = {
0, 1, 2, 2, 1, 3, // FRONT
4, 5, 6, 6, 5, 7, // BACK
8, 9, 10, 10, 9, 11, // LEFT
12, 13, 14, 14, 13, 15, // RIGHT
16, 17, 18, 18, 17, 19, // TOP
20, 21, 22, 22, 21, 23, // BOTTOM

};
private int numFaces = 6;

/**
* Sets up the drawing object data for use in an OpenGL ES context.
*/
public Cube20() {
// initialize vertex byte buffer for shape coordinates
ByteBuffer bb = ByteBuffer.allocateDirect(
// (# of coordinate values * 4 bytes per float)
vertices.length * 4);
bb.order(ByteOrder.nativeOrder());
vertexBuffer = bb.asFloatBuffer();
vertexBuffer.put(vertices);
vertexBuffer.position(0);

// initialize byte buffer for the draw list
indexBuffer = ByteBuffer.allocateDirect(indices.length * 2).order(ByteOrder.nativeOrder()).asShortBuffer();
indexBuffer.put(indices).position(0);

// prepare shaders and OpenGL program
int vertexShader = RenderUtils.loadShader(
GLES20.GL_VERTEX_SHADER,
vertexShaderCode);
int fragmentShader = RenderUtils.loadShader(
GLES20.GL_FRAGMENT_SHADER,
fragmentShaderCode);

mProgram = GLES20.glCreateProgram(); // create empty OpenGL Program
GLES20.glAttachShader(mProgram, vertexShader); // add the vertex shader to program
GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program
GLES20.glLinkProgram(mProgram); // create OpenGL program executables

this.rotateDirection = Cube.RotateDirection.NONE;
}

/**
* Encapsulates the OpenGL ES instructions for drawing this shape.
*
* @param mvpMatrix - The Model View Project matrix in which to draw
* this shape.
*/
public void draw(float[] mvpMatrix) {
// Add program to OpenGL environment
GLES20.glUseProgram(mProgram);

GLES20.glFrontFace(GLES20.GL_CCW);
GLES20.glEnable(GLES20.GL_CULL_FACE);
GLES20.glCullFace(GLES20.GL_BACK);

// scale
float scale_matrix[] = new float[16];
Matrix.setIdentityM(scale_matrix, 0);
Matrix.scaleM(scale_matrix, 0, 0.5f, 0.5f, 1);
Matrix.multiplyMM(mvpMatrix, 0, scale_matrix, 0, mvpMatrix, 0);

// get handle to vertex shader's vPosition member
mPositionHandle = GLES20.glGetAttribLocation(mProgram, "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 vColor member
mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");

// Set color for drawing the triangle
//GLES20.glUniform4fv(mColorHandle, 1, color, 0);

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

// Apply the projection and view transformation
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);
RenderUtils.checkGlError("glUniformMatrix4fv");

// Render all the faces
for (int face = 0; face < numFaces; face++) {
// Set the color for each of the faces
GLES20.glUniform4fv(mColorHandle, 1, colors[face], 0);
indexBuffer.position(face * 6);
GLES20.glDrawElements(GLES20.GL_TRIANGLES, 6, GLES20.GL_UNSIGNED_SHORT, indexBuffer);
}

// Disable vertex array
GLES20.glDisableVertexAttribArray(mPositionHandle);
GLES20.glDisable(GLES20.GL_CULL_FACE);
}
}

public class MyGLSurfaceView extends GLSurfaceView {
private volatile MyGLRenderer myGLRenderer;

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

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

myGLRenderer = new MyGLRenderer(context);
setRenderer(myGLRenderer); // Use a custom renderer
setOnTouchListener(new OnSwipeListener(context));

setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
}

class OnSwipeListener implements View.OnTouchListener {

private final GestureDetector gestureDetector;

public OnSwipeListener(Context context) {
this.gestureDetector = new GestureDetector(context, new OnFlingListener());
}

@Override
public boolean onTouch(View v, MotionEvent event) {
return this.gestureDetector.onTouchEvent(event);
}

private class OnFlingListener extends GestureDetector.SimpleOnGestureListener {
private final Object LOCK = new Object();

@Override
public boolean onDown(MotionEvent e) {
return true;
}

/**
* @return true if the event is consumed, else false
*/
@Override
public boolean onFling(MotionEvent down, MotionEvent up, float velocityX, float velocityY) {
//super.onFling(down, up, velocityX, velocityY);

float distanceX = up.getX() - down.getX();
float distanceY = up.getY() - down.getY();

final Cube20 cube = myGLRenderer.getCube();
if (!cube.getRotateDirection().equals(Cube.RotateDirection.NONE)) {
return false;
}

if (Math.abs(distanceX) > Math.abs(distanceY)) {
if (distanceX > 0) {
// RIGHT
cube.setRotation(Cube.RotateDirection.RIGHT);
} else {
// LEFT
cube.setRotation(Cube.RotateDirection.LEFT);
}
} else {
if (distanceY < 0) {
// TOP
cube.setRotation(Cube.RotateDirection.UP);
} else {
// DOWN
cube.setRotation(Cube.RotateDirection.DOWN);
}
}
//requestRender();
return true;
}
}
}
}

public class MyGLRenderer implements GLSurfaceView.Renderer {
Context context; // Application's context
private volatile int rotationAngle;
private volatile float rotateX;
private volatile float rotateY;
private boolean firstRotation = true;
private Cube20 cube;

// 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];

// Constructor with global application context
public MyGLRenderer(Context context) {
this.context = context;
}


public float getRotateX() {
return rotateX;
}

public void setRotateX(float rotateX) {
this.rotateX = rotateX;
}

public float getRotateY() {
return rotateY;
}

public void setRotateY(float rotateY) {
this.rotateY = rotateY;
}

public float getAngle() {
return rotationAngle;
}

public void setAngle(int angle) {
rotationAngle = angle;
}


// Call back when the surface is first created or re-created
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
// Set the background frame color
GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
this.cube = new Cube20();
}

// Call back to draw the current frame.
@Override
public void onDrawFrame(GL10 gl) {
// Redraw background color
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);

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

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

// zoom out a bit
Matrix.translateM(mMVPMatrix, 0, 0, 0, 4.5f);


int angleOffset = 5;
// update the angles for the x and y rotation
if (cube.getRotateDirection().equals(Cube.RotateDirection.LEFT)) {
cube.setAngleY(cube.getAngleY() - angleOffset);
} else if (cube.getRotateDirection().equals(Cube.RotateDirection.RIGHT)) {
cube.setAngleY(cube.getAngleY() + angleOffset);
} else if (cube.getRotateDirection().equals(Cube.RotateDirection.UP)) {
cube.setAngleX(cube.getAngleX() + angleOffset);
} else if (cube.getRotateDirection().equals(Cube.RotateDirection.DOWN)) {
cube.setAngleX(cube.getAngleX() - angleOffset);
}
firstRotation = false;

// rotate and draw
rotate();
cube.draw(mMVPMatrix);


// test if rotation should be stopped (lock in each 90° step)
if (cube.getRotateDirection().equals(Cube.RotateDirection.LEFT) ||
cube.getRotateDirection().equals(Cube.RotateDirection.RIGHT)) {
if (!firstRotation && cube.getAngleY() % 90 == 0) {
cube.setRotation(Cube.RotateDirection.NONE);
}
}
if (cube.getRotateDirection().equals(Cube.RotateDirection.UP) ||
cube.getRotateDirection().equals(Cube.RotateDirection.DOWN)) {
if (!firstRotation && cube.getAngleX() % 90 == 0) {
cube.setRotation(Cube.RotateDirection.NONE);
}
}

Log.i("MyGLRENDER~ ", cube.getRotateDirection().toString());
}

private void rotate() {
float[] rotationMatrix = new float[16];
Matrix.setIdentityM(rotationMatrix, 0);

// rotate in x and y direction, apply that to the intermediate matrix
Matrix.rotateM(rotationMatrix, 0, cube.getAngleX(), 1, 0, 0);
Matrix.rotateM(rotationMatrix, 0, cube.getAngleY(), 0, 1, 0);
Matrix.multiplyMM(mMVPMatrix, 0, mMVPMatrix, 0, rotationMatrix, 0);
}

// Call back after onSurfaceCreated() or whenever the window's size changes
@Override
public void onSurfaceChanged(GL10 gl, 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 Cube20 getCube() {
return cube;
}
}

public class MyGLActivity extends Activity {

private GLSurfaceView glView; // Use GLSurfaceView

// Call back when the activity is started, to initialize the view
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
glView = new MyGLSurfaceView(this); // Allocate a GLSurfaceView
this.setContentView(glView); // This activity sets to GLSurfaceView
}

// Call back when the activity is going into the background
@Override
protected void onPause() {
super.onPause();
glView.onPause();
}

// Call back after onPause()
@Override
protected void onResume() {
super.onResume();
glView.onResume();
}
}

我期望的行为:

  • 成功的滑动 react 并绕 x 轴旋转
    (intermediate screenshot)

  • 成功的滑动 react 并绕 y 轴旋转
    (intermediate screenshot)

使用此设置我遇到以下问题:

  1. 有时旋转是绕 z 轴进行的
    (intermediate screenshot)
    我从未实现过围绕立方体 z 轴的旋转,我不确定为什么要进行此旋转。我只能想象MyGLSurfaceView处的滑动监听器之间可能存在线程问题。和MyGLRenderer (它保存对立方体的引用)。

轮换在 MyGLRenderer.rotate 处完成。立方体使用单独的角度 (x/y),并且还有一个当前已完成旋转的属性(左、右、上、下、无),该属性在 OnSwipeListener 中更新.

  • 此外,如果发生 z 旋转错误 (1.),则绕 x 或 y 轴的旋转方向错误(左/右、上/下交换)

    我的猜测是角度或 RotateDirection未正确更新。该应用程序在 OnePlus 3T @ Android 8.0 上进行测试,IDE 为 Android Studio 3.2.1。

  • 最佳答案

    如果您将立方体绕 X 轴旋转了 90°,那么绕立方体 Y 轴的旋转当然就是绕世界中的 Z 轴旋转。您必须围绕世界的 Y 轴旋转,而不是围绕立方体的 Y 轴。
    为此,您必须将立方体的串联旋转存储在旋转矩阵中,并将新的旋转应用于该矩阵。

    创建旋转矩阵的成员:

    private final float[] mRotationMatrix = new float[16];

    通过单位矩阵初始化:

    Matrix.setIdentityM(mRotationMatrix, 0);

    在方法rotate中,您必须将当前动画应用到旋转矩阵。顺序必须是animationMatrix * mRotationMatrix。矩阵乘法不是commutative 。如果您不遵守乘法的顺序,您将得到与以前相同的结果,并且旋转将围绕立方体的轴而不是围绕世界的轴。

    private void rotate() {
    float[] animationMatrix = new float[16];
    Matrix.setIdentityM(animationMatrix, 0);

    // rotate in x and y direction, apply that to the intermediate matrix
    Matrix.rotateM(animationMatrix, 0, cube.getAngleX(), 1, 0, 0);
    Matrix.rotateM(animationMatrix, 0, cube.getAngleY(), 0, 1, 0);

    // concatenate the animation and the rotation matrix; the order is important
    Matrix.multiplyMM(animationMatrix, 0, animationMatrix, 0, mRotationMatrix, 0);

    Matrix.multiplyMM(mMVPMatrix, 0, mMVPMatrix, 0, animationMatrix, 0);
    }

    当动画达到 90° 时,您必须更改旋转矩阵并重置旋转角度:

    if (cube.getRotateDirection().equals(Cube.RotateDirection.LEFT) ||
    cube.getRotateDirection().equals(Cube.RotateDirection.RIGHT)) {
    if (!firstRotation && cube.getAngleY() % 90 == 0) {

    float[] newRotationMatrix = new float[16];
    Matrix.setIdentityM(newRotationMatrix, 0);
    Matrix.rotateM(newRotationMatrix, 0, cube.getAngleY(), 0, 1, 0);

    // concatenate the new 90 rotation to the rotation matrix
    Matrix.multiplyMM(mRotationMatrix, 0, newRotationMatrix, 0, mRotationMatrix, 0);

    // reset the angle
    cube.setAngleY(0);
    cube.setRotation(Cube.RotateDirection.NONE);
    }
    }
    if (cube.getRotateDirection().equals(Cube.RotateDirection.UP) ||
    cube.getRotateDirection().equals(Cube.RotateDirection.DOWN)) {
    if (!firstRotation && cube.getAngleX() % 90 == 0) {

    float[] newRotationMatrix = new float[16];
    Matrix.setIdentityM(newRotationMatrix, 0);
    Matrix.rotateM(newRotationMatrix, 0, cube.getAngleX(), 1, 0, 0);

    // concatenate the new 90 rotation to the rotation matrix
    Matrix.multiplyMM(mRotationMatrix, 0, newRotationMatrix, 0, mRotationMatrix, 0);

    // reset the angle
    cube.setAngleX(0);
    cube.setRotation(Cube.RotateDirection.NONE);
    }
    }

    关于java - OpenGL ES 2.0 Android - 立方体旋转错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53349892/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com