在 WebGL 中进行层次转换

Making hierarchical transformation in WebGL

我正在制作只包含太阳(点光源)、地球和月亮的太阳系模拟,我可以在屏幕上成功地看到这些星星,当我只对它们应用翻译时

这是示例屏幕

但是当我将轨道变换添加到地球后,我得到了这个奇怪的结果...

好像地球突然卡在了太阳里,但至少我能看到月亮不知怎么在绕着地球转

当我也对月球应用轨道变换时,我得到了这个结果..

如你所见,地球和月球都被太阳困住了...

这是代码和树,我希望如何组织它们的层次转换..

function RenderPerSec(gl, sun, SunModelMatrix, SunNormalMat, 
  earth, EarthModelMatrix, EarthNormalMat,
  moon, MoonModelMatrix, MoonNormalMat, 
  MvpMatrix, loc_uModelMatrix, loc_uNormalMatrix, loc_uMvpMatrix, 
  loc_uSunTrigger, loc_uEarthTrigger, loc_uMoonTrigger,
  w, h)
{
  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);


  ///Hierarchical Transformation///


  /*
                     P
                     |
                     V
                     |
                 SUN(Fixed)
                     |
                   EARTH
          Translation from the sun                      
           Rotation around the sun 
              /      |        \ 
tilted 23.5 degree  MOON     Rotation itself
 Along the X-axis    |
         Translation from the earth
          Rotation around the earth
              Rotation itself..

*/

  let MVPStack = [];
  MvpMatrix.setIdentity();


  //Projection
  MvpMatrix.setPerspective(60, w/h, 1, 1000);
  //View
  MvpMatrix.lookAt(0, 15, 10, 0, 0, 0, 0, 1, 0);

  //Model Transforms...

  //Sun

  SunModelMatrix.setTranslate(0, 0, 0);

  MvpMatrix.multiply(SunModelMatrix);

  MVPStack.push(new Matrix4(MvpMatrix));


  gl.uniform1i(loc_uSunTrigger, 1);
  gl.uniform1i(loc_uEarthTrigger, 0);
  gl.uniform1i(loc_uMoonTrigger, 0);

  RenderSun(gl, sun, SunModelMatrix, SunNormalMat, MvpMatrix, loc_uModelMatrix, loc_uNormalMatrix, loc_uMvpMatrix);

  //Earth, translation and rotation 23.5 along the X-axis, rotation itself
  EarthModelMatrix.setTranslate(5, 0, 0); //Translation from the sun
  EarthModelMatrix.setRotate(CURRENT_EATHR_REVOLVING_ANGLE, 0, 1, 0); //Revolving around the sun

  MvpMatrix = MVPStack.pop();
  MvpMatrix.multiply(EarthModelMatrix);

  MVPStack.push(new Matrix4(MvpMatrix));

  gl.uniform1i(loc_uSunTrigger, 0);
  gl.uniform1i(loc_uEarthTrigger, 1);
  gl.uniform1i(loc_uMoonTrigger, 0);

  RenderEarth(gl, earth, EarthModelMatrix, EarthNormalMat, MvpMatrix, loc_uModelMatrix, loc_uNormalMatrix, loc_uMvpMatrix);


  //Moon, translation and rotation itself
  MoonModelMatrix.setTranslate(2.5, 0, 0);
  MoonModelMatrix.setRotate(CURRENT_MOON_REVOLVING_ANGLE, 0, 1, 0);

  MvpMatrix = MVPStack.pop();
  MvpMatrix.multiply(MoonModelMatrix, EarthModelMatrix);

  gl.uniform1i(loc_uSunTrigger, 0);
  gl.uniform1i(loc_uEarthTrigger, 0);
  gl.uniform1i(loc_uMoonTrigger, 1);

  RenderMoon(gl, moon, MoonModelMatrix, MoonNormalMat, MvpMatrix, loc_uModelMatrix, loc_uNormalMatrix, loc_uMvpMatrix);
}

如果我删除这段代码中的 setRotate 函数,我可以获得第一个结果..

这是渲染函数

function RenderSun(gl, sun, SunModelMatrix, SunNormalMat, MvpMatrix, loc_uModelMatrix, loc_uNormalMatrix, loc_uMvpMatrix)
{
  gl.bindVertexArray(sun.vao);


  SunNormalMat.setInverseOf(SunModelMatrix);
  SunNormalMat.transpose();

  // Pass the model matrix to uModelMatrix
  gl.uniformMatrix4fv(loc_uModelMatrix, false, SunModelMatrix.elements);

  // Pass the model view projection matrix to umvpMatrix
  gl.uniformMatrix4fv(loc_uMvpMatrix, false, MvpMatrix.elements);

  // Pass the transformation matrix for normals to uNormalMatrix
  gl.uniformMatrix4fv(loc_uNormalMatrix, false, SunNormalMat.elements);

  gl.drawElements(gl.TRIANGLES, sun.n, sun.type, 0);
}

function RenderEarth(gl, earth, EarthModelMatrix, EarthNormalMat, MvpMatrix, loc_uModelMatrix, loc_uNormalMatrix, loc_uMvpMatrix)
{
  gl.bindVertexArray(earth.vao);

  EarthNormalMat.setInverseOf(EarthModelMatrix);
  EarthNormalMat.transpose();

  gl.uniformMatrix4fv(loc_uModelMatrix, false, EarthModelMatrix.elements);

  gl.uniformMatrix4fv(loc_uMvpMatrix, false, MvpMatrix.elements);

  gl.uniformMatrix4fv(loc_uNormalMatrix, false, EarthNormalMat.elements);

  gl.drawElements(gl.TRIANGLES, earth.n, earth.type, 0);
}

function RenderMoon(gl, moon, MoonModelMatrix, MoonNormalMat, MvpMatrix, loc_uModelMatrix, loc_uNormalMatrix, loc_uMvpMatrix)
{
  gl.bindVertexArray(moon.vao);

  MoonNormalMat.setInverseOf(MoonModelMatrix);
  MoonNormalMat.transpose();

  gl.uniformMatrix4fv(loc_uModelMatrix, false, MoonModelMatrix.elements);

  gl.uniformMatrix4fv(loc_uMvpMatrix, false, MvpMatrix.elements);

  gl.uniformMatrix4fv(loc_uNormalMatrix, false, MoonNormalMat.elements);

  gl.drawElements(gl.TRIANGLES, moon.n, moon.type, 0);
}

我正在使用堆栈来实现分层转换...有人可以帮助我吗?

有一篇关于此的文章here and stack based one here

一般来说,你需要做类似

的事情
push(0,0,0)
  push(sun rotation)
    draw sun
  pop()  // sun rotation
  push(earth orbit rotation)
    push(earth translation)
      push(earth rotation)
        draw earth
      pop() // earth rotation
      push(moon orbit rotation)
        push(moon translation)
          push(moon rotation)
            draw moon
          pop()  // moon rotation
        pop()  // moon translation
      pop()  // moon orbit rotation
    pop()  // earth translation
  pop()  // earth orbit rotation
pop()  // center of solar system

我不熟悉你的矩阵数学库,但名为 setTranslatesetRotate 的函数听起来不像是合适的函数。他们听起来像是设置平移或设置旋转而不是将现有矩阵乘以平移矩阵或旋转矩阵。

在大多数 3D 数学库中,要使球体出现在距旋转中心一定距离处,它会类似于

  mathlib.translate(centerOfRotation)  // move 0,0,0 to center of rotation
  mathlib.rotate(orbitRotation)        // rotate space
  mathlib.translate(orbitRadius)       // move some distance from center in rotated space

或更长的形式

  mat = identity();
  mat = mathlib.multiply(mat, mathlib.translation(centerOfRotation))
  mat = mathlib.multiply(mat, mathlib.rotatation(orbitRotation))
  mat = mathlib.multiply(mat, mathlib.translation(orbitRadius))

如果它是一个自动堆叠库,那么较短的形式应该已经堆叠了 3 个矩阵。

请注意,因为您似乎唯一遇到问题的部分是矩阵堆栈部分,这里有一些代码使用矩阵堆栈来计算 3 个矩阵(太阳、地球、月亮),然后它使用这些矩阵和 2D canvas(可以忽略)

'use strict';
const ctx = document.querySelector('canvas').getContext('2d');
const m4 = twgl.m4;

const stack = [];
const current = () => stack[stack.length - 1];
const pop = () => stack.pop();
const identity = () => stack.push(m4.identity());
const translate = (t) => stack.push(m4.translate(current(), t));
const rotate = (axis, r) => stack.push(m4.axisRotate(current(), axis, r));
const zAxis = [0, 0, 1];

function render(time) {
  time *= 0.001; // convert to seconds
  
  twgl.resizeCanvasToDisplaySize(ctx.canvas);
  const {width, height} = ctx.canvas;
  ctx.setTransform(1, 0, 0, 1, 0, 0);
  ctx.clearRect(0, 0, width, height);
  
  let sunMat;
  let earthMat;
  let moonMat;
  
  identity();
    // translate to center of solar system
    translate([width / 2, height / 2, 0]);
      rotate(zAxis, time * 4);  // sun rotation
        sunMat = current();
      pop();  // sun rotation
  
      rotate(zAxis, time / 2) // push(earth orbit rotation)
        translate([height / 4, 0, 0]); //push(earth translation)
          rotate(zAxis, time * 8); // push(earth rotation)
            earthMat = current();  //
          pop(); // earth rotation
          rotate(zAxis, time * 2); //push(moon orbit rotation)
            translate([height / 8, 0, 0]); // push(moon translation)
              rotate(zAxis, time * 16); //push(moon rotation)
                moonMat = current(); 
              pop();  // moon rotation
            pop();  // moon translation
          pop();  // moon orbit rotation
        pop();  // earth translation
      pop();  // earth orbit rotation
    pop();  // center of solar system  
  pop();  // identity
  
  drawSquare(sunMat, 40, 'orange');
  drawSquare(earthMat, 20, 'blue');
  drawSquare(moonMat, 10, 'gray');

  requestAnimationFrame(render);
}

requestAnimationFrame(render);

function drawSquare(mat, size, color) {
  ctx.setTransform(mat[0], mat[1], mat[4], mat[5], mat[12], mat[13]);
  ctx.strokeStyle = color;
  ctx.strokeRect(-size / 2, -size / 2, size, size);
}
body { margin: 0; }
canvas { width: 100vw; height: 100vh; display: block; }
<script src="https://twgljs.org/dist/4.x/twgl-full.js"></script>
<canvas></canvas>

这是一个带有显式推送的

'use strict';
const ctx = document.querySelector('canvas').getContext('2d');
const m4 = twgl.m4;

const stack = [m4.identity()];
const current = () => stack[stack.length - 1];
const currentCopy = () => stack[stack.length - 1].slice();
const push = () => stack.push(currentCopy());
const pop = () => stack.pop();
const identity = () => m4.identity(current());
const translate = (t) => m4.translate(current(), t, current());
const rotate = (axis, r) => m4.axisRotate(current(), axis, r, current());
const zAxis = [0, 0, 1];

function render(time) {
  time *= 0.001; // convert to seconds
  
  twgl.resizeCanvasToDisplaySize(ctx.canvas);
  const {width, height} = ctx.canvas;
  ctx.setTransform(1, 0, 0, 1, 0, 0);
  ctx.clearRect(0, 0, width, height);
  
  let sunMat;
  let earthMat;
  let moonMat;
  
  push();
  // translate to center of solar system
  translate([width / 2, height / 2, 0]);
    push();
      rotate(zAxis, time * 4);  // sun rotation
      sunMat = currentCopy();
    pop();  // sun rotation

    rotate(zAxis, time / 2) // earth orbit rotation
    translate([height / 4, 0, 0]); // earth translation
    
    push();
      rotate(zAxis, time * 8); // earth rotation
      earthMat = currentCopy();  //
    pop(); // earth rotation
 
    push();
      rotate(zAxis, time * 2); // moon orbit rotation
      translate([height / 8, 0, 0]); // moon translation
      rotate(zAxis, time * 16); // moon rotation
      moonMat = currentCopy(); 
    pop(); 
  pop();  
  
  drawSquare(sunMat, 40, 'orange');
  drawSquare(earthMat, 20, 'blue');
  drawSquare(moonMat, 10, 'gray');

  requestAnimationFrame(render);
}

requestAnimationFrame(render);

function drawSquare(mat, size, color) {
  ctx.setTransform(mat[0], mat[1], mat[4], mat[5], mat[12], mat[13]);
  ctx.strokeStyle = color;
  ctx.strokeRect(-size / 2, -size / 2, size, size);
}
body { margin: 0; }
canvas { width: 100vw; height: 100vh; display: block; }
<script src="https://twgljs.org/dist/4.x/twgl-full.js"></script>
<canvas></canvas>