之前网上有很多库虽然能绘制,但是效率低下,接下来我们用threejs和luma webgl库来实现在web端进行高效绘制3DGS。以下示例效果是加载了一份3DGS数据,和一个gltf模型。基本能够满帧绘制,效果视频已发到微信公众号。
实现效果
3DGS介绍
3DGS,即三维高斯溅射技术(3D Gaussian Splashing),是一种先进的三维建模和可视化技术。它主要用于计算机图形学、计算机视觉和相关领域,以实现对三维场景的高效和精确表示。
3D 高斯溅射 (3DGS) 的核心技术是通过数百万个微小的、半透明的椭球体(即“高斯溅射”)来创建和渲染 3D 场景。与依赖多边形或复杂神经网络的传统方法不同,3DGS 利用这些溅射点来表现场景。每个溅射点包含其位置、颜色、大小和透明度的信息。当经过组合后,这些溅射点能高度还原场景的真实感。
Luma介绍
Luma WebGL Library 是一个专为通过 Luma 应用捕获的高保真场景设计的 npm 包,提供了 Gaussian Splatting 技术的 WebGL 实现。它的亮点包括 LumaSplatsWebGL 和集成 Three.js 的 LumaSplatsThree,可以轻松地在现有 3D 框架中加入真实的摄影级元素。
技术分析
Luma WebGL 使用了先进的算法来实现互动渲染。其中 LumaSplatsWebGL 提供了一种高效的 WebGL 渲染方案,而 LumaSplatsThree 则将其无缝融合进 Three.js。此库不仅支持背景移除、场景光照、自定义着色器等功能,还能与 Three.js 的雾效、色调映射等特性协同工作,提供前所未有的视觉体验。
技术应用场景
- 背景移除:创建干净的前景展示,去除不必要的背景元素。
- 场景照明:利用 Luma 引擎捕捉的环境信息,照亮您的三维场景,带来身临其境的感受。
- VR 集成:结合 VR 设备,让观众完全沉浸在互动环境中。
- 高性能渲染:针对大规模实例优化,即使在高分辨率下也能保持流畅。
技术特点
- 易用性:简单安装,快速集成到 Three.js 项目中。
- 兼容性:与 Three.js 紧密合作,充分利用框架特性。
- 灵活性:提供定制化功能,如自定义着色器,满足特定需求。
- 高性能:通过禁用 MSAA 和选择性地开启集成,平衡图像质量和性能。
完整使用代码
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0">
<title>3DGS</title>
<style>
body { margin:0; padding:0; overflow:hidden; }
canvas { filter:brightness(1.1) contrast(90%) saturate(1.2); }
</style>
</head>
<body>
<script type="importmap">
{
"imports": {
"three": "https://unpkg.com/three@0.157.0/build/three.module.js",
"three/addons/": "https://unpkg.com/three@0.157.0/examples/jsm/",
"@lumaai/luma-web": "https://unpkg.com/@lumaai/luma-web@0.2.0/dist/library/luma-web.module.js"
}
}
</script>
<script type="module">
import { WebGLRenderer, PerspectiveCamera, Scene, AmbientLight, PointLight, AnimationMixer, Clock } from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
import { LumaSplatsThree } from '@lumaai/luma-web';
import { Color, FogExp2 } from 'three';
let renderer = new WebGLRenderer({ antialias: true });
renderer.domElement.style.position = 'absolute';
renderer.domElement.style.width = '100%';
renderer.domElement.style.height = '100%';
document.body.appendChild(renderer.domElement);
let camera = new PerspectiveCamera(75, 1, 0.1, 1000);
camera.position.z = 4.0;
camera.position.y = 1.2;
camera.position.x = 1.2;
let controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
let scene = new Scene();
scene.fog = new FogExp2(new Color(0xf8f8f8).convertLinearToSRGB(), 0.15);
scene.background = scene.fog.color;
let mixer;
const clock = new Clock();
let splat = new LumaSplatsThree({
source: 'https://lumalabs.ai/capture/286e34aa-ab49-4f60-bb0f-82f38ea2694e'
});
scene.add(splat);
var light = new AmbientLight(0xffffff);
scene.add(light);
var pointLight = new PointLight(0xFFFFFF, 1.5, 2000);
pointLight.position.set(0, 600, 0);
pointLight.castShadow = true;
scene.add(pointLight);
async function loadGLTFModel() {
const loader = new GLTFLoader();
try {
const gltf = await loader.loadAsync('assets/model/model2.glb');
const model = gltf.scene;
model.position.set(-1.5, -1.3, 0.5);
model.scale.set(0.8, 0.8, 0.8);
model.rotation.set(0, Math.PI / 2.2, 0);
scene.add(model);
if (gltf.animations.length > 0) {
mixer = new AnimationMixer(model);
const action = mixer.clipAction(gltf.animations[0]);
action.play();
}
} catch (error) {
console.error('Error loading GLTF model:', error);
}
}
loadGLTFModel();
function frameLoop() {
const delta = clock.getDelta();
if (mixer) {
mixer.update(delta);
}
let canvas = renderer.domElement;
let width = canvas.clientWidth;
let height = canvas.clientHeight;
if (canvas.width !== width || canvas.height !== height) {
camera.aspect = width / height;
camera.updateProjectionMatrix();
renderer.setSize(width, height, false);
}
controls.update();
renderer.render(scene, camera);
}
renderer.setAnimationLoop(frameLoop);
</script>
</body>
</html>