Rectangular motion and rectangular portal effect

While reading about particles in Docs, I found this PG. It inspired me and I thought if it’s possible to do the same, but with rectangular shape of emitter. To make it look somewhat like portal of old good Quake.
The first simple solution may look like this.

out.x = BABYLON.Scalar.RandomRange(-5, 5);
out.y = BABYLON.Scalar.RandomRange(-5, 5);

But in this case the particles will spawn on the whole square, but I wanted to make the frame only to emit the particles. For this purpose, I wanted to use the formula of rectangular motion instead of circle motion. But there is a problem. The formula of rectangular motion doesn’t exist. At least not in a simple form. So, I needed to create my own function that will simulate rectangular motion. And I really wanted to make a cyclic continuous function.

Before we start, let’s introduce some variables.

let width = 10; // size of rectangle (for now only squares are supported).
let resolution = 0.05; // step between emission points on the frame. The smaller value the denser frame will be.

You want the ratio width/resolution to be a whole number. If it’s not, the portal will be asymmetric. However, it still will work.

The final computation algorithm looks like that:

let id = 0;
let width = 10;
let resolution = 0.05;

function getNextCoordinate() {
    let pointN = id % (4 * width);
    if (pointN < width) {
        return [pointN - width / 2, width / 2];
    }
    else if (pointN < 2 * width) {
        return [width / 2, 1.5 * width - pointN];
    }
    else if (pointN < 3 * width) {
        return [2.5 * width - pointN, -width / 2];
    }
    else {
        return [-width / 2, -(3.5 * width - pointN)];
    }            
}
customEmitter.particlePositionGenerator = (index, particle, out) => {
    let value = getNextCoordinate();
    out.x = value[0];
    out.y = value[1];
    out.z = 0;
    id += resolution;
}

It doesn’t look overwhelming, but I spent hours with paper and pen drawing rectangles and trying to figure out the formula.
To make things look more pretty you may also want to increase emitRate, activeParticleCount and capacity of particle system. Especially with smaller resolution values.

One more thing. This algorithm literally circles along perimeter step by step. It doesn’t look bad, but the frame looks like dotted line a bit. To make it look more uniform, we can randomize a bit instead of relying on external counter id.

if (randomize) {
    pointN = BABYLON.Scalar.RandomRange(0, 4 * width);
}

Final result: https://playground.babylonjs.com/#GFMBMS#1.

Ways for improvements:

  1. Move randomization out of getNextCoordinate function and accept id as parameter to make things more SOLID and less impure.
  2. Support true rectangles with different width and height.
  3. Make getNextCoordinate as generator function* (just for fun).
  4. Support cubic shape with different width, height and depth (task for high tier predators only :D).

Actually…
Custom rectangle version is here: https://playground.babylonjs.com/#GFMBMS#3
a) You may want the ratio width/resolution and height/resolution to be a whole number. For symmetry.
b) In randomization section you can use smaller range to create a fractured portal. Still works and looks like charm. Try for example 60% of perimeter: pointN = BABYLON.Scalar.RandomRange(0, 0.6*perimeter);.

May not work as good with non-GPU particles. I didn’t test much, honestly. Use powerfull PC. :slight_smile:

5 Likes

Nice, but you are killing my low-end test rig with this (with a 2-3 fps :sweat_smile:). I had to devide the number of particles by 10 to recover a 50fps. On a more recent and reasonable rig (2020 MBP, 16 ram, radeon 4gb) it works at a fairly reasonable fps (yet still below the 60fps).

1 Like

Yeah, it’s all about computation power. On my rig it gives stable 164 FPS, but I have MSI RX 5700XT GAMING X Edition with 8GB VRAM.
I believe, that in real app it’s preferred to use the sequence of pre-rendered images if we need that many particles.

I could stare at it all day :astonished: