OpenGL ES入门教程(三)之为平面桌子添加混合色

OpenGL ES入门教程(三)之为平面桌子添加渐变色

    • 前言
    • 零、OpenGL ES实现混合色的原理
    • 一、修改绘制的桌子结构
      • 1. 三角形扇介绍
      • 2. 基于三角形扇结构绘制平面桌子
    • 二、为每个顶点添加颜色属性
    • 三、修改着色器
      • 1. 顶点着色器
      • 2. 片段这色器
    • 四、绘制具有混合颜色的平面桌子
      • 1. 计算跨度
      • 2. 关联顶点位置数据和颜色数据
      • 3. 绘制图形
    • 五、完整示例代码下载

前言

上一篇文章我们讲解了OpenGL ES如何绘制一个平面桌子,本文在其基础上继续讲解如何使绘制的平面桌子具有混合色,效果类似在桌子中心上面吊一盏灯,越靠近桌子中心颜色越亮白,越远离桌子中心颜色越暗灰。如果是OpenGL ES小白,在阅读本篇文章之前一定要搞懂上篇文章OpenGL ES入门教程(二)之绘制一个平面桌子

零、OpenGL ES实现混合色的原理

首先混合色指的是将两种及以上的颜色进行混合而得出的其它颜色。之前的文章中我们讲过OpenGL只有点,线,三角形三种基本图元,其余图元都是有这三种基本图元构成。很明显只有线和三角形这两种图元有实现混合色的意义,混合色最常见的实现方式就是对颜色进行线性插值,OpenGL实现混合色的原理也是如此。

  • 一条直线实现混合色,只需要给定直线两个顶点的颜色,线其余的颜色都是由这两个顶点线性插值而得。混合色结果取决于线上的位置与两个顶点之间的距离,线上的位置越接近哪个顶点,那混合色就越倾向于哪个顶点的颜色。直线上各个位置通过线性插值计算的混合色的公式如下:
    在这里插入图片描述
    可以看到distance_ratio的值越大,vertex_1_value所占的比重就越大,而对应vertex_0_value所占的比重就越小,即distance_ratio的值越大,颜色越接近于vertex_1_value的颜色。

  • 三角形平面实现混合色,只需要给定三角形三个顶点的颜色,三角形内部其余的颜色都是由这三个顶点线性插值而得。对于三角形内任何给定的点,从那个点向每个顶点所对应的点画一条直线就可以生成三个内部三角形。这三个内部三角形的面积的比例决定了那个点上每种颜色的权重。如下图所示三角形,那个点上黄色的强度就取决于黄色顶点相对的那个内部三角形的面积。距离黄色顶点越近的点,它相对的三角形就越大,在那个点的片段就越显黄。
    在这里插入图片描述
    三角形内每个点的混合色线性插值的计算公式如下:
    在这里插入图片描述

说到底,就是最普通的线性插值,只是插值插的是颜色值而已,与我们上学时数学课上的线性插值一样(z=ax+by),相信稍微思考下就明白啦~,而且下面的混合色实现也不需要我们写公式,OpenGL都封装好了。

一、修改绘制的桌子结构

我们要实现效果类似在桌子中心上面吊一盏灯,越靠近桌子中心颜色越亮白,越远离桌子中心颜色越暗灰。那我们就要在桌子的中心放置一个顶点,且该处顶点颜色是纯白色,因此,长方形平面桌子应该有如下图所示的4个三角形组成,而上一篇文章中我们实现的平面桌面是由两个三角形组成的,因此我们应该先修改桌面的结构。在这里插入图片描述

1. 三角形扇介绍

一个三角形扇以一个中心顶点作为起始,使用相邻的两个顶点创建第一个三角形,接下来的每个顶点都会创建一个三角形,围绕起始的中心点按扇形展开。为了使这个扇形闭合,我们只需要在最后重复第二个点。

三角形扇完全可以实现我们上面所提到的由4个三角形构成的长方形,该三角形扇包含的顶点如下图所示,一共由5个顶点组成。
在这里插入图片描述

2. 基于三角形扇结构绘制平面桌子

  1. 上面提到三角形扇需要5个顶点,同时由于为了使得三角形扇闭合,我们需要重复第二个顶点使其成为最后一个顶点,因此,平面桌子的坐标数据修改如下:
float[] tableVerticesWithTriangles = {/**无论是x还是y坐标,OpenGL都会把屏幕映射到[-1,1]的范围内。即屏幕的左边对应x轴的-1,右边对应+1;屏幕的底边对应y轴的-1,顶边对应+1*/// Triangle Fan0f, 0f,//三角形扇的中心点坐标-0.5f, -0.5f,//三角形扇第二个顶点坐标0.5f, -0.5f,0.5f, 0.5f,-0.5f, 0.5f,-0.5f, -0.5f,//三角形扇必须封闭,因此这个就是第二个顶点坐标// Line 1-0.5f, 0f,0.5f, 0f,// Mallets0f, -0.25f,0f,  0.25f};
  1. 调用glDrawArrays方法,第一个参数选择GL_TRIANGLE_FAN绘制三角形扇即可,平面桌子的绘制代码如下
	@Overridepublic void onDrawFrame(GL10 gl) {glClear(GL_COLOR_BUFFER_BIT);/*绘制桌子*///更新着色器中u_Color的值(蓝色)glUniform4f(uColorLocation, 0.0f, 0.0f, 1.0f, 1.0f);//参数1:绘制三角形扇;参数2:从顶点数组的开头开始读顶点;参数3:读取6个顶点//之前glVertexAttribPointer告诉过OpenGL每个顶点的位置包含两个浮点分量,因此OpenGL会使用vertexData中如下12个浮点数glDrawArrays(GL_TRIANGLE_FAN, 0, 6);/*绘制分割线*///更新u_Color的值(红色)glUniform4f(uColorLocation, 1.0f, 0.0f, 0.0f, 1.0f);//参数1:绘制直线; 参数2:从顶点数组的第6个顶点之后(即第7个顶点)开始读取;参数3:读取两个顶点glDrawArrays(GL_LINES, 6, 2);/*绘制两个木槌*///更新u_Color的值(黑色)glUniform4f(uColorLocation, 0.0f, 0.0f, 0.0f, 1.0f);glDrawArrays(GL_POINTS, 8, 1);//更新u_Color的值(绿色)glUniform4f(uColorLocation, 0.0f, 1.0f, 0.0f, 1.0f);glDrawArrays(GL_POINTS, 9, 1);}
  1. 此时,运行代码结果和上一篇文章绘制的平面桌子效果一致,只是长方形的结构由两个三角形组成变成了由4个三角形组成而已。
    在这里插入图片描述

二、为每个顶点添加颜色属性

我们之前在数组中记录了每个顶点的位置坐标数据,同理,我们也可以再定义一个数组,对应记录每个顶点的颜色数据。但为了数据处理更加高效,我们不用另外再定义一个数组存储颜色数据,完全可以把顶点的位置数据和颜色数据存储在同一个数组中。包含顶点位置坐标数据和颜色数据的数组如下,每个顶点包含x,y两个位置坐标数据,紧邻其后是r,g,b三个颜色分量数据

	float[] tableVerticesWithTriangles = {/**无论是x还是y坐标,OpenGL都会把屏幕映射到[-1,1]的范围内。即屏幕的左边对应x轴的-1,右边对应+1;屏幕的底边对应y轴的-1,顶边对应+1*/// Triangle Fan0f,    0f,   1f,   1f,   1f,//前两位是三角形扇的中心点坐标,后面三位是代表rgb的颜色分量(此处代表的颜色是白色)-0.5f, -0.5f, 0.7f, 0.7f, 0.7f,//三角形扇第二个顶点坐标0.5f, -0.5f, 0.7f, 0.7f, 0.7f,0.5f,  0.5f, 0.7f, 0.7f, 0.7f,-0.5f,  0.5f, 0.7f, 0.7f, 0.7f,-0.5f, -0.5f, 0.7f, 0.7f, 0.7f,//三角形扇必须封闭,因此这个就是第二个顶点坐标// Line 1-0.5f, 0f, 1f, 0f, 0f,//顶点颜色是红色0.5f, 0f, 0f, 1f, 0f,//顶点颜色是绿色// Mallets0f, -0.25f, 0f, 0f, 1f,//顶点颜色是蓝色0f,  0.25f, 1f, 0f, 0f//顶点颜色是红色};

三、修改着色器

1. 顶点着色器

顶点带有颜色属性的,顶点着色器代码如下。
加入了a_Color颜色属性和v_Color混合颜色属性,a_Color存储顶点的颜色,v_Color用于实现颜色的线性插值,为片段生成混合色。可以理解为咱们在第零章讲解的混合色原理,OpenGL通过varying这种特殊标记实现了。

/*
attribute:属性数据的特定标识
vec4:一种包含4个分量的向量数据类型(x,y,z,w)a_Position中x,y,z代表顶点的三维位置坐标,w是一个特殊坐标,后面会讲解
a_Position:变量名称,该名称后面OpenGL的glGetAttribLocation方法要用到,如果修改后面就要一起修改
*/
attribute vec4 a_Position;//代表位置的属性数据(x,y,z,w)
attribute vec4 a_Color;//代表颜色的属性数据(r,g,b,a)/*
varying:一种特殊的变量类型,它把给它的哪些值进行混合,并把这些混合的值发送给片段着色器。
其实所谓混合,就是通过线性插值方法生成片段中除了顶点之外的其它颜色。
例如生成直线的片段是由两个顶点构成,顶点0的a_Color是红色,顶点1的a_Color是绿色,通过将a_Color赋值给varying这种特殊类型的变量,
就可以使得越靠近顶点0的直线片段颜色越红,越靠近顶点1的直线片段颜色越绿。
*/
varying vec4 v_Color;//和C语言类似,main函数是着色器的入口函数
void main()
{//将颜色数据赋值给varying类型的变量v_Color = a_Color;//gl_Position :OpenGL特定的变量名,用于存储我们定义的顶点数据gl_Position = a_Position;//gl_PointSize:OpenGL特定的变量名,用于存储点的大小gl_PointSize = 10.0;
}

2. 片段这色器

为片段生成混合颜色的片段着色器代码如下。
将uniform标识的一个片段只有一个颜色的属性修改为varying标识的混合颜色属性,这个属性与顶点着色器定义的varying属性相对应,属性名必须一致。

//OpenGL定义float数据类型的精度(lowp;mediump;highp),就像java代码中浮点型选择float类型还是double类型。
//精度是以性能为代价的,这里选择mediump
precision mediump float;varying vec4 v_Color;//通过线性插值顶点颜色,形成混合颜色(这个参数名必须与顶点着色器中的参数名一致)//和C语言类似,main函数是着色器的入口函数
void main()
{gl_FragColor = v_Color;
}

四、绘制具有混合颜色的平面桌子

1. 计算跨度

上一篇文章中,我们讲解到通过glVertexAttribPointer方法将顶点位置与着色器进行关联,该方法中有一个跨度参数stride,我们直接赋值为0,并没有对其进行详细讲解,因为那时数组中只存储了顶点位置坐标数据,不包含颜色属性数据,因此OpenGL只需要一个顶点数据挨着一个顶点数据的读取就能得到每个顶点的正确位置坐标数据。

		//获取顶点着色器中(attribute)位置属性aPositionLocation = glGetAttribLocation(program, A_POSITION);//告诉OpenGL从vertexData中读取a_Position的数据vertexData.position(0);//将缓冲区数据中的指针指向第一个数据,即从第一个数据开始读/*public static void glVertexAttribPointer(int indx,int size,int type,boolean normalized,int stride,java.nio.Buffer ptr)将着色器中的位置属性与本地顶点数据相关联。aPositionLocation:着色器中定义的位置属性POSITION_COMPONENT_COUNT:每次从本地数组中读取两个数据(即x,y代表一个顶点坐标)GL_FLOAT:OpenGL采用的数据类型,因为我们定义的是浮点数数组,所以采用GL_FLOATvertexData:要关联的本地数据列表*/glVertexAttribPointer(aPositionLocation, POSITION_COMPONENT_COUNT, GL_FLOAT,false, 0, vertexData);

但是,现在我们的每个顶点不仅存储了位置坐标数据,同时存储了颜色属性数据,因此,第一个顶点的位置坐标数据和第二个顶点的位置坐标数据之间多个三个颜色属性数据,为了正常的获得每个顶点的位置坐标数据,我们需要把中间的颜色属性数据跨过去,所以,才有了跨度stride这个参数。因为,现在我们每个顶点位置坐标数据直接插入了三个颜色属性数据,因此下一个顶点的位置坐标数据需要跨过上一个顶点中的两个位置坐标数据和3个颜色属性数据,因此跨距stride计算方式如下(跨距的单位是字节):

	//每个float由4个字节组成private static final int BYTES_PER_FLOAT = 4;//每个顶点前两个浮点数代表位置:x,yprivate static final int POSITION_COMPONENT_COUNT = 2;//每个顶点后三个浮点数代表颜色:r,g,bprivate static final int COLOR_COMPONENT_COUNT = 3;//STRIDE代表每个顶点位置之间间隔多少个字节(因为现在顶点位置和顶点颜色数据混在一起,只有明确跨度才能查找到每个顶点的位置)private static final int STRIDE =(POSITION_COMPONENT_COUNT + COLOR_COMPONENT_COUNT) * BYTES_PER_FLOAT;

2. 关联顶点位置数据和颜色数据

通过glVertexAttribPointer函数将数组中存储的顶点数据与着色器中的属性相关联

		//获取顶点着色器中代表顶点颜色的属性aColorLocation = glGetAttribLocation(program, A_COLOR);//获取顶点着色器中代表顶点位置的属性(attribute)aPositionLocation = glGetAttribLocation(program, A_POSITION);//告诉OpenGL从vertexData中读取a_Position的数据vertexData.position(0);//将缓冲区数据中的指针指向第一个顶点的位置数据开始的位置glVertexAttribPointer(aPositionLocation, POSITION_COMPONENT_COUNT, GL_FLOAT,false, STRIDE, vertexData);//使能顶点数组glEnableVertexAttribArray(aPositionLocation);//告诉OpenGL从vertexData中读取a_Color的数据vertexData.position(POSITION_COMPONENT_COUNT); //将缓冲区中的指针指向第一个顶点的颜色数据开始的位置glVertexAttribPointer(aColorLocation, COLOR_COMPONENT_COUNT, GL_FLOAT,false, STRIDE, vertexData);//使能顶点数组glEnableVertexAttribArray(aColorLocation);

3. 绘制图形

现在每个顶点都有了自己的颜色,并且采用varying实现片段的混合色,因此,不要再调用glUniform4f方法为片段设置颜色了,每个片段的颜色都会由顶点插值而得。

	@Overridepublic void onDrawFrame(GL10 gl) {glClear(GL_COLOR_BUFFER_BIT);/*绘制桌子*///参数1:绘制三角形扇;参数2:从顶点数组的开头开始读顶点;参数3:读取6个顶点//之前glVertexAttribPointer告诉过OpenGL每个顶点的位置包含两个浮点分量,因此OpenGL会使用vertexData中12个浮点数glDrawArrays(GL_TRIANGLE_FAN, 0, 6);/*绘制分割线*///参数1:绘制直线; 参数2:从顶点数组的第6个顶点之后(即第7个顶点)开始读取;参数3:读取两个顶点glDrawArrays(GL_LINES, 6, 2);glLineWidth(10); //设置线宽,不设置线宽,无法体现线的渐变颜色/*绘制两个木槌*/glDrawArrays(GL_POINTS, 8, 1);glDrawArrays(GL_POINTS, 9, 1);}

运行代码,绘制图形效果如下图所示
在这里插入图片描述

五、完整示例代码下载

完整的代码都上传到了Gitee仓库
OpenGL_ES_DEMO中,仓库代码具有详细的提交记录,博友可以执行git命令:
git reset --hard f5081f05963ed48b4d149d4f9bc8476ee81caac8,切到本篇文章所讲解内容的完整示例代码。

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

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

相关文章

7.24 SpringBoot项目实战【审核评论】

文章目录 前言一、编写控制器二、编写服务层三、Postman测试前言 我们在 上文 7.23 已经实现了 评论 功能,本文我们继续SpringBoot项目实战 审核评论 功能。逻辑如下: 一是判断管理员权限,关于角色权限校验 在 7.5 和 7.6 分别基于 拦截器Interceptor 和 切面AOP 都实现过…

Qt OpenCV 学习(二):两个简单图片识别案例

1. 寻找匹配物体 1.1 mainwindow.h #ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow> #include <opencv2/opencv.hpp>#include <QImage> #include <QString> #include <QPixmap>QT_BEGIN_NAMESPACE namespace Ui { class Main…

TimeGPT:时序预测领域终于迎来了第一个大模型

时间序列预测领域在最近的几年有着快速的发展&#xff0c;比如N-BEATS、N-HiTS、PatchTST和TimesNet。 大型语言模型(llm)最近在ChatGPT等应用程序中变得非常流行&#xff0c;因为它们可以适应各种各样的任务&#xff0c;而无需进一步的训练。 这就引出了一个问题:时间序列的…

速达软件全系产品存在任意文件上传漏洞 附POC

@[toc] 速达软件全系产品存在任意文件上传漏洞 附POC 免责声明:请勿利用文章内的相关技术从事非法测试,由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失,均由使用者本人负责,所产生的一切不良后果与文章作者无关。该文章仅供学习用途使用。…

基于Java SSM框架+Vue实现药品保健品购物网站项目【项目源码+论文说明】

基于java的SSM框架Vue实现药品保健品购物网站演示 摘要 随着社会的发展&#xff0c;社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。 ssm药源购物网站&#xff0c;主要的模块包括两个用户&#xff0c;管理员权限&#xff1a;用…

关于我离破500粉丝感受

嘿嘿快破500粉丝啦&#xff0c;加油喔&#xff0c;感谢支持 首先&#xff0c;恭喜我在CSDN上的粉丝数量即将突破500大关&#xff01;这说明你在这个平台上的内容受到了很多人的关注和认可。 1. 保持高质量的内容输出&#xff1a;粉丝数量的增长与你在CSDN上发布的内容质量密切…

【数据库】基于封锁的数据库调度器,以及等待锁处理的优先级策略

封锁调度器的体系结构 ​专栏内容&#xff1a; 手写数据库toadb 本专栏主要介绍如何从零开发&#xff0c;开发的步骤&#xff0c;以及开发过程中的涉及的原理&#xff0c;遇到的问题等&#xff0c;让大家能跟上并且可以一起开发&#xff0c;让每个需要的人成为参与者。 本专栏会…

分享一个国内可用的免费AI-GPT网站

背景 ChatGPT作为一种基于人工智能技术的自然语言处理工具&#xff0c;近期的热度直接沸腾&#x1f30b;。 我们也忍不住做了一个基于ChatGPT的网站&#xff0c;可以免登陆&#xff01;&#xff01;国内可直接对话AI&#xff0c;也有各种提供工作效率的工具供大家使用。 可以这…

27、数据存储秒表(定时器扫描按键数码管)

数据存储 main.c #include <REGX52.H> #include "LCD1602.h" #include "Key.h" #include "AT24C02.h" #include "Delay.h"unsigned char KeyNum; unsigned int Num;void main() {LCD_Init();LCD_ShowNum(1,1,Num,5);while(1){…

Vue中下载不同文件的几种方式

当在Vue中需要实现文件下载功能时&#xff0c;我们可以有多种方式来完成。下面将介绍五种常用的方法。 1. 使用window.open方法下载文件 <template><div><button click"downloadFile(file1.pdf)">下载文件1</button><button click"…

Spring Cloud Alibaba简介

1、简介 Spring Cloud阿里(https://sca.aliyun.com/en-us/)为分布式应用开发提供一站式解决方案。它包含开发分布式应用程序所需的所有组件&#xff0c;使您可以轻松地使用Spring Cloud开发应用程序。 有了Spring Cloud阿里&#xff0c;你只需要添加一些注释和少量的配置&#…

vue2框架简易版响应式设计(观察者模式)

对于vue.js中的属性值我们要格外关注&#xff1a; $attrs 获取当前传递的参数 $listeners 获取当前组件的自定义事件 $children 获取当前组件所有子组件 $parent 获取当前组件所有父组件 $options 获取当前vue实例参数信息 $refs 获取ref所有的引用节点 设计原则&#x…

day4 节点两两交换

ListNode* swapPairs(ListNode* head) { ListNode* dummyHead new ListNode(0); // 设置一个虚拟头结点 dummyHead->next head; // 将虚拟头结点指向head&#xff0c;这样方便后面做删除操作 ListNode* cur dummyHead; while(cur->next ! nullptr && cur->…

编译原理头歌实验:实验4《算符优先分析法设计与实现》(C语言版)

任务描述 本关任务&#xff1a;加深对语法分析器工作过程的理解&#xff1b;加强对算符优先分析法实现语法分析程序的掌握&#xff1b;能够采用一种编程语言实现简单的语法分析程序&#xff1b;能够使用自己编写的分析程序对简单的程序段进行语法翻译。 相关知识 为了完成本…

【大连民族大学C语言CG题库练习题】——猪的安家问题

【问题描述】 Andy和Mary养了很多猪。他们想要给猪安家。但是Andy没有足够的猪圈&#xff0c;很多猪只能够在一个猪圈安家。举个例子&#xff0c;假如有16头猪&#xff0c;Andy建了3个猪圈&#xff0c;为了保证公平&#xff0c;剩下1头猪就没有地方安家了。Mary生气了&#xf…

(一)WtBtRunner回测大体流程

WtBtRunner是回测的主程序&#xff0c;启动改程序时&#xff0c;会进行配置文件和dll的加载 解析参数&#xff0c;由cppcli::Option完成参数解析初始化日志打印 std::string filename;if (lParam->exists())filename lParam->get<std::string>();elsefilename …

Java集合(已重写-废弃了)

# 精辟总结 其实各种八股文资料&#xff0c;他也就是围绕着核心知识展开提问的&#xff0c;你只要根据八股文把核心知识提炼出来&#xff0c;形成核心知识体系&#xff01; Java集合那是重点中的重点。最基本的概念要懂&#xff0c;核心的概念&#xff0c;那要滚瓜烂熟。 Ja…

【recrutment / Hiring / Job / Application】

Interviewee I), objected/targeted job/position1.1) Azure 平台运维工程师&#xff08;comms&social&#xff09;1.1.1), comms communication and social, for talk, content1.1.2) Cloud computing1.1.3) 拥有ITI/MCSE/RHCE相关认证或Azure认证(如Az204/Az304 have/own…

【计算机视觉】基于OpenCV计算机视觉的摄像头测距技术设计与实现

基于计算机视觉的摄像头测距技术 文章目录 基于计算机视觉的摄像头测距技术导读引入技术实现原理技术实现细节Python-opencv实现方案获取目标轮廓步骤 1&#xff1a;图像处理步骤 2&#xff1a;找到轮廓步骤完整代码 计算图像距离前置技术背景与原理步骤 1&#xff1a;定义距离…

[传智杯 #3 决赛] 面试

题目背景 disangan233 和 disangan333 去面试了&#xff0c;面试官给了一个问题&#xff0c;热心的你能帮帮他们吗&#xff1f; 题目描述 现在有 n 个服务器&#xff0c;服务器 i 最多能处理 ai​ 大小的数据。 接下来会有 k 条指令 bk​&#xff0c;指令 i 表示发送 bi​ …