参考文章:https://webglfundamentals.org/webgl/lessons/zh_cn/webgl-fundamentals.html#toc
根据学习的路线,个人弄的 webgl 的 demo,有需要的可以下载查看:【webgl-demo】
着色器
WebGL 仅仅是一个光栅化引擎,它可以根据你的代码绘制出点,线和三角形。
WebGL 在电脑的 GPU 中运行。因此你需要使用能够在 GPU 上运行的代码。 这样的代码需要提供成对的方法。并且使用一种和C或C++类似的强类型的语言 GLSL。 (GL着色语言)。每一对组合起来称作一个 program(着色程序)。
两种着色器
每对方法中一个叫顶点着色器, 另一个叫片断着色器:
1.顶点着色器:计算顶点的位置。
无论你的画布有多大,裁剪空间的坐标范围永远是 -1 到 1 。
// 一个属性值,将会从缓冲中获取数据 attribute vec4 a_position; // 所有着色器都有一个main方法 void main() { // gl_Position 是一个顶点着色器主要设置的变量 gl_Position = a_position; }
如果用 JavaScript 代替 GLSL, 可以类似
// *** 伪代码!! *** var positionBuffer = [ 0, 0, 0, 0, 0, 0.5, 0, 0, 0.7, 0, 0, 0, ]; var attributes = {}; var gl_Position; drawArrays(..., offset, count) { var stride = 4; var size = 4; for (var i = 0; i < count; ++i) { // 从positionBuffer复制接下来4个值给a_position属性 const start = offset + i * stride; attributes.a_position = positionBuffer.slice(start, start + size); runVertexShader();// 运行顶点着色器 ... doSomethingWith_gl_Position(); }
2.片断着色器:计算出当前绘制图元中每个像素的颜色值。
WebGL中的颜色值范围从 0 到 1 。
比如:vec4(1, 0, 0.5, 1):r, g, b, alpha (红,绿,蓝,阿尔法)
vec4是一个有四个浮点数据的数据类型。
- 1 代表红色值
- 0 代表绿色值
- 0.5 代表蓝色值
- 最后一个1表示阿尔法通道值
// 片断着色器没有默认精度,所以我们需要设置一个精度 // mediump是一个不错的默认值,代表“medium precision”(中等精度) precision mediump float; void main() { // gl_FragColor是一个片断着色器主要设置的变量 gl_FragColor = vec4(1, 0, 0.5, 1); // 返回“红紫色” }
四种方法
几乎整个 WebGL API 都是关于如何设置这些成对方法的状态值以及运行它们。
对于想要绘制的每一个对象,都需要先设置一系列状态值,然后通过调用 gl.drawArrays 或 gl.drawElements 运行一个着色方法对,使得你的着色器对能够在GPU上运行。这些方法对所需的任何数据都需要发送到GPU。
发送方式:
1.属性(Attributes)和缓冲
缓冲:发送到 GPU 的一些二进制数据序列
通常情况下缓冲数据包括位置,法向量,纹理坐标,顶点颜色值等
可以存储任何数据
属性:用来指明怎么从缓冲中获取所需数据并将它提供给顶点着色器
寻找属性值位置(和全局属性位置)应该在初始化的时候完成,而不是在渲染循环中。
例如你可能在缓冲中用三个32位的浮点型数据存储一个位置值
对于一个确切的属性你需要告诉它从哪个缓冲中获取数据,获取什么类型的数据(三个32位的浮点数据), 起始偏移值是多少,到下一个位置的字节数是多少
缓冲不是随意读取的
事实上顶点着色器运行的次数是一个指定的确切数字
每一次运行属性会从指定的缓冲中按照指定规则依次获取下一个值
2.全局变量(Uniforms)
全局变量在着色程序运行前赋值,在运行过程中全局有效。
3.纹理(Textures)
纹理:一个数据序列,可以在着色程序运行中随意读取其中的数据。
大多数情况存放的是图像数据
纹理仅仅是数据序列, 也可以随意存放除了颜色数据以外的其它数据。
4.可变量(Varyings)
可变量:一种顶点着色器给片断着色器传值的方式,依照渲染的图元是点, 线还是三角形,顶点着色器中设置的可变量会在片断着色器运行中获取不同的插值。
WebGL 的简单用法
WebGL的主要任务就是设置好状态并为 GLSL 着色程序提供数据。
使用 WebGL 只需要给它提供两个东西:
- 裁剪空间中的坐标值:由顶点着色器提供
- 颜色值:由片断着色器提供
注意
【webgl-utils.js】:它包含了编译和链接着色器的方法。下面的例子里将会使用。
初始化
1.在 HTML 中的 canvas(画布)对象,并获取它
<canvas id="c"></canvas>
var canvas = document.querySelector("#c");
2.创建一个 WebGL 渲染上下文(WebGLRenderingContext)
var gl = canvas.getContext("webgl"); if (!gl) { // 你不能使用WebGL! ...
3.利用 JavaScript 中创建字符串的方式创建 GLSL 字符串
方式一:用串联的方式(concatenating), 用AJAX下载,用多行模板数据。大多数三维引擎在运行时利用模板,串联等方式创建 GLSL。
方式二:将它们放在非JavaScript类型的标签中,下面代码展示的就是这种
<script id="vertex-shader-2d" type="notjs"> // 一个属性变量,将会从缓冲中获取数据 attribute vec4 a_position; // 所有着色器都有一个main方法 void main() { // gl_Position 是一个顶点着色器主要设置的变量 gl_Position = a_position; } </script> <script id="fragment-shader-2d" type="notjs"> // 片断着色器没有默认精度,所以我们需要设置一个精度 // mediump是一个不错的默认值,代表“medium precision”(中等精度) precision mediump float; void main() { // gl_FragColor是一个片断着色器主要设置的变量 gl_FragColor = vec4(1, 0, 0.5, 1); // 返回“瑞迪施紫色” } </script>
4.创建两个着色器
// 创建着色器方法,输入参数:渲染上下文,着色器类型,数据源 function createShader(gl, type, source) { var shader = gl.createShader(type); // 创建着色器对象 gl.shaderSource(shader, source); // 提供数据源 gl.compileShader(shader); // 编译 -> 生成着色器 var success = gl.getShaderParameter(shader, gl.COMPILE_STATUS); if (success) { return shader; } console.log(gl.getShaderInfoLog(shader)); gl.deleteShader(shader); }
var vertexShaderSource = document.querySelector("#vertex-shader-2d").text; var fragmentShaderSource = document.querySelector("#fragment-shader-2d").text; var vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource); var fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource);
5.将这两个着色器 link(链接)到一个 program(着色程序)
function createProgram(gl, vertexShader, fragmentShader) { var program = gl.createProgram(); gl.attachShader(program, vertexShader); gl.attachShader(program, fragmentShader); gl.linkProgram(program); var success = gl.getProgramParameter(program, gl.LINK_STATUS); if (success) { return program; } console.log(gl.getProgramInfoLog(program)); gl.deleteProgram(program); }
调用:
var program = createProgram(gl, vertexShader, fragmentShader);
6.从刚才创建的 GLSL 着色程序中找到属性值 a_position 所在的位置。
var positionAttributeLocation = gl.getAttribLocation(program, "a_position");
7.创建一个缓冲:属性值从缓冲中获取数据
var positionBuffer = gl.createBuffer();
8.绑定位置信息缓冲
WebGL可以通过绑定点操控全局范围内的许多数据,你可以把绑定点想象成一个WebGL内部的全局变量。
首先绑定一个数据源到绑定点
// 绑定点就是ARRAY_BUFFER gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
然后通过绑定点向缓冲中存放数据
// 三个二维点坐标 var positions = [ 0, 0, 0, 0.5, 0.7, 0, ]; // 最后一个参数gl.STATIC_DRAW是提示WebGL我们将怎么使用这些数据。WebGL会根据提示做出一些优化。 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
上面的代码完成了几件事:
有了一个 JavaScript 序列 positions
new Float32Array(positions) 创建了 32 位浮点型数据序列, 并从 positions 中复制数据到序列中
gl.bufferData 复制这些数据到 GPU 的 positionBuffer 对象上
渲染
1.调整画布(canvas)的尺寸以匹配它的显示尺寸
webglUtils.resizeCanvasToDisplaySize(gl.canvas);
2.把提供的 gl_Position
裁剪空间坐标对应到画布像素坐标
// WebGL裁剪空间的 -1 -> +1 分别对应到x轴的 0 -> gl.canvas.width 和y轴的 0 -> gl.canvas.height gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
3.清空画布
// 清空画布 gl.clearColor(0, 0, 0, 0); gl.clear(gl.COLOR_BUFFER_BIT);
4.运行着色程序
// 告诉它用我们之前写好的着色程序(一个着色器对) gl.useProgram(program);
5.从缓冲中获取数据给着色器中的属性
// 启用对应属性 gl.enableVertexAttribArray(positionAttributeLocation);
// 将绑定点绑定到缓冲数据(positionBuffer) gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); // 告诉属性怎么从positionBuffer中读取数据 (ARRAY_BUFFER) var size = 2; // 每次迭代运行提取两个单位数据 var type = gl.FLOAT; // 每个单位的数据类型是32位浮点型 var normalize = false; // 不需要归一化数据 var stride = 0; // 0 = 移动单位数量 * 每个单位占用内存(sizeof(type)) // 每次迭代运行运动多少内存到下一个数据开始点 var offset = 0; // 从缓冲起始位置开始读取 // gl.vertexAttribPointer是将属性绑定到当前的ARRAY_BUFFER gl.vertexAttribPointer(positionAttributeLocation, size, type, normalize, stride, offset)
绘制一个三角形
运行 GLSL 着色程序
var primitiveType = gl.TRIANGLES; // 图元类型为(三角形) var offset = 0; var count = 3; // 顶点着色器将运行三次 gl.drawArrays(primitiveType, offset, count);
- 第一次:将会从位置缓冲中读取前两个值赋给属性值
a_position.x
和a_position.y
。 - 第二次:
a_position.xy
将会被赋予后两个值 - 最后一次运行将被赋予最后两个值。
三角形在裁剪空间中的坐标如下:
0, 0, 0, 0.5, 0.7, 0,
如果画布大小是 400×300
会得到类似以下的转换
裁剪空间 屏幕空间 0, 0 -> 200, 150 0, 0.5 -> 200, 225 0.7, 0 -> 340, 150
完整代码:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" /> <title>WebGL - Fundamentals</title> <link type="text/css" href="https://webglfundamentals.org/webgl/resources/webgl-tutorials.css" rel="stylesheet" /> </head> <body> <canvas id="c"></canvas> </body> </html> <script src="https://webglfundamentals.org/webgl/resources/webgl-utils.js"></script> <script id="vertex-shader-2d" type="notjs"> // an attribute will receive data from a buffer attribute vec4 a_position; // all shaders have a main function void main() { // gl_Position is a special variable a vertex shader // is responsible for setting gl_Position = a_position; } </script> <script id="fragment-shader-2d" type="notjs"> // fragment shaders don't have a default precision so we need // to pick one. mediump is a good default precision mediump float; void main() { // gl_FragColor is a special variable a fragment shader // is responsible for setting gl_FragColor = vec4(1, 0, 0.5, 1); // return redish-purple } </script> <script> /* eslint no-console:0 consistent-return:0 */ "use strict"; function createShader(gl, type, source) { var shader = gl.createShader(type); gl.shaderSource(shader, source); gl.compileShader(shader); var success = gl.getShaderParameter(shader, gl.COMPILE_STATUS); if (success) { return shader; } console.log(gl.getShaderInfoLog(shader)); gl.deleteShader(shader); } function createProgram(gl, vertexShader, fragmentShader) { var program = gl.createProgram(); gl.attachShader(program, vertexShader); gl.attachShader(program, fragmentShader); gl.linkProgram(program); var success = gl.getProgramParameter(program, gl.LINK_STATUS); if (success) { return program; } console.log(gl.getProgramInfoLog(program)); gl.deleteProgram(program); } function main() { // Get A WebGL context var canvas = document.querySelector("#c"); var gl = canvas.getContext("webgl"); if (!gl) { return; } // Get the strings for our GLSL shaders var vertexShaderSource = document.querySelector("#vertex-shader-2d").text; var fragmentShaderSource = document.querySelector("#fragment-shader-2d") .text; // create GLSL shaders, upload the GLSL source, compile the shaders var vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource); var fragmentShader = createShader( gl, gl.FRAGMENT_SHADER, fragmentShaderSource ); // Link the two shaders into a program var program = createProgram(gl, vertexShader, fragmentShader); // look up where the vertex data needs to go. var positionAttributeLocation = gl.getAttribLocation(program, "a_position"); // Create a buffer and put three 2d clip space points in it var positionBuffer = gl.createBuffer(); // Bind it to ARRAY_BUFFER (think of it as ARRAY_BUFFER = positionBuffer) gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); var positions = [0, 0, 0, 0.5, 0.7, 0]; gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW); // code above this line is initialization code. // code below this line is rendering code. webglUtils.resizeCanvasToDisplaySize(gl.canvas); // Tell WebGL how to convert from clip space to pixels gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); // Clear the canvas gl.clearColor(0, 0, 0, 0); gl.clear(gl.COLOR_BUFFER_BIT); // Tell it to use our program (pair of shaders) gl.useProgram(program); // Turn on the attribute gl.enableVertexAttribArray(positionAttributeLocation); // Bind the position buffer. gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); // Tell the attribute how to get data out of positionBuffer (ARRAY_BUFFER) var size = 2; // 2 components per iteration var type = gl.FLOAT; // the data is 32bit floats var normalize = false; // don't normalize the data var stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next position var offset = 0; // start at the beginning of the buffer gl.vertexAttribPointer( positionAttributeLocation, size, type, normalize, stride, offset ); // draw var primitiveType = gl.TRIANGLES; var offset = 0; var count = 3; gl.drawArrays(primitiveType, offset, count); } main(); </script>
本地效果:
改造顶点着色器
改造顶点着色器:提供给它像素坐标而不是裁剪空间坐标
<script id="vertex-shader-2d" type="notjs"> // ~~attribute vec4 a_position;~~ attribute vec2 a_position; // 改成 vec2 类型是因为我们只需要用x和y值。 uniform vec2 u_resolution; // 添加了一个uniform(全局变量)叫做u_resolution void main() { // 从像素坐标转换到 0.0 到 1.0 vec2 zeroToOne = a_position / u_resolution; // 再把 0->1 转换 0->2 vec2 zeroToTwo = zeroToOne * 2.0; // 把 0->2 转换到 -1->+1 (裁剪空间) vec2 clipSpace = zeroToTwo - 1.0; gl_Position = vec4(clipSpace, 0, 1); } </script>
为了设置 u_resolution
的值我们需要找到它的位置。
var resolutionUniformLocation = gl.getUniformLocation(program, "u_resolution");
绘制一个矩形
通过绘制两个三角形来绘制一个矩形
var positions = [ 10, 20, 80, 20, 10, 30, 10, 30, 80, 20, 80, 30, ]; gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
所有类似 gl.uniformXXX
格式的方法都是设置当前着色程序的全局变量。
gl.useProgram(program); ... // 设置全局变量 分辨率 gl.uniform2f(resolutionUniformLocation, gl.canvas.width, gl.canvas.height);
WebGL 要运行六次顶点着色器来画两个三角形:
// 绘制 var primitiveType = gl.TRIANGLES; var offset = 0; var count = 6; gl.drawArrays(primitiveType, offset, count);
完整代码:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" /> <title>WebGL - 2D Rectangle</title> <link type="text/css" href="https://webglfundamentals.org/webgl/resources/webgl-tutorials.css" rel="stylesheet" /> </head> <body> <canvas id="c"></canvas> </body> <script src="https://webglfundamentals.org/webgl/resources/webgl-utils.js"></script> <!-- vertex shader --> <script id="vertex-shader-2d" type="notjs"> attribute vec4 a_position; uniform vec2 u_resolution; void main() { // convert the position from pixels to 0.0 to 1.0 vec2 zeroToOne = a_position.xy / u_resolution; // convert from 0->1 to 0->2 vec2 zeroToTwo = zeroToOne * 2.0; // convert from 0->2 to -1->+1 (clipspace) vec2 clipSpace = zeroToTwo - 1.0; gl_Position = vec4(clipSpace, 0, 1); } </script> <!-- fragment shader --> <script id="fragment-shader-2d" type="notjs"> precision mediump float; void main() { gl_FragColor = vec4(1, 0, 0.5, 1); // return redish-purple } </script> <script> "use strict"; function main() { // Get A WebGL context var canvas = document.querySelector("#c"); var gl = canvas.getContext("webgl"); if (!gl) { return; } // Use our boilerplate utils to compile the shaders and link into a program var program = webglUtils.createProgramFromScripts(gl, [ "vertex-shader-2d", "fragment-shader-2d", ]); // look up where the vertex data needs to go. var positionAttributeLocation = gl.getAttribLocation( program, "a_position" ); // look up uniform locations var resolutionUniformLocation = gl.getUniformLocation( program, "u_resolution" ); // Create a buffer to put three 2d clip space points in var positionBuffer = gl.createBuffer(); // Bind it to ARRAY_BUFFER (think of it as ARRAY_BUFFER = positionBuffer) gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); var positions = [10, 20, 80, 20, 10, 30, 10, 30, 80, 20, 80, 30]; gl.bufferData( gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW ); webglUtils.resizeCanvasToDisplaySize(gl.canvas); // Tell WebGL how to convert from clip space to pixels gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); // Clear the canvas gl.clearColor(0, 0, 0, 0); gl.clear(gl.COLOR_BUFFER_BIT); // Tell it to use our program (pair of shaders) gl.useProgram(program); // Turn on the attribute gl.enableVertexAttribArray(positionAttributeLocation); // Bind the position buffer. gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); // Tell the attribute how to get data out of positionBuffer (ARRAY_BUFFER) var size = 2; // 2 components per iteration var type = gl.FLOAT; // the data is 32bit floats var normalize = false; // don't normalize the data var stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next position var offset = 0; // start at the beginning of the buffer gl.vertexAttribPointer( positionAttributeLocation, size, type, normalize, stride, offset ); // set the resolution gl.uniform2f( resolutionUniformLocation, gl.canvas.width, gl.canvas.height ); // draw var primitiveType = gl.TRIANGLES; var offset = 0; var count = 6; gl.drawArrays(primitiveType, offset, count); } main(); </script> </html>
在本地的效果:
WebGL认为左下角是 0,0 。 想要像传统二维API那样起点在左上角,需要翻转y轴。
gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
效果如下:
如果改成 (-1, 1)
效果如下
随机绘制矩形
比如:随机绘制50个随机位置,随机大小,随机颜色的矩形
定义一个片断着色器:可以通过全局变量接收自定义颜色
<script id="fragment-shader-2d" type="notjs"> precision mediump float; uniform vec4 u_color; void main() { gl_FragColor = u_color; } </script>
代码如下:
var colorUniformLocation = gl.getUniformLocation(program, "u_color"); ... // 绘制50个随机颜色矩形 for (var ii = 0; ii < 50; ++ii) { // 创建一个随机矩形 // 并将写入位置缓冲 // 因为位置缓冲是我们绑定在 // `ARRAY_BUFFER`绑定点上的最后一个缓冲 setRectangle( gl, randomInt(300), randomInt(300), randomInt(300), randomInt(300)); // 设置一个随机颜色 gl.uniform4f(colorUniformLocation, Math.random(), Math.random(), Math.random(), 1); // 绘制矩形 gl.drawArrays(gl.TRIANGLES, 0, 6); } } // 返回 0 到 range 范围内的随机整数 function randomInt(range) { return Math.floor(Math.random() * range); } // 用参数生成矩形顶点并写进缓冲 function setRectangle(gl, x, y, width, height) { var x1 = x; var x2 = x + width; var y1 = y; var y2 = y + height; // 注意: gl.bufferData(gl.ARRAY_BUFFER, ...) 将会影响到 // 当前绑定点`ARRAY_BUFFER`的绑定缓冲 // 目前我们只有一个缓冲,如果我们有多个缓冲 // 我们需要先将所需缓冲绑定到`ARRAY_BUFFER` gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ x1, y1, x2, y1, x1, y2, x1, y2, x2, y1, x2, y2]), gl.STATIC_DRAW); }
完整的代码如下:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes"> <title>WebGL - 2D Rectangles</title> <link type="text/css" href="https://webglfundamentals.org/webgl/resources/webgl-tutorials.css" rel="stylesheet" /> </head> <body> <canvas id="canvas"></canvas> </body> <!-- vertex shader --> <script id="vertex-shader-2d" type="x-shader/x-vertex"> attribute vec2 a_position; uniform vec2 u_resolution; void main() { // convert the rectangle from pixels to 0.0 to 1.0 vec2 zeroToOne = a_position / u_resolution; // convert from 0->1 to 0->2 vec2 zeroToTwo = zeroToOne * 2.0; // convert from 0->2 to -1->+1 (clipspace) vec2 clipSpace = zeroToTwo - 1.0; gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1); } </script> <!-- fragment shader --> <script id="fragment-shader-2d" type="x-shader/x-fragment"> precision mediump float; uniform vec4 u_color; void main() { gl_FragColor = u_color; } </script> <script src="https://webglfundamentals.org/webgl/resources/webgl-utils.js"></script> <script> "use strict"; function main() { // Get A WebGL context /** @type {HTMLCanvasElement} */ var canvas = document.querySelector("#canvas"); var gl = canvas.getContext("webgl"); if (!gl) { return; } // setup GLSL program var program = webglUtils.createProgramFromScripts(gl, ["vertex-shader-2d", "fragment-shader-2d"]); // look up where the vertex data needs to go. var positionAttributeLocation = gl.getAttribLocation(program, "a_position"); // look up uniform locations var resolutionUniformLocation = gl.getUniformLocation(program, "u_resolution"); var colorUniformLocation = gl.getUniformLocation(program, "u_color"); // Create a buffer to put three 2d clip space points in var positionBuffer = gl.createBuffer(); // Bind it to ARRAY_BUFFER (think of it as ARRAY_BUFFER = positionBuffer) gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); webglUtils.resizeCanvasToDisplaySize(gl.canvas); // Tell WebGL how to convert from clip space to pixels gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); // Clear the canvas gl.clearColor(0, 0, 0, 0); gl.clear(gl.COLOR_BUFFER_BIT); // Tell it to use our program (pair of shaders) gl.useProgram(program); // Turn on the attribute gl.enableVertexAttribArray(positionAttributeLocation); // Bind the position buffer. gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); // Tell the attribute how to get data out of positionBuffer (ARRAY_BUFFER) var size = 2; // 2 components per iteration var type = gl.FLOAT; // the data is 32bit floats var normalize = false; // don't normalize the data var stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next position var offset = 0; // start at the beginning of the buffer gl.vertexAttribPointer( positionAttributeLocation, size, type, normalize, stride, offset); // set the resolution gl.uniform2f(resolutionUniformLocation, gl.canvas.width, gl.canvas.height); // draw 50 random rectangles in random colors for (var ii = 0; ii < 50; ++ii) { // Setup a random rectangle // This will write to positionBuffer because // its the last thing we bound on the ARRAY_BUFFER // bind point setRectangle( gl, randomInt(300), randomInt(300), randomInt(300), randomInt(300)); // Set a random color. gl.uniform4f(colorUniformLocation, Math.random(), Math.random(), Math.random(), 1); // Draw the rectangle. var primitiveType = gl.TRIANGLES; var offset = 0; var count = 6; gl.drawArrays(primitiveType, offset, count); } } // Returns a random integer from 0 to range - 1. function randomInt(range) { return Math.floor(Math.random() * range); } // Fill the buffer with the values that define a rectangle. function setRectangle(gl, x, y, width, height) { var x1 = x; var x2 = x + width; var y1 = y; var y2 = y + height; gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ x1, y1, x2, y1, x1, y2, x1, y2, x2, y1, x2, y2, ]), gl.STATIC_DRAW); } main(); </script> </html>
本地效果:每次刷新颜色大小都会变
拓展
type=“notjs” 是什么意思?
<script> 标签内默认放置的是 JavaScript 代码。
不定义 type 或者定义 type="javascript" 或者 type="text/javascript" ,浏览器则会将内容翻译成 JavaScript。
如果你对 type 有其它任何定义。浏览器会忽略 script 标签的内容。
对浏览器而言 type="notjs" 或者 type="foobar" 都是没有意义的。这样就可以很方便的编辑着色器。