huggingface / gsplat.js

JavaScript Gaussian Splatting library.
MIT License
1.26k stars 77 forks source link

Model Inverstion and Distortion during Rotation (on Firefox browsers) #54

Closed TravisThomp closed 3 months ago

TravisThomp commented 5 months ago

I've been having this issue when applying a rotation to an object, It works fine on smaller scenes, but once the scene consists of a large amount of gaussians. The scene begins to distort, inverse rotation at times and clip on itself, and consumes all of the ram available (tldr its a shit show lol).

Example Video

https://github.com/huggingface/gsplat.js/assets/50000930/8b2b89bd-c758-4f27-be0c-4fb9428c44e7

Ram Usage Video

https://github.com/huggingface/gsplat.js/assets/50000930/cf96640c-79ec-4211-b495-d1b1ced06917

This only occurs on firefox browsers (Tested on Version 122.0)

Notes

Current Theory

There is an overflow that occurs in the translation of the gaussians since this only occurs when the scene is large. I'm assuming that the other browsers(chrome, edge) are cleaning this up automatically causing it to work fine with them, but firefox doesn't do this?

Code to recreate (pretty much any scene with a lot of gaussians rotating)

<script lang="ts">
    import { onMount } from "svelte";
    import * as SPLAT from "gsplat";
    import { Splat, Camera, Scene } from "gsplat";

    function loadModel(scene: Scene): Promise<Splat> {
        let modelURL =
            "https://huggingface.co/datasets/dylanebert/3dgs/resolve/main/bonsai/bonsai-7k.splat";

        let model = SPLAT.Loader.LoadAsync(modelURL, scene, () => {});

        return model;
    }

    function updateModelRotation(model: Splat, tick: number) {
        const autoRotateMultipler = 0.025;

        // ERROR CAUSED BY THIS LINE(atleast it doesnt show when commented out)
        model.rotation = SPLAT.Quaternion.FromEuler(
            new SPLAT.Vector3(0, tick * autoRotateMultipler, 0),
        );
    }

    onMount(async () => {
        const canvas = document.getElementById("canvas") as HTMLCanvasElement;

        const renderer = new SPLAT.WebGLRenderer(canvas, null);
        canvas.style.background = "black";

        const scene = new SPLAT.Scene();
        const camera = new SPLAT.Camera();
        camera.rotation = SPLAT.Quaternion.FromEuler(
            new SPLAT.Vector3(0, 0, 0),
        );

        let model = await loadModel(scene);

        model.scale = new SPLAT.Vector3(2, 2, 2);

        let tick: number = 0;
        const frame = () => {
            updateModelRotation(model, tick);

            tick += 1;
            renderer.render(scene, camera);
            requestAnimationFrame(frame);
        };

        const handleResize = () => {
            renderer.setSize(canvas.clientWidth, canvas.clientHeight);
        };
        requestAnimationFrame(frame);

        window.addEventListener("resize", handleResize);
    });
</script>

<canvas id="canvas"> </canvas>

<style>
    canvas {
        width: 100vw;
        height: 100vh;
    }
</style>
mateusz-malicki commented 5 months ago

@TravisThomp after setting model.rotation you should invoke model.applyRotation(). Also, there is no point in increasing your tick - just set how much you want to rotate (but this value should be scaled to time from last render).

        var rotation = SPLAT.Quaternion.FromEuler(new SPLAT.Vector3(0, 0.1, 0));
    const frame = () => {
        model.rotation = rotation;
        model.applyRotation();
        renderer.render(scene, camera);
        requestAnimationFrame(frame);
    };

btw, I think that moving camera instead of rotating splat is better idea.

TravisThomp commented 4 months ago

yeah that is the cause, was trying to get around it bc it's extremely slow. Will work on making it more efficent

mateusz-malicki commented 4 months ago

@TravisThomp The problem is that after second look it should work without apply* call. It looks like glitches are caused by sortrer loop lagging behind rendering loop. Calling ApplyRotation slows down rendering loop, so thread with sorter has "more" time to do its job. And there is definitely a leak or at least accumulation of "timeouted" closures.

TravisThomp commented 4 months ago

Woah! fascinating, I manually added the latency that applyRotation adds and the issue and the leak goes away. This is definitely seems like the cause.

TravisThomp commented 4 months ago

Do you think that Applying the Parallel Radix sort in #11 would fix this issue?

mateusz-malicki commented 4 months ago

@TravisThomp Can You take a look a this demo? It is targeting firefox specifically. Its automatically rotating garden scene :) Should work quite smoothly, but my pc sucks, so im not sure. For chrome based browsers this one should behave...

TravisThomp commented 4 months ago

@mateusz-malicki

mateusz-malicki commented 4 months ago

@TravisThomp Thanks! Thats interesting, cuz on my machine rendering via offscreencanvas from worker (chrome version) sucks really bad on firefox...

TravisThomp commented 4 months ago

@mateusz-malicki I forgot to ask yesterday, are you working on a fix for the concurrency issue? I was gonna spend time on this today, if so I'll look into something else :p

mateusz-malicki commented 4 months ago

@TravisThomp Im on it already ;) But what i have now is rather workaround, not proper solution :/ And... for larger models there is issue with writing DepthIndex to GL buffer. For Garden-7k it can take up to 90ms on my machine (vertex count is around 4.5M if i remember correctly) Its price we have to pay for sorting on CPU i think...

TravisThomp commented 3 months ago

Any update on this? If not I'm gonna take a crack at it.

985658143 commented 1 week ago

@TravisThomp after setting model.rotation you should invoke model.applyRotation(). Also, there is no point in increasing your tick - just set how much you want to rotate (but this value should be scaled to time from last render).

        var rotation = SPLAT.Quaternion.FromEuler(new SPLAT.Vector3(0, 0.1, 0));
  const frame = () => {
      model.rotation = rotation;
      model.applyRotation();
      renderer.render(scene, camera);
      requestAnimationFrame(frame);
  };

btw, I think that moving camera instead of rotating splat is better idea.

But how to moving camera around the center, and how to reset the position of the splat?