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变量都会经过这样的内插过程

总结 

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

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

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

相关文章

在GPU服务器(Linux)上安装Anaconda和PyTorch环境

安装Anaconda3 Anaconda官网&#xff1a;https://repo.anaconda.com/archive/ 根据自己需要&#xff0c;复制安装包名字&#xff0c;以Anaconda3-2023.07-2-Linux-x86_64.sh为例 命名规则&#xff1a;Anaconda3-<版本号>-Linux-x86_64.sh 在终端输入命令&#xff0c;下…

Flink SQL你用了吗?

分析&回答 Flink 1.1.0&#xff1a;第一次引入 SQL 模块&#xff0c;并且提供 TableAPI&#xff0c;当然&#xff0c;这时候的功能还非常有限。Flink 1.3.0&#xff1a;在 Streaming SQL 上支持了 Retractions&#xff0c;显著提高了 Streaming SQL 的易用性&#xff0c;使…

基于相空间重构的混沌背景下微弱信号检测算法matlab仿真,对比SVM,PSO-SVM以及GA-PSO-SVM

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1 SVM 4.2 PSO-SVM 4.3 GA-PSO-SVM 5.算法完整程序工程 1.算法运行效果图预览 SVM: PSO-SVM: GA-PSO-SVM: 以上仿真图参考文献《基于相空间重构的混沌背景下微弱信号检测方法研究》 2.…

如何成为一名技术社区KOL?

点击文末“阅读原文”即可参与节目互动 剪辑、音频 / 卷圈 运营 / SandLiu 卷圈 监制 / 姝琦 产品统筹 / bobo 联合制作 / RTE开发者社区 录音间 / 声湃轩北京站 在本期节目中&#xff0c;我们请到「开源社」联合创始人&#xff0c;开发者关系领域的畅销书作者林旅强&…

撤回IPO背后:透视树根互联“以退为进”的成长逻辑

如果说&#xff0c;互联网的上半场属于消费互联网&#xff0c;那么下半场的主角将会是工业互联网&#xff0c;它也被称为“第四次工业革命的重要基石”。 工业互联网属于典型的“长坡厚雪”型赛道&#xff0c;前期需要在技术、资金、人才等方面进行大量投入&#xff0c;而等待…

环境安装:rpm安装jdk上线项目

Tomcat安装 解析域名 购买域名并配置 安装Docker yum 卸载以前装过的docker

java.lang.classnotfoundexception: com.android.tools.lint.client.api.vendor

Unity Android studio打包报错修复 解决方式 java.lang.classnotfoundexception: com.android.tools.lint.client.api.vendor 解决方式 在 launcherTemplate 目录下找到 Android/lintOptions 选项 加上 checkReleaseBuilds false lintOptions { abortOnError false checkRelea…

【位运算】位运算常用技巧总结

目录 前言 一.常见的小问题 1.给定一个数n,确定它的二进制表示中的第x位是0还是1 2.给定一个数n&#xff0c;将它的二进制表示中的第x位修改成1 3.给定一个数n&#xff0c;将它的二进制表示中的第x位修改成0 4.给定一个数n&#xff0c;提取它的二进制表示中最右侧的1&…

【前端自动化部署】,Devops,CI/CD

DevOps 提到Jenkins&#xff0c;想到的第一个概念就是 CI/CD 在这之前应该再了解一个概念。 DevOps Development 和 Operations 的组合&#xff0c;是一种方法论&#xff0c;并不特指某种技术或者工具。DevOps 是一种重视 Dev 开发人员和 Ops 运维人员之间沟通、协作的流程。…

2023-09-01 LeetCode每日一题(买钢笔和铅笔的方案数)

2023-09-01每日一题 一、题目编号 2240. 买钢笔和铅笔的方案数二、题目链接 点击跳转到题目位置 三、题目描述 给你一个整数 total &#xff0c;表示你拥有的总钱数。同时给你两个整数 cost1 和 cost2 &#xff0c;分别表示一支钢笔和一支铅笔的价格。你可以花费你部分或者…

小米面试题——不用加减乘除计算两数之和

前言 &#xff08;1&#xff09;刷B站看到一个面试题&#xff0c;不用加减乘除计算两数之和。 &#xff08;2&#xff09;当时我看到这个题目&#xff0c;第一反应就是感觉这是一个数电题目。不过需要采用C语言的方式编写出来。 &#xff08;3&#xff09;不过看到大佬的代码之…

3、QT 的基础控件的使用

一、qFileDialog 文件窗体 Header: #include <QFileDialog> qmake: QT widgets Inherits: QDialog静态函数接口&#xff1a; void Widget::on_pushButton_clicked() {//获取单个文件的路径名QString filename QFileDialog :: getOpenFileName(this, tr("Open Fi…

stencilJs学习之构建 Drawer 组件

前言 在之前的学习中&#xff0c;我们已经掌握了 stencilJs 中的一些核心概念和基础知识&#xff0c;如装饰器 Prop、State、Event、Listen、Method、Component 以及生命周期方法。这些知识是构建复杂组件和应用的基础&#xff0c;而抽屉组件是一个很好的示例&#xff0c;能够…

Ceph源码解析:PG peering

集群中的设备异常(异常OSD的添加删除操作)&#xff0c;会导致PG的各个副本间出现数据的不一致现象&#xff0c;这时就需要进行数据的恢复&#xff0c;让所有的副本都达到一致的状态。 一、OSD的故障和处理办法&#xff1a; 1. OSD的故障种类&#xff1a; 故障A&#xff1a;一…

使用Dbeaver连接GaussDB

1.下载DBeaver&#xff0c;官网地址 2.安装软件&#xff0c;打开软件&#xff0c;点击数据库->驱动管理器&#xff0c;具体操作如下图&#xff1a; 3、选择新建后进行参数设置&#xff0c;如下图&#xff1a; 具体参数如下图 驱动名称: GS #随便定义 驱动类型&#…

【STM32】学习笔记(串口通信)-江科大

串口通信 通信接口硬件电路电平标准USARTUSART框图 通信接口 串口是一种应用十分广泛的通讯接口&#xff0c;串口成本低、容易使用、通信线路简单&#xff0c;可实现两个设备的互相通信 单片机的串口可以使单片机与单片机、单片机与电脑、单片机与各式各样的模块互相通信&#…

【【Verilog典型电路设计之log函数的Verilog HDL设计】】

Verilog典型电路设计之log函数的Verilog HDL设计 log函数是一种典型的单目计算函数&#xff0c;与其相应的还有指数函数、三角函数等。对于单目计算函数的硬件加速器设计一般两种简单方法:一种是查找表的方式;一种是使用泰勒级数展开成多项式进行近似计算。这两种方式在设计方…

Linux学习之逻辑卷LVM用途和创建

理论基础 Linux文件系统建立在逻辑卷上&#xff0c;逻辑卷建立在物理卷上。 物理卷处于LVM中的最底层&#xff0c;可以将其理解为物理硬盘、硬盘分区或者RAID磁盘阵列&#xff0c;这都可以。卷组建立在物理卷之上&#xff0c;一个卷组可以包含多个物理卷&#xff0c;而且在卷组…

港交所MMDH行情协议

目录 一、交易时间 二、MMDH与OMD的差异 三、MMDH消息类型 四、MMDH的市场快照数据 内地市场数据枢纽-证券市场(OMD-MMDH) 港交所OMD-C对接笔记 - skylerjiang - 博客园 (cnblogs.com) 一、交易时间 图 1 港交所交易时间段 图 2 消息序列 二、MMDH与OMD的差异 图 3 标准…

时序预测 | MATLAB实现基于PSO-GRU、GRU时间序列预测对比

时序预测 | MATLAB实现基于PSO-GRU、GRU时间序列预测对比 目录 时序预测 | MATLAB实现基于PSO-GRU、GRU时间序列预测对比效果一览基本描述程序设计参考资料 效果一览 基本描述 MATLAB实现基于PSO-GRU、GRU时间序列预测对比。 1.MATLAB实现基于PSO-GRU、GRU时间序列预测对比&…