Reputation:
I have some trouble organizing the various matrix-multiplications to implement a scene graph to my WebGL-scene.
Until now, I used to have three matrices, the projectionMatrix, the viewMatrix and the modelMatrix (the normalMatrix aside), which are multiplicated in that same order. I also have a node-object, which holds basically all information needed to draw something.
While the projectionMatrix and the viewMatrix are updated separately by a drawScene
-function, the modelMatrix is equal to each nodes localMatrix, which - with every drawing-call - is set to identity and then transformed the way I want.
The nodes are stored in an array (nodeList
) and when the drawScene
-function is called, I loop through this list and tell each node to draw itself.
Now I've read the tutorial for implementing a scene graph to WebGL on www.webglfundamentals.org and tried to add this feature to my scene-function, but though this tutorial is very well written, I am still a bit confused of how to get this done.
Following this tutorial, each node has to have not just a localMatrix, but also a worldMatrix. Then, if a node is the root-element (i.e. when it has no parent-node), their localMatrix and their worldMatrix are identical, otherwise, if there is a parent-node, the worldMatrix of the child-node is computed by multiplying their localMatrix with the worldMatrix of their parent-node, so that the worldMatrix of each node is what in my former code used to be the modelMatrix / localMatrix.
Maybe I just can't see the forest for the trees, but I ask myself when and where to call the function to update the world-matrices of the nodes and when to set the matrices back to identity, once for all, just for the root-node or for each node separately?
I mean, the code provided in said tutorial does it by traversing recursively through all children of a node, but my nodeList
-array doesn't reflect the potential hierarchical relations of the nodes and with every drawing-call, the local-matrices are set back to identity. So, it cannot work this way, can it?
Upvotes: 1
Views: 2177
Reputation: 3491
EDIT
Sorry my previous answer was about something else and it is not implemented this way in tutorial (sadly).
which - with every drawing-call - is set to identity and then transformed the way I want.
Why do you set anything back to identity? You shouldnt do that.
but my
nodeList
-array doesn't reflect the potential hierarchical relations of the nodes and with every drawing-call, the local-matrices are set back to identity. So, it cannot work this way, can it?
Purpose of scene graph is to simplify and avoid additional mathematical operations. This require nodes in tree hierarchy.
Initialization
Lest have nodes from tutorial:
var node = {
localMatrix: ..., // the "local" matrix for this node
worldMatrix: ..., // the "world" matrix for this node
children: [], // array of children
thingToDraw: ??, // thing to draw at this node
};
Then you have two things, root node of entire scene and nodeList
where important nodes are (these that might be transformed in future).
var rootNode = new Node();
var nodeList = [];
Now put all the stuff in node hierarchy, all the nodes are part of rootNode. And these that might transform in the future save in nodeList.
Stages are repeat for each tick()
Stage 1 - update
Update location of all elements that you want, which means pick stuff from nodeList and transform localMatrix
. You doesnt do anything with parent or children.
Stage 2 - find world position
When all the updates are done, you need to recalculate all worldMatrices. Which means destroy old worldMatrix
and create new one. You do this with rootNode.updateWorldMatrix()
. This will go from top to bottom and calculate new worldMatrix
for each node in the tree based on parent worldMatrix and node localMatrix. It does not change localMatrix
.
Stage 3 - drawcall
Now we are back to old known projection, view and model matrix. Do something like rootNode.draw()
, which is recursive function that will draw each node in hierarchy. Model matrix = worldMatrix
.
About
As you see there is no setIdentity in whole process (worldmatrix might be set to identity insted of being destoried but I dont see any advantage in this). Bad thing about this is if you have forest full of trees and trees doesnt move nor the ground, every tree recalculate its world position anyway. This can be prevented by augumenting stage 1 and 2 and extending node with flag.
EDIT 2
Model matrix is representation how is model transformed (understand as translated, rotated and scaled) from 0,0,0 position (understand it as base point).
Scenegraph does only one thing, it changes each models basepoint from something static (= static position like 0,0,0) to something relative (another model position).
Each model still has its own model matrix, which represent its transformation from base point.
but I think you cannot transform matrices over and over and back and again and in different ways without any set-back, can you?
This is exactly what you can and should do! Matrix 4x4 contain 16 numbers and has predefined operations. Position, rotation and scale are 3 vectors, each with 3 numbers, which is 9 numbers and these represent current state of the model (in 3D, in 2D we have different transformations). We componed them into one mat4, so mat4 contains the current state of the model and we dont need to maintain 3 vectors anymore, all is in the matrix.
When you call mat4.translate()
, mat4.rotate()
, mat4.scale()
you transfrom the model. mat4.multiply(mat4)
applies not just one but set of transformations.
In addition, matrix was designed to not have its operations commutative! matA * matB != matB * matA. Which is very helpful for example, if you first rotate the camera and then translate it, it will move in direction it is looking. Sometimes you doesnt want it, but most of the time you want it.
In case you doesn't want it, you set matrix back to identity, which is reset the model to default position and then do transformation in way you want.
For example for car with velocity, with each tick()
you do just small delta translation on matrix and it will move in direction it is heading. And if player is turning right, you just do small delta rotation to the right and then translation. And you have nice smooth and easy car movement with just few lines of code.
In scene graph there is also world position for each model, which is sum of all previous parent matrices and model matrix to one. It prevents to do all the multiplications for each model separately so very deep simbling models doenst require a lot of multiplications. This is important, imagine you need to do all the multiplications 60 times per sec and all this with CPU.
This is not from my mind, all the stuff about matrices is native in GLSL already. Graphic cards are prepared to work with matrices.
I mean, you will already have recognized that I'm not that much into matrix-math
You dont have to, you just need to use 4 functions: translate, rotate, scale and mutliply and to know that model state is represent by one matrix and this is all. Everything behind is ment to be in the blackbox. You dont have to know how to get model distance from the matrix, you just need to know: once you do modelviewprojectionmatrix * vertex, model will be rendered.
Upvotes: 2