Three.js + Luma 实现高效绘制3DGS

之前网上有很多库虽然能绘制,但是效率低下,接下来我们用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>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

搞GIS图形的sky.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值