Convert order to glMatrix?

enter image description here

In both scenarios, the transforms must be replaced in glMatrix.

i.e. to achieve 1) in glMatrix:

mat4.translate(modelViewMatrix, modelViewMatrix, [0.6, 0.0, 0.0]); mat4.rotateZ(modelViewMatrix, modelViewMatrix, degToRad(45)); 

Why is the conversion order canceled?

+5
source share
3 answers

This is not only the case for WebGL, but for OpenGL in general. Indeed, this can be confusing: the order in which transformations are applied is opposite to the order in which they appear in the source code.

A simplified / abbreviated version of the pseudo-code of the code you provided is as follows:

 M = identity(); M = M * T; // Where T = Translation M = M * R; // Where R = Rotation 

It will be an even shorter form of writing

 M = T * R; 

Now imagine that you are transforming a vertex with this matrix - this can be written as

 transformedVertex = M * vertex 

Remembering that M = T * R , this is the same as

 transformedVertex = T * R * vertex 

You can also write it as

 transformedVertex = T * (R * vertex) 

or, to make it even more obvious:

 rotatedVertex = R * vertex transformedVertex = T * rotatedVertex 

So, first the vertex is rotated. (And then, the rotated top translates)


Of course, you can basically turn around. The usual way to matrix multiplication in OpenGL is post-multiplication or correct multiplication in the form

 newMatrix = oldMatrix * additionalTransformation 

(for example, you did this in your code). An alternative would be to write

 newMatrix = additionalTransformation * oldMatrix 

This is sometimes called "preliminary multiplication" or "left multiplication." So you can also write

 M = identity(); M = T * M; // Where T = Translation M = R * M; // Where R = Rotation 

so in the end

 M = R * T 

In this case, the translation appears before the rotation in the source code, and the translation will also be applied before the rotation.

But in the context of OpenGL, this is rather unusual. (And mixing both methods would be very confusing - I would not recommend this).


Note: all of this may have changed a little while glPushMatrix and glPopMatrix were still part of the OpenGL API. The way of thinking reminds of this bypassing the scene graph. You apply the “global” transforms first, and then the “local” ones.


Update:

In response to the comments: I will try to write a few words that can justify certain concepts. Summarizing it here is a bit difficult. I will try to simplify it and omit some details that are probably beyond the scope of one answer here. Some of the things mentioned here relate to how things were done in earlier versions of OpenGL and are being handled differently - although many of these concepts are still the same!

It is often possible to imagine 3D scenes as a scene graph. This is a hierarchically structured representation of the scene, usually in the form of a tree:

  root / \ nodeA nodeB / \ \ nodeA0 nodeA1 nodeB0 object object object 

Nodes contain transformation matrices (for example, rotation or translation). 3D objects are attached to these nodes. During rendering, this graph moves: each node is visited, and its object will be displayed. This is done recursively, starting from the root and visiting all the children, down to the leaves. For example, a visualizer can visit the above sites in the following order:

 root nodeA nodeA0 nodeA1 nodeB nodeB0 

During this traversal, the renderer supports the matrix stack. In earlier versions of OpenGL, special methods were created to support this stack. For example, glPushMatrix to push a copy of the current “top” matrix on the stack, and glPopMatrix to remove the topmost matrix from the stack. Or glMultMatrix to multiply the current "top" stack matrix by another.

When an object was visualized, it was always displayed using the matrix, which was located at the top of this stack. (Then there were no shaders and mat4 formats ...)

Thus, the renderer can display the scene graph using a simple recursive method like this (pseudo-code):

 void render(Node node) { glPushMatrix(); glMultMatrix(node.matrix); renderObject(node.object); foreach (child in node.children) { render(child); } glPopMatrix(); } 

By “including” rendering in a glPushMatrix / glPopMatrix rendering can always maintain the correct current matrix for the node that he has visited. Now the rendering visited these nodes and saved the matrix stack:

 Node: Matrix Stack: ----------------------------- root identity nodeA identity * nodeA.matrix nodeA0 identity * nodeA.matrix * nodeA0.matrix nodeA1 identity * nodeA.matrix * nodeA1.matrix nodeB identity * nodeB.matrix nodeB0 identity * nodeB.matrix * nodeB0.matrix 

You can see that the matrix that is used to render the object in the node is defined by the product of all the matrices along the path from the root to the corresponding node.

The possible performance benefits and elegance of these concepts may become more apparent when looking at the "large" scene graph:

 root nodeA nodeB nodeC nodeD0 nodeD1 nodeD2 ... nodeD1000 

Can calculate the product

 nodeA.matrix * nodeB.matrix * nodeC.matrix 

once , and then with this matrix multiply the matrix nodeD0 ... nodeD1000 . Conversely, if you had to expand the multiplication, you would need to calculate

 nodeD0.matrix * nodeC.matrix * nodeB.matrix * nodeA.matrix nodeD1.matrix * nodeC.matrix * nodeB.matrix * nodeA.matrix ... nodeD1000.matrix * nodeC.matrix * nodeB.matrix * nodeA.matrix 

spends a lot of resources on matrix multiplication. (These redundant calculations could have been avoided by other methods, but they would not have been so elegant and light.) A.

+5
source

I'm not sure if this glMatrix is ​​behind.

For example, to watch these videos , it seems that it’s standard to do

 m1 * m2 * m3 * vector 

and indicate the order shown in the video that matches

 gl_Position = projection * view * world * position; 

which exactly matches GL and GLSL.

It also matches glMatrix.

 var m = mat4.create(); mat4.projection(m, fov, aspect, zNear, zFar); mat4.multiply(m, m, view); mat4.translate(m, m, [x, y, z]); mat4.rotateY(m, m, someAngle); mat4.scale(m, m, [sx, sy, sz]); 

Matches exactly

 m = projection * view * translation * rotation * scale; 

It seems in front of me.

 var vs = ` uniform mat4 u_worldViewProjection; attribute vec4 position; attribute vec2 texcoord; varying vec2 v_texCoord; void main() { v_texCoord = texcoord; gl_Position = u_worldViewProjection * position; } `; var fs = ` precision mediump float; varying vec2 v_texCoord; uniform sampler2D u_diffuse; void main() { gl_FragColor = texture2D(u_diffuse, v_texCoord); } `; "use strict"; var gl = document.querySelector("canvas").getContext("webgl"); var programInfo = twgl.createProgramInfo(gl, [vs, fs]); var arrays = { position: [1, 1, -1, 1, 1, 1, 1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, 1, -1, -1, 1, -1, -1, -1, -1, 1, -1, -1, 1, -1, 1, -1, -1, 1, 1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, -1, -1, -1], normal: [1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1], texcoord: [1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1], indices: [0, 1, 2, 0, 2, 3, 4, 5, 6, 4, 6, 7, 8, 9, 10, 8, 10, 11, 12, 13, 14, 12, 14, 15, 16, 17, 18, 16, 18, 19, 20, 21, 22, 20, 22, 23], }; var bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays); var tex = twgl.createTexture(gl, { min: gl.NEAREST, mag: gl.NEAREST, src: [ 255, 0, 0, 255, 192, 192, 192, 255, 0, 0, 192, 255, 255, 0, 255, 255, ], }); var uniforms = { u_lightWorldPos: [1, 8, -10], u_lightColor: [1, 0.8, 0.8, 1], u_ambient: [0, 0, 0, 1], u_specular: [1, 1, 1, 1], u_shininess: 50, u_specularFactor: 1, u_diffuse: tex, }; function render(time) { time *= 0.001; twgl.resizeCanvasToDisplaySize(gl.canvas); gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); gl.enable(gl.DEPTH_TEST); gl.enable(gl.CULL_FACE); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); var eye = [1, 4, -6]; var target = [0, 0, 0]; var up = [0, 1, 0]; var view = mat4.create(); var camera = mat4.create(); // glMatrix lookAt is arguably backward. // It making an inverse lookAt which is far less useful. // There one camera in the scene but hundreds of other // objects that might want to use a lookAt to you know, look at things. mat4.lookAt(view, eye, target, up); //mat4.lookAt(camera, eye, target, up); //mat4.invert(view, camera); var m = mat4.create(); var fov = 30 * Math.PI / 180; var aspect = gl.canvas.clientWidth / gl.canvas.clientHeight; var zNear = 0.5; var zFar = 10; mat4.perspective(m, fov, aspect, zNear, zFar); mat4.multiply(m, m, view); mat4.translate(m, m, [1, 0, 0]); mat4.rotateY(m, m, time); mat4.scale(m, m, [1, 0.5, 0.7]); uniforms.u_worldViewProjection = m; gl.useProgram(programInfo.program); twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo); twgl.setUniforms(programInfo, uniforms); twgl.drawBufferInfo(gl, gl.TRIANGLES, bufferInfo); requestAnimationFrame(render); } requestAnimationFrame(render); 
 body { margin: 0; } canvas { width: 100vw; height: 100vh; display block; } 
 <script src="https://twgljs.org/dist/twgl-full.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/2.3.2/gl-matrix-min.js"></script> <canvas></canvas> 
0
source

Now what you need take a look at:

http://nidza.html-5.me/zlatnaspirala2/project/index.html

Source:

https://github.com/zlatnaspirala/zlatnaspirala2 https://github.com/zlatnaspirala/zlatnaspirala2/blob/master/project/zlatnaspirala/zlatnaspirala.js

Magic:

 mat4.translate(mvMatrix, [0.0, 0.0, 0.0]); xRot = YY; yRot = alfa + XX; mat4.rotate(mvMatrix, degToRad(xRot), [1, 0, 0]); mat4.rotate(mvMatrix, degToRad(yRot), [0, 1, 0]); mat4.translate(mvMatrix, [transX +TX,transY + TY,transZ +TZ]); 

1) Translate to zero

2) rotate

3) Transfer to the last or current position in the 3D world.

0
source

All Articles