Android Studio中OpenGL ES立方体颜色异常混合问题求助
立方体颜色混合异常问题排查
我通过将一个平面旋转5次的方式创建了一个各面颜色不同的立方体,并实现了触摸旋转功能。原本一切正常,但最终立方体出现了异常的颜色混合情况,效果如下:


这已经困扰我很久了,恳请帮忙解决!
Cube代码
public class PhotoCube{ public ArrayList<FloatBuffer> vertexBufferList = new ArrayList<>(); public int[][] lists = { {0,1,0,0}, //front {1,0,0,-90}, //top {0,1,0,-90}, //left {0,1,0,90}, // {1,0,0,90}, //bottom {0,1,0,180} //right }; // number of coordinates per vertex in this array static final int COORDS_PER_VERTEX = 3; static final float[] coords = { // in counterclockwise order: 0.5f, 0.5f, 0.5f, // top right -0.5f, 0.5f, 0.5f, // top left -0.5f, -0.5f, 0.5f, // bottom left 0.5f, -0.5f, 0.5f // bottom right }; static final float[][] colorList = { {1f,0f,0f,1f}, {0f,1f,0f,1f}, {1f,1f,1f,1f}, {1f,1f,0f,1f}, {1f,0f,1f,1f}, {0f,1f,1f,1f} }; public PhotoCube() { final int maxVertices = 6; for(int[] list:lists){ float[] currentCoords = coords.clone(); for(int i=0;i<12;i+=3){ float x = coords[i]; float y = coords[i+1]; float z = coords[i+2]; double angle = Math.toRadians(list[3]); currentCoords[i]=(float) ((list[0]==1)?x:(list[1]==1)?x*Math.cos(angle)+z*Math.sin(angle):x*Math.cos(angle)-y*Math.sin(angle)); currentCoords[i+1]=(float) ((list[0]==1)?y*Math.cos(angle)-z*Math.sin(angle):(list[1]==1)?y:x*Math.sin(angle)+y*Math.cos(angle)); currentCoords[i+2]=(float) ((list[0]==1)?z*Math.cos(angle)+y*Math.sin(angle):(list[1]==1)?z*Math.cos(angle)-x*Math.sin(angle):z); } ByteBuffer bb = ByteBuffer.allocateDirect( // (number of coordinate values * 4 bytes per float) currentCoords.length * 4); // use the device hardware's native byte order bb.order(ByteOrder.nativeOrder()); // create a floating point buffer from the ByteBuffer FloatBuffer vertexBuffer = bb.asFloatBuffer(); // add the coordinates to the FloatBuffer vertexBuffer.put(currentCoords); // set the buffer to read the first coordinate vertexBuffer.position(0); if(vertexBufferList.size()==maxVertices){ vertexBufferList.remove(0); } vertexBufferList.add(vertexBuffer); ByteBuffer dlb = ByteBuffer.allocateDirect( // (# of coordinate values * 2 bytes per short) drawOrder.length * 2); dlb.order(ByteOrder.nativeOrder()); drawListBuffer = dlb.asShortBuffer(); drawListBuffer.put(drawOrder); drawListBuffer.position(0); createProgram(); GLES20.glLinkProgram(mProgram); // creates OpenGL ES program executables } } public void createProgram(){ // create empty OpenGL ES Program mProgram = GLES20.glCreateProgram(); int vertexShader = MyGLRenderer.loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode); int fragmentShader = MyGLRenderer.loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode); // add the vertex shader to program GLES20.glAttachShader(mProgram, vertexShader); // add the fragment shader to program GLES20.glAttachShader(mProgram, fragmentShader); } public void draw(int order) { final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex // Add program to OpenGL ES environment GLES20.glUseProgram(mProgram); // get handle to vertex shader's vPosition member int positionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition"); // Enable a handle to the triangle vertices GLES20.glEnableVertexAttribArray(positionHandle); // Prepare the triangle coordinate data GLES20.glVertexAttribPointer(positionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, true, vertexStride, vertexBufferList.get(order)); // get handle to fragment shader's vColor member int colorHandle = GLES20.glGetUniformLocation(mProgram, "vColor"); // Set color for drawing the triangle GLES20.glUniform4fv(colorHandle, 1, colorList[order], 0); GLES20.glDrawElements( GLES20.GL_TRIANGLES, drawOrder.length, GL_UNSIGNED_SHORT, drawListBuffer); // Disable vertex array //GLES20.glDisableVertexAttribArray(positionHandle); } }
Renderer代码
public class MyGLRenderer implements GLSurfaceView.Renderer { private PhotoCube mPhotoCube; public final float[] vPMatrix = new float[16]; private final float[] projectionMatrix = new float[16]; private final float[] viewMatrix = new float[16]; private int vPMatrixHandle = -1; private volatile float mAngleX = 0; private volatile float mAngleY = 0; private float[] rotationMX = new float[16]; private float[] rotationMY = new float[16]; private float[] scratch = new float[16]; public MyGLRenderer(Context context){ } public static int loadShader(int type, String shaderCode) { 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 void onSurfaceCreated(GL10 unused, EGLConfig config) { // Set the background frame color GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); mPhotoCube = new PhotoCube(); vPMatrixHandle = GLES20.glGetUniformLocation(mPhotoCube.mProgram, "uVPMatrix"); onDrawFrame(unused); } public void onDrawFrame(GL10 unused) { // Redraw background color GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); GLES20.glEnable(GLES20.GL_BLEND); GLES20.glBlendFuncSeparate(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA, GLES20.GL_ZERO, GLES20.GL_ONE); Matrix.setRotateM(rotationMX, 0, -mAngleX, 0, 1, 0); Matrix.setLookAtM(viewMatrix, 0, 0, 0, 5, 0f, 0f, -5f, 0f, 1.0f, 0f); // Calculate the projection and view transformation Matrix.multiplyMM(vPMatrix, 0, projectionMatrix, 0, viewMatrix, 0); Matrix.multiplyMM(scratch, 0, vPMatrix, 0, rotationMX, 0); Matrix.setRotateM(rotationMY, 0, -mAngleY, scratch[0], scratch[4], scratch[8]); Matrix.multiplyMM(scratch, 0, scratch, 0, rotationMY, 0); vPMatrixHandle = GLES20.glGetUniformLocation(mPhotoCube.mProgram, "uMVPMatrix"); GLES20.glUniformMatrix4fv(vPMatrixHandle, 1, false, scratch, 0); for(int i=0;i<mPhotoCube.lists.length;i++){ mPhotoCube.draw(i); } } 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(projectionMatrix, 0, -ratio, ratio, -1, 1, 3, 7); } }
着色器代码
final String vertexShaderCode = "uniform mat4 uMVPMatrix;" + "attribute vec4 vPosition;" + "void main() {" + " gl_Position = uMVPMatrix * vPosition;" + "}"; final String fragmentShaderCode = "precision mediump float;" + "uniform vec4 vColor;" + "void main() {" + " gl_FragColor = vColor;" + "}";
问题根源及修复方案
缺少深度测试导致层级混乱
当前代码启用了混合但未开启深度测试,OpenGL无法判断面的前后顺序,后方的面会覆盖前方的面造成颜色混合。- 在
onSurfaceCreated中添加:GLES20.glEnable(GLES20.GL_DEPTH_TEST); - 修改
onDrawFrame中的清除缓冲区代码:GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
- 在
重复创建着色器程序
PhotoCube构造函数中循环每个面时都创建新的着色器程序,完全没必要,一个程序足够绘制所有面。将createProgram和glLinkProgram移到循环外:public PhotoCube() { createProgram(); GLES20.glLinkProgram(mProgram); final int maxVertices = 6; for(int[] list:lists){ // 原有顶点处理代码保留,移除循环内的createProgram和glLinkProgram调用 } }顶点属性数组未正确禁用
取消draw方法中GLES20.glDisableVertexAttribArray(positionHandle);的注释,绘制完每个面后禁用属性数组,避免后续绘制冲突。矩阵名称不一致
统一着色器和Renderer中的矩阵名称,将onSurfaceCreated中的uVPMatrix改为uMVPMatrix,与着色器代码保持一致。
内容的提问来源于stack exchange,提问作者UWU gg
相关产品推荐
相关产品推荐

