前言
之前,我们说过canvas和css和webgl坐标系的转化,其实在绘图的过程中,我们有时候可以根据物体本身作为一个坐标系来进行绘图,能够更加简单的实现我们想要的效果。我简单举个我们前端都知道的例子,比如我们在绘制普通的前端页面的时候,有一个父级dom,和子级的dom,子级dom的位置是相对于父级偏移的,我们这时候就给父级dom会添加一个position:'relative'
,子级的dom添加position:'absolute'
来使用,让子级更好的画出来,而不是使用子级dom相对于body
来进行位置偏移。
已知某点的本地坐标系求世界坐标系
举例说明
就比如这样一个立方体
我们以他的中心点到上平面交接的点,作为我们这个点的观察点,在他的本地坐标系中,它的位置是(0,1,0),(我们假设立方体的高度为2),他本地坐标的位置是(5,0,0),我们可以经过自己的理解,它的世界坐标系原点是(0,0,0),得到它的位置世界坐标系是(5,1,0)
又比如我们将这个立方体向上(y)移动了5,那么我们可以得到的是,本地坐标系变成了(5,5,0),在本地坐标系中,它的位置依旧是(0,1,0),而它的世界坐标位置是(5,6,0)
通过以上俩个例子,我们大概可以得到本地坐标和世界坐标转化的规律,也就是本地坐标系的位置减去世界坐标系位置加上这个的本地坐标。
1 2
| (5,1,0)=(5,0,0)-(0,0,0)+(0,1,0); (5,6,0)=(5,5,0)-(0,0,0)+(0,1,0);
|
公式推演
那转化为矩阵的写法,那就是:
已知:
- 世界坐标系[O1;i1,j1,k1]
- 点P
- 点P所处的本地坐标系是[O2;i2,j2,k2]
- 世界坐标系[O1;i1,j1,k1]∋本地坐标系[O2;i2,j2,k2]
[O;i,j,k]中:
根据空间向量分解定理。
由世界坐标系[O1;i1,j1,k1]可解析出四维矩阵m1:
1 2 3 4 5 6
| [ i1.x,j1.x,k1.x,0, i1.y,j1.y,k1.y,0, i1.z,j1.z,k1.z,0, O1.x,O1.y,O1.z,1 ]
|
同理,由本地坐标系[O2;i2,j2,k2]可解析出四维矩阵m2:
1 2 3 4 5 6
| [ i2.x,j2.x,k2.x,0, i2.y,j2.y,k2.y,0, i2.z,j2.z,k2.z,0, O2.x,O2.y,O2.z,1 ]
|
点P的世界位是:
公式验证
在上一步我们推导出来了一个公式,这里我们验证一下这个公式,我们将i1,j1,k1分别定义为单位向量,P点本地坐标为(4,5,6),世界坐标系为(0,0,0),本地坐标系(1,2,3),根据我们第一步的计算,可以得到P点的世界坐标位置为(1-0+4,2-0+5,3-0+6)=(5,7,9)
我们接下来用矩阵来计算一下。
1 2 3 4
| O1(0,0,0) i1(1,0,0) j1(0,1,0) k1(0,0,1)
|
此时,矩阵为
1 2 3 4 5 6
| [ 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 ]
|
同理,将i2,j2,k2也变成单位向量
1 2 3 4
| O2(1,2,3) i2(1,0,0) j2(0,1,0) k2(0,0,1)
|
此时,矩阵为
1 2 3 4 5 6
| [ 1,0,0,0, 0,1,0,0, 0,0,1,0, 1,2,3,1 ]
|
然后我们用矩阵计算得到的
1 2 3
| P1=m1*m2*(4,5,6) P1=(1+4,2+5,3+6) P1=(5,7,9)
|
m1*m2为,因为矩阵乘以单位矩阵等于它本身
1 2 3 4 5 6
| [ 1,0,0,0, 0,1,0,0, 0,0,1,0, 1,2,3,1 ]
|
然后再乘以(4,5,6)
[
1,0,0,0,
0,1,0,0
0,0,1,0
4,5,6,1
]
因为这都是列主序的矩阵,我们大学平时学的都是行主序的矩阵,你可以先转化一下再计算。计算规则就是每行乘以每一列,如下图
那么就可以得到P点的世界坐标系点位是(5,7,9),和我们猜想的一样。
借助Threejs验证公式
导入
1
| import { Group, Matrix4, Object3D,Scene, Vector3, } from 'https://unpkg.com/three/build/three.module.js';
|
创建世界坐标系和本地坐标系
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| //世界坐标系 const m1 = new Matrix4() m1.elements = [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ]
//本地坐标系 const m2 = new Matrix4() m2.elements = [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 2, 3, 1 ]
|
申明P的位置
1
| const P2 = new Vector3(4, 5, 6)
|
世界坐标系加入到场景中
1 2
| const universe = new Scene() universe.applyMatrix4(m1)
|
applyMatrix4() 通过四维矩阵赋予对象坐标系
创建一个组放本地坐标系
1 2
| const galaxy = new Group() galaxy.applyMatrix4(m2)
|
创建点位
1 2
| const sun = new Object3D() sun.position.copy(P2)
|
设置包含关系
1 2
| galaxy.add(sun) universe.add(galaxy)
|
得到世界位置
1 2 3 4
| const P1 = new Vector3() sun.getWorldPosition(P1) console.log(P1); //{x:5,y:7,z:9}
|
这个结果和我们之前推理的是一样的。
位移法则
有了上面的讲解,我们大概可以联想到位移,就是将本地坐标系和世界坐标系进行相对位置的移动。那么我们可以多位移几次看看
m1
1 2 3 4 5 6 7
| [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ]
|
m2
1 2 3 4 5 6
| [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 2, 3, 1 ]
|
m3
1 2 3 4 5 6
| [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 4, 5, 6, 1 ]
|
p
1 2 3 4 5 6
| [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 7, 8, 9, 1 ]
|
得到位置
1 2 3
| P1=m1*m2*m3*(7,8,9) P1=(1+4+7,2+5+8,3+6+9) P1=(12,15,18)
|
借助Threejs验证
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| import { Group, Matrix4, Object3D, Scene, Vector3, } from 'https://unpkg.com/three/build/three.module.js';
const m1 = new Matrix4() m1.elements = [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ]
const m2 = new Matrix4() m2.elements = [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 2, 3, 1 ]
const m3 = new Matrix4() m3.elements = [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 4, 5, 6, 1 ]
const P3 = new Vector3(7, 8, 9)
const universe = new Scene() universe.applyMatrix4(m1) console.log(universe.position) console.log(universe.matrix)
const galaxy = new Group() galaxy.applyMatrix4(m2)
const solar = new Group() solar.applyMatrix4(m3)
const earth = new Object3D() earth.position.copy(P3)
solar.add(earth) galaxy.add(solar) universe.add(galaxy)
const P1 = new Vector3() earth.getWorldPosition(P1) console.log(P1);
|
缩放法则
修改之前已知条件:
在银河系的本地坐标系[O2;i2,j2,k2]
中,让j2是单位向量的2倍:
1 2 3 4
| O2(1,2,3) i2(1,0,0) j2(0,2,0) k2(0,0,1)
|
在太阳系的本地坐标系[O3;i3,j3,k3]
,让k3是单位向量的3倍:
1 2 3 4
| O3(4,5,6) i3(1,0,0) j3(0,1,0) k3(0,0,3)
|
求:地球的世界坐标位P1
解:
由银河系的本地坐标系可得矩阵m2:
1 2 3 4 5 6
| [ 1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 1, 0, 1, 2, 3, 1 ]
|
由太阳系的本地坐标系可得矩阵m3:
1 2 3 4 5 6
| [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 3, 0, 4, 5, 6, 1 ]
|
求地球的世界坐标位P1:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| P1=m1*m2*m3*(7,8,9) m1*m2*m3=[ 1, 0, 0, 0, 0, 2, 0, 0 0, 0, 3, 0 4+1,2*5+2,6+3,1 ] m1*m2*m3=[ 1,0, 0,0, 0,2, 0,0, 0,0, 3,0, 5,12,9,1 ] P1=(7+5,16+12,27+9) P1=(12,28,36)
|
测试
基于“位移法则”的three.js代码改改:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| //本地坐标系-银河系 const m2 = new Matrix4() m2.elements = [ 1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 1, 0, 1, 2, 3, 1 ]
//本地坐标系-太阳系 const m3 = new Matrix4() m3.elements = [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 3, 0, 4, 5, 6, 1 ]
|
得到的位置依旧是P1=(12,28,36)
,可见缩放对我们求某点(已知本地坐标)求世界坐标是没有影响的,也就可以得到一个本地坐标转世界坐标的一个公式
旋转法则
我们在有了这个公式
就可以用旋转来计算本地坐标转世界坐标位置了
修改之前已知条件:
让银河系的本地坐标系[O2;i2,j2,k2]
绕j2轴逆时针旋转20°。
设:c2=cos(-20°),s2=sin(-20°)
则:
1 2 3 4
| O2(1,2,3) i2(c2,0,-s2) j2(0,1,0) k2(s2,0,c2)
|
让太阳系的本地坐标系[O3;i3,j3,k3]
绕k3轴逆时针旋转30°
设:c3=cos(30°),s3=sin(30°)
则:
1 2 3 4
| O3(4,5,6) i3(c3,-s3,0) j3(s3,c3,0) k3(0,0,1)
|
求:地球的世界坐标位P1
解:
由银河系的本地坐标系可得矩阵m2:
1 2 3 4 5 6
| [ c2, 0, s2, 0, 0, 1, 0, 0, -s2,0, c2, 0, 1, 2, 3, 1 ]
|
由太阳系的本地坐标系可得矩阵m3:
1 2 3 4 5 6
| [ c3, s3, 0, 0, -s3, c3, 0, 0, 0, 0, 1, 0, 4, 5, 6, 1 ]
|
求地球的世界坐标位P1:
1 2 3 4 5 6 7
| P1=m1*m2*m3*(7,8,9) m1*m2*m3=[ c2*c3, s3, s2*c3, 0, -c2*s3, c3, -s2*s3, 0, -s2, 0, c2, 0, c2*4-s2*6+1,5+2,s2*4+c2*6+3,1 ]
|
P1=(11.826885919330648,17.428203230275507,15.02200238270646)
注,上式很难像之前那样心算,可以直接用计算机算:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| //让银河系的本地坐标系[O2;i2,j2,k2]绕j2轴逆时针旋转20° const ang2 = -20 * Math.PI / 180 const c2 = Math.cos(ang2) const s2 = Math.sin(ang2)
//让太阳系的本地坐标系[O3;i3,j3,k3]绕k3轴逆时针旋转30° const ang3 = 30 * Math.PI / 180 const c3 = Math.cos(ang3) const s3 = Math.sin(ang3)
const m=new Matrix4() m.elements = [ c2 * c3, s3, s2 * c3, 0, -c2 * s3, c3, -s2 * s3, 0, -s2, 0, c2, 0, c2 * 4 - s2 * 6 + 1, 5 + 2, s2 * 4 + c2 * 6 + 3, 1 ] const P1 = P3.applyMatrix4(m) console.log(P1);
|
Three验证
1 2 3 4 5 6 7 8 9 10 11
| //本地坐标系-银河系 const ang2 = 20 * Math.PI / 180 const m2 = new Matrix4() m2.makeRotationY(ang2) m2.setPosition(1, 2, 3)
//本地坐标系-太阳系 const ang3 = 30 * Math.PI / 180 const m3 = new Matrix4() m3.makeRotationZ(ang3) m3.setPosition(4, 5, 6)
|
我们可以得到,俩个得到的结果是一样的
公式总结
本文一整篇下来,就是为了得到一个公式:
本地坐标转世界坐标的底层实现就是
结语
本篇文章就到这里了,更多内容敬请期待,债见~