Android OpenGLES2.0开发(三):绘制一个三角形

我们总是对陌生人太客气,而对亲密的人太苛刻

上一篇文章中,我们已经将OpenGL ES环境搭建完成。接下来我们就可以开始我们的绘图之旅了。该篇我们讲解最基本图形三角形的绘制,这是一切绘制的基础。在OpenGL ES的世界里一切图形都可以由三角形拼接绘制而成。

在Android官方文档中也介绍了三角形的绘制,本文案例我们参照了官方文档,但也做了进一步的改进,希望你能通过本节对OpenGL ES绘制图形有一个初步的认识。

坐标系

在绘制图形前我们要了解OpenGL ES的坐标系,我们知道Android的坐标系左上角为原点[0,0],而OpenGL ES的坐标系如下图:
请添加图片描述

OpenGL ES的坐标系中原点[0,0]在屏幕的中心,无论屏幕是正方形还是长方形,四个点的坐标都如上图所示,也就是边长都是为2的正方形

聪明的同学可能已经有疑问了,OpenGL ES坐标系明显是按照屏幕是正方形来的,那么如果屏幕为长方形,我按照比例绘制图形肯定会变形的。这里给出肯定的回答:是的!至于怎么解决先放下。

接下来我们先画一个三角形,至于遇到的问题,我们一个个解决^_^

三角形绘制

  1. 设置要绘制图形的坐标和颜色数据
  2. 定义顶点着色器片段着色器
  3. 创建Shader程序并链接编译好
  4. 绘制图形:使用Shader程序,将顶点、颜色数据传递到显存

1. 顶点颜色数据定义

在OpenGL ES中绘制图形必须先定义好坐标,确定图形的位置才能进行绘制。我们为坐标定义浮点数的顶点数组,然后我在构造方法中将浮点数组转化为ByteBuffer,后续我们会将它传递到OpenGL ES图像管道进行处理。

public class Triangle {// 顶点坐标缓冲区private FloatBuffer vertexBuffer;// 此数组中每个顶点的坐标数static final int COORDS_PER_VERTEX = 3;// 三角形三个点的坐标,逆时针绘制static float triangleCoords[] = {   // 坐标逆时针顺序0.0f, 0.616f, 0.0f, // top-0.5f, -0.25f, 0.0f, // bottom left0.5f, -0.25f, 0.0f  // bottom right};// 设置颜色为白色float color[] = {1.0f, 1.0f, 1.0f, 1.0f};public Triangle() {// 初始化形状坐标的顶点字节缓冲区ByteBuffer bb = ByteBuffer.allocateDirect(// (number of coordinate values * 4 bytes per float)triangleCoords.length * 4);// use the device hardware's native byte orderbb.order(ByteOrder.nativeOrder());// create a floating point buffer from the ByteBuffervertexBuffer = bb.asFloatBuffer();// add the coordinates to the FloatBuffervertexBuffer.put(triangleCoords);// set the buffer to read the first coordinatevertexBuffer.position(0);}
}

根据代码中定义的三角形的坐标,我们大概画出的三角形如下图,应该是一个等边三角形
在这里插入图片描述

形状面和环绕

在 OpenGL 中,形状的面是由三维或更多点定义的三维表面 空间。一组三个或更多个三维点(在 OpenGL 中称为顶点)具有一个正面以及一个背面。如何知道哪一面为正面,哪一面为背面呢?这个问题问得好! 答案与环绕或者定义形状点的方向有关

请添加图片描述
在此示例中,三角形的点按如上顺序定义,这也决定了他们是按逆时针的方向绘制,绘制这些坐标的顺序定义了形状的环绕方向。默认情况下,在OpenGL中,逆时针绘制的面是正面,而另外一面是背面

为什么重要的是要知道形状的哪个面是正面?答案与OpenGL的常用功能有关,称为面部剔除。面部剔除是OpenGL环境的一个选项,它允许渲染管道忽略(不计算或绘制)形状的背面,从而节省时间,内存和处理周期:

// enable face culling feature
gl.glEnable(GL10.GL_CULL_FACE);
// specify which faces to not draw
gl.glCullFace(GL10.GL_BACK);

请务必按照逆时针绘制顺序定义 OpenGL 形状的坐标

2. 创建着色器代码

public class Triangle {// 顶点着色器代码private final String vertexShaderCode ="attribute vec4 vPosition;\n" +"void main() {\n" +"  gl_Position = vPosition;\n" +"}\n";// 片段着色器代码private final String fragmentShaderCode ="precision mediump float;\n" +"uniform vec4 vColor;\n" +"void main() {\n" +"  gl_FragColor = vColor;\n" +"}\n";...
}
变量名说明备注
vPosition顶点坐标数据,我们第一步定义的顶点数据需要传给这个变量该变量名可随意修改
gl_PositionShader的内置变量,就是图形的顶点位置该变量名不可修改
vColor图元(像素)的颜色,我们第一步定义的颜色需要传给这个变量该变量名可随意修改
gl_FragColorShader的内置变量,图元颜色该变量名不可修改

上面先简单介绍下变量的含义,后续我们会详细讲解GLSL语言。

3. 创建Shader程序并链接

我们需要对上面的着色器语言进行编译链接后,才能在OpenGL ES环境中使用。编译此代码,我们需要一个实用的方法:

public class GLESUtils {/*** 加载着色器代码** @param type* @param shaderCode* @return*/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 itGLES20.glShaderSource(shader, shaderCode);GLES20.glCompileShader(shader);return shader;}
}

为了匹配Renderer生命周期方法onSurfaceCreated,我们在Triangle中创建surfaceCreated方法用来编译链接Shader程序,创建surfaceChanged来更新OpenGL ES画布大小

public class Triangle() {.../*** OpenGL ES程序句柄*/private int mProgram;public void surfaceCreated() {// 加载顶点着色器代码int vertexShader = GLESUtils.loadShader(GLES20.GL_VERTEX_SHADER,vertexShaderCode);// 加载片段着色器代码int fragmentShader = GLESUtils.loadShader(GLES20.GL_FRAGMENT_SHADER,fragmentShaderCode);// 创建空的OpenGL ES程序mProgram = GLES20.glCreateProgram();// 将顶点着色器添加到程序中GLES20.glAttachShader(mProgram, vertexShader);// 将片段着色器添加到程序中GLES20.glAttachShader(mProgram, fragmentShader);// 链接OpenGL ES程序GLES20.glLinkProgram(mProgram);}public void surfaceChanged(int width, int height) {// 设置OpenGL ES画布大小GLES20.glViewport(0, 0, width, height);}
}

4. 绘制图形

此时我们已经定义好了三角形的顶点坐标数据、片元颜色值、OpenGL ES着色器程序。接下来我们只需要将坐标数据、片元颜色值传递给着色器程序,然后执行绘制我们就可以将三角形画出来了。我们定义一个单独的方法draw用来执行绘制三角形

4.1 将Shader程序添加到OpenGL ES环境

public class Triangle() {public void draw() {// 将程序添加到OpenGL ES环境GLES20.glUseProgram(mProgram);// 重新绘制背景色为黑色GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);...}
}

4.2 传递顶点坐标和片元颜色数据

我们要从java内存将数据传递给OpenGL ES显存环境中,需要获取Shader程序属性的句柄值

注意:获取属性句柄要和Shader程序中定义的属性变量名字一样。获取属性句柄后,我们操作属性句柄就可以将数据传递给对应的变量了。

public class Triangle() {.../*** Shader程序中顶点属性的句柄*/private int positionHandle;/*** Shader程序中颜色属性的句柄*/private int colorHandle;public void surfaceCreated() {// 加载Shader程序代码...// 获取顶点着色器vPosition成员的句柄positionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");// 获取片段着色器vColor成员的句柄colorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");}
}

draw方法中设置顶点数据和颜色值

public class Triangle() {...private final int vertexCount = triangleCoords.length / COORDS_PER_VERTEX;private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertexpublic void draw() {// 将程序添加到OpenGL ES环境...// 为三角形顶点启用控制柄GLES20.glEnableVertexAttribArray(positionHandle);// 准备三角坐标数据GLES20.glVertexAttribPointer(positionHandle,     // 执行要配置的属性句柄(编号)COORDS_PER_VERTEX,  // 指定每个顶点属性的分量数GLES20.GL_FLOAT,    // 指定每个分量的数据类型false,              // 指定是否将数据归一化到 [0,1] 或 [-1,1] 范围内vertexStride,       // (步长)指定连续两个顶点属性间的字节数。如果为 0,则表示顶点属性是紧密排列的vertexBuffer        // 指向数据缓冲对象);// 设置绘制三角形的颜色GLES20.glUniform4fv(colorHandle, 1, color, 0);// 画三角形GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);// 禁用顶点阵列GLES20.glDisableVertexAttribArray(positionHandle);}
}

4.3 在GLSurfaceView中绘制三角形

以上我们已经将OpenGL ES绘制三角形的流程全部讲完,接下来我们只需要在GLSurfaceView中创建Triangle类并执行对应的方法

我们在上一篇中Android OpenGLES2.0开发(二):环境搭建已经搭建好了OpenGL ES环境,现在只需在Renderer接口中添加Triangle即可

public class TriangleGLSurfaceView extends GLSurfaceView {private Context mContext;private MyRenderer mMyRenderer;public TriangleGLSurfaceView(Context context) {super(context);init(context);}public TriangleGLSurfaceView(Context context, AttributeSet attrs) {super(context, attrs);init(context);}private void init(Context context) {mContext = context;mMyRenderer = new MyRenderer();setEGLContextClientVersion(2);setRenderer(mMyRenderer);setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);}static class MyRenderer implements Renderer {Triangle mTriangle;public MyRenderer() {mTriangle = new Triangle();}@Overridepublic void onSurfaceCreated(GL10 gl, EGLConfig config) {mTriangle.surfaceCreated();}@Overridepublic void onSurfaceChanged(GL10 gl, int width, int height) {mTriangle.surfaceChanged(width, height);}@Overridepublic void onDrawFrame(GL10 gl) {mTriangle.draw();}}
}

将GLSurfaceView添加到布局中,运行程序我们可以看到绘制的效果如下图所示:
请添加图片描述
经过上面坐标系的讲解,我们应该能料想到绘制的结果并不是一个正三角形,至于原因我想大家也应该知道,至于怎么解决我们后续再讲

最后

希望你根据上面的步骤一步一步将代码敲出来,相信你肯定对OpenGL ES绘制有一个全面的了解。虽然只是绘制一个三角形,但是上面的代码基本上是后续一切绘制的基础,也算是一个模板代码,后续别的绘制基本上就是对上面的代码微调即可实现。

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

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

相关文章

OrangePi 烧录镜像步骤

理解:第一步:烧录镜像。第二步:建立编译环境(一般是PC端的Linux虚拟机)和板卡端的文件连接。因为要传文件,一般用挂载的方法。第三步:软件程序的编译与部署。 第一步:烧录镜像步骤 …

数据分析:Python语言网络图绘制

文章目录 介绍加载R包类别导入数据下载数据画图介绍 网络图是一种图形表示法,用于展示实体之间的关系。在不同的领域中,网络图有着不同的含义和用途:在生物学中,网络图可以用来表示生物分子之间的相互作用,如蛋白质相互作用网络。 加载R包 import pandas as pd import …

Xcode 16 上传AppStore遇到第三方库 bitcode 的问题

Xcode 16 上传AppStore遇到第三方库 bitcode 的问题 最近两天更新了Xcode 16,然后正好要发布新版本的App,打包Adhoc没问题,但是上传AppStoreConnect或者TestFlight就不行解决方案参考资料 最近两天更新了Xcode 16,然后正好要发布新…

PyQT开发总结

用PyQT开发了一个界面小程序,记录一下。 pyuic和pyrcc pyuic (PYthon User Interface Compiler)是一个命令行工具,用于将 Qt Designer 生成的 .ui 文件转换成 Python 代码。pyrcc 用于处理 Qt 资源文件(如图片&#…

HTML+CSS学习笔记

目录 HTML 1.开发环境 2.创建HTML文件 3.HTML元素 3.1HTML文件结构 3.2HTML标签 3.3HTML属性​编辑​编辑 3.4HTML区块 3.4.1块元素 3.4.2行内元素 3.5HTML表单 CSS 1.CSS简介 2.CSS语法​编辑 3.CSS三种导入方式 内联样式 内部样式 外部样式 4.选择器​ 5.C…

低代码中实现数据映射的必要性与方案

在数字化转型的浪潮中,低代码平台因其快速开发和灵活性而受到越来越多企业的青睐。然而,随着业务需求的复杂化,单纯依赖低代码工具往往难以满足企业在数据处理和业务逻辑上的要求。数据映射作为连接不同数据源和业务逻辑的桥梁,显…

sheng的学习笔记-AI-时序差分学习

AI目录:sheng的学习笔记-AI目录-CSDN博客 强化学习:sheng的学习笔记-AI-强化学习(Reinforcement Learning, RL)-CSDN博客 蒙特卡罗强化学习: sheng的学习笔记-AI-蒙特卡罗强化学习-CSDN博客 什么是时序差分学习 时序…

解锁HTML的力量:从基础标签到完整网页构建

在整个学习编程技能的过程中,我们会始终基于编程的本质:输入-》函数处理-》输出 和编程语言的本质:语法糖、变量、基础函数,去理解各种编程技术和学习相关的技能。 今天开始学习编程的第一个技能点:HTML。正如编程的本…

国内可用ChatGPT-4中文镜像网站整理汇总【持续更新】

一、GPT中文镜像网站 ① yixiaai.com 支持GPT4、4o以及o1,支持MJ绘画 ② chat.lify.vip 支持通用全模型,支持文件读取、插件、绘画、AIPPT ③ AI Chat 支持GPT3.5/4,4o以及MJ绘画 二、模型知识 o1/o1-mini:最新的版本模型&am…

汇量科技大数据面试题及参考答案

如何在 SQL 中处理三个字段完全一样的去重?在 Scala 中又该如何实现? 在 SQL 中,可以使用多种方法来处理三个字段完全一样的去重。一种常见的方法是使用 DISTINCT 关键字结合多个字段来实现。例如,假设有表 table_name,包含字段 field1、field2 和 field3,可以使用以下 S…

RabbitMQ 快速入门

目录 什么是MQ 为什么要使用 MQ MQ 的分类 MQ 的选择 认识 RabbitMQ RabbitMQ 的核心部分 安装 脚本安装 docker 安装 启动 web 管理界面 创建用户 创建消息队列 基本概念 消息应答 持久化 预取值 发布确认 交换机 Exchange 概念 死信队列 死信的来源 延迟…

【工具】Java Excel转图片

【工具】Java Excel转图片 package com.yj.luban.modules.office.excel;import org.apache.poi.ss.usermodel.*; import org.apache.poi.xssf.usermodel.XSSFWorkbook;import javax.imageio.ImageIO; import java.awt.Color; import java.awt.Font; import java.awt.*; import …

深度学习03-神经网络01-什么是神经网络?

神经网络的基本概念 人工神经网络(Artificial Neural Network,ANN): 是一种模仿生物神经网络的计算模型。由多个神经元(或称为节点)组成,这些节点通过不同的连接来传递信息。 每个神经元可以接…

淘客系统开发之卷轴模式系统源码功能分析

随着互联网技术的快速发展,电商行业不断创新,探索更加高效、有趣的用户参与机制。其中,卷轴模式作为一种新兴的商业模式,以其独特的积分兑换和任务系统,在淘客系统开发中得到了广泛应用。本文将从技术角度,…

C#基础(16)实践:学生成绩管理系统

简介 通过基础部分的学习,我们已经能进行一些实际应用的开发,学生成绩系统我相信是大家基本在大学期间上程序课必定会经历的一个小项目。 这个小项目看上去简单,但是思考量却不少。 这里就不带着大家一步一步讲解了,因为里面涉…

美团测开OC!

大家好,我是洋子,最近测试社区里面的一个25届同学参加秋招,已经拿到美团测开offer,今天来分享一下他的求职经历,文末附面经 他求职目前的进展如下: 互联网大厂:字节,阿里&#xff…

shell配置文件介绍

~/.bash_profile 和 ~/.zshrc 是两种不同的 shell 配置文件,分别用于配置 Bash 和 Zsh shell 的环境。它们的主要功能是初始化 shell 环境、设置环境变量、别名、函数等,并在启动 shell 时自动加载。 1. ~/.bash_profile 作用: ~/.bash_profile 是 Bash shell 的启动配置文件…

Kafka-Manager安装及操作

文章目录 一、kafka-manager介绍二、kafka-manager安装三、Kafka-Manager操作 一、kafka-manager介绍 CMAK (Cluster Manager for Apache Kafka, previously known as Kafka Manager) CMAK (previously known as Kafka Manager) is a tool for managing Apache Kafka cluster…

LeetCode 每周算法 6(图论、回溯)

LeetCode 每周算法 6(图论、回溯) 图论算法: class Solution: def dfs(self, grid: List[List[str]], r: int, c: int) -> None: """ 深度优先搜索函数,用于遍历并标记与当前位置(r, c)相连的所有陆地&…

切换淘宝最新npm镜像源

👨‍⚕️ 主页: gis分享者 👨‍⚕️ 感谢各位大佬 点赞👍 收藏⭐ 留言📝 加关注✅! 👨‍⚕️ 收录于专栏:前端工程师 文章目录 一、🌎前言二、🌎切换淘宝最新npm镜像源2.…