Draw triangles with OpenGL ES 2.0

3

I'm learning OpenGL ES 2.0 for Android, and after some familiarization with the programming order I'm trying to draw 2 triangles on the screen, from 2 different objects. It's very simple, but it's not working, only my "triangle2" is drawn on the screen.

Could you help?

Class 1

public class MainActivity extends Activity {

GLSurfaceView myGLSurfaceView;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    myGLSurfaceView = new GLSurfaceView(this);      
    myGLSurfaceView.setEGLContextClientVersion(2);
    myGLSurfaceView.setRenderer(new RendererClass());

    setContentView(myGLSurfaceView);

}

}

Class 2 - Triangle 1

public class ObjectTriangle {

int BYTES_PER_FLOAT = 4;
int shaderProgram;
FloatBuffer vertexInBuffer;

public ObjectTriangle(){

    float[] vertexArray = {

            0.0f,  0.5f, 1.0f, 0.0f, 0.0f,
            0.0f, -0.5f, 0.0f, 1.0f, 0.0f,
            0.5f,  0.0f, 0.0f, 0.0f, 1.0f

    };

    vertexInBuffer = ByteBuffer.allocateDirect(vertexArray.length*BYTES_PER_FLOAT)
                                           .order(ByteOrder.nativeOrder())
                                           .asFloatBuffer()
                                           .put(vertexArray);

}

public void inicializeObjectShaders(){

    String vertexShaderCode = "attribute vec4 a_Position;"
                            + "attribute vec4 a_Color;"
                            + "varying vec4 v_Color;"

                            + "void main(){" 
                            +     "gl_Position = a_Position;"
                            +     "v_Color = a_Color;"
                            + "}";

    String fragmentShaderCode = "precision mediump float;" 
                              + "varying vec4 v_Color;" 

                              + "void main(){" 
                              +     "gl_FragColor = v_Color;"
                              + "}";

    int vertexShaderID = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER);
    int fragmentShaderID = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER);

    GLES20.glShaderSource(vertexShaderID, vertexShaderCode);
    GLES20.glShaderSource(fragmentShaderID, fragmentShaderCode);

    GLES20.glCompileShader(vertexShaderID);
    GLES20.glCompileShader(fragmentShaderID);

    shaderProgram = GLES20.glCreateProgram();

    GLES20.glAttachShader(shaderProgram, vertexShaderID);
    GLES20.glAttachShader(shaderProgram, fragmentShaderID);

    GLES20.glLinkProgram(shaderProgram);        

}

public void setVertexAttribPointer(){

    int aPositionLocation = glGetAttribLocation(shaderProgram, "a_Position");
    int aColorLocation = glGetAttribLocation(shaderProgram, "a_Color");

    vertexInBuffer.position(0);
    glVertexAttribPointer(aPositionLocation, 2, GL_FLOAT, false, (2+3)*BYTES_PER_FLOAT, vertexInBuffer);
    glEnableVertexAttribArray(aPositionLocation);

    vertexInBuffer.position(2);
    glVertexAttribPointer(aColorLocation, 3, GL_FLOAT, false, (2+3)*BYTES_PER_FLOAT, vertexInBuffer);
    glEnableVertexAttribArray(aColorLocation);

}

public void useProgram(){
    GLES20.glUseProgram(shaderProgram); 
}

public void draw(){
    glDrawArrays(GL_TRIANGLES, 0, 3);
}

}

Class 3 - Triangle 2 (99% equals triangle1, only changes the location of 1 vertex)

public class ObjectTriangle2 {

int BYTES_PER_FLOAT = 4;
int shaderProgram;
FloatBuffer vertexInBuffer;

public ObjectTriangle2(){

    float[] vertexArray = {

            0.0f,  0.5f, 1.0f, 0.0f, 0.0f,
           -0.5f,  0.0f, 0.0f, 1.0f, 0.0f,
            0.0f, -0.5f, 0.0f, 0.0f, 1.0f

    };

    vertexInBuffer = ByteBuffer.allocateDirect(vertexArray.length*BYTES_PER_FLOAT)
                                           .order(ByteOrder.nativeOrder())
                                           .asFloatBuffer()
                                           .put(vertexArray);

}

public void inicializeObjectShaders(){

    String vertexShaderCode = "attribute vec4 a_Position;"
                            + "attribute vec4 a_Color;"
                            + "varying vec4 v_Color;"

                            + "void main(){" 
                            +     "gl_Position = a_Position;"
                            +     "v_Color = a_Color;"
                            + "}";

    String fragmentShaderCode = "precision mediump float;" 
                              + "varying vec4 v_Color;" 

                              + "void main(){" 
                              +     "gl_FragColor = v_Color;"
                              + "}";

    int vertexShaderID = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER);
    int fragmentShaderID = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER);

    GLES20.glShaderSource(vertexShaderID, vertexShaderCode);
    GLES20.glShaderSource(fragmentShaderID, fragmentShaderCode);

    GLES20.glCompileShader(vertexShaderID);
    GLES20.glCompileShader(fragmentShaderID);

    shaderProgram = GLES20.glCreateProgram();

    GLES20.glAttachShader(shaderProgram, vertexShaderID);
    GLES20.glAttachShader(shaderProgram, fragmentShaderID);

    GLES20.glLinkProgram(shaderProgram);        

}

public void setVertexAttribPointer(){

    int aPositionLocation = glGetAttribLocation(shaderProgram, "a_Position");
    int aColorLocation = glGetAttribLocation(shaderProgram, "a_Color");

    vertexInBuffer.position(0);
    glVertexAttribPointer(aPositionLocation, 2, GL_FLOAT, false, (2+3)*BYTES_PER_FLOAT, vertexInBuffer);
    glEnableVertexAttribArray(aPositionLocation);

    vertexInBuffer.position(2);
    glVertexAttribPointer(aColorLocation, 3, GL_FLOAT, false, (2+3)*BYTES_PER_FLOAT, vertexInBuffer);
    glEnableVertexAttribArray(aColorLocation);

}

public void useProgram(){
    GLES20.glUseProgram(shaderProgram); 
}

public void draw(){
    glDrawArrays(GL_TRIANGLES, 0, 3);
}

}

Class 4 - The Renderer

public class RendererClass implements Renderer {

ObjectTriangle triangle;
ObjectTriangle2 triangle2;  

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

    GLES20.glClearColor(0.0f, 0.5f, 1.0f, 1.0f);                

    triangle = new ObjectTriangle();
    triangle.inicializeObjectShaders();
    triangle.setVertexAttribPointer();

    triangle2 = new ObjectTriangle2();
    triangle2.inicializeObjectShaders();
    triangle2.setVertexAttribPointer();     

}

@Override
public void onSurfaceChanged(GL10 arg0, int width, int height) {

    glViewport(0, 0, width, height);

}

@Override
public void onDrawFrame(GL10 glUnused) {

    GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);     

    triangle.useProgram();
    triangle.draw();    

    triangle2.useProgram();
    triangle2.draw();

}   

}
    
asked by anonymous 09.03.2014 / 15:35

1 answer

2

According to your code, you create two exactly equal programs, one for each object. What is not needed, and indeed, consumes extra resources unnecessarily.

You can use the same program to draw as many objects as needed, just by calling the ones

GLES20.glVertexAttribPointer(...);

before calls

GLES20.glDrawXXX(...);

Even if you use two or three different programs, you should call the glVertexAttribPointer before the glDrawXXX , since the vertex information is not stored together with the program, as that person already replied in the OS: link

Since you only change the program, and run two glDrawArrays , the two calls to the glDrawArrays method are effectively drawing the same thing on the screen twice, using the last thing that was assigned through glVertexAttribPointer , which in your case, were the information in triangle 2.

Another detail is how you are using stride . You are using (2+3)*BYTES_PER_FLOAT , which is not true. The stride must be the number of bytes between consecutive elements, as the OpenGL documentation says: glVertexAttribPointer .

If you only have X, Y, X, Y, X, Y coordinates, your stride would be 0. Since you have different data interspersed, your stride will not be 0, but it will not be (2+3)*BYTES_PER_FLOAT .

As far as I can understand, your data is as follows:

X, Y, R, G, B, X, Y, R, G, B, X, Y, R, G, B, ...

So, for coordinate data, there is 3 floats between each pair of coordinates: stride to aPositionLocation should be 3*BYTES_PER_FLOAT .

Now for color data, there is 2 floats between each trio of colors: stride to aColorLocation should be 2*BYTES_PER_FLOAT .

Just one more detail, for the sake of performance, if possible, it's better to create VAO's, and use them, instead of passing your vector created in Java every time you need to draw (this consumes processing unnecessarily).

So, at the time of initializing the object, just do:

int[] buf = new int[1];
//cria um buffer, e armazena seu id em buf[0]
GLES20.glGenBuffers(1, buf, 0);

//armazena o id do buffer em uma variável, para não precisar utilizar um array sempre
int bufId = buf[0];

//- Supondo que vertices seja um vetor float[] com as informações dos meus vértices
//- Se você já tivesse um Buffer, a chamada FloatBuffer.wrap é desnecessária
//- O * BYTES_PER_FLOAT é porque glBufferData precisa do tamanho em bytes, e não em elementos
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER,
                    bufId);
GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER,
                    vertices.length * BYTES_PER_FLOAT,
                    FloatBuffer.wrap(vertices),
                    GLES20.GL_STATIC_DRAW);

So, when drawing, you just have to do this instead of passing the entire vector back:

GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, bufId);
GLES20.glVertexAttribPointer(...);
GLES20.glDrawXXX(...);
    
05.05.2014 / 17:06