安卓 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的主要内容,如果未能解决你的问题,请参考以下文章

OPENGL ES 3.1是否比OPENGL ES 2.0慢?

如何在Android上使用OpenGL ES 2.0绘制点

如何在Android上将OpenGL ES 1.0代码转换为OpenGL Es 2.0?

OpenGL ES 2.0 符点精度

OpenGL ES 2.0 剪裁测试

OPENGL ES 2.0 知识串讲 ——OPENGL ES 详解II(传入绘制信息)