How to create shattering glass effect?

I want to create a glass (like a window), that potentially can be broken. I think I can create a glass looking mesh playing with alpha and refraction properties. I remember I saw something like that in docs, but I will not mind if you will point me to article or playground here :slight_smile:

But the main question is how to create a glass shattering effect? I was thinking that probably we can separate mesh into facets and apply some force to them, however I am not sure how to start and if it is a right way to do it?

Here is the example - Babylon.js Playground (click on torus knot).
Docs - Solid Particles From Mesh Facets | Babylon.js Documentation

2 Likes

Mesh shattering with baked physics | by Babylon.js | Medium is awesome :slight_smile:

4 Likes

I’m remembering an SPS demo where the billboard shatters. Can’t find it though. Maybe will ring someones bell? (oh maybe I’m thinking of that torus knot demo above, hm).

i did something vaguely similar a few years ago with SPS with a couple of physics simulation functions: https://twitter.com/benrigby/status/1168936834547814400. can dig up the code for it if that’s helpful to you.

1 Like

Something like this may be, is it?

My first demo: https://playground.babylonjs.com/#F9IC4Y.
Key points:

  1. This line increases the number of facets glass.increaseVertices(8);. It’s necessary because the regular box has only 12 facets which is not enough for the effect.
  2. This method does a lot of important things: this.attachPhysics(w, h, d);. It creates an additional invisible hitbox around the glass if the glass is too thin on some sides. It might be necessary for collision detection with fast moving objects like bullets. PhysicsImpostor is added to the glass or bounding box to detect physical collisions. Also, the glass becomes solid in this method (camera can not fly through). All of that might not be necessary for this demo, where you start the process by clicking on glass, but in real life examples you may want these features.
  3. In the playground the deterministicLockstep doesn’t work for me as smoothly as in real applications. So, you may want to play with constants and particleLifeTime depending on performance of your system.

The class is self-sufficient, all you need is dimensions and position of your glass. The class will set up all necessary physics, properties and event subscriptions that are required for work. It also cleans up everything (including subscriptions), when the work is done. It is also serialization friendly (ish), you just need to save name, w, h, d, floor level, position, rotation, quaternion and sps.vars.destroyed flag. So, this is how you can detect if glass was destroyed and it needs to be saved/loaded.

Despite the fact that the class can be easily integrated into any app and logically it seems fine, I don’t really like how the glass looks and acts itself (in terms of art). I tried to add a refraction plane, but it doesn’t work great with box. Also, the current approach seems expensive (in terms of computations).
I want to try to work with planes instead of boxes. Maybe it will look better.
Another approach is to just dispose the glass on collision and shoot the regular 2D (non-solid) particles. However, I doubt my artistic skills regarding the drawing shattering glass effect using particles. I mean, I know the API, but how to use it to create something that will look like flying glass fragments?

5 Likes

GJ. :+1: Looks live you have pretty much every parameter in there. Thanks a lot for sharing. :smiling_face_with_three_hearts: I’m gonna bookmark this base in case I need something similar.

Tested just two minutes changing a couple of values and I think it’s a real good base. Very ez to handle, twist and tweak to your likin.

Can’t be the best at everything, can you? This part would be ez for me to do but I’m sure this part of code can be of help for many of us.

Nah, I like it using the SPS. There aren’t as many example using the SPS and the particles will create other types of issues. It’s a completely different approach and handling (in my opinion).

1 Like

I tried to experiment with refraction planes and it didn’t go well. First, refraction planes don’t love boxes (especially those that have alpha). I also tried to attach the refraction plane to the regular plane, but the refraction plane appeared only from one side, from other side it was a solid white wall. Also, I don’t think that refraction planes work correctly with rectangular shapes. Probably it was designed for circle shape lenses. And the worst thing is that you have to pass an array of meshes that you want to be affected by refraction. It’s all doesn’t seem like a computation and complexity cheap procedure.

So, I continued with my initial approach. I found out that using planes instead of boxes can improve visuals a bit and reduce the computation load, because of a smaller number of facets.
Here is a playground: https://playground.babylonjs.com/#GV25YZ

The idea is similar to my previous example. But I significantly improved the class.

  1. Now, because we are using planes, we will always need to create a bounding box. It made class a bit simpler. Also, I exposed position, rotation, and rotationQuaternion as setters/getters. That made the solution more robust. Now there is significantly less chance that SPS mesh and bounding box will go out of sync. You can even move them dynamically now.
  2. I also added color property to constructor (in Color4 format aka RGBA). My getter and setter will handle its components properly. So, now you can create glass with different colors and transparency.
  3. You don’t need { isPickable: true } for your SPS for body-to-body collisions. I added it only to support pointerDown events in this demo. But it turned out that even this demo works without that setting.
  4. Depth argument has been removed. Width and height are own properties of the class now which makes it more serialisation friendly. Speaking of serialization, the basic idea of saves and loads for glass will be something like that:
// save
if (levelDependencies.glass) {
    serializationObject.glass = [];
    for (let g = 0; g < levelDependencies.glass.length; g++) {
        const glass = levelDependencies.glass[g];
        if (!glass.isDestroyed) {
            serializationObject.glass.push({
                name: glass.name,
                w: glass.w,
                h: glass.h,
                floorPositionY: glass.floorPositionY,
                color: glass.color.asArray(),
                position: glass.position.asArray(),
                rotation: glass.rotation ? glass.rotation.asArray() : null,
                rotationQuaternion: glass.rotationQuaternion ? glass.rotationQuaternion.asArray() : null,
            });
        }
    }
}

// load
for (let g = 0; g < saveFile.glass.length; g++) {
    const savedGlass = saveFile.glass[g];
    const position = BABYLON.Vector3.FromArray(savedGlass.position);
    const rotation = savedGlass.rotation ? BABYLON.Vector3.FromArray(savedGlass.rotation) : null;
    const rotationQuaternion = savedGlass.rotationQuaternion ? BABYLON.Quaternion.FromArray(savedGlass.rotationQuaternion) : null;
    this.dependencies.glass.push(
        new Glass(
            savedGlass.name,
            savedGlass.w,
            savedGlass.h,
            savedGlass.floorPositionY,
            BABYLON.Color4.FromArray(savedGlass.color),
            position,
            rotation,
            rotationQuaternion,
            scene
        )
    );
}

As of now, I think this solution fits my needs, but I still will be glad to see any other finished solutions.

Homework:
You can add a new mesh argument that will accept any mesh. In this case you can skip the plane creation and use a given mesh instead. This is how you can create glass sculptures of any shape and color, that you can also break/shatter. You still may need w and h parameters for bounding box creation though, if your sculpture is too thin.

I wonder if there’s some related technique here that could allow a mesh to “take damage”? What do you think?

I developed the one, but I didn’t include it here, because it’s a separate topic, which will take a lot of space.