1

I am trying to draw points one next to each other in OpenGL ES 2.0 from float array buffers for vertex position and color, but it can only accept 4 vertexes per draw. When I add more then tell it to draw more then it would only draw 2 non-adjacent vertexes in the array. Why does that happen? Why can't I draw more than 4 vertexes in one instance? If I can, how?


Here's an example. When I have 3*4 array data it draws them like this:

. . . ., which is done properly.

If I'd for example set 3*6 array data, it does not draw 6 points, it draws something like this:

. .


Most of my code is from the OpenGL ES Lesson project and I'm still learning and do not quite understand how things like in onSurfaceCreated are done.

Here is my OpenGLRenderer class:

@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);

}

Drawing the points:

@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);

}
Rabbid76
  • 202,892
  • 27
  • 131
  • 174
IOviSpot
  • 358
  • 3
  • 19

1 Answers1

2

The 5th parameter of glVertexAttribPointer is the byte offset between consecutive generic vertex attributes (stride) rather than the number of elements in the buffer.

This means, for an attribute of type float (size of float is 4 bytes) and with a tuple size of 3 (e.g. x, y, z coordinate), the stride parameter has to be 4 * 3 = 12.
For an attribute with a tuple size of 4 (RGBA color), the stride paramter has to be 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);

Since the vertices are tightly packed, stride can be set 0. This is a special case which is provided by glVertexAttribPointer. When stride is 0, then then it is calculated automatically by the size and type parameter.

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);

Note, when you have 4 attributes, then the number of elements (number of float values) in the buffer, coincidentally matches the byte offset between consecutive generic vertex attributes. So 4 vertices coincidentally works, but 6 vertices fail.
The size of the buffer is never set explicitly. When the glDrawArrays is called, then the enabled vertex attributes have to refer to a buffer, which is large enough to provide the required attributes.

Rabbid76
  • 202,892
  • 27
  • 131
  • 174
  • Thanks! I am gladly accepting this answer as it solved my problem. Thank you so much for summarizing the actual meaning of the striding in that method. It's basically the same thing to use in the `ByteBuffer.allocateDirect` method multiplied by the source array's length. – IOviSpot May 02 '19 at 17:55