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

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程序写得不够规范,后续章节逐渐补齐。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Ubuntu中配置交叉编译工具的三条命令的详细研究

关于该把下面的三条交叉编译配置语句加到哪里,详情见 https://blog.csdn.net/wenhao_ir/article/details/144326545 的第2点。 现在试解释下面三条交叉编译配置语句: export ARCHarm export CROSS_COMPILEarm-buildroot-linux-gnueabihf- export PATH$…

【环境搭建】WordPress本地部署搭建及历史版本插件安装(windows系统)

🏘️个人主页: 点燃银河尽头的篝火(●’◡’●) 如果文章有帮到你的话记得点赞👍收藏💗支持一下哦 【环境搭建】WordPress本地部署搭建及历史版本插件安装(windows系统) WordPress搭建环境部署(…

【开源免费】基于SpringBoot+Vue.JS大创管理系统(JAVA毕业设计)

博主说明:本文项目编号 T 081 ,文末自助获取源码 \color{red}{T081,文末自助获取源码} T081,文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析…

47 基于单片机的书库环境监测

目录 一、主要功能 二、硬件资源 三、程序编程 四、实现现象 一、主要功能 基于51单片机,采用DHT11湿度传感器检测湿度,DS18B20温度传感器检测温度, 采用滑动变阻器连接数模转换器模拟二氧化碳和氧气浓度检测,各项数值通过lc…

outlook软件配置邮箱提示“到邮件服务器的加密连接不可用”

outlook软件配置邮箱提示“到邮件服务器的加密连接不可用” 问题描述: outlook软件里邮箱提示“已断开”或配置邮箱时提示“到邮件服务器的加密连接不可用”。 解决方案: 一、更改注册表(可先导出备份) winr,输入re…

Jmeter进阶篇(30)深入探索 JMeter 监听器

前言 在性能测试领域里,Apache JMeter 是一款经典而强大的工具,而其中的监听器(Listeners)组件更是发挥着不可或缺的关键作用。 监听器就像敏锐的观察者,默默记录测试执行过程中的各种数据,作为系统性能分析的数据依据。 本文将带你全方位走进 JMeter 监听器的奇妙世界,…

微调 Llama 3.2:让 AI 更好地读取医学图像

您是否想知道人工智能模型如何学习理解医学图像?今天,我将带您完成一个令人兴奋的项目:微调 Meta 的 Llama 3.2 Vision 模型来分析放射线图像。如果您不是技术专家,也不要担心 - 我会用简单的术语来解释。 它是如何运作的&#x…

(长期更新)《零基础入门 ArcGIS(ArcMap) 》实验二----网络分析(超超超详细!!!)

相信实验一大家已经完成了,对Arcgis已进一步熟悉了,现在开启第二个实验 ArcMap实验--网络分析 目录 ArcMap实验--网络分析 1.1 网络分析介绍 1.2 实验内容及目的 1.2.1 实验内容 1.2.2 实验目的 2.2 实验方案 2.3 实验流程 2.3.1 实验准备 2.3.2 空间校正…

【Spring项目】表白墙,留言板项目的实现

阿华代码,不是逆风,就是我疯 你们的点赞收藏是我前进最大的动力!! 希望本文内容能够帮助到你!! 目录 一:项目实现准备 1:需求 2:准备工作 (1)…

如何在 Ubuntu 上安装开源监控工具 Uptime Kuma

简介 Uptime Kuma(或简称 Kuma)是一个开源监控工具,用于监控 HTTP、HTTPS、DNS 等协议的服务。Uptime Kuma 提供多种功能,如多语言支持、多个状态页面、代理支持等。 接下来,我将一步一步教大家如何进行安装和部署&am…

Idea实现定时任务

定时任务 什么是定时任务? 可以自动在项目中根据设定的时长定期执行对应的操作 实现方式 Spring 3.0 版本之后自带定时任务,提供了EnableScheduling注解和Scheduled注解来实现定时任务功能。 使用SpringBoot创建定时任务非常简单,目前主要…

Spark实训

实训目的: 介绍本实训的基本内容,描述知识目标、,以及本实训的预期效果等。 1、知识目标 (1)了解spark概念、基础知识、spark处理的全周期,了解spark技术是新时代对人才的新要求。 (2)掌握Linux、hadoop、spark、hive集群环境的搭建、HDFS分布文件系统的基础知识与应用…

LoViT: 用于手术阶段识别的长视频Transformer|文献速递-生成式模型与transformer在医学影像中的应用

Title 题目 LoViT: Long Video Transformer for surgical phase recognition LoViT: 用于手术阶段识别的长视频Transformer 01 文献速递介绍 快速发展的手术数据科学(SDS)领域旨在通过先进利用手术室(OR)内医疗设备采集的数据…

Microi 吾码:后端开发的创新引擎与代码艺术

目录 一、引言 二、Microi 吾码后端架构概述 三、Microi 吾码在数据处理与优化方面的应用 四、Microi 吾码与外部服务的集成 五、Microi 吾码在安全性方面的考量与实现 六、Microi 吾码的性能监控与调优 七、总结 一、引言 在当今数字化浪潮汹涌澎湃的时代,软…

Day2——需求分析与设计

教师端签到应用软件的需求分析; 产品经理如何写好产品需求文档(附模板) 需求分析是软件开发过程中的关键步骤,它确保了开发的软件能够满足用户的需求。以下是进行需求分析的具体步骤: 1. 确定分析目标 明确教师端签到…

TypeScript进阶

Typescript进阶 基础知识 JavaScript 的核心特点就是灵活,但随着项目规模的增大,灵活反而增加开发者的心智负担。例如在代码中一个变量可以被赋予字符串、布尔、数字、甚至是函数,这样就充满了不确定性。而且这些不确定性可能需要在代码运行…

Unity性能优化---动态网格组合(二)

在上一篇中,组合的是同一个材质球的网格,如果其中有不一样的材质球会发生什么?如下图: 将场景中的一个物体替换为不同的材质球 运行之后,就变成了相同的材质。 要实现组合不同材质的网格步骤如下: 在父物体…

【C++】求第二大的数详细解析

博客主页: [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C 文章目录 💯前言💯题目描述💯输入描述💯解题思路分析1. 题目核心要求2. 代码实现与解析3. 核心逻辑逐步解析定义并初始化变量遍历并处理输入数据更新最大值与次大值输…

修改git_bash命令行默认显示

1 背景 Git Bash默认显示用户名、主机、全路径,对于截图而言,会泄露一些隐私。 想办法去掉这些信息。 2 代码内容 # Shows Git branch name in prompt. parse_git_branch() {git branch 2> /dev/null | sed -e /^[^*]/d -e s/* \(.*\)/ (\1)/ } # …

Windwos Hyper-v 虚拟机SSH连接失败的问题

Windwos Hyper-v 虚拟机SSH连接失败的问题 一、问题现象: hyper-v里的虚拟机和宿主机都能正常访问外网,虚拟机也做了静态IP设置,但是宿主机就是无法通过SSH连接到虚拟机。 二、解决办法: 1、打开windows的高级网络设置&#x…