Back


Android - OpenGL ES 2 - Tutorial 1 - Basic Project 2D

We begin this series of tutorials on OpenGL ES 2 with a basic 2D-oriented project and with the only instructions necessary to make it executable.

Open Android Studio, click on "Start a new Android Studio project":


Select the project type "No Activity", click on "Next":


Insert the name of the project and the package, click on "Finish":


Creat e 3 classes, "MainActivity", "MainView" e "MainRenderer":






Replace the code of the newly created classes with the following:

- for "MainActivity":
package opengles2.tutorial1;

import android.app.Activity;
import android.app.ActivityManager;
import android.content.Context;
import android.content.pm.ConfigurationInfo;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Toast;

public class MainActivity extends Activity {
    // Dedicated surface for displaying OpenGL rendering.
    GLSurfaceView glSurfaceView;

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

        // Hide the activity title.
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        // Set full screen.
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);

        if (this.SupportsGLES20()) {
            // Create and set the dedicated surface.
            this.glSurfaceView = new MainView(this);
            setContentView(this.glSurfaceView);
        }
        else {
            Toast.makeText(this, "This device does not support OpenGL ES 2.0.", Toast.LENGTH_LONG).show();
        }
    }

    @Override
    protected void onPause() {
        super.onPause();

        // Pause the rendering thread on activity pause.
        if (this.glSurfaceView != null)
            this.glSurfaceView.onPause();
    }

    @Override
    protected void onResume() {
        super.onResume();

        // Resume the rendering thread on activity resume.
        if (this.glSurfaceView != null)
            this.glSurfaceView.onResume();
    }

    private boolean SupportsGLES20() {
        // Check if OpenGL ES 2.0 is supported.
        ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
        ConfigurationInfo info = am.getDeviceConfigurationInfo();
        return info.reqGlEsVersion >= 0x20000;
    }

}
- for "MainView":
package opengles2.tutorial1;

import android.content.Context;
import android.opengl.GLSurfaceView;

public class MainView extends GLSurfaceView {
    // Interface for drawing graphics.
    MainRenderer glRenderer;

    // Constructor.
    public MainView(Context context) {
        super(context);

        // Set OpenGL version.
        setEGLContextClientVersion(2);
        // Create and set the interface for drawing graphics.
        this.glRenderer = new MainRenderer();
        setRenderer(this.glRenderer);
    }

}
- for "MainRenderer":
package opengles2.tutorial1;

import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.opengl.Matrix;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

public class MainRenderer implements GLSurfaceView.Renderer {
    int width, height;

    // Projection matrix.
    private final float[] projMatrix = new float[16];
    // Camera matrix.
    private final float[] viewMatrix = new float[16];

    // Called when the surface is created or recreated.
    @Override
    public void onSurfaceCreated(GL10 unused, EGLConfig config) {
        // Set color's clear-value to black.
        GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    }

    // Called when the surface changed size.
    // Called after the surface is created and whenever the OpenGL ES surface size changes.
    @Override
    public void onSurfaceChanged(GL10 unused, int width, int height) {
        // Save size for future use.
        this.width = width;
        this.height = height;

        // Set the viewport (display area) to cover the entire window.
        GLES20.glViewport(0, 0, this.width, this.height);
        // Set projection matrix to orthographic system.
        Matrix.orthoM(this.projMatrix, 0, 0.0f, this.width - 1.0f, 0.0f, this.height - 1.0f, 5.0f, 10.0f);
        // Set the camera position (View matrix).
        Matrix.setLookAtM(this.viewMatrix, 0, 0.0f, 0.0f, 5.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f);
    }

    // Called to draw the current frame.
    @Override
    public void onDrawFrame(GL10 unused) {
        // Clear buffers to preset values.
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);

        // Your rendering code here.
    }

}
Complete everything by editing the file "AndroidManifest.xml" in the following way:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="opengles2.tutorial1">

    <uses-feature android:glEsVersion="0x00020000" android:required="true" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

        <activity
            android:name="opengles2.tutorial1.MainActivity"
            android:label="@string/app_name"
            android:screenOrientation="portrait" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
Our project will do very little, it will simply clear the frame, but let's see what happens.

Let's start with the file "AndroidManifest.xml" in which we have specified that the activity to be performed at startup is "MainActivity" (note "extends Activity" in the class) and the orientation of the screen is "portrait", that is vertically.
If we wanted to set the screen horizontally, then the mode would be "landscape", while omitting the orientation (android: screenOrientation) we will make sure that the rotation event occurs when performed by the operator with the rotation of the smartphone.

When the app starts, the method "onCreate()" is called. With "requestWindowFeature(Window.FEATURE_NO_TITLE);" we hide the title at the top of the screen and with "getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);" we set it to full screen mode.

We arrive at "GLSurfaceView", that is a dedicated surface for graphics with OpenGL.

In order to use this surface we created the class "MainView" (note "extends GLSurfaceView"), class defined when running the app with "this.glSurfaceView = new MainView(this);" and associated at the activity with "setContentView(this.glSurfaceView);".

Who really does the drawing is the "Renderer", so we have created the class "MainRenderer" (note "implements GLSurfaceView.Renderer").

When we defined "this.glSurfaceView", we called "MainView(Context context)" where we defined "Renderer" with "this.glRenderer = new MainRenderer();" and associated at "this.glSurfaceView" with "setRenderer(this.glRenderer);".

Before continuing, let's briefly review the methods "onPause()" and "onResume()" presents in the "MainActivity". Here we find "this.glSurfaceView.onPause();" and "this.glSurfaceView.onResume();", this is used to pause and restore "this.glSurfaceView" when the app is paused or restored by the user; in the absence "this.glSurfaceView" would continue to be active.

In the "Renderer" we find 3 methods:
- "onSurfaceCreated()", performed when the surface is created or recreated;
- "onSurfaceChanged()", performed after "onSurfaceCreated" and when the surface changes dimensions (for example after the rotation of the smartphone);
- "onDrawFrame()", performed after the previous methods, continuously, for drawing.

In the "onSurfaceCreated()" method:
- we set the color that we will use when we delete the frame, in this case, with "GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);", we set black without transparency. The range of values ​​is from 0.0f to 1.0f, correspondingly to RGBA values ​​from 0 to 255.

In the "onSurfaceChanged()" method:
- we set the variables "width" and "height" (width and height in pixels of our screen detected on the basis of orientation) for possible future use;
- we set the "Viewport" with "GLES20.glViewport(0, 0, this.width, this.height);". "Viewport" is the rectangular region of the screen where the frame will be displayed and, in our case, we are specifying the entire screen, from the 0,0 position (lower left corner) for a width (this.width) and a height (this.height);
- we set the projection matrix, and in our case, for 2D, with "Matrix.orthoM(this.projMatrix, 0, 0.0f, this.width - 1.0f, 0.0f, this.height - 1.0f, 5.0f, 10.0f);";
- we set the camera matrix with "Matrix.setLookAtM(this.viewMatrix, 0, 0.0f, 0.0f, -5.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f);";

In the "onDrawFrame()" method:
- we draw the frame. The "glClear" instruction clears the frame using only the color set with "glClearColor" as we are telling him to use only the one with the "GLES20.GL_COLOR_BUFFER_BIT" parameter.

Let's see better the orthogonal projection that we are using.


In this system, as you notice in the front panel, the depth of one object compared to another does not affect the aspect of the dimensions, we see them as if they were all at the same distance, the difference is made only by the order in they are placed.

With "Matrix.orthoM" we defined the volume (A-B) of our projection, specifying the limits:
- for the X axis: left 0.0f, right this.width - 1.0f
- for the Y axis: low 0.0f, high this.height - 1.0f
- for the Z axis: near 5.0f, far 10.0f

Let's review the issue "Screen"-"ViewPort"-"orthoM" for clarity.

1. Our screen has a physical width and height (which we measure in pixels), from the bottom left corner (origin coordinate x = 0 y = 0) to the top right corner (coordinate x = width-1 y = height-1).

2. With "Matrix.orthoM" we set the drawing area (in memory) with the following limits:
- left: 0.0f
- right: this.width - 1.0f
- bottom: 0.0f
- top: this.height - 1.0f

3. With "ViewPort" we define the position and the area on the screen that will contain the frame present in memory.

Then, the frame drawn in memory is displayed on the screen in the position and in the area of ​​the viewport and adapted if necessary, adapted if the reference system in memory is not in proportion with the viewport.

In our case, we have created a coordinate system in memory that coincides with the total size of the screen and also of the viewport, we have a correspondence of 1:1.

If we set with "Matrix.orthoM" the following limits (for vertical orientation):
- left: 0.0f
- right: 1.0f
- bottom: 0.0f
- top: this.height / this.width
we have a system of coordinated that, however strange it may seem, allows us to draw safely and, respecting the proportions with the viewport.

In the next basic tutorials we will continue to use the 1:1 match system, while in the advanced ones we will show what now "seems strange".

Download Project:
OpenGL ES 2 Tutorial 1

For more information on the instructions, refer to: khronos.org/registry/OpenGL-Refpages/es2.0/

Page created july 22, 2020.






  Back


2007-2020 © Incastro.com
All rights reserved