Après avoir défini les formes à dessiner avec OpenGL, vous souhaiterez probablement les dessiner. Dessiner des formes avec OpenGL ES 2.0 nécessite un peu plus de code que vous ne le pensez, car l'API fournit un contrôle important sur le pipeline de rendu graphique.
Cette leçon explique comment dessiner les formes que vous avez définies dans la leçon précédente à l'aide d'OpenGL API ES 2.0.
Initialiser les formes
Avant de dessiner, vous devez initialiser et charger les formes que vous prévoyez de dessiner. À moins que le
la structure (les coordonnées d'origine) des formes que vous utilisez dans votre programme change au cours du cours.
vous devez les initialiser dans le
Méthode onSurfaceCreated()
de votre
pour améliorer l'efficacité
de la mémoire et du traitement.
Kotlin
class MyGLRenderer : GLSurfaceView.Renderer { ... private lateinit var mTriangle: Triangle private lateinit var mSquare: Square override fun onSurfaceCreated(unused: GL10, config: EGLConfig) { ... // initialize a triangle mTriangle = Triangle() // initialize a square mSquare = Square() } ... }
Java
public class MyGLRenderer implements GLSurfaceView.Renderer { ... private Triangle mTriangle; private Square mSquare; public void onSurfaceCreated(GL10 unused, EGLConfig config) { ... // initialize a triangle mTriangle = new Triangle(); // initialize a square mSquare = new Square(); } ... }
Dessiner une forme
Dessiner une forme définie à l'aide d'OpenGL ES 2.0 nécessite beaucoup de code, car vous doit fournir beaucoup de détails au pipeline de rendu graphique. Plus précisément, vous devez définir suivantes:
- Vertex Shader : code graphique OpenGL ES permettant d'afficher les sommets d'une forme.
- Nuanceur de fragments : code OpenGL ES permettant d'afficher la face d'une forme avec des couleurs ou et des textures.
- Programme : objet OpenGL ES contenant les nuanceurs que vous souhaitez utiliser pour dessiner une ou plusieurs formes.
Vous avez besoin d'au moins un nuanceur de sommets pour dessiner une forme et un nuanceur de fragments pour la colorer.
Ces nuanceurs doivent être compilés, puis ajoutés à un programme OpenGL ES, qui est ensuite utilisé pour dessiner
la forme. Voici un exemple de définition des nuanceurs de base que vous pouvez utiliser pour dessiner une forme dans
Classe Triangle
:
Kotlin
class Triangle { private val vertexShaderCode = "attribute vec4 vPosition;" + "void main() {" + " gl_Position = vPosition;" + "}" private val fragmentShaderCode = "precision mediump float;" + "uniform vec4 vColor;" + "void main() {" + " gl_FragColor = vColor;" + "}" ... }
Java
public class Triangle { private final String vertexShaderCode = "attribute vec4 vPosition;" + "void main() {" + " gl_Position = vPosition;" + "}"; private final String fragmentShaderCode = "precision mediump float;" + "uniform vec4 vColor;" + "void main() {" + " gl_FragColor = vColor;" + "}"; ... }
Les nuanceurs contiennent du code GLSL (OpenGL Shading Language) qui doit être compilé avant d'être utilisé dans l'environnement OpenGL ES. Pour compiler ce code, créez une méthode utilitaire dans votre classe de moteur de rendu:
Kotlin
fun loadShader(type: Int, shaderCode: String): Int { // create a vertex shader type (GLES20.GL_VERTEX_SHADER) // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER) return GLES20.glCreateShader(type).also { shader -> // add the source code to the shader and compile it GLES20.glShaderSource(shader, shaderCode) GLES20.glCompileShader(shader) } }
Java
public static int loadShader(int type, String shaderCode){ // create a vertex shader type (GLES20.GL_VERTEX_SHADER) // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER) int shader = GLES20.glCreateShader(type); // add the source code to the shader and compile it GLES20.glShaderSource(shader, shaderCode); GLES20.glCompileShader(shader); return shader; }
Pour dessiner votre forme, vous devez compiler le code du nuanceur et l'ajouter à un programme OpenGL ES puis liez le programme. Effectuez cette opération dans le constructeur de votre objet dessiné pour n'effectuer une seule fois.
Remarque:La compilation des nuanceurs OpenGL ES et l'association de programmes sont coûteuses. en termes de cycles de processeur et de temps de traitement. Évitez donc de le faire plusieurs fois. Si vous vous ne connaissez pas le contenu de vos nuanceurs lors de l'exécution, vous devez créer votre code de sorte qu'ils ne créés une fois, puis mis en cache pour une utilisation ultérieure.
Kotlin
class Triangle { ... private var mProgram: Int init { ... val vertexShader: Int = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode) val fragmentShader: Int = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode) // create empty OpenGL ES Program mProgram = GLES20.glCreateProgram().also { // add the vertex shader to program GLES20.glAttachShader(it, vertexShader) // add the fragment shader to program GLES20.glAttachShader(it, fragmentShader) // creates OpenGL ES program executables GLES20.glLinkProgram(it) } } }
Java
public class Triangle() { ... private final int mProgram; public Triangle() { ... int vertexShader = MyGLRenderer.loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode); int fragmentShader = MyGLRenderer.loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode); // create empty OpenGL ES Program mProgram = GLES20.glCreateProgram(); // add the vertex shader to program GLES20.glAttachShader(mProgram, vertexShader); // add the fragment shader to program GLES20.glAttachShader(mProgram, fragmentShader); // creates OpenGL ES program executables GLES20.glLinkProgram(mProgram); } }
À ce stade, vous êtes prêt à ajouter les appels réels qui dessinent votre forme. Dessiner des formes avec OpenGL ES exige que vous spécifiiez plusieurs paramètres pour indiquer au pipeline de rendu ce que vous voulez et comment le dessiner. Les options de dessin pouvant varier en fonction de la forme, il est conseillé d'utiliser les classes de forme contiennent leur propre logique de dessin.
Créez une méthode draw()
pour dessiner la forme. Ce code définit la position et
des valeurs de couleur au nuanceur de sommets et au nuanceur de fragments de la forme, puis exécute le dessin
.
Kotlin
private var positionHandle: Int = 0 private var mColorHandle: Int = 0 private val vertexCount: Int = triangleCoords.size / COORDS_PER_VERTEX private val vertexStride: Int = COORDS_PER_VERTEX * 4 // 4 bytes per vertex fun draw() { // Add program to OpenGL ES environment GLES20.glUseProgram(mProgram) // get handle to vertex shader's vPosition member positionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition").also { // Enable a handle to the triangle vertices GLES20.glEnableVertexAttribArray(it) // Prepare the triangle coordinate data GLES20.glVertexAttribPointer( it, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, vertexBuffer ) // get handle to fragment shader's vColor member mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor").also { colorHandle -> // Set color for drawing the triangle GLES20.glUniform4fv(colorHandle, 1, color, 0) } // Draw the triangle GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount) // Disable vertex array GLES20.glDisableVertexAttribArray(it) } }
Java
private int positionHandle; private int colorHandle; private final int vertexCount = triangleCoords.length / COORDS_PER_VERTEX; private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex public void draw() { // Add program to OpenGL ES environment GLES20.glUseProgram(mProgram); // get handle to vertex shader's vPosition member positionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition"); // Enable a handle to the triangle vertices GLES20.glEnableVertexAttribArray(positionHandle); // Prepare the triangle coordinate data GLES20.glVertexAttribPointer(positionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, vertexBuffer); // get handle to fragment shader's vColor member colorHandle = GLES20.glGetUniformLocation(mProgram, "vColor"); // Set color for drawing the triangle GLES20.glUniform4fv(colorHandle, 1, color, 0); // Draw the triangle GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount); // Disable vertex array GLES20.glDisableVertexAttribArray(positionHandle); }
Une fois le code en place, il suffit d'appeler la fonction
draw()
depuis la méthode onDrawFrame()
de votre moteur de rendu:
Kotlin
override fun onDrawFrame(unused: GL10) { ... mTriangle.draw() }
Java
public void onDrawFrame(GL10 unused) { ... mTriangle.draw(); }
Lorsque vous exécutez l'application, elle doit se présenter comme suit:
Cet exemple de code présente quelques problèmes. Tout d'abord, cela ne va pas impressionner votre
amis. Deuxièmement, le triangle est un peu écrasé et change de forme lorsque vous changez d'écran
l'orientation de l'appareil. La forme est déformée en raison du fait que la partie
sommets n'ont pas été corrigés pour les proportions de la zone d'écran où les
GLSurfaceView
s'affiche. Vous pouvez résoudre ce problème en utilisant une projection et une caméra
dans la prochaine leçon.
Enfin, le triangle est fixe, ce qui est un peu ennuyeux. Dans Ajouter un mouvement, vous créez cette forme faire pivoter et faire une utilisation plus intéressante du pipeline graphique OpenGL ES.