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

news2024/9/25 13:21:18

目录

前言

在 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.coloradmin.cn/o/957967.html

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

相关文章

Ant-Design-Pro-V5: ProTable前端导出excel表格。

Prtable表格中根据搜索条件实现excel表格导出。 代码展示: index.jsx import React, { useRef, useState, Fragment, useEffect } from react; import { getLecturerList, lecturerExportExcel } from /services/train/personnel; import { getOrgList, getSelec…

打击儿童性虐待,遭多家机构反对,苹果宣布停止开发CSAM检测计划

据报道,苹果公司曾计划在其iCloud云服务中引入一项儿童性虐待资料(CSAM)检测计划,但由于反对声浪日益高涨,该计划最终宣布停止开发。CSAM检测计划的原本目的是为了帮助阻止儿童性虐待资料的传播,保护儿童的…

实例418 通过串口发送数据

实例说明 现在大多数硬件设备均采用串口技术与计算机相连,因此串口的应用程序开发越来越普遍。例如,在计算机没有安装网卡的情况下,将本机上的一些信息数据传输到另一台计算机上,那么利用串口通信就可以实现。运行本程序&#xff…

QQ聊天记录在哪个文件夹?(针对iPhone用户的详细教程)

苹果手机的QQ聊天记录一般存放在哪里?问了很多朋友都不知道,试了网上很多方法都没用,大家还有其他解决方法吗? 很多iPhone用户都不知道QQ聊天记录保存在哪个文件夹,这是很正常的现象。因为iPhone手机本身是不具备【文件…

Gatling 入门

1.新建一个测试接口项目 里面代码非常简单,就一个hi接口: package com.example.gatlingdemo.controller;import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController;import java.ti…

北京开发APP的费用明细

开发APP项目时,在功能确定后需要知道有哪些可能的费用,安排项目预算。北京开发APP的费用明细可能会包括以下几个部分,每个部分都会产生一些费用。今天和大家分享APP费用明细有哪些,希望对大家有所帮助。北京木奇移动技术有限公司&…

地理测绘基础知识(4) 照射计算

照射计算,是一种常用的三维几何计算。已知一个光源的光强图,计算光源投射到地表各处的功率密度。这种计算需求可以直观的理解为计算已知位置、指向、聚光特性的手电筒,计算地表某地点强度。 本文的推导涉及很多旋转,很容易出错和…

ElasticSearch安装为Win11服务

在windows的环境下操作是Elasticsearch,并且喜欢使用命令行 ,启动时通过cmd直接在elasticsearch的bin目录下执行elasticsearch ,这样直接启动的话集群名称会默elasticsearch,节点名称会随机生成。 停止就直接在cmd界面按CtrlC 其实我们也可以将elasticse…

Ansible学习笔记14

实现多台的分离实现: [rootlocalhost playbook]# cat example3.yaml --- - hosts: 192.168.17.105remote_user: roottasks:- name: create test1 directoryfile: path/test1/ statedirectory- hosts: 192.168.17.106remote_user: roottasks:- name: create test2 d…

Python—匹配字段

1. 「概述」 在日常开发中,经常需要对数据中的某些字段进行匹配,但这些字段可能存在微小的差异。例如,同一个招聘岗位的数据中,省份字段可能有“广西”、“广西壮族自治区”和“广西省”等不同的写法。为了处理这些情况&#xff…

二叉树的介绍

写在前面: 二叉树是数据结构课程中非常重要的内容,我们针对二叉树的概念、性质以及类型展开详细介绍。 一、概念 二叉树(Binary Tree)是n(n>0)个结点的有限集合,该集合或者空集&#xff0…

C语言用递归函数求解斐波那契数

int Fib(int n) {if (n 1 || n 2){return 1;}return Fib(n - 1) Fib(n - 2); }int main() {int i 0;printf("请输入需要第几个斐波那契数\n");scanf("%d", &i);int r Fib(i);printf("%d\n", r);return 0; }

JVM 对象的内存布局

对象头 Mark word 标记字段 用于存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等 ClassPoint 类型指针 对象指向它的类型元数据的指针,Java虚拟机通过这个指针 来…

C++——shared_ptr:make_shared的用处,与shared_ptr直接构造的区别

shared_ptr shared_ptr继承自__shared_ptr,其中有两个对象,一个是指向资源的指针,一个是控制块,指向一个引用计数对象。控制块中存储了强引用和弱引用的计数,强引用Uses代表shared_ptr对象的引用计数,弱引…

excel怎么设置任意选一个单元格纵横竖横都有颜色

有时excel表格内容过多的时候,我们通过excel设置任意选一个单元格纵横,竖横背景颜色,这样会更加具有辨识度。设置方式截图如下 设置成功后,预览的效果图

常静相伴:深度解析C++中的const与static关键字

个人主页:北海 🎐CSDN新晋作者 🎉欢迎 👍点赞✍评论⭐收藏✨收录专栏:C/C🤝希望作者的文章能对你有所帮助,有不足的地方请在评论区留言指正,大家一起学习交流!&#x1f9…

kafka 命令脚本说明以及在java中使用

一、命令行使用 1.1、topic 命令 1、关于topic,这里用window 来示例 bin\windows\kafka-topics.bat2、创建 first topic,五个分区,1个副本 bin\windows\kafka-topics.bat --bootstrap-server localhost:9092 --create --partitions 5 --replication-factor 1 -…

nacos import com.alibaba.nacos.consistency.entity.ReadRequest

1. 异常情况 import com.alibaba.nacos.consistency.entity.ReadRequest; import com.alibaba.nacos.consistency.entity.Response; import com.alibaba.nacos.consistency.entity.WriteRequest; 2. 解决方法 安装插件,然后重新编译 记住选择Java8

从零做软件开发项目系列之十——项目运维

项目结项后的运维阶段是确保软件持续稳定运行、修复问题、满足用户需求的关键时期。在这个阶段,需要建立有效的维护制度,关注各种问题,并采取相应措施来保障系统的可靠性和可持续性。 1 运维团队 开展服务运维工作,首先需要组建运…

07.Knowing When to Look

目录 前言泛读摘要Introduction小结 精讲方法Encoder-Decoder框架 for Image CaptioningSpatial Attention ModelAdaptive Attention Model Implementation DetailsEncoder-CNNDecoder-RNNTraining details Related Work实验结论 前言 本课程来自深度之眼《多模态》训练营&…