หลังจากที่คุณกำหนดรูปร่างที่จะวาดด้วย OpenGL คุณอาจต้องการวาดรูปเหล่านั้น การวาดรูปทรง กับ OpenGL ES 2.0 ใช้โค้ดมากกว่าที่คิดไว้เล็กน้อย เนื่องจาก API ให้ การควบคุมไปป์ไลน์การแสดงผลกราฟิกได้อย่างมาก
บทเรียนนี้อธิบายวิธีวาดรูปร่างที่กำหนดไว้ในบทเรียนก่อนหน้านี้โดยใช้ OpenGL API ES 2.0
เริ่มต้นรูปร่าง
ก่อนที่คุณจะวาดรูปร่างใดๆ คุณต้องเริ่มต้นและโหลดรูปร่างที่วางแผนจะวาด เว้นแต่
โครงสร้าง (พิกัดเดิม) ของรูปร่างที่คุณใช้ในโปรแกรมเปลี่ยนไปในระหว่างหลักสูตร
คุณควรกำหนดค่าเริ่มต้นใน
onSurfaceCreated()
เมธอดของ
เพื่อประสิทธิภาพหน่วยความจำและการประมวลผล
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(); } ... }
วาดรูปร่าง
การวาดรูปร่างที่กำหนดโดยใช้ OpenGL ES 2.0 จำเป็นต้องใช้โค้ดจำนวนมาก เนื่องจากคุณ จะต้องให้รายละเอียดมากมายแก่ไปป์ไลน์การแสดงผลกราฟิก โดยเฉพาะอย่างยิ่ง คุณต้องกำหนด ดังต่อไปนี้:
- Vertex Shader - โค้ดกราฟิก OpenGL ES สำหรับแสดงผลจุดยอดของรูปร่าง
- Fragment Shader - โค้ด OpenGL ES สำหรับแสดงใบหน้าของรูปร่างที่มีสี หรือ พื้นผิว
- โปรแกรม - ออบเจ็กต์ OpenGL ES ที่มีตัวปรับแสงเงาที่คุณต้องการใช้สำหรับการวาด รูปร่างอย่างน้อย 1 รูป
คุณต้องมีตัวปรับแสงเงา Vertex อย่างน้อย 1 ตัวเพื่อวาดรูปร่าง และ Fragment Shield 1 รายการเพื่อใส่สีรูปร่างนั้น
ตัวปรับแสงเงาเหล่านี้จะต้องถูกคอมไพล์แล้วเพิ่มไปยังโปรแกรม OpenGL ES ซึ่งจากนั้นก็จะถูกใช้ในการวาด
รูปร่าง มาดูตัวอย่างวิธีกำหนดตัวปรับแสงเงาพื้นฐานที่คุณสามารถใช้วาดรูปร่างใน
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;" + "}"; ... }
เครื่องมือเฉดสีมีโค้ด OpenGL Shading Language (GLSL) ที่ต้องคอมไพล์ก่อนที่จะใช้ใน สภาพแวดล้อม OpenGL ES หากต้องการคอมไพล์โค้ดนี้ ให้สร้างเมธอดยูทิลิตีในคลาสการแสดงภาพของคุณ ดังนี้
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; }
ในการวาดรูปร่าง คุณจะต้องคอมไพล์โค้ดตัวปรับแสงเงา และเพิ่มลงในโปรแกรม OpenGL ES ออบเจ็กต์ แล้วลิงก์โปรแกรมนั้น ทำเช่นนี้กับตัวสร้างของวัตถุที่วาดไว้ เพื่อดำเนินการให้เสร็จสิ้น ครั้งเดียว
หมายเหตุ: การคอมไพล์ตัวปรับแสงเงา OpenGL ES และโปรแกรมลิงก์มีค่าใช้จ่าย ในแง่ของรอบ CPU และเวลาในการประมวลผล จึงควรหลีกเลี่ยงการทำแบบนี้มากกว่า 1 ครั้ง หากอยากรู้ ไม่ทราบเนื้อหาของเครื่องมือให้เฉดสีขณะรันไทม์ คุณควรสร้างโค้ดเพื่อให้โค้ดดังกล่าว สร้าง 1 ครั้งแล้วแคชไว้ใช้ภายหลัง
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); } }
ถึงจุดนี้ คุณก็พร้อมที่จะเพิ่มการเรียกจริงที่วาดรูปร่างของคุณแล้ว การวาดรูปทรงด้วย OpenGL ES กำหนดให้คุณต้องระบุพารามิเตอร์หลายรายการเพื่อบอกไปป์ไลน์การแสดงผลสิ่งที่คุณต้องการ และวิธีวาด เนื่องจากตัวเลือกการวาดภาพอาจแตกต่างกันไปตามรูปร่าง จึงควรมี คลาสรูปร่างจะมีตรรกะการวาดของตนเอง
สร้างเมธอด draw()
สำหรับการวาดรูปร่าง โค้ดนี้จะกำหนดตำแหน่งและ
ค่าสีไปยังตัวปรับแสงเงา Vertex และตัวปรับแสงเงา Fragment แล้วเริ่มดำเนินการวาดภาพ
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); }
เมื่อคุณมีโค้ดทั้งหมดพร้อมแล้ว การวาดออบเจ็กต์นี้จะต้องมีการเรียกไปยัง
draw()
จากภายในเมธอด onDrawFrame()
ของตัวแสดงผล:
Kotlin
override fun onDrawFrame(unused: GL10) { ... mTriangle.draw() }
Java
public void onDrawFrame(GL10 unused) { ... mTriangle.draw(); }
เมื่อเรียกใช้แอปพลิเคชัน ควรมีลักษณะดังนี้
มีปัญหาบางประการกับตัวอย่างโค้ดนี้ ประการแรกคือ ไม่ได้ทำให้คุณประทับใจ
เพื่อน ประการที่สอง สามเหลี่ยมจะย่องลงเล็กน้อยและเปลี่ยนรูปร่างเมื่อคุณเปลี่ยนหน้าจอ
การวางแนวของอุปกรณ์ เหตุผลที่รูปร่างบิดเบี้ยวเนื่องมาจากการที่วัตถุ
ยังไม่มีการแก้ไขจุดยอด สําหรับสัดส่วนของพื้นที่หน้าจอ
GLSurfaceView
จะปรากฏขึ้น คุณสามารถแก้ไขปัญหานั้นโดยใช้การฉายภาพและกล้อง
ในบทเรียนถัดไป
สุดท้ายคือรูปสามเหลี่ยมที่อยู่กับที่ซึ่งน่าเบื่อ ใน บทเรียนการเพิ่มการเคลื่อนไหว คุณวาดรูปร่างนี้ หมุนเวียนและใช้ประโยชน์จากไปป์ไลน์กราฟิก OpenGL ES ให้น่าสนใจยิ่งขึ้น