GithubHelp home page GithubHelp logo

m4 multiplication about twgl.js HOT 17 CLOSED

greggman avatar greggman commented on September 7, 2024
m4 multiplication

from twgl.js.

Comments (17)

greggman avatar greggman commented on September 7, 2024 1

Sorry I never responded to this.

I don't know what the "right" way is. Basically I just swap left and right sides to whatever library's multiply function is until things work.

In this case since the matrix is often called worldViewProjection I wanted my example to do (world * view * projection) = worldViewProjeciton so it matched the name. So for example

 m4.multiply(view, projection, viewProjection); 

which is (view * projection) = (viewProjection). That seems more clear than (projection * view) = viewProjection.

If I understand matrix math correctly (and it's sad that my math sucks so bad) if I understand correctly you can just swap the multiple in the shader from

 gl_Position = matrix * position

to

gl_Position = position * matrix;

To get column vs row style results. Then just swap the first and second arguments to all matrix multiply functions. It's not quite that simple because functions like translate, rotate, that multiply in place won't do the right thing.

For those functions they were designed to mimic OpenGL's translate, rotate, scale functions so they can be used in the same order as old OpenGL examples

from twgl.js.

wujianv5 avatar wujianv5 commented on September 7, 2024

I have the same confusion as @mseefelder when I read the m4.multiply(). First of all, using column-major order is good, since that's the OpenGL convention. And if users call m4.multiply(a, b, result), I think what they expect is "result = a * b" which is matrix multiplication in math, but the implementation is like "result = b * a". A good reference of GL math is glm project.

from twgl.js.

greggman avatar greggman commented on September 7, 2024

Yea, I agree multiply is backward. I guess I'm a little afraid of switching it since it break anyone using it 😢

I could add mult and switch all the examples?

I could add mult and deprecate multiply with a single message if it's used as in

"multiply" is deprecated. use "mult" instead

I could just break everyone?

I could set the version to 2.0 and hope everyone realizes 2.0 = breaking API changes

I could rename it twgl2?

I could just remove it from twgl and make a new library for math which you include separetely?

Other ideas?

from twgl.js.

greggman avatar greggman commented on September 7, 2024

To be clear I just wrote this test

var m4 = twgl.m4;

var m1 = m4.identity();
m1 = m4.translate(m1, [1, 2, 3]);
m1 = m4.rotateZ(m1, 1.2);
m1 = m4.scale(m1, [2, 3, 4]);

var m2 = m4.identity();
m2 = m4.multiply(m2, m4.translation([1, 2, 3]));
m2 = m4.multiply(m2, m4.rotationZ(1.2));
m2 = m4.multiply(m2, m4.scaling([2, 3, 4]));

var m3 = m4.identity();
m3 = m4.multiply(m4.translation([1, 2, 3]), m3);
m3 = m4.multiply(m4.rotationZ(1.2), m3);
m3 = m4.multiply(m4.scaling([2, 3, 4]), m3);

console.log(m1);
console.log(m2);
console.log(m3);

I think m1 and m2 should be equal but m1 and m3 are equal. That's arguably wrong

from twgl.js.

greggman avatar greggman commented on September 7, 2024

I think I'm leaning toward just adding mult, changing all the samples to use it and just documenting that multiply is the opposite of mult.

that way nothing old breaks but most people won't see multiply, specially of the docs warn in red that you probably want to use mult

thoughts?

from twgl.js.

greggman avatar greggman commented on September 7, 2024

Hmmm, I did a quick search and replace of m4.multiply(a, b...) with m4.mult(b, a... but now I have the issue that it reads poorly as pointed out above

In other words where is it used to look like this

viewProjection = m4.multiply(view, projection);

now it looks like this

viewProjection = m4.mult(projection, view);

which is completely unintuitive. Yea I get that the top is backward from some other math libs and it's backwards from the translate/rotate/scale functions. Still I'm not sure which is better.

I suppose I could refactor the second version to this

projectionView = m4.mult(projection, view);

But I've never heard of a projectionView matrix. It's always a viewProjection matrix. I guess whoever picked that naming convention kind of screwed up ? 😕

from twgl.js.

greggman avatar greggman commented on September 7, 2024

glm and twgl are not the same

twgl:

<script src="twgl-full.min.js"></script>
<script>
var m4 = twgl.m4;

var m1 = m4.identity();
m1 = m4.translate(m1, [1, 2, 3]);
m1 = m4.axisRotate(m1, [2, 3, 4], 1.2);
m1 = m4.scale(m1, [4, 5, 6]);

var m2 = m4.identity();
m2 = m4.multiply(m2, m4.translation([1, 2, 3]));
m2 = m4.multiply(m2, m4.axisRotation([2, 3, 4], 1.2));
m2 = m4.multiply(m2, m4.scaling([4, 6, 7]));

var m3 = m4.identity();
m3 = m4.multiply(m4.translation([1, 2, 3]), m3);
m3 = m4.multiply(m4.axisRotation([2, 3, 4], 1.2), m3);
m3 = m4.multiply(m4.scaling([4, 5, 6]), m3);

console.log("m1:", m1);
console.log("m2:", m2);
console.log("m3:", m3);
</script>

glm:

#include <glm/gtx/matrix_operation.hpp>
#include <glm/vec3.hpp> // glm::vec3
#include <glm/vec4.hpp> // glm::vec4
#include <glm/mat4x4.hpp> // glm::mat4
#include <glm/gtc/matrix_transform.hpp> // glm::translate, glm::rotate, glm::scale, glm::perspective
#include <stdio.h>

void pm(const char* name, const glm::mat4& m1)
{
  printf("%s\n%f %f %f %f\n%f %f %f %f\n%f %f %f %f\n%f %f %f %f\n\n",
     name,
     m1[0][0], m1[0][1], m1[0][2], m1[0][3],
     m1[1][0], m1[1][1], m1[1][2], m1[1][3],
     m1[2][0], m1[2][1], m1[2][2], m1[2][3],
     m1[3][0], m1[3][1], m1[3][2], m1[3][3]);
}

int main()
{
  int Error(0);

  glm::mat4 m1 = glm::mat4();
  m1 = glm::translate(m1, glm::vec3(1, 2, 3));
  m1 = glm::rotate(m1, 1.2f, glm::vec3(2, 3, 4));
  m1 = glm::scale(m1, glm::vec3(4, 5, 6));

  glm::mat4 m2 = glm::mat4();
  m2 = m2 * glm::translate(glm::mat4(), glm::vec3(1, 2, 3));
  m2 = m2 * glm::rotate(glm::mat4(), 1.2f, glm::vec3(2, 3, 4));
  m2 = m2 * glm::scale(glm::mat4(), glm::vec3(4, 5, 6));

  glm::mat4 m3 = glm::mat4();
  m3 = glm::translate(glm::mat4(), glm::vec3(1, 2, 3)) * m3;
  m3 = glm::rotate(glm::mat4(), 1.2f, glm::vec3(2, 3, 4)) * m3;
  m3 = glm::scale(glm::mat4(), glm::vec3(4, 5, 6)) * m3;

  pm("m1:", m1);
  pm("m2:", m2);
  pm("m3:", m3);

  return Error;
}

twgl results:

m1:
1.8012336492538452 3.2969088554382324 -1.3732985258102417 0
-2.8018763065338135 2.8012335300445557 3.0500128269195557 0
4.1707634925842285 -0.49379199743270874 4.284962177276611 0
1 2 3 1


foo.html:4 m2:
1.8012336492538452 4.9453630447387695 -2.4032723903656006 0
-2.2415010929107666 3.361480236053467 4.270018100738525 0
2.7805089950561523 -0.49379199743270874 4.999122619628906 0
5.659758567810059 10.1869478225708 21.134132385253906 1


foo.html:4 m3:
1.8012336492538452 3.2969088554382324 -1.3732985258102417 0
-2.8018763065338135 2.8012335300445557 3.0500128269195557 0
4.1707634925842285 -0.49379199743270874 4.284962177276611 0
1 2 3 1

glm results:

m1:
1.801233 3.296909 -1.373298 0.000000
-2.801876 2.801233 3.050013 0.000000
4.170763 -0.493792 4.284962 0.000000
1.000000 2.000000 3.000000 1.000000

m2:
1.801233 3.296909 -1.373298 0.000000
-2.801876 2.801233 3.050013 0.000000
4.170763 -0.493792 4.284962 0.000000
1.000000 2.000000 3.000000 1.000000

m3:
1.801233 4.121136 -2.059947 0.000000
-2.241501 2.801233 3.660015 0.000000
2.780509 -0.411493 4.284962 0.000000
5.659758 8.489123 18.114967 1.000000

In twgl m1 and m3 match. In glm m1 and m2 match

from twgl.js.

wujianv5 avatar wujianv5 commented on September 7, 2024

Yes, you're right, and in my experience GLM is the right multiplication order. In OpenGL the MVP matrix actually equals "projectMatrix * viewMatrix * modelMatrix", and in a shader we usually write "gl_Position = mvp * a_position". However if we use "twgl.multiply(projectMatrix, twgl.multiply(viewMatrix, modelMatrix))" we will get the wrong MVP matrix.

from twgl.js.

greggman avatar greggman commented on September 7, 2024

from twgl.js.

wujianv5 avatar wujianv5 commented on September 7, 2024

In my opinion, I think the current implementation is buggy because people treat it as "a * b" as documented while it's actually "b * a", so I think it's better to fix the "bug" rather than make a new right implementation named "mult", and maybe this fix can be emphasized in the document or the change list.

BTW, I remember that there is a project called gl-matrix or glMatrix, I never used it before but I looked into some of the code and it seems it has some optimization in matrix calculation. Maybe you can integrate it into twgl to replace m4 and everything related to math, and focus on enhancing very useful and core functionality and "adding new features".

Anyway, it's just something flash in my mind, everything is up to you. It's a very useful project, please keep up the good work!

from twgl.js.

greggman avatar greggman commented on September 7, 2024

FYI: glMatrix is not as fast as twgl

http://greggman.github.io/webgl-matrix-benchmarks/matrix_benchmark.html

I'm also not a fan of glMatrix's API that requires a destination in all functions so I'm not personally going to switch to it. On the other hand you're free to use twgl with any math library you want. The base twgl doesn't include or even use a math library.

from twgl.js.

greggman avatar greggman commented on September 7, 2024

So I finally swapped multiply and pushed version 2.0. See changelist

Thanks for pointing out my mistake/misunderstanding. I ended up refactoring all the examples on webglfundamentals.org as well because of this.

from twgl.js.

fuzzyTew avatar fuzzyTew commented on September 7, 2024

It seems that multiply has been swapped, but the same change was not made to the prebaked transformation functions (axisRotate, translate, scale, ....). It appears these functions now perform pre-transformations (applied before the contents of the passed matrix, equivalent to a multiplication on the right), rather than post-transformations (applied after the contents of the passed matrix, equivalent to a multiplication on the left). Is this the intention? To me post-transformations would be far more intuitive.

Note, on my system twgl is only in fifth place on the matrix benchmark. glMatrix is in third. TDLMath is in first.

from twgl.js.

greggman avatar greggman commented on September 7, 2024

The m4 currently functions match OpenGL

  glMatrixMode(gl.MODELVIEW)
  glRotate(angle, axisX, axisY, axisZ)
  glTranslate(tx, ty, tz)
  glScale(sx, sy, sz)

would be this in m4

  var modelView = m4.identity();
  m4.axisRotate(modelView, [axisX, axisY, axisZ], angle, modelView);
  m4.translate(modeView, [tx, ty, tz], modelView);
  m4.scale(modelView, [sx, sy, sz], modelView);

or

  var modelView = m4.identity();
  modelView = m4.axisRotate(modelView, [axisX, axisY, axisZ], angle);
  modelView = m4.translate(modeView, [tx, ty, tz]);
  modelView = m4.scale(modelView, [sx, sy, sz]);

or this

  var modelView = m4.identity();
  modelView = m4.multiply(modelView, m4.axisRotation([axisX, axisY, axisZ], angle));
  modelView = m4.multiply(modelView, m4.translation([tx, ty, tz]));
  modelView = m4.multiply(modelView, m4.scaling([sx, sy, sz]));

or this

  var modelView = m4.identity();
  m4.multiply(modelView, m4.axisRotation([axisX, axisY, axisZ], angle), modelView);
  m4.multiply(modelView, m4.translation([tx, ty, tz]), modelView);
  m4.multiply(modelView, m4.scaling([sx, sy, sz]), modelView);

from twgl.js.

fuzzyTew avatar fuzzyTew commented on September 7, 2024

Gotcha. OpenGL does the same thing.

from twgl.js.

greggman avatar greggman commented on September 7, 2024

As for speed if you check firefox I think you'll find twgl is faster. Hopefully Chrome will fix their code as it should be faster there too

from twgl.js.

greggman avatar greggman commented on September 7, 2024

I will add the OpenGL pattern makes sense for a matrix stack or a scenegraph. you start at the root (or the projection) and apply down, then you can pop off. If you do things in the opposite order you can't do that.

In other worlds

   // house
   push
      translate(houseT); rotate(houseR); scale(houseS);
        // window
        push
           translate(windowT); rotate(windowR); scale(windowS);
        pop
        // door
        push
           translate(doorT); rotate(doorR); scale(doorS);
        pop
        // backdoor
        push
           translate(backdoorT); rotate(backdoorR); scale(backdoorS);
           // doorknob
           push
              translate(doorknobT); rotate(doorknobR); scale(doorknobS);
           pop
        pop
    pop

from twgl.js.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.