3

I know that "how to optimize this code?" kind of question is generally not welcomed in stack overflow. But I think this is only the way i could phrase my question. I wrote a code that projects a 4 dimensional points onto a 3 dimensional space. I then draw the 3d points using the p5.js library.

Here is my code: https://jsfiddle.net/dfq8ykLw/

Now my question here is, how am I supposed to make this run faster, and optimize the code? Since I am supposed to draw few thousand points (sometimes more) per frame, and calculate the rotation for each of those points, my code tend to run incredibly slowly (mac devices can run this a little faster for some reason).

I tried drawing points instead of vertices, which ended up running even slower.

Are there any suggestion of how to improve the performance? Or an advice of what kind of library to use for drawing 3 dimensional shapes?

Just to explain, my program stores the points as nested array, just like [[Point object, Point object, ...], ...]. Each array in the data works as a face, with each Point object being the vertices. I first rotate each of these points by applying 6 rotations (for axis xy, xz, xw, yz, yw and zw), then draw them by projecting them onto the 3d space.

Any help is appreciated, as I am terribly stuck!

Kenta Nomoto
  • 839
  • 6
  • 11

2 Answers2

2

in source code

Begin shape drawing. However in WEBGL mode, application performance will likely drop as a result of too many calls to beginShape() / endShape(). As a high performance alternative, ... _main.default.RendererGL.prototype.beginShape

So we may want to avoid too many beginShape calls. Idem call it on cube instead of face

beginShape()
data.forEach((hyperobject, i) => {
    // face
    for (var p in hyperobject){
        hyperobject[p].rotate(angles[0], angles[1], angles[2], angles[3], angles[4], angles[5])
        hyperobject[p].draw()
    }
    if (i % 6 === 0) {
        endShape(CLOSE);
        beginShape()
    }
})
endShape()

However there are some ugly drawn line, because default mode is TRIANGLE_FAN

_main.default.RendererGL.prototype.beginShape = function(mode) { this.immediateMode.shapeMode = mode !== undefined ? mode : constants.TRIANGLE_FAN;

So we may specify TRIANGLES instead:


function draw(){
    //noLoop()
    background(0);
    // translate(250, 250);
    for (var a in angles){
        angles[a] += angleSpeeds[a];
    }
    beginShape(TRIANGLES)
    data.forEach((hyperobject, i) => {
        // face
        const [a, b, c, d] = hyperobject.map(a => {
            a.rotate(angles[0], angles[1], angles[2], angles[3], angles[4], angles[5])
            return a
        })
        //first triangle
        a.draw()
        b.draw()
        c.draw()

        a.draw()
        b.draw()
        d.draw()
        if (i % 6 === 0) {
            endShape()
            beginShape(TRIANGLES)
        }
    })
    endShape()
}

Note that you could factorize the rotation


  const [axy, axz, axw, ayz, ayw, azw] = angles
  const f = x => [Math.cos(x), Math.sin(x)]
  const [Ca, Sa] = f(axy)
  const [Cb, Sb] = f(axz)
  const [Cc, Sc] = f(axw)
  const [Cd, Sd] = f(ayz)
  const [Ce, Se] = f(ayw)
  const [Cf, Sf] = f(azw)
  const R = [
    [Ca*Cb*Cc, -Cb*Cc*Sa, -Cc*Sb, -Sc],
    [Ca*(-Cb*Sc*Se-Ce*Sb*Sd)+Cd*Ce*Sa, -Sa*(-Cb*Sc*Se-Ce*Sb*Sd)+Ca*Cd*Ce, -Cb*Ce*Sd+Sb*Sc*Se, -Cc*Se],
    [Ca*(Sb*(Sd*Se*Sf+Cd*Cf)-Cb*Ce*Sc*Sf)+Sa*(-Cd*Se*Sf+Cf*Sd), -Sa*(Sb*(Sd*Se*Sf+Cd*Cf)-Cb*Ce*Sc*Sf)+Ca*(-Cd*Se*Sf+Cf*Sd), Cb*(Sd*Se*Sf+Cd*Cf)+Ce*Sb*Sc*Sf, -Cc*Ce*Sf],
    [Ca*(Sb*(-Cf*Sd*Se+Cd*Sf)+Cb*Ce*Cf*Sc)+Sa*(Cd*Cf*Se+Sd*Sf),-Sa*(Sb*(-Cf*Sd*Se+Cd*Sf)+Cb*Ce*Cf*Sc)+Ca*(Cd*Cf*Se+Sd*Sf), Cb*(-Cf*Sd*Se+Cd*Sf)-Ce*Cf*Sb*Sc, Cc*Ce*Cf]
  ]


  Point.prototype.rotate = function (R) {
    const X = [this.origx, this.origy, this.origz, this.origw]
    const [x,y,z,w] = prod(R, X)
    Object.assign(this, { x, y, z, w })
  }

but this is not the bottleneck, (like 1ms to 50ms for drawing), so keeping your matrix decomposition may be preferable.


I can't put the code snippet here since webgl not secure https://jsfiddle.net/gk4Lvptm/

grodzi
  • 5,633
  • 1
  • 15
  • 15
0

first see this (and all sub links) for inspiration:

especially the 4D rotations and reper4D links.

  1. Use 5x5 4D homogenuous transform matrices

    this will convert all your transformations into single matrix * vector operation without any goniometrics (repeated for each vertex) so it should be much faster. Even allows to stack operations and much more.

  2. You can port to GLSL

    You can move much of the computations (transformations included) into shaders. I know GLSL suports only 4x4 matrices but You can compute mat5 * vec5 by using mat4 and vec4 too... You just add the missing stuff into separate variable and use dot product for the missing col/row

  3. Use of VAO/VBO

    This can improve speed a lot as you would not need to pass any data to GPU during rendering anymore... However you would need to do the projection on GPU side (which is doable as unlike cross-section the projection is easy to implement).

Spektre
  • 49,595
  • 11
  • 110
  • 380