在 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
我不熟悉你的矩阵数学库,但名为 setTranslate
和 setRotate
的函数听起来不像是合适的函数。他们听起来像是设置平移或设置旋转而不是将现有矩阵乘以平移矩阵或旋转矩阵。
在大多数 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>
我正在制作只包含太阳(点光源)、地球和月亮的太阳系模拟,我可以在屏幕上成功地看到这些星星,当我只对它们应用翻译时
这是示例屏幕
但是当我将轨道变换添加到地球后,我得到了这个奇怪的结果...
好像地球突然卡在了太阳里,但至少我能看到月亮不知怎么在绕着地球转
当我也对月球应用轨道变换时,我得到了这个结果..
如你所见,地球和月球都被太阳困住了...
这是代码和树,我希望如何组织它们的层次转换..
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
我不熟悉你的矩阵数学库,但名为 setTranslate
和 setRotate
的函数听起来不像是合适的函数。他们听起来像是设置平移或设置旋转而不是将现有矩阵乘以平移矩阵或旋转矩阵。
在大多数 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>