Android OpenGL ES 学习(五) -- 渐变色

news2024/11/23 0:44:48

OpenGL 学习教程
Android OpenGL ES 学习(一) – 基本概念
Android OpenGL ES 学习(二) – 图形渲染管线和GLSL
Android OpenGL ES 学习(三) – 绘制平面图形
Android OpenGL ES 学习(四) – 正交投屏
Android OpenGL ES 学习(五) – 渐变色
代码工程地址: https://github.com/LillteZheng/OpenGLDemo.git

这次要完成的效果:
在这里插入图片描述
前面的代码中,我们的颜色是写死一种的,如何实现上面的渐变色呢?
这里就需要用到光栅化:
在这里插入图片描述
再复习一下光栅化的概念:它会图元映射成屏幕上相应的像素,生成供片段着色器使用上色的片段。

前面说道,顶点数据不止包含位置,还有其他信息,所以,在绘制顶点位置的时候,也传递顶点颜色,由 OpenGL 实现栅格化的效果。
这里,你可能会有疑惑,传递了三个颜色,也应该也是三个颜色啊,怎么会有渐变色呢?
带着疑问,我们来试试。

一. 着色器代码

前面说道,GLSL 在3.0 使用 in 和 out 来在着色器之间,传递数值。所以,我们在顶点着色器中使用 out 定义相同名字的颜色,在片段着色器中,使用 in 接收端相同的名字的颜色值。

private const val VERTEX_SHADER = """#version 300 es
          layout(location = 0) in vec4 a_Position;
          // mat4:4×4的矩阵
          uniform mat4 u_Matrix;
          //定义可以给外部赋值的顶点数据
          layout(location = 1) in vec4 a_Color;
          //给片段着色器的颜色顶点
          out vec4 vTextColor;
          void main()
          {
              // 矩阵与向量相乘得到最终的位置
              gl_Position = u_Matrix * a_Position;
              gl_PointSize = 30.0;
              //传递给片段着色器的颜色
              vTextColor = a_Color;
          
          }
  """

private const val FRAGMENT_SHADER = """#version 300 es
        precision mediump float;
        out vec4 FragColor;
        //接收端顶点着色器的数据,名字要相同
        in vec4 vTextColor;
        void main()
        {
          FragColor = vTextColor;
        }
"""

1.1 定义三角形的顶点位置和颜色

        private val POINT_DATA = floatArrayOf(
            //三角形,用三个分量,z 分量为 0
            0f,0.5f,0f,
            -0.5f,-0.5f,0f,
            0.5f,-0.5f,0f
        )
        private val COLOR_DATA = floatArrayOf(
            //颜色值 RGB
            1f,0.5f,0.5f,
            1f,0f,1f,
            0f,0.5f,1f
        )
 
 //加载到内存
 private var vertexData = BufferUtil.createFloatBuffer(POINT_DATA)
 private var colorData = BufferUtil.createFloatBuffer(COLOR_DATA)

1.2 关联和使用顶点索引数据:

    override fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) {
        GLES30.glClearColor(1f, 1f, 1f, 1f)
        makeProgram(VERTEX_SHADER, FRAGMENT_SHADER)

        uMatrix = getUniform(U_MATRIX)

        GLES30.glVertexAttribPointer(
            0, 3, GLES30.GL_FLOAT,
            false, 0, vertexData
        )
        GLES30.glEnableVertexAttribArray(0)

        GLES30.glVertexAttribPointer(
            1, 3, GLES30.GL_FLOAT,
            false, 0, colorData
        )
        GLES30.glEnableVertexAttribArray(1)
    }

1.3 绘制:

    override fun onDrawFrame(gl: GL10?) {
        //步骤1:使用glClearColor设置的颜色,刷新Surface
        GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)
        GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP,0,3)

    }

效果:
在这里插入图片描述
嗯嗯。。。 ,是的,为啥是渐变色的?不是传了三个颜色吗?
看看官网的解释:

这个图片可能不是你所期望的那种,因为我们只提供了3个颜色,而不是我们现在看到的大调色板。这是在片段着色器中进行的所谓片段插值(Fragment Interpolation)的结果。当渲染一个三角形时,光栅化(Rasterization)阶段通常会造成比原指定顶点更多的片段。光栅会根据每个片段在三角形形状上所处相对位置决定这些片段的位置。
基于这些位置,它会插值(Interpolate)所有片段着色器的输入变量。比如说,我们有一个线段,上面的端点是绿色的,下面的端点是蓝色的。如果一个片段着色器在线段的70%的位置运行,它的颜色输入属性就会是一个绿色和蓝色的线性结合;更精确地说就是30%蓝 + 70%绿。
这正是在这个三角形中发生了什么。我们有3个顶点,和相应的3个颜色,从这个三角形的像素来看它可能包含50000左右的片段,片段着色器为这些像素进行插值颜色。

什么意思呢,我的理解是,这个三角形在光栅化的时候,被分割成N多个小像素点,再填充颜色时候,也是按像素去填充的,从三角形的颜色来看,也可以知道,它是从酒红色过度到蓝色的。

为了验证这个说法也比较简单,我们绘制一条先,它只有两个点,红色和蓝色,看看表现如何,修改顶点数据为线:

        private val POINT_DATA = floatArrayOf(
            //线段
            -0.5f,0f,0f,
            0.5f,0f,0f
        )
        private val COLOR_DATA = floatArrayOf(
            //颜色值 RGB
            1f,0f,0f,
            0f,0f,1f,
        )

绘制那里从三角形改成线

    override fun onDrawFrame(gl: GL10?) {
        //步骤1:使用glClearColor设置的颜色,刷新Surface
        GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)
        GLES30.glLineWidth(10f)
        GLES30.glDrawArrays(GLES30.GL_LINES,0,2)

    }

在这里插入图片描述

二. 优化数据

上面的数据,位置和颜色是分开的,一个位置数组,对应一个颜色数组,他们需要一一对应。
但我们可以用另外一种方式,把位置和颜色放到同个数组里面,如:

        private val POINT_COLOR_DATA = floatArrayOf(
            //定点+颜色
            0f,0.5f,0f,1f,0.5f,0.5f,
            -0.5f,-0.5f,0f,1f,0f,1f,
            0.5f,-0.5f,0f,0f,0.5f,1f
        )
private var vertexData = BufferUtil.createFloatBuffer(POINT_COLOR_DATA)

这样,我们只需要一个数组,加载一次内存就可以了。

修改加载索引的方式:

    override fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) {
        GLES30.glClearColor(1f, 1f, 1f, 1f)
        makeProgram(VERTEX_SHADER, FRAGMENT_SHADER)

        uMatrix = getUniform(U_MATRIX)

        vertexData.position(0)
        //步进为 24
        GLES30.glVertexAttribPointer(
            0, 3, GLES30.GL_FLOAT,
            false, 24, vertexData
        )
        GLES30.glEnableVertexAttribArray(0)
        //颜色地址从3开始,前面3个为位置
        vertexData.position(3)
        GLES30.glVertexAttribPointer(
            1, 3, GLES30.GL_FLOAT,
            false, 24, vertexData
        )
        GLES30.glEnableVertexAttribArray(1)
    }

两个点要解释:

步进为啥是24:
顶点着色器允许我们指定任何以顶点属性为形式的输入。这使其具有很强的灵活性的同时,它还的确意味着我们必须手动指定输入数据的哪一个部分对应顶点着色器的哪一个顶点属性。所以,我们必须在渲染前指定OpenGL该如何解释顶点数据。
数据之间是紧密排列的,所以,当只有顶点数据的时候,我们认为它是正确能被获取的:
在这里插入图片描述

  • 位置数据被储存为32位(4字节)浮点值。
  • 每个位置包含3个这样的值。
  • 在这3个值之间没有空隙(或其他值)。这几个值在数组中紧密排列(Tightly Packed)。
  • 数据中第一个值在缓冲开始的位置。

从这里的解释来看,我们第一次完成的渐变色,也可以修改成:

//旧方案
 GLES30.glVertexAttribPointer(
            0, 3, GLES30.GL_FLOAT,
            false, 0, vertexData
        )
 // 根据步进定义,定个分量个数 * 4(字节)
 GLES30.glVertexAttribPointer(
    0, 3, GLES30.GL_FLOAT,
    false, 3 * 4, vertexData
)

也能正常绘制渐变色的三角形。

现在插入了颜色值,所以它的步进为 6 * 4 = 24:
在这里插入图片描述
vertexData.position(3)
对每个顶点属性来说,他们的其实位置都不同,位置默认为0,而颜色值的偏移量是在位置之后,所以偏移量为3。

这样,渐变色我们就学习完了。

参考:
https://learnopengl-cn.github.io/01%20Getting%20started/04%20Hello%20Triangle/
https://learnopengl-cn.github.io/01%20Getting%20started/05%20Shaders/
https://juejin.cn/post/7145094035521470500

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

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

相关文章

【实习之velocity 三 Vtl-引入资源】

文章目录一、#include1.作用:引入外部资源,引入的资源不会被引擎所解析2.语法:#include(resource)二、#parse作用:引入的外部资源,引入的资源将被引擎所解析语法:#parse(resource)三、define作用:定义重用模块(不带参数)语法:四、evaluate作用:动态计算,动态计算可以…

学习笔记:内存四区

内存分区模型 1内存分区模型 C程序在执行I将内存大方向划分为4个区域 ●代码区:存放函数体的二进制代码,由操作系统进行管理的 ●全局区:存放全局变量和静态变量以及常量 ●栈区:由编译器自动分配释放存放函数的参数值局部变量等 ●堆区:由程序员分配和释放若程序员…

【WSN布局】基于LICHTENBERG算的多目标传感器选择和放置优化问题研究附matlab代码

✅作者简介:热爱科研的Matlab仿真开发者,修心和技术同步精进,matlab项目合作可私信。 🍎个人主页:Matlab科研工作室 🍊个人信条:格物致知。 更多Matlab仿真内容点击👇 智能优化算法 …

如何改变胆小怕事的性格?

胆小怕事 就是指一个人做事总是十分胆怯,畏畏缩缩,举手投足也不够大气,经常要看别人的脸色,害怕麻烦上身,对强势的人也会显得唯唯诺诺,战战兢兢。胆小怕事 是某种心理问题的表现,或者说性格缺陷…

基于Q-learning方法的地铁列车时刻表重新调度

文章信息《Metro Train Timetable Rescheduling Based on Q-learning Approach》是发表在2020 IEEE 23rd International Conference on Intelligent Transportation Systems (ITSC)上的一篇文章。摘要在地铁系统中,不可预测的干扰会影响正常运行,给乘客带…

什么是短网址?如何调用接口生成短地址?

随着网络应用的深入和普及,网址资源越来越少了,长尾网址也派上用场了,只是网址太长不方便识别与记录。因此,就有了短网址替代长网址的技术接口。 随着SEO的重要性越来越明显,在推广的时候如果把网页链接缩短可以获得更…

【wireshark】如何获取一个设备的IP地址

问题 开发中往往会出现无法知道设备正确的IP地址,从而无法连接到设备。 解决方式: 使用软件工具wireshark来获取设备IP地址。 可以实现不同网段捕获设备IP 具体流程: 1. 下载wireshark抓包程序 https://www.wireshark.org/download.htm…

MySQL日志(undo log 和 redo log 实现事务的原子性/持久性/一致性)

日志的重要性 日志绝对是数据库的核心. 持久化的日志记录了各种重要的信息.数据的恢复需要依赖日志。 慢查询sql语句需要用到慢查询日志。以及错误日志中保存着mysqld数据库服务端在启动过程中发生的重大错误信息... 数据库重要组成 本质上来说是一个文件系统 (两大重要组…

PHP+MySQL基于thinkphp的企业信息销售展示系统的设计

公司企业网站,是一个供为企业推广的平台,是完全的,高速的,开放的,其核心思想是提供一个以自然语言为主的用户界面,让用户能够更好的刚加方便快捷的管理物流信息的一个渠道和平台。本课题的开发工具可以使用PHP开发语言和MySQL数据进行的开发。 该系统的基本功能包括用户注册登录…

JAVA 设计模式篇

JAVA 设计模式篇1、UML类图2、设计原则2.1、开闭原则2.2、里氏代换原则2.3、依赖倒转原则2.4、接口隔离原则2.5、迪米特法则2.6、合成复用原则3、设计模式3.1、单例模式3.1.1、单例模式实现——饿汉式3.1.1.1、静态变量实现3.1.1.2、静态方法实现3.1.1.3、枚举方式3.1.2、单例模…

GRPC远程调用

FAQ | gRPC1. gRPC原理 FAQ | gRPC Asynchronous-API tutorial | C | gRPC 1.1 什么是RPC RPC 即远程过程调用协议(Remote Procedure Call Protocol),可以让我们像调用本地对象一样发起远程调用。RPC 凭借其强大的治理功能,成…

Linux 中的文件简单说明

Linux 中的文件简单说明 作者:Grey 原文地址: 博客园:Linux 中的文件简单说明 CSDN:Linux 中的文件简单说明 说明 本文基于 CentOS 7 根目录(/)下文件夹主要作用 [rootlinux /]# ll / total 16 lrwxrwxrwx. 1 root root…

VMOS虚拟机开源,游戏安全面临新挑战

相信大家对虚拟机并不陌生,一台设备可以模拟出多个操作系统,完美解决了不同场景下设备限制问题,还节约了购买软硬件设备的成本,为工作和生活提供了不少便利,得到了广泛的应用。 而虚拟机技术却被游戏黑灰产所利用&…

全球约有 150 亿台设备在运行 Java,收费后还能用吗?

据估算,全球约有 150 亿台设备在运行 Java™。约900万 Java 程序员.... https://www.oracle.com/java/technologies/downloads/archive 一、Java8及之前的版本均免费 我们可以看到上图中绿色的部分均是免费版本大家可以随便下载随便使用。 二、最后的免费版本 jd…

将光耦合进入单模光纤的最佳工作距离

摘要 光纤是现代光学系统中最通用的部件之一。它们最重要的特点之一是它们能够在远距离(甚至几公里)内以极低的损耗传输光能。另一方面,以一种能够达到尽可能高的效率的方式将光耦合到光纤中通常是一项非常精细的需求:例如&…

美苏太空竞赛历年卫星火箭发射以及历史事件介绍

1957 时间苏联美国折叠时间7月16日在与政府官员的会晤中,科罗廖夫和格鲁什科提出了开发超重型火箭的想法。美国海军对先锋号火箭进行试射。5月1日10月4日卫星号火箭发射了斯普特尼克1号,即第一颗人造卫星。美国海军对先锋号火箭进行试射。10月23日11月3…

ArcGIS API4.X + API文档 本地部署(Tomcat)

前言: js.arcgis.com有时候不太稳定,导致项目或者自己测试代码需要等待远程资源请求,体验感及其不好,能自己掌控的资源最好就别去拿在线的,当然服务器稳定就另当别论。(所以本地部署有两种含义:…

ModStartCMS v5.3.0 任务调度记录,模块市场优化

系统介绍 ModStart 是一个基于 Laravel 模块化极速开发框架。模块市场拥有丰富的功能应用,支持后台一键快速安装,让开发者能快的实现业务功能开发。 系统完全开源,基于 Apache 2.0 开源协议,免费且不限制商业使用。 功能特性 丰…

在vue项目中下载swiper出现:Do not use ‘new’ for side effects报错问题

我报错的情况是:我在单文件组件vue文件中使用new Swiper,报了两个错误,图示: 第一个错误是:‘Swiper’ is not defined 第二个错误是:Do not use ‘new’ for side effects 解决办法: &#x…

【软件测试】7年资深带你详探什么是测试开发?

目录:导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜)前言 在一些大公司里&…