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

news2024/12/12 19:32:29

Android显示系统(02)- OpenGL ES - 概述
Android显示系统(03)- OpenGL ES - GLSurfaceView的使用
Android显示系统(04)- OpenGL ES - Shader绘制三角形
Android显示系统(05)- OpenGL ES - Shader绘制三角形(使用glsl文件)
Android显示系统(06)- OpenGL ES - VBO和EBO和VAO
Android显示系统(07)- OpenGL ES - 纹理Texture
Android显示系统(08)- OpenGL ES - 图片拉伸

一、前言:

上一篇文章我们使用了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");
          }
          // 关闭 BufferedReader
          bufferedReader.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和fragmentShader
        String 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() {
        // 使用program
        GLES30.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.coloradmin.cn/o/2256863.html

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

相关文章

【Golang】Go语言编程思想(六):Channel,第一节,介绍Channel

Channel 下面的几个例子将会展示如何定义一个 channel: func chanDemo() {var c chan int // chan int 的含义是, c 是一个 channel, 里面的内容是 int// 上面的声明语句将会创建一个 nil channel, c nil, 它的作用将在 select 当// 中体现 }创建一个非 nil 的 c…

怎么获取Java高并发经验与系统设计技能?

如何获得高并发经验? 这是系统邀请我回答的一个问题,由此也引发了我的一些思考:为什么人人都想要获得高并发经验;想拥有高并发系统设计技能? 其原因LZ认为主要有以下三点: 涨薪:有高并发系统设…

Spherical Harmonics (SH)球谐函数的原理及应用【3DGS】

Spherical Harmonics (SH)球谐函数的原理及应用【3DGS】 前言球谐函数(Spherical Harmonics, SH)球谐函数不同阶的表达式以及有什么不同?具体介绍球谐函数基函数球谐函数 前言 高斯泼溅Gaussian Splatting (GS) GS 模…

Java版-图论-拓扑排序与有向无环图

拓扑排序 拓扑排序说明 对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边<u,v>∈E(G),则u在线性序列中出现在v之前。通常,这样的线性序列称为满足拓扑次序(Topological Order)的序列…

如何在 Odoo18 视图中添加关联数据看板按钮 | 免费开源ERP实施诀窍

文 / 开源智造 Odoo亚太金牌服务 引言 关联数据看板按钮乃是 Odoo 当中的一项强效功能&#xff0c;它容许用户顺遂地访问相关记录&#xff0c;或者直接从模型的表单视图施行特定操作。它们为用户给予了对重要信息的疾速访问途径&#xff0c;并简化了工作流程&#xff0c;由此…

TCP客户端服务器端通信(线程池版)

1、什么是监听套接字&#xff0c;和UDP相比&#xff0c;TCP为什么文件描述符变多了&#xff1f; 在网络编程中&#xff0c;TCP和UDP是两种常见的传输协议&#xff0c;它们之间最大的不同之一在于连接的管理方式。为了更好地理解这个区别&#xff0c;我们可以用一个生动的比喻来…

【Linux】通过crond服务设置定时执行shell脚本,实际执行时间却延迟了8小时

一、问题描述 通过使用crond服务设置定时任务&#xff0c;在每天凌晨的2:00执行脚本&#xff0c;但检查结果时发现&#xff0c;实际执行时间却在上午10点。 检查shell脚本执行结果发现&#xff0c;实际执行脚本时间在上午10:00&#xff0c;延迟了8小时。 检查系统时间&#xf…

Git基础笔记

目录 1.Git 常用命令 2.Git 分支操作 3.远程仓库操作 Git 概述 Git 是一个免费的、开源的 分布式版本控制系统 &#xff0c;可以快速高效地处理从小型到大型的各种 项目 1.Git 常用命令 1.设置用户签名 git config --global user.name 用户名 2.设置用户签名 git config…

PADS系列:绘制RTL8306原理图的过程

大家好&#xff0c;我是山羊君Goat。 在所有相关的元件都被创建到了原理图库之后&#xff0c;就可以正式开始原理图的绘制了。不过绘制过程中也是会按照一定的顺序来进行的&#xff0c;这样可以达到事半功倍的效果。 首先就是主芯片的放置&#xff0c;这里有三个主芯片&#x…

GCP Case:MountKirk Games

游戏后端 根据游戏活动动态放大或缩小。 连接到托管的nos0l数据库服务。 运行定制的linux发行版。 游戏分析平台 根据游戏活动来扩大或缩小规模直接处理来自游戏服务器的传入数据。 处理由于移动网络缓慢而迟到的数据。 通过sql查询来访问至少10tb的历史数据 处理由用户…

OpenCV相机标定与3D重建(10)眼标定函数calibrateHandEye()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 计算手眼标定&#xff1a; g T c _{}^{g}\textrm{T}_c g​Tc​ cv::calibrateHandEye 是 OpenCV 中用于手眼标定的函数。该函数通过已知的机器人…

【CSS in Depth 2 精译_072】第 12 章 CSS 排版与间距概述 + 12.1 间距设置(上):究竟该用 em 还是 px

当前内容所在位置&#xff08;可进入专栏查看其他译好的章节内容&#xff09; 第四部分 视觉增强技术 ✔️【第 12 章 CSS 排版与间距】 ✔️ 12.1 间距设置 ✔️ 12.1.1 使用 em 还是 px ✔️12.1.2 对行高的深入思考12.1.3 行内元素的间距设置 文章目录 第 12 章 排版与间距…

数据结构代码归纳

1.线性表 线性表的顺序表示 定义与初始化 typedef struct SqList{ElemType data[MaxSize];//ElemType *data 开动态数组 int length; }Sqlist; void InitList(SqList &L){L.length0;//若静态数组//若动态数组 //L.data(ElemType*)malloc(sizeof(ElemType)*MaxSize); }…

数据结构 (36)各种排序方法的综合比较

一、常见排序方法分类 插入排序类 直接插入排序&#xff1a;通过构建有序序列&#xff0c;对于未排序数据&#xff0c;在已排序序列中从后向前扫描&#xff0c;找到相应位置并插入。希尔排序&#xff1a;是插入排序的一种改进版本&#xff0c;先将整个待排序的记录序列分割成为…

SpringMVC全局异常处理

一、Java中的异常 定义&#xff1a;异常是程序在运行过程中出现的一些错误&#xff0c;使用面向对象思想把这些错误用类来描述&#xff0c;那么一旦产生一个错误&#xff0c;即创建某一个错误的对象&#xff0c;这个对象就是异常对象。 类型&#xff1a; 声明异常&#xff1…

【高中生讲机器学习】28. 集成学习之 Bagging 随机森林!

创建时间&#xff1a;2024-12-09 首发时间&#xff1a;2024-12-09 最后编辑时间&#xff1a;2024-12-09 作者&#xff1a;Geeker_LStar 嘿嘿&#xff0c;你好呀&#xff01;我又来啦~~ 前面我们讲完了集成学习之 Boooooosting&#xff0c;这篇我们来看看集成学习的另一个分支…

springSecurity权限控制

权限控制&#xff1a;不同的用户可以使用不同的功能。 我们不能在前端判断用户权限来控制显示哪些按钮&#xff0c;因为这样&#xff0c;有人会获取该功能对应的接口&#xff0c;就不需要通过前端&#xff0c;直接发送请求实现功能了。所以需要在后端进行权限判断。&#xff0…

李飞飞的生成式3D场景,对数字孪生的未来影响几何?

大家好&#xff0c;我是日拱一卒的攻城师不浪&#xff0c;致力于技术与艺术的融合。这是2024年输出的第47/100篇文章。 前言 这两天&#xff0c;AI界的教母李飞飞团队重磅发布了空间智能生成式AI大模型。 仅通过一张图片就能够生成一个可操作和交互的3D空间场景。 空间智能的…

意图识别模型使用 基于BERT的对话意图和槽位联合识别 CPU运行BERT模型-亲测成功

意图识别模型使用 基于BERT的对话意图和槽位联合识别 CPU运行BERT模型-亲测成功 我们在开发AI-Agent智能体时&#xff0c;通常会使用提示词工程设置场景的带入&#xff0c;在实际项目中会有很多场景&#xff0c;如果所有提示词都放一起就会超过Token限制&#xff0c;则不得不拆…

OSG开发笔记(三十七):OSG基于windows平台msvc2017x64编译器官方稳定版本OSG3.4.1搭建环境并移植Demo

​若该文为原创文章&#xff0c;未经允许不得转载 本文章博客地址&#xff1a;https://blog.csdn.net/qq21497936/article/details/144258047 各位读者&#xff0c;知识无穷而人力有穷&#xff0c;要么改需求&#xff0c;要么找专业人士&#xff0c;要么自己研究 长沙红胖子Qt…