
Demo: tympanus.net/Tutorials/F...
code: github.com/akella/fake...
WebGL 如今变得越来越流行,它让我们能够为网页创建独特的交互式动画。你可能见过使用 Blotter.js 制作的文字扭曲效果,或者用 THREE.MeshLine 创建的动画线条。今天你将学习如何使用原生 WebGL 创建一个交互式的"伪"3D 效果。
如果你使用一些社交软件,可能见过 3D 照片。3D 照片让场景栩栩如生。我们可以用任意照片和一点点代码来重现这种效果。
通常,这类效果会依赖于 Three.js 或 Pixi.js,这些强大的库在编码时提供了许多有用的功能。今天我们不使用任何库,而是直接使用原生 WebGL API。
那么让我们开始吧。
入门准备
对于这个效果,我们将使用原生 WebGL API。
一个帮助你快速入门 WebGL 的网站: webglfundamentals.org。
WebGL 常因其冗长而被诟病,这是有原因的。所有全屏着色器效果(即使是 2D 的)的基础都是某种平面或网格,即所谓的"四边形"(quad),它被拉伸到整个屏幕上。说到冗长,在 three.js 中我们只需写 THREE.PlaneGeometry(1,1)
就能创建一个 1×1 的平面,而在原生 WebGL 中我们需要这样做:
js
let vertices = new Float32Array([
-1, -1,
1, -1,
-1, 1,
1, 1,
])
let buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
现在我们有了平面,就可以对使用顶点着色器和片段着色器了。
准备图像
为了让这个效果生效,我们需要为图像创建一个深度图(depth map) 。构建深度图的主要原则是:我们必须根据图像中物体的 Z 位置(即远近)来分离图像的不同部分,从而将前景与背景区分开。
为此,我们可以在 Photoshop 中打开图像,并按照以下方式在原始照片上绘制灰色区域:

(图像展示了一座山,你可以看到,物体离相机越近,深度图中的区域就越亮。)
下一节我们将解释为什么这种着色方式是有道理的。
着色器(Shaders)
渲染逻辑主要在着色器中完成。正如 MDN Web 文档中所述:
着色器是用 OpenGL ES 着色语言(GLSL)编写的程序,它接收构成形状的顶点的信息,并生成将像素渲染到屏幕上所需的数据:即像素的位置和颜色。绘制 WebGL 内容时会运行两个着色器函数:顶点着色器和片段着色器。
一个学习着色器的资源: The Book of Shaders。
顶点着色器不会做太多事情,它只是显示顶点:
glsl
attribute vec2 position;
void main() {
gl_Position = vec4(position, 0, 1);
}
最有趣的部分将发生在片段着色器中。我们在其中加载两张图像:
glsl
void main() {
vec4 depth = texture2D(depthImage, uv);
gl_FragColor = texture2D(originalImage, uv); // 仅显示原始照片
}
记住,深度图是黑白的。对着色器来说,颜色只是一个数字:1 是白色,0 是纯黑。uv
变量是一个二维坐标,用于存储要显示的像素信息。利用这两样东西,我们可以使用深度信息来稍微移动原始照片的像素。
让我们从鼠标移动开始:
glsl
vec4 depth = texture2D(depthImage, uv);
gl_FragColor = texture2D(originalImage, uv + mouse);
看起来是这样的:

现在让我们加入深度:
glsl
vec4 depth = texture2D(depthImage, uv);
gl_FragColor = texture2D(originalImage, uv + mouse * depth.r);
效果如下:

由于纹理是黑白的,我们可以只取红色通道(depth.r
),并将其乘以鼠标在屏幕上的位置值。这意味着,像素越亮,它跟随鼠标移动的距离就越大;而暗的像素则几乎不动。就这么简单,却能产生如此漂亮的 3D 幻觉。
参考资料与致谢
- Gyronorm 库 by Doruk Eker
- 照片来源:Cosmic Timetraveler(Unsplash)
- 照片来源:Chelsea Ferenando(Unsplash)
- 照片来源:Rio Syhputra(Unsplash)
- 照片来源:Jonatan Pie(Unsplash)