为了使画布的像素数和显示大小匹配, 我这里使用了一个辅助方法,你可以在这里获取更多相关信息。
这里的例子中,有独立窗口显示的示例大多使用400×300像素大小的画布。 但是如果像稍后展示的示例那样嵌在页面中,它就会被拉伸以填满可用空间 (你也可以点击示例下方的“点此在新窗口中浏览”在独立窗口中查看示例)。 通过使用CSS调整画布尺寸可以轻松处理这些情况。
点击(此处)折叠或打开
- webglUtils.resizeCanvasToDisplaySize(gl.canvas)
点击(此处)折叠或打开
- gl.viewport(0, 0, gl.canvas.width, gl.canvas.height)
我们用0, 0, 0, 0清空画布,分别对应 r, g, b, alpha (红,绿,蓝,阿尔法)值, 所以在这个例子中我们让画布变透明了。
点击(此处)折叠或打开
-
// 清空画布
-
gl.clearColor(0, 0, 0, 0);
- gl.clear(gl.COLOR_BUFFER_BIT)
点击(此处)折叠或打开
-
// 告诉它用我们之前写好的着色程序(一个着色器对)
- gl.useProgram(program)
点击(此处)折叠或打开
- 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(positionAttributeLocation, size, type, normalize, stride, offset)
从GLSL的顶点着色器中注意到a_position属性的数据类型是vec4:
点击(此处)折叠或打开
- attribute vec4 a_position
我们终于可以让WebGL运行我们的GLSL着色程序了。
点击(此处)折叠或打开
-
var primitiveType = gl.TRIANGLES;
-
var offset = 0;
-
var count = 3;
- gl.drawArrays(primitiveType, offset, count)
因为我们设置primitiveType(图元类型)为 gl.TRIANGLES(三角形), 顶点着色器每运行三次WebGL将会根据三个gl_Position值绘制一个三角形, 不论我们的画布大小是多少,在裁剪空间中每个方向的坐标范围都是 -1 到 1 。
由于我们的顶点着色器仅仅是传递位置缓冲中的值给gl_Position, 所以三角形在裁剪空间中的坐标如下:
点击(此处)折叠或打开
-
0, 0,
-
0, 0.5,
- 0.7, 0,
点击(此处)折叠或打开
-
裁剪空间 屏幕空间
-
0, 0 -> 200, 150
-
0, 0.5 -> 200, 225
- 0.7, 0 -> 340, 150
这里有一个在线示例:
点击(此处)折叠或打开
-
"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.getElementById("c");
-
var gl = canvas.getContext("webgl");
-
if (!gl) {
-
return;
-
}
-
-
// Get the strings for our GLSL shaders
-
var vertexShaderSource = document.getElementById("2d-vertex-shader").text;
-
var fragmentShaderSource = document.getElementById("2d-fragment-shader").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
你可能会好奇为什么这个三角形从中间开始然后朝向右上方。裁剪空间的x坐标范围是 -1 到 +1. 这就意味着0在中间并且正值在它右边。
至于它为什么在上方,是因为裁剪空间中 -1 是最底端 +1 是最顶端, 这也意味值0在中间,正值在上方。
对于描述二维空间中的物体,比起裁剪空间坐标你可能更希望使用屏幕像素坐标。 所以我们来改造一下顶点着色器,让我们提供给它像素坐标而不是裁剪空间坐标。 这是我们新的顶点着色器。
点击(此处)折叠或打开
-
<script id="2d-vertex-shader" type="notjs">
-
-
//attribute vec4 a_position;
-
attribute vec2 a_position;
-
-
uniform vec2 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>
接着我们添加了一个uniform(全局变量)叫做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.useProgram(program);
-
-
...
-
-
// 设置全局变量 分辨率
- gl.uniform2f(resolutionUniformLocation, gl.canvas.width, gl.canvas.height)
点击(此处)折叠或打开
-
// 绘制
-
var primitiveType = gl.TRIANGLES;
-
var offset = 0;
-
var count = 6;
- gl.drawArrays(primitiveType, offset, count)
注意: 这个和以后的例子都将使用webgl-utils.js, 它包含了编译和链接着色器的方法。没有必要让一些样板代码干扰示例代码。
点击(此处)折叠或打开
-
"use strict";
-
-
function main() {
-
// Get A WebGL context
-
var canvas = document.getElementById("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, ["2d-vertex-shader", "2d-fragment-shader"]);
-
-
// 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
点击(此处)折叠或打开
- gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1)
接下来让我们来定义一个可以生成矩形的方法,这样我们就可以调用它定义形状不一的多个矩形。 同时我们需要矩形的颜色是可设置的。
首先我们定义一个片断着色器,可以通过全局变量接收自定义颜色。
点击(此处)折叠或打开
-
<script id="2d-fragment-shader" 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);
- }
点击(此处)折叠或打开
-
"use strict";
-
-
function main() {
-
// Get A WebGL context
-
/** @type {HTMLCanvasElement} */
-
var canvas = document.getElementById("canvas");
-
var gl = canvas.getContext("webgl");
-
if (!gl) {
-
return;
-
}
-
-
// setup GLSL program
-
var program = webglUtils.createProgramFromScripts(gl, ["2d-vertex-shader", "2d-fragment-shader"]);
-
-
// 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