WebGL Varing变量的作用和内插过程,及执行Varing时涉及的图形装配、光栅化、颜色插值、片元着色器执行机制等详解

目录

前言

在 WebGL 或 OpenGL 中,“varying” 是一种用于在顶点着色器和片元着色器之间传递数据的特殊类型的变量。它允许在顶点着色器对数据进行处理后,在片元着色器中使用该处理后的数据进行进一步计算。 

彩色三个点 

​编辑

彩色三个点示例代码

varying变量使用规范 

彩色三角形

几何形状的装配和光栅化

红色三角形示例代码

顶点坐标 --> 图形装配 --> 光栅化 --> 执行片元着色器

图元装配过程

光栅化过程

顶点着色器和片元着色器之间图形装配和光栅化的过程详解

第1步:执行顶点着色器,缓冲区对象中的第1个坐标(0.0,0.5)被传递给attribute变量a_Position。一旦一个顶点的坐标被赋值给了gl_Position,它就进入了图形装配区域,并暂时储存在那里。你应该还记得,我们仅仅显式地向a_Position赋了x分量和y分量,所以向z分量和w分量赋的是默认值,进入图形装配区域的坐标其实是(0.0,0.5,0.0,1.0)。

第2步:再次执行顶点着色器,类似地,将第2个坐标(-0.5,-0.5,0.0,1.0)传入并储存在装配区。

第3步:第3次执行顶点着色器,将第3个坐标(0.5,-0.5,0.0,1.0)传入并储存在装配区。现在,顶点着色器执行完毕,三个顶点坐标都已经处在装配区了。

第4步:开始装配图形。使用传入的点坐标,根据gl.drawArrays()的第一个参数信息(gl.TRIANGLES)来决定如何装配。本例使用三个顶点来装配出一个三角形。

第5步:显示在屏幕上的三角形是由片元(像素)组成的,所以还需要将图形转化为片元,这个过程被称为光栅化(rasterization)。光栅化之后,我们就得到了组成这个三角形的所有片元。在上图中的最后一步,你可以看到光栅化后得到的组成三角形的片元。

调用片元着色器

做个试验:根据片元的位置来确定片元颜色

gl.drawingBufferWidth / gl.drawingBufferHeight

显示效果

varying变量的作用和内插过程

varying变量的行为

​编辑varying变量的内插​编辑

颜色值的内插

总结 


前言

在 WebGL 或 OpenGL 中,“varying” 是一种用于在顶点着色器和片元着色器之间传递数据的特殊类型的变量。它允许在顶点着色器对数据进行处理后,在片元着色器中使用该处理后的数据进行进一步计算。 

彩色三个点 

彩色三个点示例代码

// 顶点着色器
var VSHADER_SOURCE ='attribute vec4 a_Position;\n' +'attribute vec4 a_Color;\n' +'varying vec4 v_Color;\n' + // varying 变量'void main() {\n' +'  gl_Position = a_Position;\n' +'  gl_PointSize = 10.0;\n' +'  v_Color = a_Color;\n' +  // 将数据传给片元着色器'}\n';// 片元着色器
var FSHADER_SOURCE ='precision mediump float;\n' + // 设置varing精度'varying vec4 v_Color;\n' +    // 从顶点着色器接受数据'void main() {\n' +'  gl_FragColor = v_Color;\n' +'}\n';function main() {var canvas = document.getElementById('webgl');var gl = getWebGLContext(canvas);// 设置顶点的坐标和颜色var n = initVertexBuffers(gl);gl.clearColor(0.0, 0.0, 0.0, 1.0);gl.clear(gl.COLOR_BUFFER_BIT);gl.drawArrays(gl.POINTS, 0, n);
}function initVertexBuffers(gl) {var verticesColors = new Float32Array([// 顶点坐标和颜色0.0,  0.5,  1.0,  0.0,  0.0, -0.5, -0.5,  0.0,  1.0,  0.0, 0.5, -0.5,  0.0,  0.0,  1.0, ]);var n = 3; // 顶点数量// 创建缓冲区对象var vertexColorBuffer = gl.createBuffer();  if (!vertexColorBuffer) {console.log('Failed to create the buffer object');return false;}// 将顶点坐标和颜色写入缓冲区对象gl.bindBuffer(gl.ARRAY_BUFFER, vertexColorBuffer);gl.bufferData(gl.ARRAY_BUFFER, verticesColors, gl.STATIC_DRAW);var FSIZE = verticesColors.BYTES_PER_ELEMENT;// 获取a_Position的存储位置,分配缓冲区并开启var a_Position = gl.getAttribLocation(gl.program, 'a_Position');gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, FSIZE * 5, 0);gl.enableVertexAttribArray(a_Position);  // 开启变量// 获取a_Color的存储位置,分配缓冲区并开启var a_Color = gl.getAttribLocation(gl.program, 'a_Color');gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, FSIZE * 5, FSIZE * 2);gl.enableVertexAttribArray(a_Color);  // 开启缓冲区分配return n;
}

varying变量使用规范 

 在顶点着色器中,我们声明了attribute变量a_Color用以接收颜色数据(第4行),然后声明了新的varying变量v_Color,该变量负责将颜色值将被传给片元着色器(第5行)。注意,varying变量只能是float(以及相关的vec2,vec3,vec4,mat2,mat3和mat4)类型的。

我们将a_Color变量的值直接赋给之前声明的v_Color变量(第9行)。 

那么,片元着色器该如何接收这个变量呢?答案很简单,只需要在片元着色器中也声明一个(与顶点着色器中的那个varying 变量同名)varying变量就可以了:

在WebGL中,如果顶点着色器与片元着色器中有类型和命名都相同的varying变量,那么顶点着色器赋给该变量的值就会被自动地传入片元着色器,如下图所示。 

所以,顶点着色器赋给v_Color变量的值(第9行)被传递给了片元着色器中的v_Color变量,然后片元着色器将v_Color赋值给gl_FragColor,这样每个顶点的颜色将被修改(第17行)。 

数组verticesColor中有两种不同类型的数据(坐标和颜色)。现在的颜色有3个分量值,所以每个顶点所占字节数是FSIZE*5,需要修改相应的gl.vertexAttribPointer()函数的stride参数和offset参数(第53和58行)。

最后,执行绘图命令(第27行),在浏览器中绘制了红、蓝、绿三个点。

彩色三角形

让我们来看看将gl.drawArrays()函数的第一个参数改成gl.TRIANGLES后会怎样(第27行)

程序的结果如下图所示,程序绘制了一个颜色平滑过渡的、三个角各是红、绿、蓝颜色的三角形。

我们只改变了一个参数,程序的运行结果却从三个不同颜色的孤立的点变成了一个颜色平滑过渡的三角形。到底发生了什么?

几何形状的装配和光栅化

为了简单起见,这里拿一个红色三角形的代码来解释

红色三角形示例代码

 我们在initVertexBuffers()函数中将顶点坐标写入了缓冲区对象(第50行和第52行),然后将缓冲区对象分配给a_Position变量(第74行)。最后调用gl.drawArrays()执行顶点着色器(第46行)。当顶点着色器执行时,缓冲区中的三个顶点坐标依次传给了a_Position变量(第4行),再赋值给gl_Position(第6行),这样WebGL系统就可以根据顶点坐标进行绘制。在片元着色器中,我们将红色的RGBA值(1.0,0.0,0.0,1.0)赋给gl_FragColor,这样就画出了一个红色的三角形。

可是直到现在,你还是不明白这究竟是如何做到的?在你向gl_Position给出了三角形的三个顶点的坐标时,片元着色器又怎样才能进行所谓的逐片元操作呢?

如下图显示了问题所在,程序向gl_Position给出了三个顶点的坐标,谁来确定这三个点就是三角形的三个顶点?最终,为了填充三角形内部,谁来确定哪些像素需要被着色?谁来负责调用片元着色器,片元着色器又是怎样处理每个片元的?

顶点坐标 --> 图形装配 --> 光栅化 --> 执行片元着色器

 在顶点着色器和片元着色器,有这样两个步骤:

  • 图元装配过程

  这一步的任务是,将孤立的顶点坐标装配成几何图形。几何图形的类别由gl.drawArrays()函数的第一个参数决定。

  • 光栅化过程

  这一步的任务是,将装配好的几何图形转换为片元。

上图所示,gl_Position实际上是几何图形装配(geometric shape assembly)阶段的输入数据。注意,几何图形装配过程又被称为图元装配过程(primitive assembly process),因为被装配出的基本图形(点、线、面)又被称为图元(primitives)。 

顶点着色器和片元着色器之间图形装配和光栅化的过程详解

第1步:执行顶点着色器,缓冲区对象中的第1个坐标(0.0,0.5)被传递给attribute变量a_Position。一旦一个顶点的坐标被赋值给了gl_Position,它就进入了图形装配区域,并暂时储存在那里。你应该还记得,我们仅仅显式地向a_Position赋了x分量和y分量,所以向z分量和w分量赋的是默认值,进入图形装配区域的坐标其实是(0.0,0.5,0.0,1.0)。

第2步:再次执行顶点着色器,类似地,将第2个坐标(-0.5,-0.5,0.0,1.0)传入并储存在装配区。

第3步:第3次执行顶点着色器,将第3个坐标(0.5,-0.5,0.0,1.0)传入并储存在装配区。现在,顶点着色器执行完毕,三个顶点坐标都已经处在装配区了。

第4步:开始装配图形。使用传入的点坐标,根据gl.drawArrays()的第一个参数信息(gl.TRIANGLES)来决定如何装配。本例使用三个顶点来装配出一个三角形。

第5步:显示在屏幕上的三角形是由片元(像素)组成的,所以还需要将图形转化为片元,这个过程被称为光栅化(rasterization)。光栅化之后,我们就得到了组成这个三角形的所有片元。在上图中的最后一步,你可以看到光栅化后得到的组成三角形的片元。

上图为了示意,只显示了10个片元。实际上,片元数目就是这个三角形最终在屏幕上所覆盖的像素数。如果修改了gl.drawArrays()的第1个参数,那么第4步的图形装配、第5步的片元数目和位置就会相应地变化。比如说,如果这个参数是gl.LINE,程序就会使用前两个点装配出一条线段,舍弃第3个点;如果是gl.LINE_LOOP,程序就会将三个点装配成为首尾相接的折线段,并光栅化出一个空心的的三角形(不产生中间的像素)。

调用片元着色器

一旦光栅化过程结束后,程序就开始逐片元调用片元着色器。在下图中,片元着色器被调用了10次,每调用一次,就处理一个片元(为了整洁,下图省略了中间步骤)。对于每个片元,片元着色器计算出该片元的颜色,并写入颜色缓冲区。直到第15步最后一个片元被处理完成,浏览器就会显示出最终的结果。

红色三角形代码中的片元着色器将每个片元的颜色都指定为红色,如下所示。因此,浏览器就绘制出了一个红色的三角形。

做个试验:根据片元的位置来确定片元颜色

这样可以证明片元着色器对每个片元都执行了一次。光栅化过程生成的片元都是带有坐标信息的,调用片元着色器时这些坐标信息也随着片元传了进去,我们可以通过片元着色器中的内置变量来访问片元的坐标(如下表)。 

 为了证明片元着色器是逐片元执行的,我们修改了原红色三角形程序的第12行(gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0)),如下所示:

从片元着色器的程序代码中可见,三角形中每个片元的颜色,其红色分量和蓝色分量都是根据片元的位置计算得到的。注意,canvas中的Y轴方向和WebGL系统中的Y轴方向是相反的,而且WebGL中的颜色分量值区间为0.0到1.0,所以你需要将Y轴坐标除以<canvas>元素的高度(400像素)以将其压缩到0.0到1.0之间。我们将gl.drawingBufferWidth(颜色缓冲区的宽度)gl.drawingBufferHeight(颜色缓冲区的高度)的值传给uniform变量u_Width和u_Height。下面彩色三角形显示了程序的运行结果:一个三角形,像素颜色由像素的位置决定,从左上方到右下方呈现一个渐变效果。 

gl.drawingBufferWidth / gl.drawingBufferHeight

  gl.uniform1f(u_Width, gl.drawingBufferWidth); // 颜色缓冲区的宽度,下面同理 高度gl.uniform1f(u_Height, gl.drawingBufferHeight);

显示效果

由于片元颜色取决于它的坐标位置,所以很自然地,片元颜色会随着片元位置逐渐变化,三角形呈现平滑的颜色渐变效果。

varying变量的作用和内插过程

现在,我们已经了解了顶点着色器与片元着色器之间的几何图形装配和光栅化过程,明白了WebGL系统是怎样逐片元执行片元着色器的了。

回到上面彩色三角形程序,这个程序也可以用刚学到的知识来解释为什么在顶点着色器中只是指定了每个顶点的颜色,最后得到了一个具有渐变色彩效果的三角形呢?事实上,我们把顶点的颜色赋值给了顶点着色器中的varying变量v_Color,它的值被传给片元着色器中的同名、同类型变量(即片元着色器中的varying变量v_Color),如下图所示。但是,更准确地说,顶点着色器中的v_Color变量在传入片元着色器之前经过了内插过程。所以,片元着色器中的v_Color变量和顶点着色器中的v_Color变量实际上并不是一回事,这也正是我们将这种变量称为“varying”(变化的)变量的原因。

varying变量的行为

varying变量的内插

 更准确地说,在红色三角形中,我们在varying变量中为三角形的3个不同顶点指定了3种不同颜色,而三角形表面上这些片元的颜色值都是WebGL系统用这3个顶点的颜色内插出来的。

例如,考虑一条两个端点的颜色不同的线段。一个端点的颜色为红色(1.0,0.0,0.0),而另一个端点的颜色为蓝色(0.0,0.0,1.0)。我们在顶点着色器中向varying变量v_Color赋上这两个颜色(红色和蓝色),那么WebGL就会自动地计算出线段上的所有点(片元)的颜色,并赋值给片元着色器中的varying变量v_Color(如下图所示)。

颜色值的内插

在这个例子中RGBA中的R值从1.0降低为0.0,而B值则从0.0上升至1.0,线段上的所有片元的颜色值都会被恰当地计算出来——这个过程就被称为内插过程(interpolation process)。一旦两点之间每个片元的新颜色都通过这种方式被计算出来后,它们就会被传给片元着色器中的v_Color变量。 

再来看红色三角形的程序代码。在顶点着色器中,我们将三角形的3个顶点的颜色赋给了varying变量v_Color(第9行),然后片元着色器中的varying变量 v_Color就接收到了内插之后的片元颜色。在片元着色器中,我们把片元的颜色赋值给gl_FragColor变量(第19行),这样就绘制出了一个彩色的三角形,同理,每一个varying变量都会经过这样的内插过程

总结 

顶点着色器和片元着色器之间的过程非常重要。光栅化也是三维图形学的关键技术之一,它负责将矢量的几何图形转变为栅格化的片元(像素)。图形被转化为片元之后,我们就可以在片元着色器内做更多的事情,如为每个片元指定不同的颜色。颜色可以内插出来,也可以直接编程指定。 

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

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

相关文章

大数据可视化大屏实战项目(4)物流数据云看台(包括动态登陆页面)—HTML+CSS+JS【源码在文末】(可用于比赛项目或者作业参考中)

大数据可视化大屏实战项目(4)物流数据云看台(包括动态登陆页面)—HTMLCSSJS【源码在文末】(可用于比赛项目或者作业参考中🐕🐕🐕) 一,项目概览 ☞☞☞☞☞☞…

【STM32】学习笔记(TIM定时器)

TIM(Timer)定时器 定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断 16位计数器、预分频器、自动重装寄存器的时基单元,在72MHz计数时钟下可以实现最大59.65s的定时 不仅具备基本的定时中断功能,而且…

Javase | IO流

目录: 1.输入 (Intput/Read)2.输出 (Output/Write)3.IO4.IO流5.IO流的分类:5.1 分类总述5.2 按照 “流的方向” 进行分类5.3 按照 “读取数据的方式” 进行分类 6.IO包下要重点掌握的流:6.1 文件专属 (流)6.2 转换流 ( 将字节流转换为字符流 …

2023.9 - java - 浅拷贝

与 js的浅拷贝不同: 在 JavaScript 中, Object.assign() 或 spread 运算符等方法可以实现浅拷贝,但只针对对象的第一层属性进行复制。如果一个对象只包含基本数据类型的属性,那么对浅拷贝出来的对象进行修改不会影响原始对象&…

C#安装“Windows 窗体应用(.NET Framework)”

目录 背景: 第一步: 第二步: 第三步: 总结: 背景: 如下图所示:在Visual Studio Installer创建新项目的时候,想要添加windows窗体应用程序,发现里面并没有找到Windows窗体应用(.NET Framework)模板,快捷搜索也没有发现&#…

22.3D等距社交媒体菜单的悬停特效

效果 源码 <!doctype html> <html><head><meta charset="utf-8"><title>CSS Isometric Social Media Menu</title><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.…

springboot web开发登录拦截器

在SpringBoot中我们可以使用HandlerInterceptorAdapter这个适配器来实现自己的拦截器。这样就可以拦截所有的请求并做相应的处理。 应用场景 日志记录&#xff0c;可以记录请求信息的日志&#xff0c;以便进行信息监控、信息统计等。权限检查&#xff1a;如登陆检测&#xff…

SQL sever中用户管理

目录 一、用户管理常见方法 二、用户管理方法示例 2.1. 创建登录账户&#xff1a; 2.1.1 检查是否创建账户成功&#xff1a; 2.2. 创建数据库用户&#xff1a; 2.2.1检查用户是否创建成功&#xff1a; 2.3. 授予权限&#xff1a; 2.3.1授予 SELECT、INSERT 和 U…

什么是BEM命名规范(Block-Element-Modifier Notation)?它有什么优势?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ BEM命名规范&#xff08;Block-Element-Modifier Notation&#xff09;⭐ BEM命名结构⭐ 优势⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎…

如何解决分库分表主键问题?

分析&回答 从问题角度出发&#xff1a;我们需要一个全局唯一的 id 来支持&#xff0c;排序问题等。这都是你实际生产环境中必须考虑的问题。可以先看下我们之前的文章分布式系统唯一ID如何生成&#xff1f; 雪花算法和雪花算法的变种是大家常用的 喵呜面试助手&#xff1…

春秋云镜 CVE-2018-1273

春秋云镜 CVE-2018-1273 Spring-data-commons 远程命令执行漏洞 靶标介绍 Spring Data是一个用于简化数据库访问&#xff0c;并支持云服务的开源框架&#xff0c;Spring Data Commons是Spring Data下所有子项目共享的基础框架。Spring Data Commons 在2.0.5及以前版本中&…

【算法】滑动窗口

滑动窗口应用场景 关键词&#xff1a; 满足xxx条件&#xff08;计算结果&#xff0c;出现次数&#xff0c;同时包含&#xff09; 最长/最短 子串/子数组/子序列 例如&#xff1a;长度最小的子数组 滑动窗使用思路&#xff08;寻找最长&#xff09; 核心&#xff1a;左右双指…

git大文件推送报错

报错信息 不多掰扯&#xff0c;直接上报错信息和截图 Delta compression using up to 8 threadsRPC failde; HTTP 413 curl 22 The requested URL returned error: 413 Request Entity Too Large从以上的报错信息不难看出推送仓库的时候&#xff0c;请求体过大&#xff0c;为…

第五讲Java面向对象——类及类的成员

前言 前面几讲,我们讲解了java的基础知识,也写了很多代码,有没有发现,每次编写代码前都会新建一个类,并且开头是public class修饰。可能有些同学不知道什么是类,以及前面我们提到方法。那么本讲,就要开始深入了解他们。 在开始讲解,我们将先了解一下编程的思想,面向对…

8.Redis-set

Set 常用命令saddsmemberssismemberscardspopsmovesrem集合间操作sinter 交集sinterstoresunion 并集sunionstoresdiff 差集sdiffstore 命令总结 内部编码应用场景使用 set来保存用户的“标签” set(集合)就是把一些有关联的数据放刀一起。 它与list的区别如下&#xff1a; 集合…

zookeeper 集群

zookeeper 集群 1、zookeeper 集群说明 initLimit 是Zookeeper用它来限定集群中的Zookeeper服务器连接到Leader的时限 syncLimit 限制了follower服务器与leader服务器之间请求和应答之间的时限 服务器名称与地址&#xff1a;集群信息&#xff08;服务器编号&#xff0c;服务器…

Blender界面学习03 原点、鼠标所在位置的缩放与旋转

物体的坐标原点可以移动 放大缩小时默认是屏幕中央&#xff0c;修改为鼠标在哪儿就缩放哪儿 默认旋转时围绕屏幕的中心 可以修改为指定对象旋转

【网络教程】群晖轻松设置钉钉机器人使用Webhook发送通知消息,分分钟搞定!

文章目录 准备设置相关链接准备 演示环境:群晖DSM7.2(其他版本操作雷同)需要提前准备好你的钉钉机器人webhook链接,如果你还不会设置/获取,请点击 参考这篇文章 或自行某度设置 打开群晖,进入控制面板 —> 通知设置 —> Webhooks,如下图 然后点击新增,提供商选择…

使用栈检查括号的合法性 C 实现

使用栈检查括号的合法性 思路讲解&#xff1a;首先从数组数组0下标开始&#xff0c;如果是左括号直接无脑压入栈&#xff0c;直到出现右括号开始判断合法与否。遇到右括号分两种情况&#xff0c;第一种是空栈的情况&#xff0c;也就是说我们第一个字符就是右括号&#xff0c;那…

应用于伺服电机控制、 编码器仿真、 电动助力转向、发电机、 汽车运动检测与控制的旋变数字转换器MS5905P

MS5905P 是一款 12bit 分辨率的旋变数字转换器。 片上集成正弦波激励电路&#xff0c;正弦和余弦允许输入峰峰值 幅度为 2.3V 到 4.0V &#xff0c;可编程激励频率为 10kHz 、 12kHz 、 15kHz 、 20kHz 。 转换器可并行或串行输出角度 和速度对应的数字量。 MS5905…