本打算从 Object3D.js 入手,奈何发现其中好多基础的矩阵操作不明白,还是从基础的数学操作开始吧。
Math.js
该文件中基本上是一些简单数学运算的实现。
generateUUID()
蛮有趣的一个函数,用JS生成UUID,参考了StackOverflow上的回答:https://stackoverflow.com/questions/105034/create-guid-uuid-in-javascript/21963136#21963136。可见,如果想要优化JS的运行性能的话,还是有好多可以考虑的事情。最前边的那个 _lut 是LookUp Table的意思,也是为这个函数服务的。
euclideanModule(n, m)
第一次接触到这个东西,应该是对取余操作做了一个round操作?反正就是不会出现负值:
THREE.Math.euclideanModulo(-3, 2)// 1-3 % 2// -1THREE.Math.euclideanModulo(-2, 2)// 0-2 % 2// -0
另外,这里关于JavaScript的+0和-0还是一个蛮有趣的点:https://stackoverflow.com/questions/7223359/are-0-and-0-the-same
isPowerOfTwo(val)
判断是不是2的幂次,值得稍微学习一下:
( value & ( value - 1 ) ) === 0 && value !== 0;
其它
其它的应该都比较直观,没有很多值得关注的地方:
clampmapLinearlerp: 线性插值,这名字取得。。。smoothstep,smootherstep:平滑的差值吧算是,挺常用的randInt,randFloat,randFloatSpreaddegToRad,radToDegceilPowerOfTwo,floorPowerOfTwo
Vector2.js
就是个简单的长度为2的向量类,作为一个3D的库,这些基本数据结构还是要有的。
width, height
为类添加了width和height属性,并设置了setter和getter。不太清楚这个的具体作用,感觉有些鸡肋?可能会在某些场合用到并。
isVector2
Three基本上为每个类都有一个 isXXX 的属性,用来判断object的类型,感觉可以学习借鉴。
clone()
clone一个对象如何操作?学习了!
clone: function () {return new this.constructor( this.x, this.y );},
addVectors(a, b)
它是设置了当前对象作为运算的接受者,那样的话,如果想从a,b创建一个新的vector,就得先new出来。
对于这样的实现,我不知道是设计者有意为之,还是受限于JS的语法?我感觉ES6的Class的static method更优雅的样子。
链式调用
可能这算是一种风格吧,虽然感觉这样改来改去的有时候会乱。调用的对象可能会改变,但是传入的对象不会改变,比如:
lerpVectors: function ( v1, v2, alpha ) {return this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 );},
rotateAround(center, angle)
二维的旋转矩阵:https://en.wikipedia.org/wiki/Rotation_matrix
Vector3.js
transformDirection(m)
仿射变换,y = Ax, 这里把向量当作方向,相当于变换到新空间中
projectOnVector(v)
复习一下向量投影的公式:https://en.wikipedia.org/wiki/Vector_projection
_
projectOnPlane(planeNormal)
原始向量-到法线到投影向量,就是到平面的投影向量了。
reflect(normal)
镜像向量的话,减去2倍的到法线投影向量就OK了,很优雅
setFromMatrixPosition(m)
指的是平移变换的分量:
setFromMatrixScale(m)
获取缩放变换的方法:每列的长度!平移和旋转都不改变前三列的向量长度。
Vector4.js
特别的地方不多,主要有两个地方。
setAxisAngleFromQuaternion(q)
AxisAngle是Three中对旋转呢的另一种表示方式,vector4的前三个分量表示旋转的Axis,最后一个是旋转的角度。
这里有相应的转换公式:http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm
setAxisAngleFromRotationMatrix(m)
也是将另一种表示旋转的方法表示成AxisAngle
Matrix3.js
矩阵这个数据结构在Three中相当重要,好多变换操作都离不开矩阵。
set(n11, n12, ...)
因为在矩阵运算中,如果是column-major的矩阵话,效率会高一点(虽然具体怎么高不太清楚,猜测是因为3D变换的一些矩阵运算用到的成列读取的更多吧)所以Three在内部实现中使用的是column-major的,但是在给用户的API中,为了照顾用户习惯,是row-major的方式排列参数顺序的,这一点需要注意。
premultiply(m)
左乘的意思,与 multiply(m) 刚好相反
transposeIntoArray(r)
这里相当于transpose然后存到数组中,其实意思就是按照row-major的方式存储为数组,而 toArray() 是按照column-major的方式(也就是内部存储的方式)。
getNormalMatrix(mat4)
return this.setFromMatrix4( matrix4 ).getInverse( this ).transpose();
normal matrix是个数学概念,指的是一个矩阵求逆之后,再求转置。具体的用法似乎在图形学里边用到挺多的。
https://paroj.github.io/gltut/Illumination/Tut09%20Normal%20Transformation.html
http://www.lighthouse3d.com/tutorials/glsl-12-tutorial/the-normal-matrix/
啊 第一个教程说的太好了!!normal matrix,应该就是对法线应用的变换矩阵。
如图所示,如果只是对法线应用物体的变换的话,法线信息会错误(中间的图),我们更希望得到右边的图,然后经过一番推导,就可以得到这个normal matrix就是逆的转置。
setUvTransform(tx, ty, sx, sy, rotation, cx, cy)
UV坐标变换,实现可以借鉴一下,不过对于UV坐标自己还不是太熟悉,需要进一步学习。
scale , rotate , translate
有点奇怪,文档中并没有说明什么,应该deprecated了
git commit说是另一种设定贴图变换的API
Matrix4.js
Matrix4在Three中用到的应该更多,因为图形学里的变换都采用4x4的矩阵。
extractRotation
去除translate分量(最后一列)和scale分量(作归一化操作),就得到了rotation的变换
getInverse(m)
求逆的优化:http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm
makeRotation...()
旋转公式,绕固定轴以及任意轴
makeShear(x, y, z)
Shear变换的方程
https://www.tutorialspoint.com/computer_graphics/3d_transformation.htm
makePerspective(...)
生成透视投影矩阵
https://www.scratchapixel.com/lessons/3d-basic-rendering/perspective-and-orthographic-projection-matrix/building-basic-perspective-projection-matrix
https://www.scratchapixel.com/lessons/3d-basic-rendering/perspective-and-orthographic-projection-matrix/opengl-perspective-projection-matrix
具体推导自己有功夫再看,不过这个系列教程讲的似乎很可以,循序渐进!
makeOrthographic(...)
生成Orthographic的投影矩阵
https://en.wikipedia.org/wiki/Orthographic_projection
相比Perspective简单很多,主要是两个操作:
- scale:归一化到-1,1的空间中
- translate:将坐标中心移到原点
