WebGL编程指南 - WebGL入门

初识绘图流程、缓冲区、着色器、attribute和uniform变量

先画一个蓝色的正方形

html代码:

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta http-equiv="X-UA-Compatible" content="IE=edge" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Document</title></head><body onload="main()">  //指定 onload 属性,告诉浏览器<body>元素加载完从何处开始执行<canvas id="example" width="400" height="400">Please use a browser that supports "canvas"</canvas><script src="./js/DrawRectangle.js"></script></body>
</html>

JS代码:

// DrawRectangle.js
function main() {// 获取<canvas>元素let canvas = document.getElementById('example')if (!canvas) {console.log('Failed to retrieve the <canvas> element')return}// 获取绘制二维图形的绘图上下文let ctx = canvas.getContext('2d')// 绘制蓝色矩形ctx.fillStyle = 'rgba(0,0,255,1.0)'ctx.fillRect(120, 10, 150, 150)
}

HTML5引入<canvas>标签,允许JavaScript动态的绘制图形

 <body οnlοad="main()">  //指定 onload 属性,告诉浏览器<body>元素加载完从何处开始执行,作为JavaScript程序的入口

绘制蓝色矩形的js程序的,为了在<canvas>上绘制二维图形,需要经过三个步骤:

  • 获取<canvas>元素
  • 向该元素请求二维图形的“绘制上下文”
  • 在绘图上下文上调用相应的绘图函数,绘制二维图形

ctx对象的fillRect方法,方法的语法为 context.fillRect(x,y,width,height),x,y为矩形左上角的坐标,剩下两个是宽度和高度

canvas 中的坐标和 OpenGL 一致

清空绘图区示例与背景色设置(初识多缓冲区模型与颜色缓冲区)

效果:

代码:

<!DOCTYPE html>
<html lang="en"><head><meta charset="utf-8"/><meta http-equiv="X_UA_Compatible" content="IE=edge"/><meta name="viewport" content="width=device-width,inital-scale=1.0"/><title>Document</title></head><body onload="main()"><canvas id="webgl" width="400" height="400"></canvas><script src="HelloCanvas.js"></script></body>
</html>
// HelloCanvas.js
function main() {// 获取<canvas>元素let canvas = document.getElementById('webgl')// 获取WebGL绘图上下文// let gl = getWebGLContext(canvas) (书中的函数)let gl = canvas.getContext('webgl')if (!gl) {console.log('Failed to get the rendering context for WebGL')return}// 指定清空<canvas>的颜色gl.clearColor(0.0, 0.0, 0.0, 1.0) // 黑色// 清空<canvas>gl.clear(gl.COLOR_BUFFER_BIT)}

因为没找到书中的函数库,所以参考了别人的代码

示例程序中 main 函数的执行流:

设置 canvas 背景色

  1. 一旦指定背景色后,背景色会贮存在WebGL系统中,在下次调用gl.clearColor()方法之前不会改变。所以,如果未来还需要再次清空绘图区,没必要再指定一次颜色。
  2. 这是挂载在WebGLRenderingContext对象上的方法。

gl.clear(buffer)

清空颜色缓冲区将导致 WebGL 清空页面上的 <canvas> 区域

WebGL中的gl.clear()方法继承自OpenGL,基于多基本缓冲区模型

绘制一个点 与 静态着色器

相关内容:着色器初识、使用着色器的WebGL程序结构、初始化着色器、顶点着色器及内置变量、片元着色器及内置变量、齐次坐标、WebGL坐标系统、JavaScript程序和着色器程序的协同运行机制
相关函数:initShaders(), vec4(), gl.drawArrays()

示例程序将在原点(0.0, 0.0, 0.0)处的10个像素大的红色的点。
因为WebGL处理的是三维图形,所以我们有必要为这个点指定三维坐标。 

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8" /><meta http-equiv="X-UA-Compatible" content="IE=edge" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Draw a point (1)</title>
</head>
<body onload="main()"><canvas id="webgl" width="400" height="400">Please use the browser supporting "canvas"</canvas><script src="../libs/webgl-utils.js"></script><script src="../libs/webgl-debug.js"></script><script src="../libs/cuon-utils.js"></script><script src="HelloPoint1.js"></script>
</body>
</html>
// HelloPoint1.js
// 顶点着色器程序
var VSHADER_SOURCE ='void main() {\n' +' gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n' + // 设置坐标' gl_PointSize = 10.0;\n' + // 设置尺寸'}\n'
// 片元着色器程序
var FSHADER_SOURCE ='void main() {\n' +' gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n' + // 设置颜色'}\n'
// 主程序
function main() {// 获取<canvas>元素let canvas = document.getElementById('webgl')// 获取WebGL绘图上下文// let gl = getWebGLContext(canvas)let gl = canvas.getContext('webgl')if (!gl) {console.log('Failed to get the rendering context for WebGL')return}// 初始化着色器if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {console.log('Failed to initialize shaders')return}// 设置<canvas>背景色gl.clearColor(0.0, 0.0, 0.0, 1.0)// 清空<canvas>gl.clear(gl.COLOR_BUFFER_BIT)// 绘制一个点gl.drawArrays(gl.POINTS, 0, 1)
}

着色器程序以字符串的形式嵌入在 JavaScript 文件中,在程序真正开始运行前它就已经设置好了

WebGL中有两种着色器:

  • 顶点着色器(Vertex shader):顶点着色器是用来描述顶点特性(如位置、颜色等)的程序。顶点(vertex)是指二维或三维空间中的一个点,比如二维或三维图形的端点或交点。
  • 片元着色器(Fragment shader):进行逐片元处理过程如光照的程序。片元(fragment)是一个WebGL术语,你可以将其理解为像素(图像的单元)。

JavaScript 读取了着色器的相关信息,然后存在 WebGL 系统中以供调用

WebGL 程序的执行流程

初始化着色器

顶点着色器先执行,它对gl_Posetion变量和gl_PointSize变量进行赋值,并将他们传入片元着色器,然后片元着色器再执行。 

实际上,片元着色器并不直接接收两个变量,而是接收经过光栅化处理后的片元值,这一部分会在书的第五章进行讨论

WebGL程序包括运行在浏览器中的JavaScript和运行在WebGL系统中的着色器程序两个部分

顶点着色器

// 顶点着色器程序
var VSHADER_SOURCE ='void main() {\n' +' gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n' + // 设置坐标' gl_PointSize = 10.0;\n' + // 设置尺寸'}\n'

顶点着色器设置了点的位置和大小。该语言本身和C语言一样,必须包含一个main()函数,main()函数前面的关键字void表示这个函数没有返回值,同时也不能指定参数。

与大多数语言一样,着色器程序使用=操作符进行赋值,将点的位置和尺寸分别赋值给gl_Positiongl_PointSize,这两个变量是内置在顶点着色器之中的:

注:gl_Position必须被赋值,否则着色器无法正常工作;gl_PointSize并不是必须的,默认值为1.0。

如果类型赋值错误,如10.0变成10,就会报错

gl_Position 表示顶点的位置,一般顶点只需要三个点即可,用内置函数 vec() 创建 vec4类型的变量

vec4(v0, v1, v2, v3):根据v0,v1,v2,v3值创建vec4对象
参数:
v0, v1, v2, v3: 指定4个浮点型分量;
返回值:
由v0, v1, v2, v3组成的vec4对象。

在示例中,我们添加了1.0作为函数第四分量。在WebGL(或者说OpenGL)中,将gl_Positon设置为vec4类型的变量实际上是采用了齐次坐标的方式,因为它能提高处理三维数据的效率,所以在三维图形系统中被大量使用。关于齐次坐标可以看下面的说明:

片元着色器

顶点着色器控制点的位置和大小,片元着色器控制点的颜色。

片元着色器的作用是处理片元,使其显示在屏幕上

// 片元着色器程序
var FSHADER_SOURCE ='void main() {\n' +' gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n' + // 设置颜色'}\n'

绘制操作

  // 绘制一个点gl.drawArrays(gl.POINTS, 0, 1)

建立着色器之后,与HelloCanvas.js示例一样,进行设置背景色和清空绘制区域的操作,最后进行绘制操作。绘制操作采用的方法为gl.drawArrays()

当程序调用 gl.drawArrays() 时,顶点着色器将其执行 count 次,每次处理一个顶点

示例程序函数参数设置如下:

  • 因为我们绘制的是单独的点,所以设置第一个参数为gl.POINTS;
  • 设置第二个参数为0,表示从第1个顶点(虽然只有一个顶点)开始画起;
  • 第三个参数count为1,表示程序中只绘制1个点

WebGL 坐标系统

本身不是左手也不是右手,可以认为时右手坐标系

由此可见,WebGL坐标系和<canvas>绘图区的坐标系不同,需要将前者映射到后者。在<canvas>绘图区,WebGL坐标系统如下图所示:

绘制一个点2 与 attribute变量(动态设置顶点着色器)

相关内容:用JavaScript程序从着色器外部向着色器传输数据(动态,而不是静态写在着色器中)、attribute变量、uniform变量
相关函数:gl.getAttribLocation()、gl.vertexAttrib3f()及其同族函数

小结:动态设置着色器参数,主要包括以下几步:1.在着色器语言中,设置attribute或uniform变量,将变量赋值到主函数参数中;2.JavaScript程序中通过相关函数获取变量存储地址;3.通过相关函数,向该地址传输数据。

在前一节中,点的位置是直接编(“硬编码”)写在顶点着色器中的,虽然易于理解但缺乏可扩展性;这一节中,Web程序可以将顶点的位置坐标从JavaScript传到着色器程序中,然后再对应位置上绘制点

目前而言,将位置信息从JavaScript程序中传给顶点着色器,有两种方式:attribute变量和uniform变量。

  • attribute变量:传输与顶点相关的数据
  • uniform变量:传输那些对于所有顶点都相同(或与顶点无关)的数据

两个变量都是GLSL ES变量,被用来从外部向顶点着色器内传输数据,示例中选用attribute变量,示例代码如下:

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta http-equiv="X-UA-Compatible" content="IE=edge" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Draw a point (1)</title></head><body onload="main()"><canvas id="webgl" width="400" height="400">Please use the browser supporting "canvas"</canvas><!-- 书中的函数库 --><script src="./libs/webgl-utils.js"></script><script src="./libs/webgl-debug.js"></script><script src="./libs/cuon-utils.js"></script><script src="./js/HelloPoint2.js"></script></body>
</html>
// HelloPoint2.js
// 顶点着色器
var VSHADER_SOURCE ='attribute vec4 a_Position;\n' +'void main(){\n' +' gl_Position = a_Position;\n' +' gl_PointSize = 10.0;\n' +'}\n'
// 片元着色器
var FSHADER_SOURCE ='void main() {\n' +' gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n' + // 设置颜色'}\n'
// 主函数
function main() {// 获取<canvas>元素let canvas = document.getElementById('webgl')// 获取WebGL上下文let gl = canvas.getContext('webgl')if (!gl) {console.log('Failed to get the rendering context for WebGL')}// 初始化着色器if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {console.log('Failed to initialize shaders')return}// 获取attribute变量的存储位置let a_Position = gl.getAttribLocation(gl.program, 'a_Position')if (a_Position < 0) {console.log('Failed to get the storage location of a_Position')}// 将顶点位置传输给attribute变量gl.vertexAttrib3f(a_Position, 0.0, 0.0, 0.0)// 设置<canvas>背景色gl.clearColor(0.0, 0.0, 0.0, 1.0)// 清除<canvas>gl.clear(gl.COLOR_BUFFER_BIT)// 绘制一个点gl.drawArrays(gl.POINTS, 0, 1)
}

attribute变量的声明与赋值:

在顶点着色器的第一行GLSL ES语言中声明了此变量(第3行):

// 顶点着色器
var VSHADER_SOURCE ='attribute vec4 a_Position;\n' +'void main(){\n' +' gl_Position = a_Position;\n' +' gl_PointSize = 10.0;\n' +'}\n'

为了使用 attribute 变量,需要包含一下步骤:

  • 在顶点着色器中,声明 attribute 变量
  • 将 attribute 变量赋值给 gl_Position 变量
  • 向 attribute 变量传输数据

具体使用讲解:

attribute变量的声明按照以下的格式:<存储限定符><类型><变量名>

  • 关键词attribute被称为存储限定符(storage qualifier),它表示接下来的变量(示例中是a_Position)是一个attribute变量。attribute变量必须声明成全局变量,数据将从着色器外部传给该变量。

与一般的JavaScript变量赋值不同,在WebGL系统之外向系统内的变量赋值需要知道该变量的存储地址,再通过存储地址向变量传输数据。获取attribute变量地址的部分代码如下:

  // 获取attribute变量的存储位置let a_Position = gl.getAttribLocation(gl.program, 'a_Position')if (a_Position < 0) {console.log('Failed to get the storage location of a_Position')}

gl.getAttribLocation(program, name):获取由name参数指定的attribute变量的存储地址。
参数:
program: 指定包含顶点着色器和片元着色器的着色器程序对象;
name: 指定想要获取其存储地址的attribute变量的名称;
返回值:
大于等于0:attribute变量的存储地址
-1:指定的attribute变量不存在,或者其命名具有gl_或webgl_前缀

向 attribute 变量赋值

  // 将顶点位置传输给attribute变量gl.vertexAttrib3f(a_Position, 0.0, 0.0, 0.0)

通过存储地址向 attribute 变量赋值

代码中我们传递了三个浮点数,而a_Position变量得数据类型时vec4,此处该方法会默认将第四个分量设置为1.0。颜色值得第4个分量为1.0表示该颜色完全不透明,而齐次坐标得第4个分量为1.0使齐次坐标和三维坐标对应起来,所以1.0是一个“安全”的第4分量。下一部分对其同族函数的说明中介绍了默认填充的方式。

//矢量版本 可以接收类型化数组 矢量版本的函数在原函数的末尾加了字母‘v’
var position = new Float32Array([1.0, 2.0, 3.0, 1.0])
gl.vertexAttrib4fv(a_Position, position)
//对于接收数组的矢量版本,函数名中的数字代表所接收数组中的元素个数。

也可以加一个顶点大小数据,控制绘制的大小:部分代码

var VSHADER_SOURCE = 'attribute vec4 a_Position;\n' + 'attribute float a_PointSize;\n' +'void main(){\n' + 'gl_Position = a_Position;\n' + 'gl_PointSize = a_PointSize;\n' + '}\n'let a_PointSize = gl.getAttribLocation(gl.program,'a_PointSize');if(a_Position < 0) {console.log('Failed to get the storage location of a_Position');return;}gl.vertexAttrib3f(a_Position,0.5,0.0,0.0);gl.vertexAttrib1f(a_PointSize,50.0);

写的太长了,网页开始卡顿了,再开一篇,这一章真的很长

参考:【《WebGL编程指南》读书笔记-WebGL入门】_getwebglcontext-CSDN博客

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

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

相关文章

stm32 为什么有2个晶振?8mhz+32.768k

1个是系统时钟晶振是单片机内部系统的主时钟源&#xff0c;它负责控制整个系统的时钟频率。这个晶振的频率一般比较高&#xff0c;通常在几十MHz到几百MHz不等。它和CPU以及各种总线之间相互配合&#xff0c;从而协同工作。 另外一个是外设时钟晶振则通常用于单片机的内部外设…

【大模型问答测试】大模型问答测试脚本实现(第二版)——接入pytest与代码解耦

背景 接上一篇&#xff0c;【大模型问答测试】大模型问答测试脚本实现&#xff08;第一版&#xff09;。 在实现自动化的时候&#xff0c;原先把很多方法与request请求写在一块了&#xff0c;趁着目前实现接口数量较少&#xff0c;决定对代码进行解耦&#xff0c;并且清晰目录…

大数据-172 Elasticsearch 索引操作 与 IK 分词器 自定义停用词 Nginx 服务

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

[论文阅读]RGB-Depth Fusion GAN for Indoor Depth Completion

摘要 由于固有的限制&#xff0c;如无法感知透明物体和有限的距离范围&#xff0c;室内深度传感器捕获的原始深度图像通常会有大面积的深度值缺失。这种不完整的深度图给许多后续视觉任务带来了负担&#xff0c;尽管提出了很多深度补全方法来缓解这一问题。但是现有的大多数方…

解决k8s集群中安装ks3.4.1开启日志失败问题

问题 安装kubesphere v3.4.1时&#xff0c;开启了日志功能&#xff0c;部署时有三个pod报错了 Failed to pull image “busybox:latest”: rpc error: code Unknown desc failed to pull and unpack image “docker.io/library/busybox:latest”: failed to copy: httpRead…

字节跳动青训营——入营考核解答(持续更新中~~~)

考核内容&#xff1a; 在指定的题库中自主选择不少于 15 道算法题并完成解题&#xff0c;其中题目难度分配如下&#xff1a; 简单题不少于 10 道中等题不少于 4 道困难题不少于 1 道 解答代码 16.DNA 序列还原 &#xff08;简单&#xff09; 代码实现&#xff1a; public…

零基础Java第八期:一维数组(1)

目录 一、 一维数组的基本概念 1.1. 什么是数组 1.2. 数组的创建及初始化 1.3. 数组的使用 二、数组是引用类型 2.1. 初始JVM的内存分布 2.2. 基本类型变量与引用类型变量 2.3. 引用变量的理解 2.4. null 三、数组的应用场景 3.1. 作为函数的参数 3.2. 作为函数的返…

2024台州赛CTFwp

备注&#xff1a; 解题过程中&#xff0c;关键步骤不可省略&#xff0c;不可含糊其辞、一笔带过。解题过程中如是自己编写的脚本&#xff0c;不可省略&#xff0c;不可截图&#xff08;代码字体可以调小&#xff1b;而如果代码太长&#xff0c;则贴关键代码函数&#xff09;。…

【HarmonyOS NEXT】服务端向终端推送消息——获取Push Token

【需求】 获取Push Token 【文档】 https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/push-get-token-V5 【代码】 // EntryAbility.ets 文件 import { pushService } from kit.PushKit; export default class EntryAbility extends UIAbility {onCreat…

第T7周:咖啡豆识别

>- **&#x1f368; 本文为[&#x1f517;365天深度学习训练营](https://mp.weixin.qq.com/s/0dvHCaOoFnW8SCp3JpzKxg) 中的学习记录博客** >- **&#x1f356; 原作者&#xff1a;[K同学啊](https://mtyjkh.blog.csdn.net/)**难度&#xff1a;夯实基础⭐⭐语言&#xff…

Qt5.14.2 安装详细教程(图文版)

Qt 是一个跨平台的 C 应用程序开发框架&#xff0c;主要用于开发图形用户界面&#xff08;GUI&#xff09;程序&#xff0c;但也支持非 GUI 程序的开发。Qt 提供了丰富的功能库和工具&#xff0c;使开发者能够在不同平台上编写、编译和运行应用程序&#xff0c;而无需修改代码。…

如何通过CDN优化网站服务器访问速度?

CDN&#xff0c;即内容分发网络&#xff08;Content Delivery Network&#xff09;&#xff0c;在现代互联网中起着重要作用。它可以显著提升网站服务器的访问速度。以下是CDN在加速网站访问方面的主要优势及其工作原理。 1. 全球分布的服务器节点 CDN通过在全球范围内布设多个…

CANoe与C#联合仿真方案

引言 CANoe作为一款强大的网络仿真工具,能够模拟各种通信协议,尤其是在汽车领域的CAN、LIN、Ethernet等协议。而C#作为一种广泛使用的编程语言,能够为CANoe提供灵活的用户界面和逻辑控制。本文将探讨如何将CANoe与C#结合,实现高效的联合仿真方案。 1. 系统架构 联合仿真…

Python学习的自我理解和想法(16)

学的是b站的课程&#xff08;千锋教育&#xff09;&#xff0c;跟老师写程序&#xff0c;不是自创的代码&#xff01; 今天是学Python的第16天&#xff0c;从今天开始&#xff0c;每天一到两个常用模块&#xff0c;更完恢复到原来的。开学了&#xff0c;时间不多&#xff0c;写…

Bug:通过反射修改@Autowired注入Bean的字段,明确存在,报错 NoSuchFieldException

【BUG】通过Autowired注入了一个Bean SeqNo&#xff0c;测试的时候需要修改其中的字段。通过传统的反射&#xff0c;无论如何都拿不到信息&#xff0c;关键是一方面可以通过IDEA跳转&#xff0c;一方面debug也确实能看到这个字段。但是每次调用set方法报错&#xff1a;NoSuchFi…

Nuxt.js 应用中的 app:templatesGenerated 事件钩子详解

title: Nuxt.js 应用中的 app:templatesGenerated 事件钩子详解 date: 2024/10/19 updated: 2024/10/19 author: cmdragon excerpt: app:templatesGenerated 是 Nuxt.js 的一个生命周期钩子,在模板编译到虚拟文件系统(Virtual File System, VFS)之后被调用。这个钩子允许…

【数据分享】1901-2023年我国省市县三级逐月最低气温(免费获取/Shp/Excel格式)

之前我们分享过1901-2023年1km分辨率逐月最低气温栅格数据&#xff08;可查看之前的文章获悉详情&#xff09;&#xff0c;该数据来源于国家青藏高原科学数据中心&#xff0c;很多小伙伴拿到数据后反馈栅格数据不太方便使用&#xff0c;问我们能不能把数据处理为更方便使用的Sh…

Android15之解决gdb:Remote register badly formatted问题(二百三十六)

简介&#xff1a; CSDN博客专家、《Android系统多媒体进阶实战》一书作者 新书发布&#xff1a;《Android系统多媒体进阶实战》&#x1f680; 优质专栏&#xff1a; Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a; 多媒体系统工程师系列【…

C++临时变量的常量性

C 临时变量的常量性-CSDN博客 #include <iostream> using namespace std; void print(string& str) {cout << str << endl; }int main() {print("hello world");//print(string("hello world"));return 0; }编译器根据字符串"…

探索 Coconut: Python 的新篇章

文章目录 探索 Coconut: Python 的新篇章背景&#xff1a;为何选择 Coconut&#xff1f;Coconut 是什么&#xff1f;如何安装 Coconut&#xff1f;简单的库函数使用方法1. 惰性列表2. 模式匹配3. 函数组合4. 协程5. 模式匹配数据类型 场景应用1. Web 开发2. 数据处理3. 异步编程…