安卓 OpenGL ES 2.0 VBO

Posted

技术标签:

【中文标题】安卓 OpenGL ES 2.0 VBO【英文标题】:Android OpenGL ES 2.0 VBO 【发布时间】:2014-08-18 13:42:15 【问题描述】:

我花了几天时间试图让 VBO 使用 OpenGL ES 2.0 在 android 上工作,但我似乎无法让它工作。

这是我正在使用的代码:

/* The Android shader code */
private static final String[] androidVertexShaderCode = new String[] 
        "attribute vec4 vertexPosition;",
        "void main() ",
        "  gl_Position = vertexPosition;",
        "" ;

private static final String[] androidFragmentShaderCode = new String[] 
        "precision mediump float;",
        "uniform vec4 colour;",
        "void main() ",
        "  gl_FragColor = colour;",
        "" ;

/* The Android shader */
public Shader androidShader;

/* The constructor with the render mode and the 
 * number of vertex values given */
public AndroidRenderer(int renderMode, int vertexValuesCount) 
    super(renderMode, vertexValuesCount);
    //Add this renderer to the list
    allRenderers.add(this);
    usage = GLES20.GL_STATIC_DRAW;
    //Setup the Android shader
    this.androidShader = new AndroidShader();
    this.androidShader.vertexShader = ShaderUtils.createShader(ArrayUtils.toStringList(androidVertexShaderCode), Shader.VERTEX_SHADER);
    this.androidShader.fragmentShader = ShaderUtils.createShader(ArrayUtils.toStringList(androidFragmentShaderCode), Shader.FRAGMENT_SHADER);
    this.androidShader.create();


/* The method used to setup the buffers,
 * assumes the vertices have already been set */
public void setupBuffers() 
    //Create the vertices buffer
    this.verticesBuffer = BufferUtils.createFlippedBuffer(this.verticesData);
    int[] vh = new int[1];
    GLES20.glGenBuffers(1, vh, 0);
    //Setup the vertices handle
    this.verticesHandle = vh[0];

    //Bind the vertices buffer and give OpenGL the data
    GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, this.verticesHandle);
    GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, Float.BYTES * verticesData.length, this.verticesBuffer, this.usage);
    GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);

    //Check to see whether the normals have been set
    if (this.normalsData != null) 
        //Create the normals buffer
        this.normalsBuffer = BufferUtils.createFlippedBuffer(this.normalsData);
        int[] nh = new int[1];
        GLES20.glGenBuffers(1, nh, 0);
        //Setup the normals handle
        this.normalsHandle = nh[0];

        //Bind the normals buffer and give OpenGL the data
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, this.normalsHandle);
        GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, Float.BYTES * verticesData.length, this.normalsBuffer, this.usage);
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
    

    //Check to see whether the colours have been set
    if (this.colourData!= null) 
        //Create the colours buffer
        this.coloursBuffer = BufferUtils.createFlippedBuffer(this.colourData);
        int[] ch = new int[1];
        GLES20.glGenBuffers(1, ch, 0);
        //Setup the colours handle
        this.coloursHandle = ch[0];

        //Bind the colours buffer and give OpenGL the data
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, this.coloursHandle);
        GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, Float.BYTES * colourData.length, this.coloursBuffer, this.usage);
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
    

    //Check to see whether the texture coordinates have been set
    if (this.textureData != null) 
        //Create the texture coordinates buffer
        this.texturesBuffer = BufferUtils.createFlippedBuffer(this.textureData);
        int[] th = new int[1];
        GLES20.glGenBuffers(1, th, 0);
        //Setup the texture coordinates handle
        this.texturesHandle = th[0];

        //Bind the texture coordinates buffer and give OpenGL the data
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, this.texturesHandle);
        GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, Float.BYTES * textureData.length, this.texturesBuffer, this.usage);
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
    


/* The method used to draw the object */
public void render() 
    this.androidShader.use();
    short[] indices = new short[this.verticesData.length];
    for (short a = 0; a < indices.length; a++)
        indices[a] = a;
    ShortBuffer indicesBuffer = BufferUtils.createFlippedBuffer(indices);
    //Enable the arrays as needed
    int vertexPositionAttribute = GLES20.glGetAttribLocation(this.androidShader.program, "vertexPosition");
    int normalAttribute = 0;
    int colourAttribute = 0;
    int texturesAttribute = 0;
    GLES20.glEnableVertexAttribArray(vertexPositionAttribute);
    GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, this.verticesHandle);
    GLES20.glVertexAttribPointer(vertexPositionAttribute, this.vertexValuesCount, GLES20.GL_FLOAT, false, 0, 0);
    if (this.normalsData != null) 
        GLES20.glEnableVertexAttribArray(normalAttribute);
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, this.normalsHandle);
        GLES20.glVertexAttribPointer(normalAttribute, 2, GLES20.GL_FLOAT, false, 0, 0);
    
    if (this.colourData != null) 
        colourAttribute = GLES20.glGetAttribLocation(this.androidShader.program, "colour");
        Log.d("HELLO", "" + colourAttribute);
        GLES20.glEnableVertexAttribArray(colourAttribute);
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, this.coloursHandle);
        GLES20.glVertexAttribPointer(colourAttribute, this.colourValuesCount, GLES20.GL_FLOAT, false, 0, 0);
    
    if (this.textureData != null) 
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, this.texturesHandle);
        GLES20.glEnableVertexAttribArray(texturesAttribute);
        GLES20.glVertexAttribPointer(texturesAttribute, this.textureValuesCount, GLES20.GL_FLOAT, false, 0, 0);
    
    //Draw the arrays
    GLES20.glDrawElements(this.renderMode, indices.length, GLES20.GL_UNSIGNED_SHORT, indicesBuffer);
    //Disable the arrays as needed
    if (this.normalsData != null)
        GLES20.glDisableVertexAttribArray(normalAttribute);
    if (this.textureData != null)
        GLES20.glDisableVertexAttribArray(texturesAttribute);
    if (this.colourData != null)
        GLES20.glDisableVertexAttribArray(colourAttribute);
    GLES20.glDisableVertexAttribArray(vertexPositionAttribute);
    this.androidShader.stopUsing();

目前我还没有实现法线或纹理的使用,因为我只是想绘制一个三角形,其中每个顶点都是不同的颜色。我知道着色器应该工作,因为我得到这个工作,使用相同的着色器和顶点/颜色数据只渲染一种颜色。

当我查看 LogCat 时,消息: : GL_INVALID_VALUE 正在被打印出来。然后我决定打印出使用 glGetAttribLocation 找到的 colourAttribute 的值,结果是 -1。经过进一步研究,我发现了这个:https://www.opengl.org/sdk/docs/man/html/glGetAttribLocation.xhtml,它指出返回 -1 的值“如果命名的属性变量不是指定程序对象中的活动属性”。

在谷歌搜索后,我没有找到解决三角形不渲染问题的方法,所以不胜感激。

谢谢。

【问题讨论】:

您确定着色器是否正确编译,并且 colorData 是具有 4 个元素的浮点数组。 是的,colorData 是从 4 个值 (rgba) 生成的,并且着色器之前正在工作,当我使用此处显示的类似方法时:developer.android.com/training/graphics/opengl/draw.html 在 draw 方法中,实际上我复制了来自同一组教程的着色器代码。 【参考方案1】:

vertexAttributePointer 方法的步幅始终为 0。这意味着您可以使用颜色值覆盖顶点缓冲区中的顶点位置。

GLES20.glVertexAttribPointer(colourAttribute, this.colourValuesCount, GLES20.GL_FLOAT, false, **0**, 0);

您必须将 stride 增加先前顶点属性大小总和的偏移量。在您的情况 4 中,因为 vertexPosition 向量的 vec4 。 这同样适用于纹理和法线。

看看这里glVertexAttribPointer的定义:http://androidbook.com/akc/display?url=displaynoteimpurl&ownerUserId=android&reportId=4229

此外,您可以尝试为片段着色器添加固定颜色,例如:

gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);

这样你就避免了 colorHandle 对渲染过程有任何影响。

另外尽量避免一开始使用IndexBuffer而不使用

glDrawElements(.. , ..) 

方法只是直接使用带有一个

的vertexBufferObject
glDrawArrays(.., .., )

方法。

如果可行,您当然可以切换回 IndexBuffer 方法。

由于评论而更新

我现在看到另一个问题。您错过了链接着色器并使用 glLinkProgram 和 glUseProgram 启用它。看看这里:Shader for Android OpenGL ES

这篇文章还向您展示了如何输出着色器程序的编译错误。

【讨论】:

我做了你建议的一切,唯一有效的是添加一个固定的颜色,问题是 glGetAttribLocation 返回-1,这是不对的,所以我不认为着色器甚至从程序本身获取颜色。 哇,我刚刚再次检查了 LogCat,现在还有另一个问题 "<1545>

以上是关于安卓 OpenGL ES 2.0 VBO的主要内容,如果未能解决你的问题,请参考以下文章