当前位置:网站首页>OpenGL ES 学习初识(1)
OpenGL ES 学习初识(1)
2022-07-06 07:01:00 【换元不配限】
1.认识OpenGL ES
OpenGL是跨平台2D/3D图形API,而OpenGL ES是在前者基础上扩展的版本,适用于手机等嵌入式设备。具有以下版本:
版本 | 解释 |
---|---|
OpenGL ES 1.0 | 2003年发布 |
OpenGL ES 1.1 | 支持了多纹理,顶点缓冲对象等 |
OpenGL ES 2.0 | 2007年发布,基于OpenGL 2.0,删除了1.x中的固定管线(pipeline),提供可编程的管线 |
OpenGL ES 3.0 | 2012年发布,向后兼容2.x |
OpenGL ES 3.1 | 2014年发布 |
OpenGL ES 3.2 | 2015年发布 |
OpenGL ES 1.x 和OpenGL ES2.0不兼容的,是完全两种不同的实现。
OpenGL EX 1.x仅支持固定管线渲染,可以固件支持或软件模拟,但渲染能力有限。
OpenGL EX 2.X采用可编程的渲染管线,提高了渲染能力。但编程难度也提高了,且要求设备中必须有对应GPU硬件支持。
上面图3-16和图3-21分别是OpenGL 1.x和OpenGL 2.x渲染管线过程示意图。“变换和光照”---->“顶点着色器”
“纹理环境和颜色求和”、“雾”、“Alpha测速”---->“片元着色器”
本系列文章将主要介绍OpenGL ES2.x
2.初识OpenGL ES2.x程序面貌
在认识了OpenGL ES基础后,下面直接带来一个入门级Demo(本程序官网也有类似的可以学习)。代码中添加了注释,可以更好帮助入门学习。
2.1 效果图
2.2 工具类ShaderUtil.java
public class ShaderUtil {
private static final String TAG = "ShaderUtil";
/**
* 加载着色器编码进GPU并编译
*
* @param shaderType 着色器类型
* @param source 着色器脚本字符串
* @return
*/
public static int loadShader(int shaderType, String source) {
int shader = GLES20.glCreateShader(shaderType);
if (shader != 0) {
GLES20.glShaderSource(shader, source);
GLES20.glCompileShader(shader);
int[] compiled = new int[1];
GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
if (compiled[0] == 0) {
Log.e(TAG, "loadShader: ES20_ERROR,Could not compile shader " + shaderType + ";");
Log.e(TAG, "loadShader: ES20_ERROR,shader = " + GLES20.glGetShaderInfoLog(shader));
GLES20.glDeleteShader(shader);
shader = 0;
}
}
return shader;
}
/**
* 创建着色器程序
*
* @param vertexSource
* @param fragmentSource
* @return
*/
public static int createProgram(String vertexSource, String fragmentSource) {
//加载顶点着色器
int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
if (vertexShader == 0) {
return 0;
}
//加载片元着色器
int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
if (pixelShader == 0) {
return 0;
}
int program = GLES20.glCreateProgram();
if (program != 0) {
GLES20.glAttachShader(program, vertexShader);
checkGLError("glAttachShader");
GLES20.glAttachShader(program, pixelShader);
checkGLError("glAttachShader");
GLES20.glLinkProgram(program);
int[] linkStatus = new int[1];
GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
if (linkStatus[0] != GLES20.GL_TRUE) {
Log.e(TAG, "createProgram: ES20_ERROR,Could not link program");
Log.e(TAG, "createProgram: ES20_ERROR,program = " + GLES20.glGetProgramInfoLog(program));
GLES20.glDeleteProgram(program);
program = 0;
}
}
return program;
}
/**
* 检测每一步操作是否有错误的方法
*
* @param op
*/
public static void checkGLError(String op) {
int error;
while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
Log.d(TAG, ">>>>>> " + op + " :glError " + error);
throw new RuntimeException(op + ":glError " + error);
}
}
/**
* 从sh脚本中加载着色器内容
*
* @param fname
* @param resources
* @return
*/
public static String loadFromAssetsFile(String fname, Resources resources) {
String result = null;
try {
InputStream in = resources.getAssets().open(fname);
int ch = 0;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
while ((ch = in.read()) != -1) {
baos.write(ch);//获取的信息写入流
}
byte[] buffer = baos.toByteArray();
baos.close();
in.close();
result = new String(buffer, "UTF-8");
result = result.replaceAll("\\r\\n", "\n");
} catch (IOException e) {
e.printStackTrace();
}
Log.d(TAG, "loadFromAssetsFile: result = " + result);
return result;
}
}
2.3 三角形Triangle
public class Triangle {
public static float[] mProjMatrix = new float[16];//4x4投影矩阵
public static float[] mVMatrix = new float[16];//摄像机位置朝向的参数矩阵
public static float[] mMvPMatrix;//总变换矩阵
private int program;//自定义渲染管线着色器程序id
private int muMVPMatrixHandle;//总变换矩阵引用
private int maPositionHandle;//顶点位置属性引用
private int maColorHandle;//顶点颜色属性引用
private String mVertexShader;//顶点着色器代码脚本
private String mFragmentShader;//片元着色器代码脚本
private static float[] mMMatrix = new float[16];//具体物体的3D变换矩阵,包括旋转、平移、缩放
private FloatBuffer mVertexBuffer;//顶点坐标数据缓冲
private FloatBuffer mColorBuffer;//顶点着色数据缓冲
private int vCount = 0;//顶点数量
public float xAngle = 0;//绕X轴旋转的角度
public Triangle(MyTDView myTDView) {
initVertexData();
initShader(myTDView);
}
/**
* 初始化着色器
* @param myTDView
*/
private void initShader(MyTDView myTDView) {
mVertexShader = ShaderUtil.loadFromAssetsFile("vertex.sh", myTDView.getResources());
mFragmentShader = ShaderUtil.loadFromAssetsFile("frag.sh", myTDView.getResources());
program = ShaderUtil.createProgram(mVertexShader, mFragmentShader);
maPositionHandle = GLES20.glGetAttribLocation(program, "aPosition");
maColorHandle = GLES20.glGetAttribLocation(program, "aColor");
muMVPMatrixHandle = GLES20.glGetUniformLocation(program, "uMVPMatrix");
}
/**
* 绘制三角形
*/
public void drawSelf() {
GLES20.glUseProgram(program);
Matrix.setRotateM(mMMatrix, 0, 0, 0, 1, 0);//初始化变换矩阵
Matrix.translateM(mMMatrix, 0, 0, 0, 1);//设置沿Z轴正向位移
Matrix.rotateM(mMMatrix, 0, xAngle, 1, 0, 0);//设置绕X轴旋转
GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, Triangle.getFinalMatrix(mMMatrix), 0);
GLES20.glVertexAttribPointer(maPositionHandle, 3, GLES20.GL_FLOAT, false, 3 * 4, mVertexBuffer);
GLES20.glVertexAttribPointer(maColorHandle, 4, GLES20.GL_FLOAT, false, 4 * 4, mColorBuffer);
GLES20.glEnableVertexAttribArray(maPositionHandle);
GLES20.glEnableVertexAttribArray(maColorHandle);
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vCount);
}
/**
* 初始化顶点数据
*/
private void initVertexData() {
vCount = 3;
final float UNIT_SIZE = 0.2f;//设置单位长度
//顶点坐标数组
float vertices[] = new float[]{
-4 * UNIT_SIZE, 0, 0,
0, -4 * UNIT_SIZE, 0,
4 * UNIT_SIZE, 0, 0
};
ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 4);
vbb.order(ByteOrder.nativeOrder());//设置字节顺序为本地操作系统顺序
mVertexBuffer = vbb.asFloatBuffer();//转换为浮点型缓冲
mVertexBuffer.put(vertices);//在缓冲区内写入数据
mVertexBuffer.position(0);//设置缓冲区起始位置
//顶点颜色数组
float colors[] = new float[]{
1, 1, 1, 0,
0, 0, 1, 0,
0, 1, 0, 0
};
ByteBuffer cbb = ByteBuffer.allocateDirect(colors.length * 4);
cbb.order(ByteOrder.nativeOrder());
mColorBuffer = cbb.asFloatBuffer();
mColorBuffer.put(colors);
mColorBuffer.position(0);
}
/**
* 最终变换矩阵
*
* @param spec
* @return
*/
public static float[] getFinalMatrix(float[] spec) {
//初始化总变换矩阵
mMvPMatrix = new float[16];
Matrix.multiplyMM(mMvPMatrix, 0, mVMatrix, 0, spec, 0);
Matrix.multiplyMM(mMvPMatrix, 0, mProjMatrix, 0, mMvPMatrix, 0);
return mMvPMatrix;
}
}
2.4 渲染类MyTDView
GLSurfaceView和GLSurfaceView.Render的使用
public class MyTDView extends GLSurfaceView {
private static final String TAG = "MyTDView";
//每次三角形旋转的角度
private final float ANGLE_SPAN = 0.375f;
//线程
private RotateThread mRotateThread;
//自定义渲染器
private SceneRenderer mSceneRenderer;
public MyTDView(Context context) {
super(context);
//使用OpenGLES2.0
this.setEGLContextClientVersion(2);
mSceneRenderer = new SceneRenderer();
this.setRenderer(mSceneRenderer);
this.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
}
private class SceneRenderer implements GLSurfaceView.Renderer {
Triangle mTriangle;
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
GLES20.glClearColor(0, 0, 0, 1.0f);//设置屏幕背景颜色
mTriangle = new Triangle(MyTDView.this);
GLES20.glEnable(GLES20.GL_DEPTH_TEST);
mRotateThread = new RotateThread();
mRotateThread.start();
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
GLES20.glViewport(0, 0, width, height);//设置视口
float ratio = width / height;
//设置透视投影
Matrix.frustumM(Triangle.mProjMatrix, 0, -ratio, ratio, -1, 1, 1, 10);
//设置摄像机
Matrix.setLookAtM(Triangle.mVMatrix, 0, 0, 0, 3,
0f, 0f, 0f, 0f, 1.0f, 0.0f);
}
@Override
public void onDrawFrame(GL10 gl) {
GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
mTriangle.drawSelf();
}
}
private class RotateThread extends Thread {
//设置循环标识位
public boolean flag = true;
@Override
public void run() {
super.run();
while (flag) {
mSceneRenderer.mTriangle.xAngle = mSceneRenderer.mTriangle.xAngle + ANGLE_SPAN;
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
2.5 顶点着色器和片元着色器代码
顶点着色器和片元着色器资源放置在assets目录下。本案例使用的如下所示:
2.5.1 frag.sh
precision mediump float;
varying vec4 vColor;
void main(){
gl_FragColor = vColor;
}
2.5.2 vertex.sh顶点着色器
uniform mat4 uMVPMatrix;//总变换矩阵
attribute vec3 aPosition;//顶点位置
attribute vec4 aColor;//顶点颜色
varying vec4 vColor;//用于传递给片元着色器的易变变量
void main(){
gl_Position = uMVPMatrix * vec4(aPosition,1);//根据总变换矩阵计算此次绘制顶点的位置
vColor = aColor;
}
2.6 其他
public class MainActivity extends AppCompatActivity {
private MyTDView mTDView;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
mTDView = new MyTDView(this);
mTDView.requestFocus();
mTDView.setFocusableInTouchMode(true);
setContentView(mTDView);
}
@Override
protected void onResume() {
super.onResume();
mTDView.onResume();
}
@Override
protected void onPause() {
super.onPause();
mTDView.onPause();
}
@Override
protected void onDestroy() {
super.onDestroy();
}
}
本示例程序可以直接运行,查看效果。
【参考】
- https://www.khronos.org/opengles/
- 《OpenGL ES应用开发实践指南 Android卷 Kevin Brothaler》
- https://en.wikipedia.org/wiki/OpenGL_ES
- https://developer.android.google.cn/guide/topics/graphics/opengl
- https://developer.android.google.cn/training/graphics/opengl
- Android3D游戏开发技术宝典OpenGL ES 2.0
边栏推荐
- 前缀和数组系列
- 漏了监控:Zabbix对Eureka instance状态监控
- UNIPRO Gantt chart "first experience": multi scene exploration behind attention to details
- Simple use of JWT
- At the age of 26, I changed my career from finance to software testing. After four years of precipitation, I have been a 25K Test Development Engineer
- The psychological process from autojs to ice fox intelligent assistance
- 简单描述 MySQL 中,索引,主键,唯一索引,联合索引 的区别,对数据库的性能有什么影响(从读写两方面)
- Call, apply, bind rewrite, easy to understand with comments
- Is it difficult for girls to learn software testing? The threshold for entry is low, and learning is relatively simple
- Pallet management in SAP SD delivery process
猜你喜欢
kubernetes集群搭建Zabbix监控平台
作者已死?AI正用藝術征服人類
19. Actual memory management of segment page combination
基于PyTorch和Fast RCNN快速实现目标识别
26岁从财务转行软件测试,4年沉淀我已经是25k的测开工程师...
Huawei equipment configuration ospf-bgp linkage
C语言_双创建、前插,尾插,遍历,删除
After sharing the clone remote project, NPM install reports an error - CB () never called! This is an error with npm itself.
因高额网络费用,Arbitrum 奥德赛活动暂停,Nitro 发行迫在眉睫
Interface automation test framework: pytest+allure+excel
随机推荐
Leetcode daily question (1870. minimum speed to arrive on time)
26岁从财务转行软件测试,4年沉淀我已经是25k的测开工程师...
PCL realizes frame selection and clipping point cloud
AttributeError: Can‘t get attribute ‘SPPF‘ on <module ‘models.common‘ from ‘/home/yolov5/models/comm
UWA Pipeline 2.2.1 版本更新说明
Introduction to ros2 installation and basic knowledge
《从0到1:CTFer成长之路》书籍配套题目(周更)
Refer to how customer push e-commerce does content operation
AttributeError: Can‘t get attribute ‘SPPF‘ on <module ‘models. common‘ from ‘/home/yolov5/models/comm
Fedora/rehl installation semanage
作者已死?AI正用艺术征服人类
因高额网络费用,Arbitrum 奥德赛活动暂停,Nitro 发行迫在眉睫
L'Ia dans les nuages rend la recherche géoscientifique plus facile
Pallet management in SAP SD delivery process
19. Actual memory management of segment page combination
When my colleague went to the bathroom, I helped my product sister easily complete the BI data product and got a milk tea reward
UWA pipeline version 2.2.1 update instructions
Apache DolphinScheduler源码分析(超详细)
Map of mL: Based on the adult census income two classification prediction data set (whether the predicted annual income exceeds 50K), use the map value to realize the interpretable case of xgboost mod
Reflex WMS中阶系列3:显示已发货可换组