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

仅使用无依赖库的WebGL,如何在场景地面创建无限网格?

纯WebGL实现3D场景无限地面网格

核心思路

不用生成海量顶点,而是通过片元着色器计算实现无限网格:利用相机位置动态偏移网格坐标,只在视野范围内绘制网格线,同时根据透视距离调整线宽,模拟无限延伸的效果。

步骤1:准备全屏四边形(顶点数据)

我们不需要复杂的3D模型,用一个覆盖整个视口的全屏四边形(2个三角形)即可,所有网格逻辑都在片元着色器里处理:

// 全屏四边形顶点数据(裁剪空间坐标)
const quadVertices = 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,
]);

步骤2:编写顶点着色器

顶点着色器的作用是把裁剪空间坐标传递给片元着色器,同时提供坐标转换的基础数据:

attribute vec2 a_position;
varying vec2 v_clipPos;

void main() {
  v_clipPos = a_position;
  gl_Position = vec4(a_position, 0.0, 1.0);
}

步骤3:编写片元着色器(核心逻辑)

这部分是实现无限网格的关键,主要完成坐标转换、网格计算和颜色混合:

precision mediump float;

varying vec2 v_clipPos;
uniform mat4 u_invViewProj; // 视图投影矩阵的逆矩阵
uniform vec3 u_cameraPos;   // 相机世界位置
uniform vec2 u_gridSize;    // 网格尺寸(主网格/次网格)
uniform vec3 u_gridColor;   // 次网格颜色
uniform vec3 u_thickColor;  // 主网格线颜色

void main() {
  // 把裁剪空间坐标转为世界空间坐标
  vec4 worldPos = u_invViewProj * vec4(v_clipPos, 0.0, 1.0);
  worldPos /= worldPos.w;
  // 固定在y=0的地面层,同时根据相机位置偏移网格,实现随动效果
  vec2 gridPos = worldPos.xz - mod(u_cameraPos.xz, u_gridSize.y);
  
  // 计算次网格线的像素距离,根据透视调整线宽
  vec2 gridLine = abs(fract(gridPos / u_gridSize.y) - 0.5) * u_gridSize.y;
  float lineWidth = 0.02 * length(worldPos.xyz - u_cameraPos);
  float line = min(gridLine.x, gridLine.y);
  
  // 计算主网格线
  vec2 thickGridLine = abs(fract(gridPos / u_gridSize.x) - 0.5) * u_gridSize.x;
  float thickLine = min(thickGridLine.x, thickGridLine.y);
  
  // 混合主/次网格颜色
  vec3 color = mix(u_gridColor, u_thickColor, step(thickLine, lineWidth));
  color = mix(vec3(1.0), color, step(line, lineWidth));
  
  // 远处网格渐隐,增强真实感
  float alpha = 1.0 - smoothstep(50.0, 100.0, length(worldPos.xyz - u_cameraPos));
  gl_FragColor = vec4(color, alpha);
}

步骤4:渲染逻辑

每次渲染时更新相机相关参数,绘制全屏四边形即可:

// 初始化时获取uniform变量位置
const invViewProjLoc = gl.getUniformLocation(program, 'u_invViewProj');
const cameraPosLoc = gl.getUniformLocation(program, 'u_cameraPos');
const gridSizeLoc = gl.getUniformLocation(program, 'u_gridSize');
const gridColorLoc = gl.getUniformLocation(program, 'u_gridColor');
const thickColorLoc = gl.getUniformLocation(program, 'u_thickColor');

// 帧渲染函数
function render() {
  // 计算视图投影矩阵的逆矩阵(需自行实现矩阵逆运算,可参考标准3D矩阵公式)
  const viewProj = calculateViewProjectionMatrix(camera);
  const invViewProj = invertMatrix(viewProj);
  
  // 传入uniform参数
  gl.uniformMatrix4fv(invViewProjLoc, false, invViewProj);
  gl.uniform3f(cameraPosLoc, camera.pos.x, camera.pos.y, camera.pos.z);
  gl.uniform2f(gridSizeLoc, 10.0, 1.0); // 主网格10单位,次网格1单位
  gl.uniform3f(gridColorLoc, 0.7, 0.7, 0.7); // 次网格浅灰
  gl.uniform3f(thickColorLoc, 0.5, 0.5, 0.5); // 主网格深灰
  
  // 绘制全屏四边形
  gl.bindBuffer(gl.ARRAY_BUFFER, quadBuffer);
  gl.enableVertexAttribArray(posAttribLoc);
  gl.vertexAttribPointer(posAttribLoc, 2, gl.FLOAT, false, 0, 0);
  gl.drawArrays(gl.TRIANGLES, 0, 6);
}

关键注意事项

  • 矩阵运算:需自行实现视图投影矩阵及其逆矩阵的计算,这是WebGL 3D场景的基础能力。
  • 深度测试:开启gl.enable(gl.DEPTH_TEST),确保网格被场景中其他物体正确遮挡。
  • 性能优化:全屏四边形仅6个顶点,性能开销极低,完全适配无限网格的需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 19:14:52