You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

问题根源及修复方案

  1. 缺少深度测试导致层级混乱
    当前代码启用了混合但未开启深度测试,OpenGL无法判断面的前后顺序,后方的面会覆盖前方的面造成颜色混合。

    • 在onSurfaceCreated中添加:
      GLES20.glEnable(GLES20.GL_DEPTH_TEST);
      
    • 修改onDrawFrame中的清除缓冲区代码:
      GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
      
  2. 重复创建着色器程序
    PhotoCube构造函数中循环每个面时都创建新的着色器程序,完全没必要,一个程序足够绘制所有面。将createProgram和glLinkProgram移到循环外:

    public PhotoCube() {
        createProgram();
        GLES20.glLinkProgram(mProgram);
        
        final int maxVertices = 6;
        for(int[] list:lists){
            // 原有顶点处理代码保留,移除循环内的createProgram和glLinkProgram调用
        }
    }
    
  3. 顶点属性数组未正确禁用
    取消draw方法中GLES20.glDisableVertexAttribArray(positionHandle);的注释,绘制完每个面后禁用属性数组,避免后续绘制冲突。

  4. 矩阵名称不一致
    统一着色器和Renderer中的矩阵名称,将onSurfaceCreated中的uVPMatrix改为uMVPMatrix,与着色器代码保持一致。

内容的提问来源于stack exchange,提问作者UWU gg

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 23:15:37