0
\$\begingroup\$

We can look at the most popular engines like Unity and Unreal engine (maybe Godot and other).

As far as I understand (at least I would do this), we store the original mesh, and then for every change (shift, rotation or scaling) we turn the change into a matrix, and apply it to all vertices of the copy of the original mesh, with which we will work later.

But it seems to me that this will not be very fast, so I doubt if this is really so.

I tried to see how the transform is implemented in the source code of the Unreal engine, but ... there is so much code that after spending a few hours searching, I gave up.

I will be glad to see a link to an implementation in an unreal engine (or any popular engine in general), but extra points - if you explain the code.

PS: if you know several implementations of transformation, then I would be glad if you tell me about the known ones and, if possible, describe their pros and cons.

\$\endgroup\$
2
  • \$\begingroup\$ I will still be happy to see the implementation of the transformation in any of the popular engines, you can reply with a link to the source code (for example, on github), I will appreciate it \$\endgroup\$
    – 0dminnimda
    Mar 16 at 10:53
  • 1
    \$\begingroup\$ To simplify, you can program what the GPU does using shaders which are compiled and executed onto the GPU. Shader languages do not perform these computation at a very low level, for example the GLSL, the shader language used by OpenGL, has a syntax close to C and a matrix multiplication is just a one-line instruction. Those computation are then performed directly at hardware level, which is optimized to do so way faster than a CPU. For example, a CPU can have a circuit to multiply 2 numbers while a GPU can have thousands of circuits able to do in parallel a matrix multiplication. \$\endgroup\$
    – Sacha
    Mar 16 at 11:01
2
\$\begingroup\$

You correctly describe how transformations work, you're just making a wrong assumption that the process seems slow. Graphics processing units are tailor-made to do this specific thing, i.e. linear algebra calculations.

My thinking is that you have a wrong view of what a GPU is and compare it to a CPU. If a CPU has 4-8 cores, a GPU has 1000s of them and each small core can undertake a single calculation. In a fraction of a second, all of the 1000s of GPU cores can calculate 1000s of multiplications between mesh position vectors and transformation matrices since these operations can be vectorized.

Regarding where in the source code this happens in the engines above, you will probably not find it in the form you are expecting since it happens on the GPU side (i.e. you will not see matrix multiplication code). GPU APIs usually provide ways to load the mesh data and the transformation data in their memory and the calculations are dictated via GPU-specific programs called shaders.

\$\endgroup\$
3
  • \$\begingroup\$ Thanks for the answer, I think I will study in more detail how the GPU works. \$\endgroup\$
    – 0dminnimda
    Mar 16 at 10:47
  • \$\begingroup\$ So the fastest way to change geometry is to use vertex shaders? \$\endgroup\$
    – 0dminnimda
    Mar 16 at 11:13
  • \$\begingroup\$ Exactly, that's their whole purpose, they dictate where are the mesh vertices will end up when preparing to render each frame. \$\endgroup\$
    – PentaKon
    Mar 16 at 12:01
3
\$\begingroup\$

There's an additional detail I want to clarify here:

As far as I understand (at least I would do this), we store the original mesh, and then for every change (shift, rotation or scaling) we turn the change into a matrix, and apply it to all vertices of the copy of the original mesh, with which we will work later.

This seems to imply that when I translate a mesh, the engine stops everything, moves every single vertex in the mesh, stores their new positions, then continues executing the next line of code. If I then rotate the mesh, it loads those stored positions after the previous translation, rotates every single vertex, then stores the translated+rotated results...

That's not how it works.

Instead, with each instance of a rendered mesh we store a "transformation" data structure, usually consisting of a translation vector, a rotation quaternion, an axis-aligned scale triplet, and a reference to a parent transformation (for hierarchical nesting of objects). We might also store a local-to-world or world-to-local matrix, or generate those on demand when needed.

When I rotate an object, the engine just updates the quaternion stored in its transformation data. When I translate it, the engine updates the translation vector. When I scale it, the engine updates the scale triplet. And that's it. Only those 3-4 numbers need to be recalculated immediately. We can then set a dirty flag to let us know the transformation has changed, and regenerate the matrix the next time it's needed.

When it comes time to render the objects, we run through our scene graph and update the transformation matrices of any objects that have changed (or whose parents have changed). Then we send that matrix and the raw, unchanged vertex data to the GPU to render.

Note that nowhere in this process so far have we actually moved a single vertex. Our mesh data often stays effectively read-only in memory. I can have thousands of differently-transformed instances of an object all referring to a single copy of the mesh data for that object, with every vertex exactly where I put it in the 3D modelling tool when authoring it.

The transformations get applied on demand in the GPU. The vertex shader gets passed each vertex in its original as-modelled position (what we call "object space"), and a matrix to use to transform that vertex into world space, or directly into the camera's projection space. So no matter how many transformations I've applied to the vertex, the shader only needs to do a couple of matrix multiplications to find its final displayed position (and possibly transform its normal, tangent, etc). All those sequences of translations and rotations get folded down into a single matrix, so computing the result of 1000 transformations is no more expensive than 1 for each vertex.

That transformed vertex position gets sent down the pipeline to be rasterized into pixel positions to draw, then it's effectively forgotten at the end of the draw call. We don't store the transformed vertices anywhere, we just re-transform them again cheaply next frame.

Now, I've glossed over here a few special cases where we will compute new vertex positions CPU-side, like when we're dynamically batching multiple meshes together into one common coordinate space to draw in a single call. But those are exceptions to the rule.

\$\endgroup\$
6
  • \$\begingroup\$ I am interested in physical interaction between dynamic meshes i.e. at some points I will have to change the structure of the mesh and I think at this point I will have to save the changes that I applied to the mesh (obviously, this is not just a shift, rotation or scaling). \$\endgroup\$
    – 0dminnimda
    Mar 17 at 9:04
  • \$\begingroup\$ to put it bluntly, I'm making a 4d engine, and I'm trying to figure out how I can render the image better. in this case, I'm trying to go the easy way and first generate a 3d mesh, which can then be rendered by a 3d graphics engine \$\endgroup\$
    – 0dminnimda
    Mar 17 at 9:05
  • \$\begingroup\$ anyway, thanks for the info, it's good to know \$\endgroup\$
    – 0dminnimda
    Mar 17 at 9:11
  • \$\begingroup\$ To get accurate answers about how to do that well, you should make sure to describe your specific use case in your question. \$\endgroup\$
    – DMGregory
    Mar 17 at 11:51
  • \$\begingroup\$ @DMGregory I suppose collision proxies are managed and updated on the CPU since collision checking and response belong to the game logic rather than graphics. Hence the need for simple (though effective) collision meshes and fast detection algorithms? \$\endgroup\$
    – liggiorgio
    Jul 16 at 12:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.