💦 Another Splat implementation for Threejs

demo: Splats - CodeSandbox

it is based on antimatter15 and has been reworked to integrate with threejs.

  • each splat is a regular object3d and behaves as such
  • it shares data when you use the same splat multiple times
  • you can have multiple splats with semi-correct depth ordering via alphatest
  • it also supports alphahash for realistic ordering

i’ve made the code from a vanilla perspective, it’s a SplatLoader and a ShaderMaterial. you can already pick it up from drei source if you don’t use react+three or wait until this is available in vanilla-drei (today or tomorrow).

updates


a first vanilla prototype sandbox: Threejs basic example (forked) - CodeSandbox

vanilla splat is available officially https://twitter.com/0xca0a/status/1732165252710559917

10 Likes

That one example with physics is just :exploding_head:

1 Like

@Fennec thanks! to do that was a major struggle, making them behave as regular Object3D’s i mean. and now that they are it can be integrated into anything.

__
and some news, a first vanilla sandbox is here: Threejs basic example (forked) - CodeSandbox

@vis_prime and i are toying with it some more and publish the vanilla api, but looks like this so far

const loader = new SplatLoader(renderer)
const [shoeSplat, kittchenSplat] = await Promise.all([
  loader.loadAsync("nike.splat"),
  loader.loadAsync("kitchen-7k.splat")
])

const shoe1 = new Splat(shoeSplat, camera, { alphaTest: 0.1 })
shoe1.scale.setScalar(0.5)
shoe1.position.set(0, 1.6, 2)
scene.add(shoe1)

// This will re-use the same data, only one load, parse, worker & buffer
const shoe2 = new Splat(shoeSplat, camera, { alphaTest: 0.1 })
shoe2.scale.setScalar(0.5)
shoe2.position.set(0, 1.6, -1.5)
shoe2.rotation.set(Math.PI, 0, Math.PI)
scene.add(shoe2)

const kittchen = new Splat(kittchenSplat, camera)
kittchen.position.set(0, 0.25, 0)
scene.add(kittchen)
4 Likes

I know that’s why I was blown away, the fact that it behave like an Object3D open so many possibilities, not only physics.

That’s some quality work! thanks a lot for sharing.

1 Like

Thanks for sharing, i’m really interested in it!

vanilla splat is available officially

4 Likes

For those who might be interested, here is a link to my online SPLAT Viewer using vanilla-drei splat.

It is currently set for loading a single splat either locally or remotely.

The code is available here so you can see how to import it.

It also supports loading of the Luma AI links via its URL option. This will show the foreground only but if you want the background as well then download the html file and modify it yourself.

2 Likes

@drcmda
Thank you very much for this implementation. I’m trying to change the color at some indices when a user does something. How should I update the state? It seems like doing this screws up the rendering, and the splat is rendered as a point cloud:

    const { covAndColorData, covAndColorTexture, gl, bufferTextureWidth } = this.splat;
    const covAndColorData_uint8 = new Uint8Array(covAndColorData.buffer);

    const context = gl.getContext();
    const covAndColorTextureProperties = gl.properties.get(covAndColorTexture);
    context.bindTexture(context.TEXTURE_2D, covAndColorTextureProperties.__webglTexture);

    for (const i of inds) {
        covAndColorData_uint8[i * 16 + 12] = this.selectionColor[0] * 255;
        covAndColorData_uint8[i * 16 + 13] = this.selectionColor[1] * 255;
        covAndColorData_uint8[i * 16 + 14] = this.selectionColor[2] * 255;

        // Calculate the texture coordinates and dimensions of the region to update
        const xoffset = i % bufferTextureWidth;
        const yoffset = Math.floor(i / bufferTextureWidth);
        const width = 1;
        const height = 1;

        context.texSubImage2D(
          context.TEXTURE_2D,
          0,
          xoffset,
          yoffset,
          width,
          height,
          context.RGBA_INTEGER,
          context.UNSIGNED_INT,
          covAndColorData,
          i * 4,
        );
    }

    gl.resetState();

    covAndColorTexture.needsUpdate = true;