代码之家  ›  专栏  ›  技术社区  ›  vir us

每个顶点的Libgdx自定义着色器属性

  •  3
  • vir us  · 技术社区  · 9 年前

    经过几天的挣扎,我来到了这里。我正在尝试将自定义逐顶点vec3属性传递给自定义着色器,该属性基于 this 辅导的本教程描述了如何传递一个实际工作良好的自定义制服。然而,当我试图修改代码以传递我的自定义逐顶点属性时,似乎没有任何东西传递到顶点着色器,我无法确定如何使其工作。

    到目前为止,我已经完成了以下工作:

    我用modelBuilder创建了几个框。createBox()(所以我确信每个模型都有24个顶点)

    然后我生成了一个FloatBuffer,其中包含如下实际属性数据:

        int[] data = new int[]{x1, y1, z1, x1, y1, z1, ...}
    
        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(data.length * 4);
        byteBuffer.order(ByteOrder.nativeOrder());
        mAttributeBuffer = byteBuffer.asFloatBuffer();
        mAttributeBuffer.put(data);
        mAttributeBuffer.position(0); 
    

    然后,我正在初始化相应的属性位置变量(成功,a_coord>=0):

        a_coord = program.getAttributeLocation("a_coord");
    

    之后,在自定义着色器的libgdx侧 render(Renderable) 方法我将缓冲区传递给OpenGL,如下所示:

        program.setVertexAttribute(a_coord, 3, Gdx.gl20.GL_FLOAT, false, 0, mAttributeBuffer);
    

    我的自定义顶点着色器如下所示:

    attribute vec3 a_position;
    attribute vec3 a_normal;
    attribute vec2 a_texCoord0;
    
    uniform mat4 u_worldTrans;
    uniform mat4 u_projTrans;
    
    
    varying vec2 v_texCoord0;
    
    //my custom attribute
    attribute vec2 a_coord;
    
    void main() {
        v_texCoord0 = a_texCoord0;
        float posY =  a_position.y + a_coord.y;
        gl_Position = u_projTrans * u_worldTrans * vec4(a_position.x, posY, a_position.z, 1.0);
    }
    

    问题是

    此时,每个顶点的a_coord为0。我缺少什么以及如何将自定义属性正确传递给顶点着色器?

    我猜问题出在VBO字段和libGDX向顶点传递属性数据的方式上,但我仍然无法确定如何使其工作。

    如果有人能在这个问题上给我指出正确的方向,我会很高兴。

    完整代码:

    主应用程序侦听器类 :

    public class ProtoGame implements ApplicationListener {
    
        public ProtoGame()
        {
            super();
        }
    
        public PerspectiveCamera cam;
        public CameraInputController camController;
        public Model model;
        public Array<ModelInstance> instances = new Array<ModelInstance>();
        public ModelBatch modelBatch;
    
        @Override
        public void create () {
            cam = new PerspectiveCamera(67, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
            cam.position.set(0f, 8f, 8f);
            cam.lookAt(0,0,0);
            cam.near = 1f;
            cam.far = 300f;
            cam.update();
    
            camController = new CameraInputController(cam);
            Gdx.input.setInputProcessor(camController);
    
            ModelBuilder modelBuilder = new ModelBuilder();
            model = modelBuilder.createBox(1f, 1f, 1f,
                    new Material(),
                    VertexAttributes.Usage.Position | VertexAttributes.Usage.Normal | VertexAttributes.Usage.TextureCoordinates);
    
            Color colorU = new Color(), colorV = new Color();
            for (int x = -5; x <= 5; x+=2) {
                for (int z = -5; z<=5; z+=2) {
                    ModelInstance instance = new ModelInstance(model, x, 0, z);
                    //this is where I'll put per-vertex attribute data for every instance
                    //but for now it's hardcoded in the Shader class so the data is the same across instances  
    
                    TestShader.DoubleColorAttribute attr = new TestShader.DoubleColorAttribute(TestShader.DoubleColorAttribute.DiffuseUV,
                            colorU.set((x+5f)/10f, 1f - (z+5f)/10f, 0, 1),
                            colorV.set(1f - (x+5f)/10f, 0, (z+5f)/10f, 1));
                    instance.materials.get(0).set(attr);
                    instances.add(instance);
                }
            }
    
    
            modelBatch = new ModelBatch(new BaseShaderProvider() {
    
                @Override
                protected Shader createShader(Renderable renderable) {
                    return new TestShader();
                }
    
            });
        }
    
        @Override
        public void render () {
            camController.update();
    
            Gdx.gl.glViewport(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
            Gdx.gl.glClearColor(1, 1, 1, 1);
            Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
    
            modelBatch.begin(cam);
            for (ModelInstance instance : instances)
                modelBatch.render(instance);
            modelBatch.end();
        }
    
        @Override
        public void dispose () {
            model.dispose();
            modelBatch.dispose();
        }
    }
    

    自定义libgdx着色器类:

    public class TestShader implements Shader {
        private FloatBuffer mAttributeBuffer;
    
    
    
        ShaderProgram program;
        Camera camera;
        RenderContext context;
        int u_projTrans;
        int u_worldTrans;
        int u_colorU;
        int u_colorV;
    
        int a_coord;
    
        private static String getCustomVertexShader() {
            return Gdx.files.internal("shader/test.vertex.glsl").readString();
        }
    
        private static String getCustomFragmentShader() {
            return Gdx.files.internal("shader/test.fragment.glsl").readString();
        }
    
    
        @Override
        public void init() {
    
            program = new ShaderProgram(getCustomVertexShader(), getCustomFragmentShader());
            if (!program.isCompiled())
                throw new GdxRuntimeException(program.getLog());
    
            //tutorial's logic to init custom uniform locations
            u_projTrans = program.getUniformLocation("u_projTrans");
            u_worldTrans = program.getUniformLocation("u_worldTrans");
            u_colorU = program.getUniformLocation("u_colorU");
            u_colorV = program.getUniformLocation("u_colorV");
    
            //initing custom attribute location
            a_coord = program.getAttributeLocation("a_coord");
    
    
            //generating data and passing it to nio Buffer
            float data[] = generateData();
    
            ByteBuffer byteBuffer = ByteBuffer.allocateDirect(data.length * 4);
            byteBuffer.order(ByteOrder.nativeOrder());
            mAttributeBuffer = byteBuffer.asFloatBuffer();
            mAttributeBuffer.put(data);
            mAttributeBuffer.position(0);
        }
    
        private float[] generateData() {
            Vector3[] dataArray = new Vector3[1];
            dataArray[0] = new Vector3(2, 2, 2);
    
            int components = 3;
            int vertexPerModel = 24;
            float[] data = new float[dataArray.length * components  * vertexPerModel];
            for(int i = 0; i < dataArray.length; ++i){
                int i3 = i * components;
                for(int j = 0; j < vertexPerModel; ++j) {
                    int j3 = j * components;
                    data[i3 + 0 + j3] = dataArray[i].x;
                    data[i3 + 1 + j3] = dataArray[i].y;
                    data[i3 + 2 + j3] = dataArray[i].z;
                }
            }
            return data;
        }
    
        @Override
        public void dispose() {
            program.dispose();
        }
    
        @Override
        public void begin(Camera camera, RenderContext context) {
            this.camera = camera;
            this.context = context;
            program.begin();
            program.setUniformMatrix(u_projTrans, camera.combined);
            context.setDepthTest(GL20.GL_LEQUAL);
            context.setCullFace(GL20.GL_BACK);
        }
    
        @Override
        public void render(Renderable renderable) {
            program.setUniformMatrix(u_worldTrans, renderable.worldTransform);
            //tutorial's logic to pass uniform
            DoubleColorAttribute attribute = ((DoubleColorAttribute) renderable.material.get(DoubleColorAttribute.DiffuseUV));
            program.setUniformf(u_colorU, attribute.color1.r, attribute.color1.g, attribute.color1.b);
            program.setUniformf(u_colorV, attribute.color2.r, attribute.color2.g, attribute.color2.b);
    
    
            //passing my custom attributes to the vertex shader
            program.setVertexAttribute(a_coord, 3, Gdx.gl20.GL_FLOAT, false, 0, mAttributeBuffer);
    
    
            renderable.mesh.render(program, renderable.primitiveType,
                    renderable.meshPartOffset, renderable.meshPartSize);
        }
    
        @Override
        public void end() {
            program.end();
        }
    
        @Override
        public int compareTo(Shader other) {
            return 0;
        }
    
        @Override
        public boolean canRender(Renderable renderable) {
            return renderable.material.has(DoubleColorAttribute.DiffuseUV);
        }
    }
    
    3 回复  |  直到 9 年前
        1
  •  6
  •   vir us    9 年前

    最后,我能够将自定义属性传递给顶点着色器!非常感谢@Xoppa为我指明了正确的方向。

    这是我目前得到的有效解决方案(我愿意就如何以更优雅的方式实施它提供进一步的建议):

    首先,正如Xoppa在评论中所说,需要在构建模型时创建一个提供自定义顶点结构的模型。因此,模型创建可能如下所示:

        VertexAttribute posAttr = new VertexAttribute(VertexAttributes.Usage.Position, 3, ShaderProgram.POSITION_ATTRIBUTE);
        ...
        VertexAttribute customVertexAttr = new VertexAttribute(512, 3, "a_custom");
        VertexAttributes vertexAttributes = new VertexAttributes(
                posAttr,
                ...
                customVertexAttr);
    
        ModelBuilder modelBuilder = new ModelBuilder();
        modelBuilder.begin();
        modelBuilder.
                part("box", GL20.GL_TRIANGLES, vertexAttributes, new Material()).
                box(1f, 1f, 1f);
        model = modelBuilder.end();
    

    或与 MeshBuilder :

        MeshBuilder meshBuilder = new MeshBuilder();
        VertexAttributes vertexAttributes = new VertexAttributes(...);
        meshBuilder.begin(vertexAttributes);
        meshBuilder.part("box", GL20.GL_TRIANGLES);
        meshBuilder.setColor(color);
        meshBuilder.box(1f, 1f, 1f);
        Mesh mesh = meshBuilder.end();
    

    该代码将根据提供的属性创建包含附加数据的顶点的模型。是时候填充相应的顶点阵列了。为此,您需要一个网格-它存储顶点数组-一个一个顶点的压缩属性的平面数组。所以,您需要的是每个顶点的属性数量以及需要修改的属性的偏移量。Mesh存储所有数据:

    Mesh mesh = model.meshes.get(0);
    int numVertices = mesh.getNumVertices();
    // vertex size and offset are in byte so we need to divide it by 4
    int vertexSize = mesh.getVertexAttributes().vertexSize / 4;
    //it's possible to use usage int here passed previously to VertexAttribute constructor. 
    VertexAttribute customAttribute = mesh.getVertexAttribute(512)
    int offset = customAttribute.offset / 4;
    
    float[] vertices = new float[numVertices * vertexSize];
    mesh.getVertices(vertices);
    

    我们准备好传递数据:

    List<Vector3> customData ...
    
    for(int i = 0; i < numVertices; ++i){
        int index = i * vertexSize + offset;
        vertices[index + 0] = customData.get(i).x;
        vertices[index + 1] = customData.get(i).y;
        vertices[index + 2] = customData.get(i).z;
    }
    

    不要忘记将更新的顶点数组传递回网格:

        mesh.updateVertices(0, vertices);
    

    就是这样。

    这里还有一个helper方法的实现,它使用 Usage 带有自定义属性的标志:

    private VertexAttributes createMixedVertexAttribute(int defaultAtributes, List<VertexAttribute> customAttributes){
        VertexAttributes defaultAttributes = MeshBuilder.createAttributes(defaultAtributes);
        List<VertexAttribute> attributeList = new ArrayList<VertexAttribute>();
        for(VertexAttribute attribute: defaultAttributes){
            attributeList.add(attribute);
        }
        attributeList.addAll(customAttributes);
        VertexAttribute[] typeArray = new VertexAttribute[0];
        VertexAttributes mixedVertexAttributes = new VertexAttributes(attributeList.toArray(typeArray));
        return mixedVertexAttributes;
    }
    

    完整的源 :

    public class ProtoGame implements ApplicationListener {
    
    private static final int CUSTOM_ATTRIBUTE_USAGE = 512;
    
    public ProtoGame()
    {
        super();
    }
    
    public PerspectiveCamera cam;
    public CameraInputController camController;
    public Model model;
    public Array<ModelInstance> instances = new Array<ModelInstance>();
    public ModelBatch modelBatch;
    
    @Override
    public void create () {
        cam = new PerspectiveCamera(67, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
        cam.position.set(0f, 8f, 8f);
        cam.lookAt(0, 0, 0);
        cam.near = 1f;
        cam.far = 300f;
        cam.update();
    
        camController = new CameraInputController(cam);
        Gdx.input.setInputProcessor(camController);
    
    
        Model model = createModelWithCustomAttributes();
        Mesh mesh = model.meshes.get(0);
        setCustomAttributeData(mesh);
    
    
        Color colorU = new Color(), colorV = new Color();
        for (int x = -5; x <= 5; x+=2) {
            for (int z = -5; z<=5; z+=2) {
                ModelInstance instance = new ModelInstance(model, x, 0, z);
                TestShader.DoubleColorAttribute attr = new TestShader.DoubleColorAttribute(TestShader.DoubleColorAttribute.DiffuseUV,
                        colorU.set((x+5f)/10f, 1f - (z+5f)/10f, 0, 1),
                        colorV.set(1f - (x+5f)/10f, 0, (z+5f)/10f, 1));
                instance.materials.get(0).set(attr);
                instances.add(instance);
            }
        }
    
    
        modelBatch = new ModelBatch(new BaseShaderProvider() {
    
            @Override
            protected Shader createShader(Renderable renderable) {
                return new TestShader();
            }
    
        });
    }
    
    
    
    
    @Override
    public void render () {
        camController.update();
    
        Gdx.gl.glViewport(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
        Gdx.gl.glClearColor(1, 1, 1, 1);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
    
        modelBatch.begin(cam);
        for (ModelInstance instance : instances)
            modelBatch.render(instance);
        modelBatch.end();
    }
    
    private Model createModelWithCustomAttributes() {
        int defaultAttributes = VertexAttributes.Usage.Position | VertexAttributes.Usage.Normal | VertexAttributes.Usage.TextureCoordinates;
        VertexAttribute customVertexAttr = new VertexAttribute(CUSTOM_ATTRIBUTE_USAGE, 3, "a_custom");
    
        List<VertexAttribute> customAttributeList = new ArrayList<VertexAttribute>();
        customAttributeList.add(customVertexAttr);
    
        VertexAttributes vertexAttributes = createMixedVertexAttribute(defaultAttributes, customAttributeList);
    
        ModelBuilder modelBuilder = new ModelBuilder();
        modelBuilder.begin();
        modelBuilder.
                part("box", GL20.GL_TRIANGLES, vertexAttributes, new Material()).
                box(1f, 1f, 1f);
        return modelBuilder.end();
    }
    
    private void setCustomAttributeData(Mesh mesh) {
        int numVertices = mesh.getNumVertices();
    
        int vertexSize = mesh.getVertexAttributes().vertexSize / 4;
        int offset = mesh.getVertexAttribute(CUSTOM_ATTRIBUTE_USAGE).offset / 4;
    
        float[] vertices = new float[numVertices * vertexSize];
        mesh.getVertices(vertices);
    
        for(int i = 0; i < numVertices; ++i){
            int index = i * vertexSize + offset;
            vertices[index + 0] = i;
            vertices[index + 1] = i;
            vertices[index + 2] = i;
        }
        mesh.updateVertices(0, vertices);
    }    
    
    @Override
    public void dispose () {
        model.dispose();
        modelBatch.dispose();
    }
    
    private VertexAttributes createMixedVertexAttribute(int defaultAtributes, List<VertexAttribute> customAttributes){
        VertexAttributes defaultAttributes = MeshBuilder.createAttributes(defaultAtributes);
        List<VertexAttribute> attributeList = new ArrayList<VertexAttribute>();
        for(VertexAttribute attribute: defaultAttributes){
            attributeList.add(attribute);
        }
        attributeList.addAll(customAttributes);
        VertexAttribute[] typeArray = new VertexAttribute[0];
        VertexAttributes mixedVertexAttributes = new VertexAttributes(attributeList.toArray(typeArray));
        return mixedVertexAttributes;
    }
    
    
    @Override
    public void resize(int width, int height) {
    }
    
    @Override
    public void pause() {
    }
    
    @Override
    public void resume() {
    }
    }
    

    顶点著色引擎 :

    attribute vec3 a_position;
    attribute vec3 a_normal;
    attribute vec2 a_texCoord0;
    
    uniform mat4 u_worldTrans;
    uniform mat4 u_projTrans;
    
    
    varying vec2 v_texCoord0;
    
    attribute vec3 a_custom;
    
    void main() {
        v_texCoord0 = a_texCoord0;
        float posX =  a_position.x + a_custom.x;
        float posY =  a_position.y + a_custom.y;
        float posZ =  a_position.z + a_custom.z;
        gl_Position = u_projTrans * u_worldTrans * vec4(posX, posY, posZ, 1.0);
    }
    

    片段着色器

    #ifdef GL_ES 
    precision mediump float;
    #endif
    
    uniform vec3 u_colorU;
    uniform vec3 u_colorV;
    
    varying vec2 v_texCoord0;
    
    void main() {
        gl_FragColor = vec4(v_texCoord0.x * u_colorU + v_texCoord0.y * u_colorV, 1.0);
    }
    
        2
  •  0
  •   stillsuit    9 年前

    实际上,可以直接调用setVertexAttributes并使其在不需要网格的情况下工作。只需要像这样扩展ShaderProgram

    public class ShaderProgramExtended extends ShaderProgram {
    
    public ShaderProgramExtended(String v, String f){
        super(v,f);
    }
    
    /*
    This is VERY NAUGHTY. Mario and Nathan probably made it private for a reason
     */
    public int getProgram(){
    
        int result;
        try{
            Field field = ShaderProgram.class.getDeclaredField("program");
            field.setAccessible(true);
            Object value = field.get(this);
            field.setAccessible(false);
            if (value == null) {
                result =  0;
            }else{
                result = (Integer) value;
            }
        } catch (NoSuchFieldException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
        return result;
    }
    
    public void begin(int program){
        Gdx.gl20.glUseProgram(program);
    }
    
    public void draw(int mode, int first, int count){
        Gdx.gl20.glDrawArrays(mode,first,count);
    }
    

    然后像平常一样调用它,只需将着色器对象整数发送到新的begin方法的“微小”更改

    public class TestlibGDXv2 extends ApplicationAdapter {
    
    private final String tag = (this).getClass().getSimpleName();
    String message = "";
    
    private static final int FLOAT_BYTES = 4;
    FloatBuffer vertexData;
    FloatBuffer colorData;
    ShaderProgramExtended shader;
    
    private static final String COLOR_ATTRIBUTE = ShaderProgram.COLOR_ATTRIBUTE;
    private int aColourLocation;
    
    private static final String POSITION_ATTRIBUTE = ShaderProgram.POSITION_ATTRIBUTE;
    private int aPositionLocation;
    
    
    /*
    Anti-clockwise winding order. Note, we could share two of the vertices. Haven't for clarity.
    */
    float[] vertices = {
            -0.5f, -0.5f,
            0.5f, -0.5f,
            -0.5f, 0.5f,
            -0.5f, 0.5f,
            0.5f, -0.5f,
            0.5f, 0.5f
    };
    
    /*
    Need to colour each vertex, so need 6.
     */
    
    float[] colors = {1.0f, 0.0f, 0.0f, 1.0f,
            0.0f,1.0f,0.0f,1.0f,
            0.0f,0.0f,1.0f,1.0f,
            0.0f,0.0f,1.0f,1.0f,
            0.0f,1.0f,0.0f,1.0f,
            1.0f,0.0f,0.0f,1.0f
    };
    
    
    @Override
    public void create() {
    
        /*
        Convert from Dalvik VM to OpenGL native
         */
        vertexData = ByteBuffer.allocateDirect(vertices.length * FLOAT_BYTES)
                .order(ByteOrder.nativeOrder())
                .asFloatBuffer();
    
        colorData = ByteBuffer.allocateDirect(colors.length * FLOAT_BYTES)
                .order(ByteOrder.nativeOrder())
                .asFloatBuffer();
    
        vertexData.put(vertices).position(0);
        colorData.put(colors).position(0);
    
        initialiseShaders();
    
    }
    
    @Override
    public void render() {
    
        Gdx.gl.glClearColor(0, 0, 0, 1);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
    
        shader.begin(shader.getProgram());
        shader.setVertexAttribute(aPositionLocation, 2, GL20.GL_FLOAT, false, 0, vertexData);
        shader.enableVertexAttribute(aPositionLocation);
        shader.setVertexAttribute(aColourLocation, 4, GL20.GL_FLOAT, false, 0, colorData);
        shader.enableVertexAttribute(aColourLocation);
    
        shader.draw(GL20.GL_TRIANGLES, 0, 6);
        shader.end();
    }
    
    private void initialiseShaders() {
    
    
        String vertexShaderSource =
                        "#version 130\n" +
                        "attribute vec4 " + POSITION_ATTRIBUTE + ";\n" + // x,y,z and w
                        "attribute vec4 " + COLOR_ATTRIBUTE + ";\n" + // r,g,b and a
                        "varying vec4 v_color;\n" + // pass to fragment shader
                        "void main(){\n" +
                        "   v_color = "+ COLOR_ATTRIBUTE + ";\n" +
                        "   gl_Position = " + POSITION_ATTRIBUTE + ";\n" +
                        "}";
    
        String fragmentShaderSource =
                        "#version 130\n" +
                        "#ifdef GL_ES\n" +
                        "   precision mediump float;\n" + // medium a good balance between speed and quality
                        "#endif\n" +
                        "varying vec4 v_color;\n" + // incoming from vertex shader
                        "void main(){\n" +
                        "   gl_FragColor = v_color;\n" +
                        "}";
    
        shader = new ShaderProgramExtended(vertexShaderSource,fragmentShaderSource);
        aPositionLocation = shader.getAttributeLocation(POSITION_ATTRIBUTE);
        aColourLocation = shader.getAttributeLocation(COLOR_ATTRIBUTE);
    
    }
    

    }

    属性“a_color”(来自ShaderProgram.color_attribute)也很可能是“a_custom”。

    当我试图从WebGL书籍中学习OpenGLES时,我使用了这种方法——它们比libGDX OpenGL书籍多得多。然而,仅使用上述内容进行学习,着色器现在不再由libGDX管理,因此当上下文丢失时,在Android上无法正常工作。不过,桌面电脑也可以。

    干杯

    约翰

        3
  •  0
  •   Mingheng Wang    4 年前

    根据Xoppa的评论,我设法使自定义属性发挥作用。其实很简单!以下是ES 3.0示例:

    #version 300 es
    //v30.glsl
    in vec3 vertexPosition;
    in vec3 vertexColor;
    out vec3 outColor;
    
    void main()
    {
        outColor = vertexColor;
        gl_Position = vec4(vertexPosition, 1.0);
    }
    

    片段着色器:

    #version 300 es
    //f30.glsl
    precision mediump float;
    
    in vec3 outColor;
    
    out vec4 fragColor;
    
    void main()
    {
        fragColor = vec4(outColor, 1.0);
    }
    

    Gl30网格.java

    package com.example.jgles;
    
    import com.badlogic.gdx.ApplicationAdapter;
    import com.badlogic.gdx.Gdx;
    import com.badlogic.gdx.graphics.GL30;
    import com.badlogic.gdx.graphics.Mesh;
    import com.badlogic.gdx.graphics.VertexAttribute;
    import com.badlogic.gdx.graphics.VertexAttributes;
    import com.badlogic.gdx.graphics.glutils.ShaderProgram;
    
    public class Gl30Mesh extends ApplicationAdapter
    {
        private Mesh triangleMesh;
        private ShaderProgram passShader;
        
        @Override
        public void create()
        {
            final float [] combinedData = {
                -0.8f, -0.8f, 0.0f, 1.0f, 0.0f, 0.0f,
                0.8f, -0.8f, 0.0f, 0.0f, 1.0f, 0.0f,
                0.0f, 0.8f, 0.0f, 0.0f, 0.0f, 1.0f
            };
            VertexAttribute vertexChannel = new VertexAttribute(VertexAttributes.Usage.Generic, 3, "vertexPosition");
            VertexAttribute colorChannel = new VertexAttribute(VertexAttributes.Usage.Generic, 3, "vertexColor");
            String vertexShader = Gdx.files.internal("v30.glsl").readString();
            String fragmentShader = Gdx.files.internal("f30.glsl").readString();
            passShader = new ShaderProgram(vertexShader, fragmentShader);
            if (!passShader.isCompiled()){
                throw new IllegalStateException(passShader.getLog());
            }
            passShader.bind();
            triangleMesh = new Mesh(true, 3, 0, vertexChannel, colorChannel);
            triangleMesh.setVertices(combinedData);
        }
    
        @Override
        public void render()
        {
            Gdx.gl.glViewport(0, 0, 640, 480);
            Gdx.gl.glClearColor(0, 0, 1.0f, 0);
            Gdx.gl.glClear(GL30.GL_COLOR_BUFFER_BIT);
            triangleMesh.render(passShader, GL30.GL_TRIANGLES);
        }
    }
    

    在我看来,当您已经在使用自定义着色器时,您可以使用着色器中的任何名称,而不是POSITION_ATTRIBUTE、NORMAL_ATTRIBOTE等。正如Xoppa所说,ShaderProgram。setVertexAttribute根本不起作用。