首页 > 解决方案 > 如何从 OpenGL ES 2.0 中的数组中读取 4 个以上的顶点数据?

问题描述

我试图从浮点数组缓冲区中为顶点位置和颜色绘制一个相邻的点OpenGL ES 2.0,但每次绘制只能接受 4 个顶点。当我添加更多然后告诉它绘制更多时,它只会在数组中绘制 2 个不相邻的顶点。为什么会这样?为什么我不能在一个实例中绘制超过 4 个顶点?如果可以,怎么做?


这是一个例子。当我有3*4数组数据时,它会像这样绘制它们:

. . . .,这是正确完成的。

例如,如果我设置3*6数组数据,它不会绘制 6 个点,它会绘制如下内容:

. .


我的大部分代码来自OpenGL ES课程项目,我仍在学习并且不太了解像 in 这样的事情是如何onSurfaceCreated完成的。

这是我的 OpenGLRenderer 类:

@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {

    ...

    final String vertexShader =
            "uniform mat4 u_MVPMatrix;      \n"                 // A constant representing the combined model/view/projection matrix.

                    + "attribute vec4 a_Position;     \n"       // Per-vertex position information we will pass in.
                    + "attribute vec4 a_Color;        \n"       // Per-vertex color information we will pass in.

                    + "varying vec4 v_Color;          \n"       // This will be passed into the fragment shader.

                    + "void main()                    \n"       // The entry point for our vertex shader.
                    + "{                              \n"
                    + "   v_Color = a_Color;          \n"       // Pass the color through to the fragment shader.
                                                                // It will be interpolated across the triangle.
                    + "   gl_Position = u_MVPMatrix   \n"       // gl_Position is a special variable used to store the final position.
                    + "               * a_Position;   \n"       // Multiply the vertex by the matrix to get the final point in
                    + "}                              \n";      // normalized screen coordinates.

    final String fragmentShader =
            "precision mediump float;       \n"                 // Set the default precision to medium. We don't need as high of a
                                                                // precision in the fragment shader.
                    + "varying vec4 v_Color;          \n"       // This is the color from the vertex shader interpolated across the
                                                                // triangle per fragment.
                    + "void main()                    \n"       // The entry point for our fragment shader.
                    + "{                              \n"
                    + "   gl_FragColor = v_Color;     \n"       // Pass the color directly through the pipeline.
                    + "}                              \n";

    // Load in the vertex shader.
    int vertexShaderHandle = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER);

    if (vertexShaderHandle != 0)
    {
        // Pass in the shader source.
        GLES20.glShaderSource(vertexShaderHandle, vertexShader);

        // Compile the shader.
        GLES20.glCompileShader(vertexShaderHandle);

        // Get the compilation status.
        final int[] compileStatus = new int[1];
        GLES20.glGetShaderiv(vertexShaderHandle, GLES20.GL_COMPILE_STATUS, compileStatus, 0);

        // If the compilation failed, delete the shader.
        if (compileStatus[0] == 0)
        {
            GLES20.glDeleteShader(vertexShaderHandle);
            vertexShaderHandle = 0;
        }
    }

    if (vertexShaderHandle == 0)
    {
        throw new RuntimeException("Error creating vertex shader.");
    }

    // Load in the fragment shader shader.
    int fragmentShaderHandle = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER);

    if (fragmentShaderHandle != 0)
    {
        // Pass in the shader source.
        GLES20.glShaderSource(fragmentShaderHandle, fragmentShader);

        // Compile the shader.
        GLES20.glCompileShader(fragmentShaderHandle);

        // Get the compilation status.
        final int[] compileStatus = new int[1];
        GLES20.glGetShaderiv(fragmentShaderHandle, GLES20.GL_COMPILE_STATUS, compileStatus, 0);

        // If the compilation failed, delete the shader.
        if (compileStatus[0] == 0)
        {
            GLES20.glDeleteShader(fragmentShaderHandle);
            fragmentShaderHandle = 0;
        }
    }

    if (fragmentShaderHandle == 0)
    {
        throw new RuntimeException("Error creating fragment shader.");
    }

    // Create a program object and store the handle to it.
    int programHandle = GLES20.glCreateProgram();

    if (programHandle != 0)
    {
        // Bind the vertex shader to the program.
        GLES20.glAttachShader(programHandle, vertexShaderHandle);

        // Bind the fragment shader to the program.
        GLES20.glAttachShader(programHandle, fragmentShaderHandle);

        // Bind attributes
        GLES20.glBindAttribLocation(programHandle, 0, "a_Position");
        GLES20.glBindAttribLocation(programHandle, 1, "a_Color");

        // Link the two shaders together into a program.
        GLES20.glLinkProgram(programHandle);

        // Get the link status.
        final int[] linkStatus = new int[1];
        GLES20.glGetProgramiv(programHandle, GLES20.GL_LINK_STATUS, linkStatus, 0);

        // If the link failed, delete the program.
        if (linkStatus[0] == 0)
        {
            GLES20.glDeleteProgram(programHandle);
            programHandle = 0;
        }
    }

    if (programHandle == 0)
    {
        throw new RuntimeException("Error creating program.");
    }

    // Set program handles. These will later be used to pass in values to the program.
    mMVPMatrixHandle = GLES20.glGetUniformLocation(programHandle, "u_MVPMatrix");
    mPositionHandle = GLES20.glGetAttribLocation(programHandle, "a_Position");
    mColorHandle = GLES20.glGetAttribLocation(programHandle, "a_Color");

    // Tell OpenGL to use this program when rendering.
    GLES20.glUseProgram(programHandle);

}

绘制点:

@Override
public void onDrawFrame(GL10 gl) {

    GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);

    Matrix.setIdentityM(mModelMatrix, 0);

    drawPoints();

}

// Like I said, 4 and below works without any issue
private final int vertexesToDraw = 6;

// x, y, z (z being upwards)
private final float[] vertexPosData = {
        0.0f, 0.0f, 0.0f,
        1.0f, 0.0f, 0.0f,
        2.0f, 0.0f, 0.0f,
        3.0f, 0.0f, 0.0f,
        4.0f, 0.0f, 0.0f,
        5.0f, 0.0f, 0.0f
};

// red, green, blue, alpha
private final float[] vertexColorData = {
        1.0f, 0.0f, 0.0f, 1.0f,
        0.0f, 1.0f, 0.0f, 1.0f,
        0.0f, 0.0f, 1.0f, 1.0f,
        1.0f, 0.0f, 0.0f, 1.0f,
        0.0f, 1.0f, 0.0f, 1.0f,
        0.0f, 0.0f, 1.0f, 1.0f
};

FloatBuffer mVertexPos, mVertexColor;

// this gets initialized first
private void setupPoints() {

    mVertexPos = ByteBuffer.allocateDirect(vertexPosData.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
    mVertexPos.put(vertexPosData).position(0);
    mVertexPos.position(0);

    mVertexColor = ByteBuffer.allocateDirect(vertexColorData.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
    mVertexColor.put(vertexColorData).position(0);
    mVertexColor.position(0);

}

private void drawPoints() {

    GLES20.glVertexAttribPointer(mPositionHandle, 3, GLES20.GL_FLOAT, false, 3 * vertexesToDraw, mVertexPos);
    GLES20.glEnableVertexAttribArray(mPositionHandle);

    GLES20.glVertexAttribPointer(mColorHandle, 4, GLES20.GL_FLOAT, false, 4 * vertexesToDraw, mVertexColor);
    GLES20.glEnableVertexAttribArray(mColorHandle);

    Matrix.multiplyMM(mMVPMatrix, 0, mViewMatrix, 0, mModelMatrix, 0);
    Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mMVPMatrix, 0);

    GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mMVPMatrix, 0);
    GLES20.glDrawArrays(GLES20.GL_POINTS, 0, vertexesToDraw);

}

标签: javaandroidopengl-esglslopengl-es-2.0

解决方案


的第 5 个参数glVertexAttribPointer是连续通用顶点属性 ( stride) 之间的字节偏移量,而不是缓冲区中元素的数量。

这意味着,对于 float 类型的属性(float 的大小为 4 个字节)并且元组大小为 3(例如 x、y、z 坐标),stride 参数必须为 4 * 3 = 12。
对于具有元组大小为 4(RGBA 颜色),步幅参数必须为 4 * 4 = 16。

GLES20.glVertexAttribPointer(mPositionHandle, 3, GLES20.GL_FLOAT, false, 3 * 4, mVertexPos);
GLES20.glEnableVertexAttribArray(mPositionHandle);

GLES20.glVertexAttribPointer(mColorHandle, 4, GLES20.GL_FLOAT, false, 4 * 4, mVertexColor);
GLES20.glEnableVertexAttribArray(mColorHandle);

由于顶点紧密排列,因此可以将步幅设置为 0。这是由 提供的特殊情况glVertexAttribPointer。当stride为 0 时,则由sizetype参数自动计算。

GLES20.glVertexAttribPointer(mPositionHandle, 3, GLES20.GL_FLOAT, false, 0, mVertexPos);
GLES20.glEnableVertexAttribArray(mPositionHandle);

GLES20.glVertexAttribPointer(mColorHandle, 4, GLES20.GL_FLOAT, false, 0, mVertexColor);
GLES20.glEnableVertexAttribArray(mColorHandle);

请注意,当您有 4 个属性时,缓冲区中的元素数量(浮点值的数量)恰好匹配连续通用顶点属性之间的字节偏移量。所以 4 个顶点巧合工作,但 6 个顶点失败。
缓冲区的大小从不明确设置。调用时glDrawArrays,启用的顶点属性必须引用一个缓冲区,该缓冲区足够大以提供所需的属性。


推荐阅读