如何调试 android 上的 OpenglES 着色器错误?

How to debug OpenglES shader errors on android?

我正在为 android 构建一个 opengl es 应用程序,我在编译着色器时遇到了一些问题。有谁知道如何让 shaderInfoLog 显示在屏幕上?

这里有一些代码可以更好地理解:

public class MainActivity extends Activity {    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        surfaceView=new GLSurfaceView(this);
        Renderer renderer = new Renderer();
        surfaceView.setEGLContextClientVersion(2);
        surfaceView.setRenderer(renderer);
        setContentView(surfaceView);
        
    }
}

渲染器class

public class Renderer implements GLSurfaceView.Renderer{
    
    private Object tri;
    private int glError = 0;

    
    Renderer(){
      
    }
    
    public void onSurfaceCreated(GL10 gl,EGLConfig config){
      float[] positions = //simple quad vertices
      {0.5f,0.5f,0.0f,
      0.5f,-0.5f,0.0f,
      -0.5f,-0.5f,0.0f,
      -0.5f,0.5f,0.0f};
      
      int[] index = {1,2,4,2,4,3}; //simple quad indices
      
      ObjectLoader loader = new ObjectLoader();
      tri = loader.makeObject(positions, index);
    }
    
    public void onSurfaceChanged(GL10 gl, int width, int height){
      GLES20.glViewport(0,0,width,height);
    }
    
    public void onDrawFrame(GL10 gl){
      GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); //here I debug opengl errors as different colored backgrounds
      GLES20.glClearColor(1.0f,1.0f,1.0f,1.0f);
      if(glError == GLES20.GL_NO_ERROR){
        GLES20.glClearColor(1.0f,0.0f,0.0f,1.0f);
      }else if(glError == GLES20.GL_INVALID_ENUM){
        GLES20.glClearColor(0.0f,1.0f,0.0f,1.0f);
      }else if(glError == GLES20.GL_INVALID_FRAMEBUFFER_OPERATION){
        GLES20.glClearColor(0.0f,0.0f,1.0f,1.0f);
      }else if(glError == GLES20.GL_INVALID_OPERATION){
        GLES20.glClearColor(1.0f,1.0f,0.0f,1.0f);
      }else if(glError == GLES20.GL_INVALID_VALUE){
        GLES20.glClearColor(0.0f,1.0f,1.0f,1.0f);
      }else if(glError == GLES20.GL_OUT_OF_MEMORY){
        GLES20.glClearColor(1.0f,0.0f,1.0f,1.0f);
      }
      
      
      render(tri);
    }
    
    private void render(Object obj){
      GLES30.glBindVertexArray(obj.vaoID);
      GLES30.glEnableVertexAttribArray(0);
      obj.shader.start();
      GLES30.glDrawElements(GLES20.GL_TRIANGLES, obj.vertcount, GLES20.GL_UNSIGNED_INT, 0);
      obj.shader.stop();
      GLES30.glDisableVertexAttribArray(0);
      GLES30.glBindVertexArray(0);
      //get the errors for debug on the next frame
      glError = GLES20.glGetError();
    }
}

对象class

public class Object {
    
    public int vaoID;
    public int vertcount;
    public StaticShader shader;
    
    Object(int id, int count, StaticShader s){
      vaoID = id;
      vertcount = count;
      shader = s;
    }
}

ObjectLoader class

public class ObjectLoader {
    
    ObjectLoader(){}
    
    public Object makeObject(float[] positions,int[] indices){
      int vaoID = CreateVAO();
      bindIndicesBuffer(indices);
      storeDataInVao(0,positions,vaoID);
      unbindVao();
      
      StaticShader shader = new StaticShader();
      return new Object(vaoID,indices.length/3, shader);
    }
    
    private int CreateVAO(){
      int[] vaoID = new int[1];
      GLES30.glGenVertexArrays(1,vaoID,0);
      GLES30.glBindVertexArray(vaoID[0]);
      return vaoID[0];
    }
    
    private void storeDataInVao(int index,float[] data, int vaoID){
      int[] vboID = new int[1];
      GLES20.glGenBuffers(1,vboID,0);
      GLES30.glBindBuffer(GLES20.GL_ARRAY_BUFFER,vboID[0]);
      FloatBuffer floatbuffer = makeFloatBuffer(data);
      GLES30.glBufferData(GLES30.GL_ARRAY_BUFFER,data.length*4,floatbuffer,GLES30.GL_STATIC_DRAW);
      GLES30.glVertexAttribPointer(index, 3, GLES30.GL_FLOAT,false,0,0);
      GLES30.glBindBuffer(GLES20.GL_ARRAY_BUFFER,0);
    }
    
    private void bindIndicesBuffer(int[] indices){
      int[] vboID = new int[1];
      GLES20.glGenBuffers(1,vboID,0);
      GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER,vboID[0]);
      IntBuffer buffer = makeIntBuffer(indices);
      GLES20.glBufferData(GLES20.GL_ELEMENT_ARRAY_BUFFER,indices.length,buffer,GLES20.GL_STATIC_DRAW);
    }
    
    private IntBuffer makeIntBuffer(int[] data){
      IntBuffer buffer = ByteBuffer.allocateDirect(data.length*4).order(ByteOrder.nativeOrder()).asIntBuffer();
      buffer.put(data);
      buffer.flip();
      return buffer;
    }
    
    private FloatBuffer makeFloatBuffer(float[] array){
      FloatBuffer floatbuffer = ByteBuffer.allocateDirect(array.length*4).order(ByteOrder.nativeOrder()).asFloatBuffer();
      floatbuffer.put(array);
      floatbuffer.flip();
      return floatbuffer;
    }
    
    private void unbindVao(){
      GLES30.glBindVertexArray(0);
    }
}

StaticShader只是调用了ShaderProgram的构造函数,所以我就把ShaderProgram

public abstract class ShaderProgram {
    
    private int programID;
    private int vertexShader;
    private int fragmentShader;
    
    public String shaderError = "none";
    
    private String vertCode = "void main(){gl_Position = vec4(0.0,0.0,0.0,1.0);}";
    private String fragCode = "void main(){gl_FragColor = vec4(1.0,1.0,1.0,1.0);}";
    
    ShaderProgram(){
       vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertCode);
       fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragCode);
       programID = GLES20.glCreateProgram();
       GLES20.glAttachShader(programID, fragmentShader);
       GLES20.glAttachShader(programID, vertexShader);
       GLES20.glLinkProgram(programID);
       GLES20.glValidateProgram(programID);
    }
    
    abstract void bindAttributes();
    
    private int loadShader(int type, String code){
      int shader = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER);
      
      if(shader == 0){
        System.exit(-1);
      }
      
      GLES20.glShaderSource(shader, code);
      if(GLES20.glGetError() == GLES20.GL_INVALID_OPERATION){
        //System.exit(-1);
      }
      GLES20.glCompileShader(shader);
      final int[] compileStatus = new int[1];
    GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compileStatus, 0);
    if (compileStatus[0] == 0) 
    {               
      
      shaderError = GLES20.glGetShaderInfoLog(shader);
      
        GLES20.glDeleteShader(shader);
        shader = 0;
        //System.exit(-1);
    }
    return shader;
    }
    
    public void start(){
      GLES20.glUseProgram(programID);
    }
    
    public void stop(){
      GLES20.glUseProgram(0);
    }
}

我的目标是通过任何方式在屏幕上显示 ShaderProgram.shaderError。

我想我还应该说 GLSurfaceView 在 MainActivity.onCreate() 完成之前不会启动,GLSurfaceView 与 MainActivity 在不同的线程上并且 GLSurfaceView 无法访问 MainActivity 的上下文,使得不可能使用 Toast.makeText().

对于那些不想阅读整个代码的人,这是层次结构:

MainActivity - 为 GLSurfaceView 创建一个在 MainActivity 完成之前未启动的线程 --> GLSurfaceView

GLSurfaceView

|

渲染器

|

对象加载器

|

静态着色器+对象

|

着色器程序

您总是创建类型为 GLES20.GL_FRAGMENT_SHADER 的着色器对象。使用 type 参数代替常量 GLES20.GL_FRAGMENT_SHADER:

int shader = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER);

int shader = GLES20.glCreateShader(type);

这是我的旧 post,但我最终找到了解决方案(在 Java 中):

//shader is the shader ID returned by GLES30.glCreateShader();
final int[] compileStatus = new int[1];
GLES30.glGetShaderiv(shader, GLES30.GL_COMPILE_STATUS, compileStatus, 0);
if(compileStatus[0] == 0){
    System.err.println("[HERE ->] " + GLES30.glGetShaderInfoLog(shader)); 
    Log.e("Shader Source : ", GLES30.glGetShaderSource(shader));
}

它会打印着色器错误和之后的源代码