You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在WebGL中实现类似Sketchfab的全屏背景图(非CSS方式)

哈哈,这个问题我刚好折腾过!其实不用把背景放在zfar位置那么麻烦,直接在屏幕空间画个全屏四边形,再处理纹理的适配就能实现和CSS background-size: cover一模一样的效果,和Sketchfab的思路差不多。给你一步步拆解:

核心思路:屏幕空间全屏四边形 + 纹理适配

不用纠结相机的zfar计算,背景本来就是贴在屏幕上的,直接用**标准化设备坐标(NDC)**画一个铺满屏幕的四边形,再通过着色器计算纹理的缩放比例,实现居中裁剪的cover效果。

步骤1:创建全屏四边形

直接用NDC坐标(范围是(-1,-1)到(1,1))定义四边形的顶点,这样不需要MVP矩阵,顶点直接输出到屏幕就能铺满整个画布。顶点数据要包含位置和UV坐标:

// 顶点数据:[x, y, u, v]
const fullscreenQuadVertices = new Float32Array([
  -1.0, -1.0,  0.0, 0.0, // 左下
   1.0, -1.0,  1.0, 0.0, // 右下
  -1.0,  1.0,  0.0, 1.0, // 左上
   1.0,  1.0,  1.0, 1.0  // 右上
]);

把这些数据传入WebGL缓冲区,设置好顶点属性(位置和UV)即可。

步骤2:实现Cover纹理适配(关键!)

在片元着色器里计算纹理和屏幕的宽高比,调整UV坐标让纹理居中铺满屏幕,超出部分自动裁剪:

片元着色器代码

precision mediump float;
varying vec2 v_uv; // 从顶点着色器传入的原始UV(0-1范围)
uniform sampler2D u_bgTexture;
uniform vec2 u_textureSize; // 纹理的宽高
uniform vec2 u_screenSize;  // 画布的宽高

void main() {
  // 计算纹理和屏幕的宽高比
  float texRatio = u_textureSize.x / u_textureSize.y;
  float screenRatio = u_screenSize.x / u_screenSize.y;

  // 计算缩放因子:让纹理的短边适配屏幕,长边超出
  vec2 scale = vec2(1.0);
  if (texRatio > screenRatio) {
    // 纹理比屏幕宽 → 按屏幕高度缩放,宽度超出裁剪
    scale.x = texRatio / screenRatio;
  } else {
    // 屏幕比纹理宽 → 按屏幕宽度缩放,高度超出裁剪
    scale.y = screenRatio / texRatio;
  }

  // 调整UV坐标,让纹理居中
  vec2 adjustedUv = (v_uv - 0.5) * scale + 0.5;

  // 采样纹理输出颜色
  gl_FragColor = texture2D(u_bgTexture, adjustedUv);
}

顶点着色器代码

attribute vec2 a_position;
attribute vec2 a_uv;
varying vec2 v_uv;

void main() {
  v_uv = a_uv;
  gl_Position = vec4(a_position, 0.0, 1.0); // 直接输出NDC坐标,w=1确保在屏幕上
}

步骤3:绘制顺序与深度处理

  • 先绘制背景:绘制前关闭深度测试(gl.disable(gl.DEPTH_TEST)),这样背景会直接覆盖整个屏幕,不会被后续的3D模型遮挡。
  • 绘制完背景后,再开启深度测试(gl.enable(gl.DEPTH_TEST)),绘制你的3D模型即可。

绘制流程示例

// 绘制背景
gl.disable(gl.DEPTH_TEST);
// 设置uniform:纹理尺寸、屏幕尺寸
gl.uniform2f(textureSizeLoc, bgTexture.width, bgTexture.height);
gl.uniform2f(screenSizeLoc, canvas.width, canvas.height);
// 绑定背景纹理
gl.bindTexture(gl.TEXTURE_2D, bgTexture);
// 绘制全屏四边形(用TRIANGLE_STRIP只需要4个顶点)
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);

// 绘制3D模型
gl.enable(gl.DEPTH_TEST);
// ... 你的3D模型绘制代码 ...

为什么不用zfar位置的平面?

用相机zfar位置的平面需要计算平面尺寸(比如width = 2 * zfar * tan(fov/2),height = width / aspectRatio),但这种方法需要绑定相机的MVP矩阵,而且本质上还是在3D空间绘制,不如直接在屏幕空间画四边形高效——毕竟背景不需要和3D场景有空间交互,直接贴屏才是最优解。

这样实现出来的效果就和Sketchfab的背景完全一致,完美复刻background-size: cover的行为,而且纯WebGL实现,不需要依赖CSS。

内容的提问来源于stack exchange,提问作者user2331095

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 07:28:15