-
Notifications
You must be signed in to change notification settings - Fork 0
WebGL基础
WebGL可以在你的浏览器里显示迷人的实时3D图形,但是很多人却不知道WebGL实际上是一个光栅化API,并非3D API。
WebGL只关心两个东西:剪切空间坐标和颜色。使用WebGL的程序员的工作就是用这两个东西来提供WebGL。你用两个阴影来实现它:一个顶点阴影提供剪切空间坐标,一个碎片阴影提供颜色。
不管你的canvas是什么尺寸,剪切空间坐标总是从-1到+1。下面是一个以简单方式来显示WebGL的例子:
// Get A WebGL context
var canvas = document.getElementById('canvas');
var gl = canvas.getContext('experimental-webgl');
// setup a GLSL program
var program = createProgramFromScripts(gl, ["2d-vertex-shader", "2d-fragment-shader"]);
gl.useProgram(program);
// look up where the vertex data needs to go.
var positionLocation = gl.getAttribLocation(progam, 'a_position');
// 创建一个缓冲区然后把一个剪切空间矩形(两个三角形)放进去
var buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(
gl.ARRAY_BUFFER,
new Float32Array([
-1.0, -1.0,
1.0, -1.0,
-1.0, 1.0,
-1.0, 1.0,
1.0, -1.0,
1.0, 1.0],
gl.STATIC_DRAW);
gl.enableVertexAttribArray(positionLocation);
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
// draw
gl.drawArrays(gl.TRIANGLES, 0, 6);
下面的代码片段是上面提到的两个阴影:
<script id="2d-vertex-shader" type="x-shader/x-vertex">
attribute vec2 a_position;
void main() {
gl_Position = vec4(a_position, 0, 1);
}
</script>
<script id="2d-fragment-shader" type="x-shader/x-fragment">
void main() {
gl_FragColor = vec4(0, 1, 0, 1); // green
}
</script>
上面的代码就会在整个canvas区域里画一个绿色的矩形。
再强调一遍,剪切空间坐标总是从-1到1,它和canvas的尺寸没有关系。上面的例子我们除了把位置数据传了进去外什么也没做,你可能想用像素而不是剪切空间来绘制2D图形,下面是另一种顶点阴影:
<script id="2d-vertex-shader" type="x-shader/x-vertex">
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>
现在我们可以把我们的数据从剪切空间变成像素:
// set the resolution
var resolutionLocation = gl.getUniformLocation(program, "u_resolution");
gl.uniform2f(resolutionLocation, canvas.width, canvas.height);
// 把矩形的大小设置为10,20到80,30像素
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
10, 20,
80, 20,
10, 30,
10, 30,
80, 20,
80, 30]), gl.STATIC_DRAW);
你可能会注意到矩形被绘制在了canvas区域的底部,WebGL把左下角作为坐标0,0。为了让它符合常见的以左上角为原点的2D绘图坐标,我们可以这样翻转坐标系:
gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
现在,让我们把定义矩形的代码放到一个函数中,这样我们就可以调用它来绘制各种大小的矩形。同样我们也可以把颜色参数化。我们需要一个定义了颜色输入的碎片阴影:
<script id="2d-fragment-shader" type="x-shader/x-fragment">
precision medium float;
uniform vec4 u_color;
void main() {
gl_FragColor = u_color;
}
</script>
下面的代码会用随机的颜色在随机的位置画50个矩形:
var colorLocation = gl.getUniformLocation(program, 'u_color');
...
// create a buffer
var buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.enableVertexAttribArray(positionLocation);
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
// 用随机颜色绘制50个矩形
for (var ii = 0; ii < 50; ++ii) {
// 设置一个随机的矩形
setRectangle(gl, randomInt(300), randomInt(300), randomInt(300), randomInt(300));
// 设置一个随机颜色
gl.uniform4f(colorLocation, Math.random(), Math.random(), Math.random(), 1);
// 绘制矩形
gl.drawArrays(gl.TRANGLES, 0, 6);
}
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, new Float32Array([
x1, y1,
x2, y1,
x1, y2,
x1, y2,
x2, y1,
x2, y2
], gl.STATIC_DRAW);
}
通过上面的例子,希望你能看出,WebGL
实际上是一个相当简单的API。虽然它可以通过加入更复杂的阴影来呈现出3D效果,但是WebGL
本身是相当简单的2D API
。
如果你在WebGL
方面还是新手,并且不知道GLSL是什么东西,你需要了解一些WebGL工作的原理。
type="x-shader/x-vertex"
和type="x-shader/x-fragment"
是用来做什么的呢?对于<script>
标签中的内容,如果没有指定type属性,或者type="javascript"
或是type="text/javascript"
,浏览器默认会按照javascript来进行解析,如果type被指定为其它值,浏览器不会对之进行解析。
我们可以用这个特性来把阴影放在标签中,还可以通过type的值来决定是把阴影编译成顶点阴影还是碎片阴影。
上面例子中的createProgramFromScripts
就会用指定的id查找脚本,并根据标签的type来决定要创建什么样的阴影。