WebGL通过gl.viewport和gl.scissor组合实现多视口渲染与像素级裁剪:先用gl.viewport划分区域并配合scissor测试精确限制绘制范围,需显式管理状态、坐标转换及清除逻辑,Three.js封装了对应方法提升开发效率。

WebGL 本身不直接提供“多视口”或“视口裁剪”的高级抽象,但通过 gl.viewport 和 gl.scissor 配合状态管理,完全可以实现精确的多区域渲染和裁剪效果。关键不是调用某个现成函数,而是控制渲染上下文的状态切换与区域限制。
用 gl.viewport 切分多个渲染区域
每个视口对应一块独立的 canvas 区域,通过反复设置 gl.viewport(x, y, width, height) 并清屏、绘制,就能在同一个 canvas 上叠加多个 3D 视图:
- 每次调用
gl.viewport()前,必须确保已绑定正确的 framebuffer(默认是 canvas 的 back buffer) - 不同视口之间要手动清除各自区域:
gl.enable(gl.SCISSOR_TEST)+gl.scissor()+gl.clear(),否则会污染相邻区域 - 注意坐标系:y 轴原点在 canvas 底部,
gl.viewport(0, 0, w, h)是左下角起始,不是 CSS 的左上角 - 示例:左半屏渲染主视角,右半屏渲染俯视图,只需两次 viewport 设置 + 两套 camera 和 scene 渲染逻辑
用 gl.scissor 实现像素级裁剪
gl.scissor 不改变投影,只限制像素写入范围,常用于 UI 遮罩、局部更新或嵌入式 3D 元素(如 React-Three-Fiber 中的 <View> 组件):
- 启用前必须调用
gl.enable(gl.SCISSOR_TEST),绘制完记得gl.disable(gl.SCISSOR_TEST) -
gl.scissor(x, y, width, height)的y同样是底部坐标,若需匹配 DOM 位置,需做canvas.height - y - height转换 - 它和
gl.viewport可组合使用:viewport 定义逻辑分辨率,scissor 定义实际绘制像素边界 - React-Three-Next 的
View组件正是靠这套机制,把多个<View>映射为互不重叠的 scissor 区域
避免常见状态污染问题
多视口渲染极易因状态残留导致画面错乱,核心在于显式管理 WebGL 上下文状态:
立即学习“Java免费学习笔记(深入)”;
- 每次切换视口前,重置关键状态:
gl.colorMask(true,true,true,true)、gl.depthMask(true)、gl.blendFunc()等 - 不要依赖“之前没动过”的假设——WebGL 是状态机,任何第三方库(如 Three.js)都可能修改深度测试或混合模式
- 推荐封装一个
renderToViewport(x, y, w, h, renderFn)工具函数,自动处理 viewport/scissor/enable/disable/clear 流程 - 调试时可临时禁用 scissor,观察是否真被裁剪——若画面仍错位,问题大概率出在矩阵或 camera 设置
结合 Three.js 使用更简洁
原生 WebGL 写法繁琐,Three.js 提供了更高层支持:
-
renderer.setViewport(x, y, width, height)和renderer.setScissor(x, y, width, height)自动转换坐标并管理状态 -
renderer.setScissorTest(true)控制开关,配合render(scene, camera)即可局部渲染 - 多个
WebGLRenderer实例虽可行,但开销大;单 renderer + 多 viewport 更高效 - 注意:Three.js 默认不清除 scissor 区域外像素,需手动
renderer.clearColor = new THREE.Color(0x000000); renderer.clear();控制


















