Draw triangles with Opengl ES 2.0

Asked

Viewed 280 times

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 something very simple, but it’s not working, only my "triangle2" is drawn on the screen.

Could 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% equal to the triangle)

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

}   

}

1 answer

2

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

It is possible to use the same program to draw as many objects as necessary, just by calling the appropriate ones

GLES20.glVertexAttribPointer(...);

before the calls

GLES20.glDrawXXX(...);

Even if you use two or three different programs, you should call the glVertexAttribPointer before the glDrawXXX, because vertex information is not stored together with the program, as this person has already responded in the OS: https://stackoverflow.com/questions/18645148/what-is-the-scope-of-glvertexattribpointer-and-glenablevertexattribarray

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

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

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

From what I can understand, your data is as follows::

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

Thus, for coordinate data, there are 3 floats between each pair of coordinates: the stride for aPositionLocation should be 3*BYTES_PER_FLOAT.

For color data, there are 2 floats between each trio of colors: the stride for aColorLocation should be 2*BYTES_PER_FLOAT.

Just one more detail, for the sake of performance, if possible, it’s best to create VAO’s, and use them instead of passing your Java-created vector 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 that, when drawing, it is necessary to do just this, instead of passing the whole vector again:

GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, bufId);
GLES20.glVertexAttribPointer(...);
GLES20.glDrawXXX(...);

Browser other questions tagged

You are not signed in. Login or sign up in order to post.