04 | GPU与渲染管线:如何用WebGL绘制最简单的几何图形?

2020-06-29 月影
《跟月影学可视化》
课程介绍


讲述:月影

时长:大小21.52M


你好,我是月影。今天,我们要讲 WebGL。
WebGL 是最后一个和可视化有关的图形系统,也是最难学的一个。为啥说它难学呢?我觉得这主要有两个原因。第一,WebGL 这种技术本身就是用来解决最复杂的视觉呈现的。比如说,大批量绘制复杂图形和 3D 模型,这类比较有难度的问题就适合用 WebGL 来解决。第二,WebGL 相对于其他图形系统来说,是一个更“开放”的系统。
我说的“开放”是针对于底层机制而言的。因为,不管是 HTML/CSS、SVG 还是 Canvas,都主要是使用其 API 来绘制图形的,所以我们不必关心它们具体的底层机制。也就是说,我们只要理解创建 SVG 元素的绘图声明,学会执行 Canvas 对应的绘图指令,能够将图形输出,这就够了。但是,要使用 WebGL 绘图,我们必须要深入细节里。换句话说就是,我们必须要和内存、GPU 打交道,真正控制图形输出的每一个细节。
所以,想要学好 WebGL,我们必须先理解一些基本概念和原理。那今天这一节课,我会从图形系统的绘图原理开始讲起,主要来讲 WebGL 最基础的概念,包括 GPU、渲染管线、着色器。然后,我会带你用 WebGL 绘...

展开全文
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。

精选留言

  • 筑梦师刘渊
    2020-06-30
    作业一 查了下资料,webgl支持的图元类型有七种,分别是 gl.POINTS(点), gl.LINES(线段), gl.LINE_STRIP(线条), gl.LINE_LOOP(回路), gl.TRIANGLES(三角形), gl.TRIANGLE_STRIP(三角带), gl.TRIANGLE_FAN(三角扇)。 要绘制空心三角形,gl.LINE_STRIP(线条)、gl.LINES(线段)、 gl.LINE_LOOP(回路)都可以实现。 但是gl.LINES(线段)需要写入六个顶点([-1, -1, 0, 1, 0, 1, 1, -1, 1, -1,-1, -1]), gl.LINE_STRIP(线条)也需要写入四个顶点([-1, -1, 0, 1, 1, -1,-1, -1]),而gl.LINE_LOOP(回路),只需要是三个顶点([-1, -1, 0, 1, 1, -1]),因此gl.LINE_LOOP(回路)是最佳选择 作业二 a. 先封装一个生成多边形顶点坐标数组的函数 function createCircleVertex(x, y, r, n) { const sin = Math.sin; const cos = Math.cos; const perAngel = (2 * Math.PI) / n; const positionArray = []; for (let i = 0; i < n; i++) { const angel = i * perAngel; const nx = x + r * cos(angel); const ny = y + r * sin(angel); positionArray.push(nx, ny); } return new Float32Array(positionArray); } b. 封装一个生成正多角星顶点的数组函数 function create2CircleVertex(x, y, r, R, n) { const sin = Math.sin; const cos = Math.cos; const perAngel = Math.PI / n; const positionArray = []; for (let i = 0; i < 2 * n; i++) { const angel = i * perAngel; if (i % 2 !== 0) { const Rx = x + R * cos(angel); const Ry = y + R * sin(angel); positionArray.push(Rx, Ry); } else { const rx = x + r * cos(angel); const ry = y + r * sin(angel); positionArray.push(rx, ry); } } return new Float32Array(positionArray); } 1. 正四边形 const points = createCircleVertex(0, 0, 0.5, 4); 2. 正五边形 const points = createCircleVertex(0, 0, 0.5, 5); 3. 正六角星 const points = create2CircleVertex(0, 0, 0.3, 0.6, 6); 以上要绘制空心用gl.LINE_LOOP图元,实心用gl.TRIANGLE_FAN图元 1)空心:gl.drawArrays(gl.LINE_LOOP, 0, points.length / 2); 2)实心:gl.drawArrays(gl.TRIANGLE_FAN, 0, points.length / 2);
    展开

    作者回复: 很棒!

    共 2 条评论
    37
  • Cailven
    2020-06-29
    补充:vs不仅仅只有postion值,一般通过attribute 进行属性赋值。在图形学管顶点操作叫做VAO(vertex array object),而vao操作的float数据底层是vbo。不过如果用了threejs后很多图元操作就依赖引擎直接就解决了,但在Threejs中依然可以通过shaderMatiral通过setAttribute给bufferGeometry的顶点赋值。 不过个人在这几年的图形学学习中觉得vs相对还是简单的,fs对于像素的操作很像当年给photoshop写滤镜的过程。不过如果真的是玩片元着色炫技可以看看shadertoy里关于用remaching技术构建距离场用体素算法在片元里构建另一个三维引擎的效果。希望月影大大回头可以针对这方面有所加餐。感谢! 不过这课程来的太晚,如果几年前能早点接触到这门课,估计会在图形学方面少走很多弯路,谢谢月影大大。
    展开

    作者回复: VAO是一种组织顶点数据的方式,也是webgl里面常用的方式,它的好处之一是不用每次操作都一一绑定每一组不同的顶点数据。这些属于具体webgl使用上的问题,随着专栏的课程内容深度会有更多介绍。shadertoy很不错的平台,在后面介绍像素处理的课程里会看到一部分shadertoy上比较有趣的例子。

    
    16
  • xiao豪
    2020-07-06
    老师,将数据存入缓存再拿出来是有什么意义呢?

    作者回复: 因为webgl实际上是JS与GPU进行交互,所以要先将JS的数据存入缓存,在webgl程序运行的时候底层从缓存中读取数据给shader,在shader中完成图形绘制。所以是一读一写的过程

    共 2 条评论
    5
  • 宁康
    2020-06-29
    正n边型,r是外接圆半径 getPolygonPoints( n, r ){ const stepAngle = 2*Math.PI / n let initAngle = 0 const pointArray = [] for(let i = 0; i < n; i++) { // 存储x坐标 pointArray.push(r * Math.cos(initAngle)) // 存储y坐标 pointArray.push(r * Math.sin(initAngle)) initAngle += stepAngle } return pointArray } // 正十边型坐标点 const ponitsArray = getPolygonPoints(10, 1) const ponits = new Float32Array(ponitsArray) gl.drawArrays(gl.TRIANGLE_FAN, 0, ponits.length / 2)
    展开

    作者回复: 赞~

    
    5
  • 宁康
    2020-06-29
    1、gl_Position 设置顶点,这个我查了一下,第四个值设置为2.0也可以实现缩小一倍。 gl_Position = vec4(position, 0.0, 2.0); 2、空心三角形: gl.drawArrays(gl. LINE_LOOP, 0, ponits.length / 2) 3、绘制多边形 a.定义多边形的(x, y)坐标 const ponits = new Float32Array([ -1, -1, 0, -2, 1, -1, 1, 1, -1, 1 ]) b.绘制多边形 gl.drawArrays(gl.TRIANGLE_FAN, 0, ponits.length / 2)
    展开

    作者回复: 不错~

    
    3
  • Kevin
    2020-07-16
    实现了一个正多边形的样例,动态修改边数。 https://codesandbox.io/s/practice-canvas-vme4k?file=/src/pages/RegularPolygonWebGL.vue
    展开

    作者回复: 赞

    
    2
  • Kevin
    2020-07-15
    问题一: 绘制空心三角形使用回路线条:gl.LINE_LOOP gl.drawArrays(gl.LINE_LOOP, 0, points.length / 2); WebGL可绘制的图元有以下7种,来源网络查找:https://www.jianshu.com/p/1e750f20ec23 点 gl.POINTS 线段 gl.LINES 线条 gl.LINE_STRIP 回路线条 gl.LINE_LOOP 三角形 gl.TRIANGLES 三角带 gl.TRIANGLE_STRIP 三角扇 gl.TRIANGLE_FAN
    展开

    作者回复: 嗯嗯

    
    2
  • 浩荡如空气
    2021-11-28
    请问JS数据存入的Buffer和FrameBuffer是什么关系啊
    共 1 条评论
    1
  • 彧豪
    2021-05-21
    话说月影大大, 这个缓冲区和内存有什么关系?是一回事吗?那缓存呢, 缓冲区 内存 缓存这三者怎么理解呢?页面仔表示对这些计算机结构 原理方面的东西不是太了解, 求解答
    共 1 条评论
    1
  • 国旗
    2020-09-23
    老师问下MDN文档里WebGL常数这儿,表格里十六进制的‘Value’表示的意思是类似于CPU指令寄存器么? Getting GL parameter information这节BLEND_EQUATION,BLEND_EQUATION_RGB的value都是0x8009,也有点不理解 https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/Constants

    作者回复: 就是一个代表状态常量的数值,设计的时候定下来的。

    
    1
  • 不见飞刀
    2020-08-29
    "图形中有多少个像素点,着色器程序在 GPU 中就会被同时执行多少次。" 顶点着色器是否一样还是有几个顶点就执行多少次呢? 传给片元的varying变量会线性差值,那么插值这一步发生在哪呢?
    展开

    作者回复: 是的,顶点着色器执行次数和顶点数量有关。插值发生在光栅化的时候。

    共 3 条评论
    1
  • 莫轩竹
    2020-08-01
    请问编写glsl有什么智能提示插吗,我用的vscode?

    作者回复: vscode有插件,商店里搜一下就有

    
    1
  • 我母鸡啊!
    2020-07-03
    作业1 : gl.drawArrays传入gl.LINE_LOOP

    作者回复: 嗯嗯

    
    1
  • 我母鸡啊!
    2020-07-03
    所以在webgl中最小的图元是三角形?

    作者回复: 点、线段和三角形

    
    1
  • miaomiao
    2020-07-03
    月影老师,你好,有个问题想请教下,如果我想实现12条心电波形折线图,每条波形图的每个脉搏段会依据脉搏类型展示不同的颜色,用户交互:用户可以选择特定的脉搏段,统一修改这个脉搏段类型,这样类型变化,12条心电图的对应脉搏段颜色也变化。这种用canvas好还是webgl好?用canvas的话,需要去获取用户选定的范围,对画布元素进行局部重绘,目前有没有可以只通过更改数据,根据数据变化进行自动重绘的图形库?

    作者回复: 这种canvas和webgl都行,spritejs可以根据数据变化自动重绘,你可以试试。有什么问题直接问我或者仓库提issue

    共 4 条评论
    1
  • 王子晨
    2020-07-01
    老师请问用webGL绘制复杂的图形,会不会设置多个顶点和片元着色程序?还是说一直在修改一个顶点和片元着色程序?

    作者回复: 会创建多个webgl program 对象,每个对象对应一个顶点着色器和一个片元着色器。只要切换 webgl program 就行

    共 2 条评论
    1
  • 量子蔷薇
    2020-06-30
    我在codepen写了作业,https://codepen.io/quantum-rose/pen/QWyqexL 不确定自己对WebGL绘图的机制理解对了没,关于封装复用代码那部分,感觉我可能复用了一些不需要复用的代码。期待之后的学习! 我的六角星只有描边,本质是在不清空画布的情况下画了两个三角形,不知道有没有更好的做法,如果是实心六角星,把我的代码中drawStar函数里的LINE_LOOP换成TRIANGLE_FAN就能画出来,但是其他n角星并不能正确画出来。

    作者回复: 很棒,六角星这么实现是不错的思路。另一种思路是把顶点计算出来之后做三角剖分,在后续课程中很快就会学习到啦。

    
    1
  • 城南花已开
    2021-12-17
    老师,webgl在vue项目中要怎么使用,我用.frag为后缀的文件写glsl语法,引入到vue组件,不起效果
    
    
  • 何以解忧
    2021-12-13
    看了一下,推荐的shader book ,还有后面的内容,似乎很多操作都是在片元着色器中进行的,一版顶点着色器,我们只要传入顶点数据,不进行其他的处理么
    
    
  • Leon two✌🏻
    2021-12-07
    老师你好,下面的 buffer 的数据绑定给顶点着色器的 position 变量这一步有些疑问 const bufferId = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, bufferId); gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW); const vPosition = gl.getAttribLocation(program, 'position');获取顶点着色器中的position变量的地址 gl.vertexAttribPointer(vPosition, 2, gl.FLOAT, false, 0, 0);给变量设置长度和类型 gl.enableVertexAttribArray(vPosition);激活这个变量 获取position变量之后,好像也没有看到具体的赋值操作,他是怎么关联到 points 的buffer数据呢,而且如果我之前创建了多个 buffer,position变量又是指向的哪个呢
    展开
    
    