在 Processing 中为 PMatrix3D 创建一个 rotate3D() 函数
Creating a rotate3D() function for PMatrix3D in Processing
前段时间,我仅基于 CSS 转换编写了一个有点烦躁的徽标。
你可以 fiddle 超过 https://document.paris/
效果感觉不错,click/touch拖动旋转logo感觉很自然
我记得我用头撞墙,直到我发现我可以很容易地链接 CSS 变换,只需链接它们即可。
transform: matrix3d(currentMatrix) rotate3d(x, y, z, angle);
最重要的是,为了获得 currentMatrix,我会简单地用 jQuery 做 m = $('#logobackground').css('transform');
,浏览器会神奇地 return 计算矩阵而不是原始矩阵“css " 这实际上避免了我处理矩阵或无限堆叠 rotate3D() 属性。
所以最难的部分是根据鼠标输入计算 rotate3D 参数(x、y、z、角度)。理论上,将这部分转换为 java 应该没有问题,所以我将跳过它。
现在
我正在尝试使用 Processing 做完全相同的事情,但有两个问题:
- 处理中没有rotate3D()。
- 没有浏览器可以自动 apply/chain 转换和 return 当前矩阵状态。
这是我正在处理的 plan/implementation :
我需要一个“currentMatrix”来将每一帧应用于场景
PMatrix3D currentMatrix = new PMatrix3D();
在 setup()
中,我将其设置为“单位矩阵”,根据我的理解,这相当于“无转换”。
// set currentMatrix to identity Matrix
currentMatrix.set(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
每一帧我都会计算一个变换矩阵并将其应用于 currentMatrix。
然后我会把这个矩阵应用到场景中。
// Apply Matrix to the currentMatrix
void mouseRotate() {
float diag = sqrt(pow(width,2)+pow(height,2));
float x = deltaX()/ diag * 10; // deltaX = difference between previous prevous MouseX and current mouseX)
float y = deltaY()/ diag * 10; // deltaY = same with Y axis
float angle = sqrt( pow(x, 2) + pow(y, 2) );
currentMatrix.apply( rotate3D(y,x,0,angle) );
}
// Apply Matrix to the scene
applyMatrix(currentMatrix);
PMatrix3D 参考 : https://processing.github.io/processing-javadocs/core/processing/core/PMatrix3D.html
ApplyMatrix() 参考:https://processing.org/reference/applyMatrix_.html
然后我需要做的就是将 rotate3D css 变换实现为一个函数,该函数 return 是一个变换矩阵。
基于我在此页面上找到的内容 https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/rotate3d()
我实现了第一个功能:
PMatrix3D rotate3D(float x, float y, float z, float a) {
PMatrix3D rotationMatrix = new PMatrix3D();
rotationMatrix.set(
1+(1-cos(a))*(pow(x,2)-1), z*sin(a)+x*y*(1-cos(a)), -y*sin(a)+x*z*(1-cos(a)), 0,
-z*sin(a)+x*y*(1-cos(a)), 1+(1-cos(a))*(pow(y,2)-1), x*sin(a)+y*z*(1-cos(a)), 0,
y*sin(a)+x*z*(1-cos(a)), -x*sin(a)+y*z*(1-cos(a)), 1+(1-cos(a))*(pow(z,2)-1), 0,
0,0,0,1
);
return rotationMatrix;
}
并根据我在此页面上找到的内容 https://drafts.csswg.org/css-transforms-2/#Rotate3dDefined
我实现了这个其他功能:
PMatrix3D rotate3Dbis(float getX, float getY, float getZ, float getA) {
float sc = sin(getA/2)*cos(getA/2);
float sq = pow(sin(getA/2),2);
float normalizer = sqrt( pow(getX,2) + pow(getY,2) + pow(getZ,2) );
float x = getX/normalizer;
float y = getY/normalizer;
float z = getZ/normalizer;
PMatrix3D rotationMatrix = new PMatrix3D();
rotationMatrix.set(
1-2*(pow(y,2)+pow(z,2))*sq, 2*(x*y*sq-z*sc), 2*(x*z*sq+y*sc), 0,
2*(x*y*sq+z*sc), 1-2*(pow(x,2)+pow(z,2))*sq, 2*(y*z*sq-x*sc), 0,
2*(x*z*sq-y*sc), 2*(y*z*sq+x*sc), 1-2*(pow(x,2)+pow(y,2)*sq), 0,
0, 0, 0, 1
);
return rotationMatrix;
}
测试时,它们不会用相同的输入产生完全相同的结果(尽管差异有点“对称”,这让我认为它们至少在某种程度上是等价的?)还有 rotate3Dbis () 倾向于生成 NaN 数字,尤其是当我不移动鼠标时 (x & y = 0)。
但最重要的是,最后还是不行。当我使用 rotate3D()
时,绘图不会旋转,而是逐渐缩小,并且 rotate3Dbis()
由于 NaN 而无法正确呈现。
总题:
我试图从了解转换矩阵的人那里获得指导,并试图缩小问题所在的范围。我对 rotate3D()
的 processing/java 实现是否存在缺陷?或者问题会来自其他地方吗?我的 rotate3D()
和 rotate3Dbis
函数是否等效?
部分答案是添加到第一个 rotate3D 函数的修复程序。
我需要标准化 x、y、z 值以避免奇怪的缩放。
我发布了代码的当前状态(为了简单起见,我跳过了一些部分):
// Mouse movement since last fame on X axis
float deltaX() {
return (float)(mouseX-pmouseX);
}
// Mouse movement since last fame on Y axis
float deltaY() {
return (float)(mouseY-pmouseY);
}
// Convert user input into angle and amount to rotate to
void mouseRotate() {
double diag = Math.sqrt(Math.pow(width,2)+Math.pow(height,2));
double x = deltaX()/ diag * 50;
double y = -deltaY()/ diag * 50;
double angle = Math.sqrt( x*x + y*y );
currentMatrix.apply( rotate3D((float)y,(float)x,0,(float)angle) );
}
// Convert those values into a rotation matrix
PMatrix3D rotate3D(float getX, float getY, float getZ, float getA) {
float normalizer = sqrt( getX*getX + getY*getY + getZ*getZ );
float x = 0;
float y = 0;
float z = 0;
if (normalizer != 0) {
x = getX/normalizer;
y = getY/normalizer;
z = getZ/normalizer;
}
float x2 = pow(x,2);
float y2 = pow(y,2);
float z2 = 0;
float sina = sin(getA);
float f1cosa = 1-cos(getA);
PMatrix3D rotationMatrix = new PMatrix3D(
1+f1cosa*(x2-1), z*sina+x*y*f1cosa, -y*sina+x*z*f1cosa, 0,
-z*sina+x*y*f1cosa, 1+f1cosa*(y2-1), x*sina+y*z*f1cosa, 0,
y*sina+x*z*f1cosa, -x*sina+y*z*f1cosa, 1+f1cosa*(z2-1), 0,
0, 0, 0, 1
);
return rotationMatrix;
}
// Draw
draw() {
mouseRotate();
applyMatrix(currentMatrix);
object.render();
}
我认为使用这种方法可以让我“堆叠”相对于屏幕而不是相对于对象的累积旋转。但是结果好像总是相对绘制的对象做旋转。
我没有使用相机,因为我基本上只想旋转物体本身。我实际上有点迷失了 atm 我应该旋转什么以及什么时候新应用的旋转是相对于用户的,并且之前应用的旋转是保守的。
正如您已经提到的,使用先前和当前的鼠标坐标,您可能只需在 X 轴和 Y 轴上旋转即可:
PVector cameraRotation = new PVector(0, 0);
void setup(){
size(900, 900, P3D);
rectMode(CENTER);
strokeWeight(9);
strokeJoin(MITER);
}
void draw(){
//update "camera" rotation
if (mousePressed){
cameraRotation.x += -float(mouseY-pmouseY);
cameraRotation.y += float(mouseX-pmouseX);
}
background(255);
translate(width * 0.5, height * 0.5, 0);
rotateX(radians(cameraRotation.x));
rotateY(radians(cameraRotation.y));
rect(0, 0, 300, 450);
}
您分享的 Document Paris 示例也使用了缓动。你可以看看这个minimal easing Processing example
这是上面应用了缓动的版本:
PVector cameraRotation = new PVector();
PVector cameraTargetRotation = new PVector();
float easing = 0.01;
void setup(){
size(900, 900, P3D);
rectMode(CENTER);
strokeWeight(9);
strokeJoin(MITER);
}
void draw(){
//update "camera" rotation
if (mousePressed){
cameraTargetRotation.x += -float(mouseY-pmouseY);
cameraTargetRotation.y += float(mouseX-pmouseX);
}
background(255);
translate(width * 0.5, height * 0.5, 0);
// ease rotation
rotateX(radians(cameraRotation.x -= (cameraRotation.x - cameraTargetRotation.x) * easing));
rotateY(radians(cameraRotation.y -= (cameraRotation.y - cameraTargetRotation.y) * easing));
fill(255);
rect(0, 0, 300, 450);
fill(0);
translate(0, 0, 3);
rect(0, 0, 300, 450);
}
此外,还有一个名为 PeasyCam 的库可以让这一切变得更简单。
如果您确实想使用 PMatrix3D 实现您自己的版本,这里有一些可以节省您时间的提示:
- 当你实例化时
PMatrix3D()
它是单位矩阵。如果您应用了转换并且想要 reset()
身份。
- 如果您想围绕
PMatrix3D()
轴旋转 rotate(float angleInRadians, float axisX, float axisY, float axisZ)
覆盖应该会有所帮助。
- 此外,您可以在没有
PMatrix3D
的情况下离开,因为 resetMatrix()
将重置全局转换矩阵,您可以直接调用 rotate(float angleInRadians, float axisX, float axisY, float axisZ)
。
前段时间,我仅基于 CSS 转换编写了一个有点烦躁的徽标。
你可以 fiddle 超过 https://document.paris/
效果感觉不错,click/touch拖动旋转logo感觉很自然
我记得我用头撞墙,直到我发现我可以很容易地链接 CSS 变换,只需链接它们即可。
transform: matrix3d(currentMatrix) rotate3d(x, y, z, angle);
最重要的是,为了获得 currentMatrix,我会简单地用 jQuery 做 m = $('#logobackground').css('transform');
,浏览器会神奇地 return 计算矩阵而不是原始矩阵“css " 这实际上避免了我处理矩阵或无限堆叠 rotate3D() 属性。
所以最难的部分是根据鼠标输入计算 rotate3D 参数(x、y、z、角度)。理论上,将这部分转换为 java 应该没有问题,所以我将跳过它。
现在
我正在尝试使用 Processing 做完全相同的事情,但有两个问题:
- 处理中没有rotate3D()。
- 没有浏览器可以自动 apply/chain 转换和 return 当前矩阵状态。
这是我正在处理的 plan/implementation :
我需要一个“currentMatrix”来将每一帧应用于场景
PMatrix3D currentMatrix = new PMatrix3D();
在 setup()
中,我将其设置为“单位矩阵”,根据我的理解,这相当于“无转换”。
// set currentMatrix to identity Matrix
currentMatrix.set(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
每一帧我都会计算一个变换矩阵并将其应用于 currentMatrix。 然后我会把这个矩阵应用到场景中。
// Apply Matrix to the currentMatrix
void mouseRotate() {
float diag = sqrt(pow(width,2)+pow(height,2));
float x = deltaX()/ diag * 10; // deltaX = difference between previous prevous MouseX and current mouseX)
float y = deltaY()/ diag * 10; // deltaY = same with Y axis
float angle = sqrt( pow(x, 2) + pow(y, 2) );
currentMatrix.apply( rotate3D(y,x,0,angle) );
}
// Apply Matrix to the scene
applyMatrix(currentMatrix);
PMatrix3D 参考 : https://processing.github.io/processing-javadocs/core/processing/core/PMatrix3D.html
ApplyMatrix() 参考:https://processing.org/reference/applyMatrix_.html
然后我需要做的就是将 rotate3D css 变换实现为一个函数,该函数 return 是一个变换矩阵。
基于我在此页面上找到的内容 https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/rotate3d()
我实现了第一个功能:
PMatrix3D rotate3D(float x, float y, float z, float a) {
PMatrix3D rotationMatrix = new PMatrix3D();
rotationMatrix.set(
1+(1-cos(a))*(pow(x,2)-1), z*sin(a)+x*y*(1-cos(a)), -y*sin(a)+x*z*(1-cos(a)), 0,
-z*sin(a)+x*y*(1-cos(a)), 1+(1-cos(a))*(pow(y,2)-1), x*sin(a)+y*z*(1-cos(a)), 0,
y*sin(a)+x*z*(1-cos(a)), -x*sin(a)+y*z*(1-cos(a)), 1+(1-cos(a))*(pow(z,2)-1), 0,
0,0,0,1
);
return rotationMatrix;
}
并根据我在此页面上找到的内容 https://drafts.csswg.org/css-transforms-2/#Rotate3dDefined
PMatrix3D rotate3Dbis(float getX, float getY, float getZ, float getA) {
float sc = sin(getA/2)*cos(getA/2);
float sq = pow(sin(getA/2),2);
float normalizer = sqrt( pow(getX,2) + pow(getY,2) + pow(getZ,2) );
float x = getX/normalizer;
float y = getY/normalizer;
float z = getZ/normalizer;
PMatrix3D rotationMatrix = new PMatrix3D();
rotationMatrix.set(
1-2*(pow(y,2)+pow(z,2))*sq, 2*(x*y*sq-z*sc), 2*(x*z*sq+y*sc), 0,
2*(x*y*sq+z*sc), 1-2*(pow(x,2)+pow(z,2))*sq, 2*(y*z*sq-x*sc), 0,
2*(x*z*sq-y*sc), 2*(y*z*sq+x*sc), 1-2*(pow(x,2)+pow(y,2)*sq), 0,
0, 0, 0, 1
);
return rotationMatrix;
}
测试时,它们不会用相同的输入产生完全相同的结果(尽管差异有点“对称”,这让我认为它们至少在某种程度上是等价的?)还有 rotate3Dbis () 倾向于生成 NaN 数字,尤其是当我不移动鼠标时 (x & y = 0)。
但最重要的是,最后还是不行。当我使用 rotate3D()
时,绘图不会旋转,而是逐渐缩小,并且 rotate3Dbis()
由于 NaN 而无法正确呈现。
总题:
我试图从了解转换矩阵的人那里获得指导,并试图缩小问题所在的范围。我对 rotate3D()
的 processing/java 实现是否存在缺陷?或者问题会来自其他地方吗?我的 rotate3D()
和 rotate3Dbis
函数是否等效?
部分答案是添加到第一个 rotate3D 函数的修复程序。 我需要标准化 x、y、z 值以避免奇怪的缩放。
我发布了代码的当前状态(为了简单起见,我跳过了一些部分):
// Mouse movement since last fame on X axis
float deltaX() {
return (float)(mouseX-pmouseX);
}
// Mouse movement since last fame on Y axis
float deltaY() {
return (float)(mouseY-pmouseY);
}
// Convert user input into angle and amount to rotate to
void mouseRotate() {
double diag = Math.sqrt(Math.pow(width,2)+Math.pow(height,2));
double x = deltaX()/ diag * 50;
double y = -deltaY()/ diag * 50;
double angle = Math.sqrt( x*x + y*y );
currentMatrix.apply( rotate3D((float)y,(float)x,0,(float)angle) );
}
// Convert those values into a rotation matrix
PMatrix3D rotate3D(float getX, float getY, float getZ, float getA) {
float normalizer = sqrt( getX*getX + getY*getY + getZ*getZ );
float x = 0;
float y = 0;
float z = 0;
if (normalizer != 0) {
x = getX/normalizer;
y = getY/normalizer;
z = getZ/normalizer;
}
float x2 = pow(x,2);
float y2 = pow(y,2);
float z2 = 0;
float sina = sin(getA);
float f1cosa = 1-cos(getA);
PMatrix3D rotationMatrix = new PMatrix3D(
1+f1cosa*(x2-1), z*sina+x*y*f1cosa, -y*sina+x*z*f1cosa, 0,
-z*sina+x*y*f1cosa, 1+f1cosa*(y2-1), x*sina+y*z*f1cosa, 0,
y*sina+x*z*f1cosa, -x*sina+y*z*f1cosa, 1+f1cosa*(z2-1), 0,
0, 0, 0, 1
);
return rotationMatrix;
}
// Draw
draw() {
mouseRotate();
applyMatrix(currentMatrix);
object.render();
}
我认为使用这种方法可以让我“堆叠”相对于屏幕而不是相对于对象的累积旋转。但是结果好像总是相对绘制的对象做旋转。
我没有使用相机,因为我基本上只想旋转物体本身。我实际上有点迷失了 atm 我应该旋转什么以及什么时候新应用的旋转是相对于用户的,并且之前应用的旋转是保守的。
正如您已经提到的,使用先前和当前的鼠标坐标,您可能只需在 X 轴和 Y 轴上旋转即可:
PVector cameraRotation = new PVector(0, 0);
void setup(){
size(900, 900, P3D);
rectMode(CENTER);
strokeWeight(9);
strokeJoin(MITER);
}
void draw(){
//update "camera" rotation
if (mousePressed){
cameraRotation.x += -float(mouseY-pmouseY);
cameraRotation.y += float(mouseX-pmouseX);
}
background(255);
translate(width * 0.5, height * 0.5, 0);
rotateX(radians(cameraRotation.x));
rotateY(radians(cameraRotation.y));
rect(0, 0, 300, 450);
}
您分享的 Document Paris 示例也使用了缓动。你可以看看这个minimal easing Processing example
这是上面应用了缓动的版本:
PVector cameraRotation = new PVector();
PVector cameraTargetRotation = new PVector();
float easing = 0.01;
void setup(){
size(900, 900, P3D);
rectMode(CENTER);
strokeWeight(9);
strokeJoin(MITER);
}
void draw(){
//update "camera" rotation
if (mousePressed){
cameraTargetRotation.x += -float(mouseY-pmouseY);
cameraTargetRotation.y += float(mouseX-pmouseX);
}
background(255);
translate(width * 0.5, height * 0.5, 0);
// ease rotation
rotateX(radians(cameraRotation.x -= (cameraRotation.x - cameraTargetRotation.x) * easing));
rotateY(radians(cameraRotation.y -= (cameraRotation.y - cameraTargetRotation.y) * easing));
fill(255);
rect(0, 0, 300, 450);
fill(0);
translate(0, 0, 3);
rect(0, 0, 300, 450);
}
此外,还有一个名为 PeasyCam 的库可以让这一切变得更简单。
如果您确实想使用 PMatrix3D 实现您自己的版本,这里有一些可以节省您时间的提示:
- 当你实例化时
PMatrix3D()
它是单位矩阵。如果您应用了转换并且想要reset()
身份。 - 如果您想围绕
PMatrix3D()
轴旋转rotate(float angleInRadians, float axisX, float axisY, float axisZ)
覆盖应该会有所帮助。 - 此外,您可以在没有
PMatrix3D
的情况下离开,因为resetMatrix()
将重置全局转换矩阵,您可以直接调用rotate(float angleInRadians, float axisX, float axisY, float axisZ)
。