Android显示系统(05)- OpenGL ES - Shader绘制三角形(使用glsl文件)

一、前言:

上一篇文章我们使用了Shader绘制了一个基本的三角形,但是,发现那样写Shader程序特别麻烦,各种加双引号,还没有语法高亮提示。因为glsl也和java、c++一样是一门语言,实际工程项目都是单独的glsl文件管理的,本节我们整改下之前的项目。

二、整改步骤:

  • 新建assets目录,管理glsl资源文件;
  • 新增ShaderController类来操作glsl文件;
  • Shader代码移植到glsl文件当中;

三、编码:

1、创建glsl文件:

  • 新建assets目录:

    在这里插入图片描述

    在这里插入图片描述

  • 新建glsl文件:

    在这里插入图片描述

2、编写Shader程序:

顶点着色器:

文件路径:.\app\src\main\assets\triangle_vertex.glsl

attribute vec4 vPosition;void main() {gl_Position = vPosition;
}

片元着色器:

文件路径:.\app\src\main\assets\triangle_fragment.glsl

precision mediump float;uniform vec4 vColor;
void main() {gl_FragColor = vColor;
}

3、 新建ShaderController类管理Shader程序:

文件路径:com/example/glsurfaceviewdemo/ShaderController.java

package com.example.glsurfaceviewdemo;import android.content.Context;
import android.opengl.GLES30;
import android.util.Log;import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;public class ShaderController {/*** 从 assets 文件夹中读取指定文件的内容并返回为字符串** @param filename 文件名* @param context  上下文对象* @return 读取的文件内容字符串*/public static String loadShaderCodeFromFile(String filename, Context context) {// 用于存储读取的着色器代码的字符串StringBuilder shaderCode = new StringBuilder();try {InputStream inputStream = context.getAssets().open(filename);// 使用 BufferedReader 包装输入流,以便逐行读取文件内容BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));String line;// 逐行读取文件内容并将每行内容追加到 shaderCode 中while ((line = bufferedReader.readLine()) != null) {shaderCode.append(line).append("\n");}// 关闭 BufferedReaderbufferedReader.close();} catch (IOException e) {e.printStackTrace();}// 返回读取的文件内容字符串return shaderCode.toString();}// 创建并编译着色器public static int compileShader(int type, String shaderCode) {// 创建一个着色器int shader = GLES30.glCreateShader(type);// 将着色器代码设置到着色器对象中GLES30.glShaderSource(shader, shaderCode);// 编译着色器GLES30.glCompileShader(shader);return shader;}/*** 创建 OpenGL Program 对象,用于链接顶点着色器和片段着色器** @param vertexShader   顶点着色器源代码* @param fragmentShader 片段着色器源代码* @return 创建的 OpenGL Program 对象 ID*/public static int createGLProgram(String vertexShader, String fragmentShader) {// 编译生成顶点着色器int vShader = compileShader(GLES30.GL_VERTEX_SHADER, vertexShader);if (vShader == 0) {Log.e("GLProgram", "Failed to compile vertex shader.");return 0; // 返回0表示创建失败}// 编译生成片元着色器int fShader = compileShader(GLES30.GL_FRAGMENT_SHADER, fragmentShader);if (fShader == 0) {Log.e("GLProgram", "Failed to compile fragment shader.");GLES30.glDeleteShader(vShader); // 删除已经生成的顶点着色器return 0;}// 创建一个OpenGL程序int program = GLES30.glCreateProgram();if (program == 0) {Log.e("GLProgram", "Failed to create OpenGL program.");GLES30.glDeleteShader(vShader);GLES30.glDeleteShader(fShader);return 0;}// attach两个编译好的着色器到program当中GLES30.glAttachShader(program, vShader);GLES30.glAttachShader(program, fShader);// 链接OpenGL程序GLES30.glLinkProgram(program);// 检查链接结果是否成功int[] linkStatus = new int[1];GLES30.glGetProgramiv(program, GLES30.GL_LINK_STATUS, linkStatus, 0);if (linkStatus[0] == 0) {Log.e("GLProgram", "Failed to link program: " + GLES30.glGetProgramInfoLog(program));GLES30.glDeleteProgram(program);GLES30.glDeleteShader(vShader);GLES30.glDeleteShader(fShader);return 0;}// 删除着色器,因为已经链接到程序中,不再需要保留GLES30.glDeleteShader(vShader);GLES30.glDeleteShader(fShader);Log.i("GLProgram", "GL program created successfully.");return program;}
}

4、修改原来的Triangle类:

文件路径:`com/example/glsurfaceviewdemo/Triangle.java````java
package com.example.glsurfaceviewdemo;import android.content.Context;
import android.opengl.GLES30;import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;import javax.microedition.khronos.opengles.GL;public class Triangle {// 顶点数据是float类型,因此,使用这个存储private FloatBuffer mVertexBuffer;private int mProgram;// 定义的三角形顶点坐标数组private final float[] mTriangleCoords = new float[]{0.0f, 0.2f, 0.0f,   // 顶部-0.5f, -0.5f, 0.0f, // 左下角0.5f, -0.5f, 0.0f   // 右下角};public Triangle(Context context) {// 1.初始化顶点缓冲区,存储三角形坐标// 为顶点坐标分配DMA内存空间ByteBuffer byteBuffer = ByteBuffer.allocateDirect(mTriangleCoords.length * 4);// 设置字节顺序为本地字节顺序(会根据硬件架构自适应大小端)byteBuffer.order(ByteOrder.nativeOrder());// 将字节缓冲区转换为浮点缓冲区mVertexBuffer = byteBuffer.asFloatBuffer();// 将顶点三角形坐标放入缓冲区mVertexBuffer.put(mTriangleCoords);// 设置缓冲区的位置指针到起始位置mVertexBuffer.position(0);// 2.加载并编译vertexShader和fragmentShaderString vertexShaderCode = ShaderController.loadShaderCodeFromFile("triangle_vertex.glsl", context);String fragmentShaderCode = ShaderController.loadShaderCodeFromFile("triangle_fragment.glsl", context);// 3.创建一个OpenGL程序,并链接程序mProgram = ShaderController.createGLProgram(vertexShaderCode, fragmentShaderCode);}// 定义的fragment的颜色数组,表示每个像素的颜色private final float[] mColor = new float[]{0.0f, 1.0f, 0.0f, 1.0f};// 顶点着色器的位置句柄private int mPositionHandle = 0;// 片元着色器的位置句柄private int mColorHandle = 0;private final int COORDS_PER_VERTEX = 3;public void draw() {// 使用programGLES30.glUseProgram(mProgram);// 获取顶点着色器的位置句柄mPositionHandle = GLES30.glGetAttribLocation(mProgram, "vPosition");// 启用顶点属性数组GLES30.glEnableVertexAttribArray(mPositionHandle);// 准备三角形坐标数据// 重置缓冲区位置mVertexBuffer.position(0);// 指定顶点属性数据的格式和位置GLES30.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX, GLES30.GL_FLOAT, false, 0, mVertexBuffer);// 获取片元着色器的颜色句柄mColorHandle = GLES30.glGetUniformLocation(mProgram, "vColor");// 设置绘制三角形的颜色GLES30.glUniform4fv(mColorHandle, 1, mColor, 0);// 绘制三角形GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0, mTriangleCoords.length / COORDS_PER_VERTEX);// 禁用顶点属性数组GLES30.glDisableVertexAttribArray(mPositionHandle);}
}
```

四、运行:

在这里插入图片描述

五、小结:

本文主要是讲原来的shader代码拆分到对应的glsl文件中去,为了保证连贯性,Shader没有增减语句,但是,实际工程中来说,这个shader程序写得不够规范,后续章节逐渐补齐。

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

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

相关文章

Linux显卡驱动安装

前言 使用Windows配置环境失败,其中有一个包只有Linux版本,Windows版本的只有python3.10的,所以直接选用Linux来配置环境,显卡安装比较麻烦,单独出一期。 显卡驱动安装 参考文章:Ubuntu显卡驱动安装和这…

【Linux】进程控制

目录 一、进程创建1.1 fork函数1.2 fork函数返回值1.3 写时拷贝1.4 fork常规用法1.5 fork调用失败的原因1.6 使用fork创建多进程 二、进程退出2.1 进程退出场景2.1.1 进程运行完毕2.1.2 代码异常终止2.1.3 小结 2.2 进程常见退出方法2.2.1 return2.2.2 调用exit函数2.2.3 调用_…

smart-doc 使用

文档地址 添加插件 <plugin><groupId>com.ly.smart-doc</groupId><artifactId>smart-doc-maven-plugin</artifactId><version>3.0.9</version><configuration><includes><!--格式为&#xff1a;groupId:artifactId;…

Spring04——注解开发

Spring3.0启用了纯注解开发模式&#xff0c;使用Java类替代配置文件&#xff0c;开启了Spring快速开发赛道 Java类代替Spring核心配置文件&#xff0c; 配置类&#xff08;Configuration&#xff09; Configuration注解用于设定当前类为配置类ComponentScan注解用于设定扫描路…

ImportError: cannot import name ‘implements‘ from ‘zope.interface‘

ImportError: cannot import name ‘implements’ from ‘zope.interface’ 1. 问题分析 问题原因&#xff1a; /home/user/.conda/envs/vectornet/lib/python3.8/site-packages/apex/interfaces.py中在使用zope.interace中使用了老表达。 2. 解决办法 原文件内容&#xff…

多线程的操作

1、Thread类 1.1 Thread类的作用 上篇博文中我们了解了线程与操作系统的关系&#xff1a;线程是操作系统中的概念&#xff0c;操作系统内核实现了线程这样的机制, 并且对用户层提供了一些 API 供用户使用&#xff0c;Java 标准库中 Thread 类可以视为是对操作系统提供的 API 进…

51单片机应用开发(进阶)---串口接收字符命令

实现目标 1、巩固UART知识&#xff1b; 2、掌握串口接收字符数据&#xff1b; 3、具体实现目标&#xff1a;&#xff08;1&#xff09;上位机串口助手发送多字符命令&#xff0c;单片机接收命令作相应的处理&#xff08;如&#xff1a;openled1 即打开LED1;closeled1 即关…

3-5 C常用的字符串库函数

1.0 字符串库函数 strlen()函数用于返回字符串的长度&#xff0c;不包括结尾\0 uint32_t strlen(char *str) {uint32_t len 0;while (str[len] ! \0){len;}return len; } 编译器在处理字符串时&#xff0c;会自动的在数据末尾添加ASCI码“0对应十进制0&#xff0c;便于程序对…

python语法基础---正则表达式(补充)

&#x1f308;个人主页&#xff1a;羽晨同学 &#x1f4ab;个人格言:“成为自己未来的主人~” 上一篇文章中&#xff0c;我们讲到了贪婪匹配和非贪婪匹配&#xff0c;我们在这篇文章中&#xff0c;主要讲的就是贪婪匹配和非贪婪匹配的剩下的部分&#xff0c;话不多说&#x…

如何在谷歌浏览器中设置网络代理

在当今的网络环境中&#xff0c;使用代理服务器可以增强您的隐私和安全性。如果您希望在谷歌浏览器中设置网络代理&#xff0c;本文将为您提供详细的步骤指南。此外&#xff0c;我们还会简要介绍如何使用谷歌浏览器的任务管理器、查看Cookies以及更换侧边栏位置&#xff0c;以便…

【AI系统】MobileNet 系列

MobileNet 系列 在本文会介绍 MobileNet 系列&#xff0c;重点在于其模型结构的轻量化设计&#xff0c;主要介绍详细的轻量化设计原则&#xff0c;基于这原则&#xff0c;MobileNetV1 是如何设计成一个小型&#xff0c;低延迟&#xff0c;低功耗的参数化模型&#xff0c;可以满…

分层架构 IM 系统之 Router 假在线分析

通过对分层架构 IM 系统的分析&#xff0c;Router 的核心职责是作为中央存储记录在线客户端与 Entry 节点之间的映射关系&#xff0c;在本质上 Router 是一个内存数据库。 客户端已经离线&#xff0c;Entry 还未感知&#xff0c;或者 Entry 已经感知并且切断了连接&#xff0c;…

04 创建一个属于爬虫的主虚拟环境

文章目录 回顾conda常用指令创建一个爬虫虚拟主环境Win R 调出终端查看当前conda的虚拟环境创建 spider_base 的虚拟环境安装完成查看环境是否存在 为 pycharm 配置创建的爬虫主虚拟环境选一个盘符来存储之后学习所写的爬虫文件用 pycharm 打开创建的文件夹pycharm 配置解释器…

在Java的xml的sql语句里面的某一个参数是list集合的时候

经常在Java里面&#xff0c;遇到这样的问题&#xff0c;sql的一个查询语句&#xff0c;它的某一个参数是一个List集合&#xff0c;然而&#xff0c;在xml.mapper文件里面的时候&#xff0c;不知道如何去组成这个查询语句&#xff0c;不知道兄弟们是否经常忘记如何去写这个语句&…

pdf转word/markdown等格式——MinerU的部署:2024最新的智能数据提取工具

一、简介 MinerU是开源、高质量的数据提取工具&#xff0c;支持多源数据、深度挖掘、自定义规则、快速提取等。含数据采集、处理、存储模块及用户界面&#xff0c;适用于学术、商业、金融、法律等多领域&#xff0c;提高数据获取效率。一站式、开源、高质量的数据提取工具&…

一文讲清楚ROS2中多线程、并发、回调组的概念和基础使用

前言 在机器人开发中&#xff0c;多线程的使用司空见惯。ROS2借助executor类帮助开发者简化多线程的使用&#xff0c;但是还是得先把基本概念搞清楚&#xff0c;才能正确的使用。本文解释了ROS1和ROS2中的并发/多线程概念&#xff0c;并且给出了ROS2版本一些实际例子帮助理解。…

《向量数据库指南》——Mlivus Cloud:OPPO的向量数据库选型秘籍

Why Mlivus Cloud? —— 向量数据库选型的深度剖析与实战分享 在当今这个数据驱动的时代,向量数据库作为处理非结构化数据的重要工具,正逐渐受到业界的广泛关注。OPPO,作为全球知名的智能手机制造商,也在这场技术变革中积极探索和实践。他们在向量检索的道路上,从最初的…

MySQL:锁机制

锁是计算机协调多个进程或线程并发访问某一资源的机制&#xff08;避免争抢&#xff09;。 在数据库中&#xff0c;除传统的计算资源&#xff08;如 CPU、RAM、I/O 等&#xff09;的争用以外&#xff0c;数据也是一种供许多用户共享的资源。如何保证数据并发访问的一致性、有效…

HTMLCSS :动态效果的玫瑰花

这段代码通过 HTML 和 CSS 的结合&#xff0c;创建了一个动态的花朵效果&#xff0c;展示了 CSS 动画和定位的强大功能。 演示效果 HTML&CSS <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equ…

深入浅出:SOME/IP-SD的工作原理与应用

目录 往期推荐 相关缩略语 SOME/IP 协议概述 协议介绍 SOME/IP TP 模块概述和 BSW 模块依赖性 原始 SOME/IP 消息的Header格式 SOME/IP-SD 模块概述 模块介绍 BSW modules依赖 客户端-服务器通信示例 Message 结构 用于SD服务的BSWM状态处理 往期推荐 ETAS工具…