OpenGL ES 绘制一个三角形(2)

news2024/11/18 7:44:56

OpenGL ES 绘制一个三角形(2)

简述

本节我们基于Android系统,使用OpenGL ES来实现绘制一个三角形。在OpenGL ES里,三角形是一个基础图形,其他的图形都可以使用三角形拼接而成,所以我们就的案例就基于这个开始。
在Android系统中,提供给上层应用的View都是通过Canvas的接口来绘制,虽然底层最终也是通过OpenGL ES来实现的,但是由于上层被封装了,我们无法通过这个来实现我们想要实现的demo,我们需要使用GLSurfaceView来实现。

GLSurfaceView继承自SurfaceView,我们知道SurfaceView和一般的View不同,会有自己的Surface,而GLSurfaceView则在SurfaceView的基础上,会初始化EGL的上下文环境。其实我们直接使用SurfaceView也是可以使用OpenGL ES的,只不过GLSurfaceView给我们提供了一些生命周期管理的辅助,在大多数场景使用起来更加方便。

GLSurfaceView提供的是EGL环境,我们想要绘制一个三角形所需要做的事如下:

  • 创建一个GLSurfaceView
  • 配置EGL(其实GLSurfaceView帮助我们做了大多数的事)
  • 使用OpenGL ES接口绘制图像
    • 配置顶点缓冲区
    • 实现顶点着色器和片段着色器
    • 调用drawCall
  • 交换缓冲区呈现图像

本节主要是实现demo,对OpenGL渲染大体流程有个感知,一些api的细节可以不需要关注,后续会对每个点会有更详细的介绍。

绘制一个三角形

配置OpenGL ES

在AndroidManifeast.xml里配置
主要就是配置一条
其中glEsVersion是版,我们这里用OpenGL ES 3.0来写demo。

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
    <uses-feature android:glEsVersion="0x00030000" android:required="true"/>
    <application>
    // ...
    </activity>
</application>

自定义GLSurfaceView

GLSurfaceView通过setRenderer暴露一个Renderer,Renderer有三个接口onSurfaceCreated/onSurfaceChanged/onDrawFrame。
GLSurfaceView处理了EGL环境相关的逻辑,onDrawFrame则会控制VSync,在需要渲染的时候调用。
onSurfaceChanged是Surface变化的情况下会调用,而onSurfaceCreated则是Surface创建时回调,onDrawFrame和我们自定义View时候的onDraw有一些类似。

public class DemoGLSurfaceView extends GLSurfaceView {
    public DemoGLSurfaceView(Context context) {
        super(context);
        init();
    }

    public DemoGLSurfaceView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public void init() {
        // 设置版本
        setEGLContextClientVersion(3);
        Renderer renderer = new Renderer() {
            @Override
            public void onSurfaceCreated(GL10 gl, EGLConfig config) {
                // ...
            }

            @Override
            public void onSurfaceChanged(GL10 gl, int width, int height) {
                // 设定视口,类似相机,相机移动则渲染的图像相对位置变化。
                GLES30.glViewport(0, 0, width, height);
            }

            @Override
            public void onDrawFrame(GL10 gl) {
                // ...
            }
        };
        setRenderer(renderer);
    }
}

配置顶点缓冲区数据

由于我们只是要画一个固定的三角形,顶点缓冲区里的数据都是固定的,所以我们在onSurfaceCreated填充,只需要一次即可。
glGenBuffers是创建一个顶点缓冲区Buffer,第二个参数是一个int数组,创建的顶点缓冲区id会通过这个数组返回,后续使用这个id来使用这个buffer。
我们需要先调用glBindBuffer绑定buffer,然后再通过glBufferData将数据传到缓冲区中。
GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0)则是用来清除Buffer的绑定操作的,OpenGL的接口设计像是一个状态机,bind上一个Buffer才能对这个Buffer进行操作,如果需要操作其他Buffer则需要bind其他Buffer。
vertexArray有三个节点,是三个顶点的x,y,z坐标。OpenGL的坐标系是x,y,z都是(-1,1)。

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

@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
    // 清除背景颜色
    GLES30.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    // 创建顶点缓冲区
    int[] idBuffer = new int[1];
    GLES30.glGenBuffers(1, idBuffer, 0);
    vertexBufferId = idBuffer[0];

    // 将数据转化成ByteBuffer
    FloatBuffer vertexBuffer = ByteBuffer.allocateDirect(vertexArray.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
    vertexBuffer.put(vertexArray);
    vertexBuffer.position(0);

    GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vertexBufferId);
    // 顶点缓冲区数据填充
    GLES30.glBufferData(
            GLES30.GL_ARRAY_BUFFER,
            vertexArray.length * 4,
            vertexBuffer,
            GLES30.GL_STATIC_DRAW
    );
    GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0);

    // 初始化shader
    shaderProgramId = initShaderProgram(vertexShaderCode, fragmentShaderCode);
}

配置着色器

着色器是一段给GPU执行的程序,所以其实就是一段代码,我们需要调用对应接口来编译链接。
GLES30.glCreateShader创建一个Shader,参数表示着色器的类型,GL_VERTEX_SHADER为顶点着色器,GL_FRAGMENT_SHADER为片段着色器。
vertexShaderCode字符串是我们配置的顶点着色器,gl_Position是出参,这里是直接做了透传。
fragmentShaderCode是片段着色器,gl_FragColor是出参,是颜色,而uniform vec4 vColor是统一变量,我们设置统一变量直接作为片段着色器的出参。
通过api编译连接后,我们会将它关联到一个Program上,initShaderProgram返回到就是program到id。

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;" +
                "}";

private int initShaderProgram(String vertexShaderCode, String fragmentShaderCode) {
    // 编译顶点着色器
    int vertexShader = GLES30.glCreateShader(GLES30.GL_VERTEX_SHADER);
    GLES30.glShaderSource(vertexShader, vertexShaderCode);
    GLES30.glCompileShader(vertexShader);

    // 编译片段着色器
    int fragmentShader = GLES30.glCreateShader(GLES30.GL_FRAGMENT_SHADER);
    GLES30.glShaderSource(fragmentShader, fragmentShaderCode);
    GLES30.glCompileShader(fragmentShader);

    // 链接着色器
    int program = GLES30.glCreateProgram();
    GLES30.glAttachShader(program, vertexShader);
    GLES30.glAttachShader(program, fragmentShader);
    GLES30.glLinkProgram(program);
    return program;
}

@Override
public void onDrawFrame(GL10 gl) {
    // ...
    // 使用编译好的着色器
    GLES30.glUseProgram(shaderProgramId);
    // ...
}

配置顶点布局/渲染

首先我们需要调用glClear清空屏幕,glUseProgram配置着色器程序,glBindBuffer绑定之前填充的Buffer。
属性需要通过glEnableVertexAttribArray使能才可使用,我们这里需要使能vPosition属性。
后续会使用glVertexAttribPointer告诉GPU顶点缓冲区布局情况,顶点缓冲区本质就是一段内存,不过没有glVertexAttribPointer,GPU并不知道怎么使用这个数据。
后面配置vColor作为颜色,(1,1,1,1)分别为RGBA,白色。
最后调用glDrawArrays来渲染三角形。

@Override
public void onDrawFrame(GL10 gl) {
    // 清除屏幕
    GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);
    // 使能着色器程序
    GLES30.glUseProgram(shaderProgramId);

    // 绑定Buffer
    GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vertexBufferId);
    // 获取vPosition属性
    int positionLocation = GLES30.glGetAttribLocation(shaderProgramId, "vPosition");
    // 属性需要使能才可使用
    GLES30.glEnableVertexAttribArray(positionLocation);
    // 告诉GPU顶点缓冲区的布局情况,即那些数据的意义是什么。
    // 这是CPU向GPU传数据的一种方式,我们这里是告诉GPU,我们前面bind的顶点缓冲区是什么数据。
    // 第一个参数是attr的id,第二个参数表示每一个顶点有几个数,第三个参数为数据类型,第四个是参数是否需要归一化
    // 第五个参数是步长,表示每个顶点占用了多少字节,0表示顶点都是紧凑的,GPU会通过计算来计算步长,最后一个参数表示offset。
    GLES30.glVertexAttribPointer(positionLocation, 3, GLES30.GL_FLOAT, false, 0, 0);

    // 配置统一变量,用于CPU和GPU通信的
    int colorLocation = GLES30.glGetUniformLocation(shaderProgramId, "vColor");
    GLES30.glUniform4f(colorLocation, 1.0f, 1.0f, 1.0f, 1.0f);

    // 调用DrawCall绘制三角形
    GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0, 3);

    // 清除配置
    GLES30.glDisableVertexAttribArray(positionLocation);
    GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0);
    GLES30.glUseProgram(0);
}

效果

三角形之所以不是正三角形是因为屏幕是长方形的。
在这里插入图片描述

小结

本节通过OpenGL ES实现了一个三角形的渲染,对于每个接口使用只做了一个简单的介绍,想必首次学习OpenGL的同学会有很多疑问,比如怎么渲染多个目标,怎么实现渐变颜色等,我们的后续会对每一个点做更细节的学习,这一节主要是了解一下OpenGL的总体渲染流程,大概知道OpenGL接口是怎么工作的即可,后续的介绍也会基于本章的demo。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2175505.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

添加vscode插件C/C++ snippets,快速生成LVGL .c/.h文件模版

文章目录 一、安装插件二、在安装目录下添加c.json和cpp.json文件①在 C:/Users/yourname/AppData/Roaming/Code/User/snippets/ 目录下创建 c.json 并填入如下内容&#xff1a;②在 C:/Users/yourname/AppData/Roaming/Code/User/snippets/ 目录下创建 cpp.json 并填入如下内容…

SQL学习1

24.9.28学习目录 一.数据库1.SQL语句基础2.匹配条件 一.数据库 对于嵌入式的数据库&#xff0c;其使用的是SQLite这种小型数据库&#xff1b; 在ubuntu中的下载方法 //字符界面 sudo apt-get install sqlite3//图形界面 sudo apt-get install sqlitemanSQLite特点&#xff1a…

windows系统中后台运行java程序

在windows系统中后台运行java程序&#xff0c;就是在启动java程序后&#xff0c;关闭命令行行窗口执行。 1、命令行方式 命令行方式运行java程序 启动脚本如下&#xff1a; echo off start java -jar app.jar exit启动后的结果如下 这种方式下&#xff0c;会马上启动一个命…

【RocketMQ】RocketMQ发送不同类型消息

&#x1f3af; 导读&#xff1a;本文介绍了RocketMQ消息队列系统中的几种消息发送模式及其应用场景&#xff0c;包括同步消息、异步消息以及事务消息。同步消息确保了消息的安全性&#xff0c;但牺牲了一定的性能&#xff1b;异步消息提高了响应速度&#xff0c;适用于对响应时…

搬砖6、Python函数和模块的使用

函数和模块的使用 在讲解本章节的内容之前&#xff0c;我们先来研究一道数学题&#xff0c;请说出下面的方程有多少组正整数解。 事实上&#xff0c;上面的问题等同于将8个苹果分成四组每组至少一个苹果有多少种方案。想到这一点问题的答案就呼之欲出了。 可以用Python的程序来…

人工智能开发实战照片智能搜索功能实现

内容提要 项目分析预备知识项目实战 一、项目分析 1、提出问题 随着人民生活水平的提高和手机照相功能的日趋完美&#xff0c;我们不经意中拍摄了很多值得回忆的时刻&#xff0c;一场说走就走的旅行途中也记录下许多令人心动的瞬间&#xff0c;不知不觉之中&#xff0c;我们…

Time-MoE : 时间序列领域的亿级规模混合专家基础模型

Time-MoE : 时间序列领域的亿级规模混合专家基础模型 时间序列预测一直是量化研究和工业应用中的重要课题。随着深度学习技术的发展&#xff0c;大规模预训练模型在自然语言处理和计算机视觉领域取得了显著进展&#xff0c;但在时间序列预测领域&#xff0c;这些模型的规模和运…

【归回预测】归回预测│PSO-ELM与标准ELM多输入预测对比源代码

摘要 本文比较了基于粒子群优化&#xff08;PSO&#xff09;和标准极限学习机&#xff08;ELM&#xff09;算法的电力负荷多输入预测模型。利用真实电力负荷数据集&#xff0c;对两种方法的预测性能进行了全面的评估&#xff0c;使用了均方误差&#xff08;MSE&#xff09;、平…

【文心智能体 | AI大师工坊】如何使用智能体插件,完成一款旅游类智能体的开发,来体验一下我的智能体『​​​​​​​厦门CityWalk』

目录 1.1、智能体运行效果 1.2、创作灵感来源 1.3、如何制作智能体 1.4、可能会遇到的几个问题 1.5、快速调优指南 『厦门CityWalk&#x1f680;』我的优质智能体&#xff1a;https://0nxj3k.smartapps.baidu.com/?_swebfr1&_swebScene3621000000000000 在当今这个全…

青动CRM V3.2.1

全面解决企业销售团队的全流程客户服务难题旨在助力企业销售全流程精细化、数字化管理&#xff0c;全面解决企业销售团队的全流程客户服务难题&#xff0c;帮助企业有效盘活客户资源、量化销售行为&#xff0c;合理配置资源、建立科学销售体系&#xff0c;提升销售业绩。标准授…

【宝藏妙招,轻松拿捏!】如何防止U盘资料被复制?U盘文件防拷贝的五种措施!

小李&#xff1a;“小张&#xff0c;你上次借我的U盘还回来的时候&#xff0c;我总觉得里面的资料好像被人动过了&#xff0c;有没有什么办法可以防止U盘里的资料被复制啊&#xff1f;” 小张&#xff1a;“当然有啦&#xff01;现在数据安全这么重要&#xff0c;防止U盘资料被…

贪心的思想

803.区间合并 给定 n 个区间 [li,ri]&#xff0c;要求合并所有有交集的区间。 注意如果在端点处相交&#xff0c;也算有交集。 输出合并完成后的区间个数。 例如&#xff1a;[1,3] 和 [2,6] 可以合并为一个区间 [1,6]。 输入格式 第一行包含整数 n。 接下来 n 行&#x…

如何通过GSR排名系统迅速提升谷歌排名?

如果你希望在谷歌上迅速提升某个关键词排名&#xff0c;或者某个关键词无论怎么优化都无法上首页&#xff0c;那么GSR关键词排名系统你就可以关注一下&#xff0c;GSR系统可以在短时间内帮助你进一步提升至首页。与传统的SEO方法不同&#xff0c;GSR侧重于外部优化&#xff0c;…

C语言进阶版第13课—字符函数和字符串函数2

文章目录 1. strstr函数的使用和模拟实现1.1 strstr函数的使用1.2 模拟实现strstr函数1.3 strstr函数和strncpy函数、puts函数的混合使用 2. strtok函数的使用**3. strerror函数的使用** 1. strstr函数的使用和模拟实现 1.1 strstr函数的使用 strstr函数是用来通过一个字符串来…

《迁移学习》—— 将 ResNet18 模型迁移到食物分类项目中

文章目录 一、迁移学习的简单介绍1.迁移学习是什么&#xff1f;2.迁移学习的步骤 二、数据集介绍三、代码实现1. 步骤2.所用到方法介绍的文章链接3. 完整代码 一、迁移学习的简单介绍 1.迁移学习是什么&#xff1f; 迁移学习是指利用已经训练好的模型&#xff0c;在新的任务上…

牛顿迭代法求解x 的平方根

牛顿迭代法是一种可以用来快速求解函数零点的方法。 为了叙述方便&#xff0c;我们用 C C C表示待求出平方根的那个整数。显然&#xff0c; C C C的平方根就是函数 f ( x ) x c − C f(x)x^c-C f(x)xc−C 的零点。 牛顿迭代法的本质是借助泰勒级数&#xff0c;从初始值开始快…

【软件测试】最新Linux大全(超详细!超级全!)

目录 前言1. 操作系统是干什么的2. Linux 是什么3. 为什么要学习 Linux4. Linux 发行版本5. Linux 系统特点6. Linux 安装7. Linux 系统启动8. Linux 操作方式9. Shell 与命令10. 命令格式 一、 Linux终端命令格式1. 终端命令格式2. 查阅命令帮助信息 二、 常用Linux命令的基本…

项目计划软件如何助力企业策略规划和执行监控

项目管理软件助力任务、时间和协作管理&#xff0c;如ZohoProjects集成了任务管理、时间跟踪、协作工具等功能&#xff0c;提高性价比&#xff0c;适合不同规模团队。其简化流程、专业度高&#xff0c;成为企业提升效率的重要工具。 一、项目计划软件的由来 项目计划软件的历史…

暴雨受邀出席2024 AI大模型生态算力峰会

9月25日&#xff0c;2024 AI大模型生态暨算力峰会在北京国家会议中心正式开幕&#xff0c;AI行业头部厂家、业界专家及人工智能行业精英齐聚一堂&#xff0c;暴雨华北大区产品总监丁海受邀出席并发表演《用AI奔赴新质生产力》的主题演讲&#xff0c;深度诠释了人工智能如何驱动…

解开BL锁之后如何安装模块及安装注意事项

本文是在解开BL锁的前提下进行的。 解开BL锁请参考:出厂非澎湃OS手机解BL锁 本文 参考&#xff1a; Magisk中文网 Magisk资源分享 ROM基地 我安装了这几个模块&#xff0c;切记先按照救砖模块。 解开BL锁之后&#xff0c;需要将下载系统ROM包提取boot.img。 目前我知道的又…