- 原文地址:Day 5. Interleaved buffers
- 原文做者:Andrei Lesnitsky
这是 WebGL 系列的第 5 天教程,天天都有新文章发布。html
源代码在这里github
第 4 天咱们已经学会了如何使用着色器的 varyings
。今天咱们将探讨另外一个概念,咱们先把昨天的做业解决:web
WebGL 是与 GPU 协同渲染内容的 API。JavaScript 是由 v8 在 CPU 上执行的,虽然 GPU 没法执行 JavaScript,但仍能够对其编程。编程
GPU 可以识别 GLSL 语言,咱们不只会熟悉 WebGL API,还会熟悉这种新语言。canvas
GLSL 是一种相似于 C 的编程语言,所以对于 JavaScript 开发人员来讲很容易学习和编写。编程语言
可是,咱们在哪里编写 glsl 代码?如何将其传递给 GPU 以执行?函数
接下来,咱们建立一个新的 js 文件,并获取对 WebGL 渲染上下文的引用学习
📄 index.htmlwebgl
</head> <body> <canvas></canvas> - <script src="./src/canvas2d.js"></script> + <script src="./src/webgl-hello-world.js"></script> </body> </html>
📄 src/webgl-hello-world.js
const canvas = document.querySelector('canvas'); const gl = canvas.getContext('webgl');
GPU 可执行的程序是经过 WebGL 渲染上下文方法建立的。
📄 src/webgl-hello-world.js
const canvas = document.querySelector('canvas'); const gl = canvas.getContext('webgl'); + + const program = gl.createProgram();
GPU程序包含两个“功能”,
这些功能称为 shaders
WebGL,支持多种类型的着色器
在这个示例中,咱们将使用 vertex
和 fragment
着色器。
二者均可以使用 createShader
方法建立
📄 src/webgl-hello-world.js
const gl = canvas.getContext('webgl'); const program = gl.createProgram(); + + const vertexShader = gl.createShader(gl.VERTEX_SHADER); + const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
如今让咱们编写最简单的着色器:
📄 src/webgl-hello-world.js
const vertexShader = gl.createShader(gl.VERTEX_SHADER); const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); + + const vShaderSource = ` + void main() { + + } + `;
对于具备必定 C/C++ 经验的人来讲,这应该看起来很熟悉。
不像 C 或 C++ 语言的 main
没有返回值,这里的 main
会分配一个值到全局变量 gl_Position
。
📄 src/webgl-hello-world.js
const vShaderSource = ` void main() { - + gl_Position = vec4(0, 0, 0, 1); } `;
如今,让咱们仔细看一下分配的内容。
着色器中有不少功能。
vec4
函数建立一个由 4 个份量组成的向量。
gl_Position = vec4(0, 0, 0, 1);
看起来很奇怪,咱们生活在三维世界中,第四部分究竟是什么?是时间吗?😕
并非的
事实证实,这种添加容许使用许多不错的技术来处理 3D 数据。
在经典的笛卡尔坐标系中定义了三维坐标点,附加的第四维将此点更改成齐次坐标。它仍然表明三维空间中的一个点,而且能够经过一对简单的函数,轻松演示如何构造此类坐标。
如今,咱们能够忽略第四部分组件,并将其设置为 1.0
好的,咱们有一个着色器变量,另外一个变量中有着色器源。咱们如何链接这两个呢?
📄 src/webgl-hello-world.js
gl_Position = vec4(0, 0, 0, 1); } `; + + gl.shaderSource(vertexShader, vShaderSource);
GLSL 着色器应进行编译才能执行:
📄 src/webgl-hello-world.js
`; gl.shaderSource(vertexShader, vShaderSource); + gl.compileShader(vertexShader);
能够用检索找到编译结果,此方法返回 compiler
并输出。若是是空字符串,仍然都很好。
📄 src/webgl-hello-world.js
gl.shaderSource(vertexShader, vShaderSource); gl.compileShader(vertexShader); + + console.log(gl.getShaderInfoLog(vertexShader));
咱们须要对片断着色器执行相同的操做,同时咱们实现一个辅助功能,该功能也将用于片断着色器。
📄 src/webgl-hello-world.js
} `; - gl.shaderSource(vertexShader, vShaderSource); - gl.compileShader(vertexShader); + function compileShader(shader, source) { + gl.shaderSource(shader, source); + gl.compileShader(shader); - console.log(gl.getShaderInfoLog(vertexShader)); + const log = gl.getShaderInfoLog(shader); + + if (log) { + throw new Error(log); + } + } + + compileShader(vertexShader, vShaderSource);
最简单的片断着色器的外观如何?彻底相同。
📄 src/webgl-hello-world.js
} `; + const fShaderSource = ` + void main() { + + } + `; + function compileShader(shader, source) { gl.shaderSource(shader, source); gl.compileShader(shader);
片断着色器的计算结果是一种颜色,它也是 4 个份量 (r, g, b, a)
的向量。与 CSS 不一样,值在[0..1]
范围内,而不是[0..255]
。片断着色器的计算结果应分配给变量gl_FragColor
。
📄 src/webgl-hello-world.js
const fShaderSource = ` void main() { - + gl_FragColor = vec4(1, 0, 0, 1); } `; } compileShader(vertexShader, vShaderSource); + compileShader(fragmentShader, fShaderSource);
如今咱们应该使用着色器链接 program
。
📄 src/webgl-hello-world.js
compileShader(vertexShader, vShaderSource); compileShader(fragmentShader, fShaderSource); + + gl.attachShader(program, vertexShader); + gl.attachShader(program, fragmentShader);
接下来是连接程序。须要在这一阶段来验证顶点着色器和片断着色器是否相互兼容(咱们将在后面详细介绍)。
📄 src/webgl-hello-world.js
gl.attachShader(program, vertexShader); gl.attachShader(program, fragmentShader); + + gl.linkProgram(program);
咱们的应用程序可能有几个程序,因此咱们应该在发出绘图调用以前告诉 gpu 咱们要使用哪一个程序。
📄 src/webgl-hello-world.js
gl.attachShader(program, fragmentShader); gl.linkProgram(program); + + gl.useProgram(program);
好了,咱们准备画点东西。
📄 src/webgl-hello-world.js
gl.linkProgram(program); gl.useProgram(program); + + gl.drawArrays();
WebGL 能够渲染几种类型的"原语"
接着咱们传递要渲染的原始类型
📄 src/webgl-hello-world.js
gl.useProgram(program); - gl.drawArrays(); + gl.drawArrays(gl.POINTS);
有一种方法能够将包含有关图元位置信息的输入数据传递到顶点着色器,所以咱们须要将要渲染的第一个原语的索引进行传递。
📄 src/webgl-hello-world.js
gl.useProgram(program); - gl.drawArrays(gl.POINTS); + gl.drawArrays(gl.POINTS, 0);
还有原语的计数
📄 src/webgl-hello-world.js
gl.useProgram(program); - gl.drawArrays(gl.POINTS, 0); + gl.drawArrays(gl.POINTS, 0, 1);
什么都没呈现😢出现什么问题了?