Nachdem Sie mit OpenGL gezeichnete Formen definiert haben, möchten Sie diese wahrscheinlich zeichnen. Formen zeichnen mit OpenGL ES 2.0 erfordert etwas mehr Code, als Sie sich vorstellen können, da die API eine Kontrolle über die Grafik-Rendering-Pipeline.
In dieser Lektion erfahren Sie, wie Sie die Formen, die Sie in der vorherigen Lektion definiert haben, mithilfe des OpenGL- ES 2.0 API.
Formen initialisieren
Bevor Sie zeichnen, müssen Sie die Formen, die Sie zeichnen möchten, initialisieren und laden. Es sei denn, die
Struktur (die ursprünglichen Koordinaten) der Formen, die Sie in Ihrem Programm verwenden, ändern sich im Laufe des Kurses
der Ausführung sollten Sie sie im
onSurfaceCreated()
Methode Ihres
Renderer für Speicher- und Verarbeitungseffizienz.
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(); } ... }
Formen zeichnen
Das Zeichnen einer definierten Form mit OpenGL ES 2.0 erfordert sehr viel Code, da Sie viele Details für die Grafik-Rendering-Pipeline bereitstellen müssen. Insbesondere müssen Sie die Folgendes:
- Vertex Shader: OpenGL ES-Grafikcode zum Rendern der Eckpunkte einer Form.
- Fragment Shader: OpenGL ES-Code zum Rendern der Fläche einer Form mit Farben oder Texturen.
- Programm: Ein OpenGL ES-Objekt, das die Shader enthält, die Sie zum Zeichnen verwenden möchten. eine oder mehrere Formen.
Sie benötigen mindestens einen Scheitel-Shader zum Zeichnen einer Form und einen Fragment-Shader zum Einfärben der Form.
Diese Shader müssen kompiliert und dann einem OpenGL ES-Programm hinzugefügt werden, das dann zum Zeichnen von
der Form. Hier ist ein Beispiel für die Definition von Basis-Shadern, die Sie zum Zeichnen einer Form in der
Klasse 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;" + "}"; ... }
Shader enthalten OpenGL Shading Language-Code (GLSL), der vor der Verwendung in OpenGL ES-Umgebung ausführen. Erstellen Sie zum Kompilieren dieses Codes eine Dienstprogrammmethode in Ihrer Renderer-Klasse:
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; }
Um Ihre Form zu zeichnen, müssen Sie den Shader-Code kompilieren und ihn einem OpenGL ES-Programm hinzufügen Objekt und dann das Programm verknüpfen. Führen Sie diesen Schritt im Konstruktor des gezeichneten Objekts aus. einmal.
Hinweis:Das Kompilieren von OpenGL ES-Shadern und Verknüpfungsprogrammen ist teuer. in Bezug auf CPU-Zyklen und Verarbeitungszeit, daher sollten Sie dies nicht mehrmals tun. Wenn Sie den Inhalt Ihrer Shader zur Laufzeit nicht kennen, sollten Sie Ihren Code so erstellen, werden einmal erstellt und dann für die spätere Verwendung im Cache gespeichert.
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); } }
Jetzt können Sie die tatsächlichen Aufrufe hinzufügen, die Ihre Form zeichnen. Formen zeichnen mit Bei OpenGL ES musst du mehrere Parameter angeben, um der Rendering-Pipeline mitzuteilen, was du möchtest. und wie sie gezeichnet werden. Da die Zeichnungsoptionen je nach Form variieren können, ist es ratsam, Ihre Formklassen enthalten eine eigene Zeichenlogik.
Erstellen Sie eine draw()
-Methode zum Zeichnen der Form. Mit diesem Code werden die Position und
Farbwerte an den Scheitelpunkt-Shader und den Fragment-Shader der Form zu und führt dann die Zeichnung aus.
.
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); }
Sobald Sie den gesamten Code eingerichtet haben, erfordert das Zeichnen dieses Objekts nur einen Aufruf der Methode
draw()
in der onDrawFrame()
-Methode des Renderers:
Kotlin
override fun onDrawFrame(unused: GL10) { ... mTriangle.draw() }
Java
public void onDrawFrame(GL10 unused) { ... mTriangle.draw(); }
Wenn Sie die Anwendung ausführen, sollte sie in etwa so aussehen:
Bei diesem Codebeispiel gibt es einige Probleme. Zunächst einmal wird es Ihre
Freunde. Zweitens ist das Dreieck etwas zusammengedrückt und ändert seine Form, wenn man den Bildschirm
Ausrichtung des Geräts. Die Form ist verzerrt, weil das Objekt
Die Eckpunkte wurden nicht für die Proportionen des Bildschirmbereichs korrigiert, in dem die
GLSurfaceView
wird angezeigt. Dieses Problem können Sie mit einer Projektion und einer Kamera
erhalten Sie in der nächsten Lektion.
Schließlich steht das Dreieck fest, was ein wenig langweilig ist. Im Bewegung hinzufügen, Sie erstellen diese Form. und die Nutzung der OpenGL ES-Grafikpipeline interessanter machen.