【OpenGL ES】繪制正方形

1 前言

【OpenGL ES】繪制三角形 中介紹了繪制三角形的方法,本文將介紹繪制正方形的方法。

OpenGL 以點(diǎn)、線段、三角形為圖元,沒有提供繪制正方形內(nèi)部的接口。要繪制正方形內(nèi)部,必須通過(guò)三角形拼接而成,如下圖,是通過(guò)GL_TRIANGLE_FAN 模式繪制正方形。

img

繪制的坐標(biāo)點(diǎn)如下,由于此坐標(biāo)是以畫布中心為坐標(biāo)原點(diǎn),水平向右為 x 軸正方向,豎直向上為 y 軸正方向,其值是相對(duì)畫布寬度和高度的比值。一般而言,畫布寬度和高度不相等,造成同樣數(shù)值的 x 和 y,對(duì)應(yīng)的 dp 值不相等。

float[] vertex = new float[] {
 0.5f, 0.5f, 0.0f,
 -0.5f, 0.5f, 0.0f,
 -0.5f, -0.5f, 0.0f,
 0.5f, -0.5f, 0.0f
};

因此,需要對(duì)坐標(biāo)進(jìn)行調(diào)整,如下:

public float[] adjustCoord(float[] coords, int width, int height) {
 float ratio = width > height ? (1.0f * height / width) : (1.0f * width / height);
 int start = width > height ? 0 : 1;
 float[] tempCoord =  Arrays.copyOf(coords, coords.length);
 int num = tempCoord.length / 3;
 for (int i = 0; i < num; i++) {
 tempCoord[start + i * 3] *= ratio;
 }
 return tempCoord;
}

本文完整代碼資源見→【OpenGL ES】繪制正方形

項(xiàng)目目錄如下:

2 案例

MainActivity.java

package com.zhyan8.square;

import android.opengl.GLSurfaceView;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {
 private GLSurfaceView mGlSurfaceView;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 mGlSurfaceView = new MyGLSurfaceView(this);
 setContentView(mGlSurfaceView);
 mGlSurfaceView.setRenderer(new MyRender(this));
 }

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

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

MyGLSurfaceView.java

package com.zhyan8.square;

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

public class MyGLSurfaceView extends GLSurfaceView {
 public MyGLSurfaceView(Context context) {
 super(context);
 setEGLContextClientVersion(3);
 }

 public MyGLSurfaceView(Context context, AttributeSet attrs) {
 super(context, attrs);
 setEGLContextClientVersion(3);
 }
}

MyRender.java

package com.zhyan8.square;

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

import java.nio.FloatBuffer;

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

public class MyRender implements GLSurfaceView.Renderer {
 private FloatBuffer vertexBuffer;
 private GLUtils mGLUtils;

 public MyRender(Context context) {
 mGLUtils = new GLUtils(context);
 }

 @Override
 public void onSurfaceCreated(GL10 gl, EGLConfig eglConfig) {
 //設(shè)置背景顏色
 GLES30.glClearColor(0.1f, 0.2f, 0.3f, 0.4f);
 //編譯著色器
 final int vertexShaderId = mGLUtils.compileShader(GLES30.GL_VERTEX_SHADER, R.raw.vertex_shader);
 final int fragmentShaderId = mGLUtils.compileShader(GLES30.GL_FRAGMENT_SHADER, R.raw.fragment_shader);
 //鏈接程序片段
 int programId = mGLUtils.linkProgram(vertexShaderId, fragmentShaderId);
 GLES30.glUseProgram(programId);
 }

 @Override
 public void onSurfaceChanged(GL10 gl, int width, int height) {
 //設(shè)置視圖窗口
 GLES30.glViewport(0, 0, width, height);
 getFloatBuffer(width, height);
 }

 @Override
 public void onDrawFrame(GL10 gl) {
 //將顏色緩沖區(qū)設(shè)置為預(yù)設(shè)的顏色
 GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);
 GLES30.glLineWidth(5);
 //啟用頂點(diǎn)的數(shù)組句柄
 GLES30.glEnableVertexAttribArray(0);
 //準(zhǔn)備坐標(biāo)數(shù)據(jù)
 GLES30.glVertexAttribPointer(0, 3, GLES30.GL_FLOAT, false, 0, vertexBuffer);
//        GLES30.glDrawArrays(GLES30.GL_POINTS, 0, 4); //繪制正方形的四個(gè)頂點(diǎn)
//        GLES30.glDrawArrays(GLES30.GL_LINE_LOOP, 0, 4); //繪制正方形的四條邊
 GLES30.glDrawArrays(GLES30.GL_TRIANGLE_FAN, 0, 4); //繪制正方形的內(nèi)部
 //禁止頂點(diǎn)數(shù)組句柄
 GLES30.glDisableVertexAttribArray(0);
 }

 private void getFloatBuffer(int width, int height) {
 float[] vertex = new float[] {
 0.5f, 0.5f, 0.0f,
 -0.5f, 0.5f, 0.0f,
 -0.5f, -0.5f, 0.0f,
 0.5f, -0.5f, 0.0f
 };
 float[] tempVertex = mGLUtils.adjustCoord(vertex, width, height);
 vertexBuffer = mGLUtils.getFloatBuffer(tempVertex);
 }
}

GLUtils.java

package com.zhyan8.square;

import android.content.Context;
import android.opengl.GLES30;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.util.Arrays;

public class GLUtils {
 private Context mContext;

 public GLUtils(Context context) {
 mContext = context;
 }

 //調(diào)整坐標(biāo)
 public float[] adjustCoord(float[] coords, int width, int height) {
 float ratio = width > height ? (1.0f * height / width) : (1.0f * width / height);
 int start = width > height ? 0 : 1;
 float[] tempCoord =  Arrays.copyOf(coords, coords.length);
 int num = tempCoord.length / 3;
 for (int i = 0; i < num; i++) {
 tempCoord[start + i * 3] *= ratio;
 }
 return tempCoord;
 }

 public FloatBuffer getFloatBuffer(float[] floatArr) {
 FloatBuffer fb = ByteBuffer.allocateDirect(floatArr.length * Float.BYTES)
 .order(ByteOrder.nativeOrder())
 .asFloatBuffer();
 fb.put(floatArr);
 fb.position(0);
 return fb;
 }

 //通過(guò)代碼片段編譯著色器
 public int compileShader(int type, String shaderCode){
 int shader = GLES30.glCreateShader(type);
 GLES30.glShaderSource(shader, shaderCode);
 GLES30.glCompileShader(shader);
 return shader;
 }

 //通過(guò)外部資源編譯著色器
 public int compileShader(int type, int shaderId){
 String shaderCode = readShaderFromResource(shaderId);
 return compileShader(type, shaderCode);
 }

 //鏈接到著色器
 public int linkProgram(int vertexShaderId, int fragmentShaderId) {
 final int programId = GLES30.glCreateProgram();
 //將頂點(diǎn)著色器加入到程序
 GLES30.glAttachShader(programId, vertexShaderId);
 //將片元著色器加入到程序
 GLES30.glAttachShader(programId, fragmentShaderId);
 //鏈接著色器程序
 GLES30.glLinkProgram(programId);
 return programId;
 }

 //從shader文件讀出字符串
 private String readShaderFromResource(int shaderId) {
 InputStream is = mContext.getResources().openRawResource(shaderId);
 BufferedReader br = new BufferedReader(new InputStreamReader(is));
 String line;
 StringBuilder sb = new StringBuilder();
 try {
 while ((line = br.readLine()) != null) {
 sb.append(line);
 sb.append("\n");
 }
 br.close();
 } catch (Exception e) {
 e.printStackTrace();
 }
 return sb.toString();
 }
}

vertex_shader.glsl

#version 300 es
layout (location = 0) in vec4 vPosition;
void main() {
 gl_Position  = vPosition;
 gl_PointSize = 20.0; //頂點(diǎn)大?。℅L_POINTS模式下才會(huì)生效)
}

頂點(diǎn)著色器的作用:進(jìn)行矩陣變換位置、根據(jù)光照公式計(jì)算頂點(diǎn)顏?、?成 / 變換紋理坐標(biāo),并且把位置和紋理坐標(biāo)發(fā)送到片元著色器。

頂點(diǎn)著色器中,如果沒有指定默認(rèn)精度,則 int 和 float 的默認(rèn)精度都為 highp。

fragment_shader.glsl

#version 300 es
precision mediump float; //聲明float型變量的精度為mediump
out vec4 fragColor;
void main() {
 fragColor = vec4(0.2, 1.0, 1.0, 1.0); //頂點(diǎn)、邊線、內(nèi)部填充顏色
}

片元著色器的作用:處理經(jīng)光柵化階段生成的每個(gè)片元,計(jì)算每個(gè)像素的顏色和透明度。

在片元著色器中,浮點(diǎn)值沒有默認(rèn)的精度值,每個(gè)著色器必須聲明一個(gè)默認(rèn)的 float 精度。

運(yùn)行結(jié)果:

1)GL_POINTS 模式

2)GL_LINE_LOOP 模式

3)GL_TRIANGLE_FAN 模式

聲明:本文轉(zhuǎn)自【OpenGL ES】繪制正方形

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容