网站建设岗位能力评估表,找做网站的客户,电子商务网站建设项目的阶段的划分,百度助手本文紧接上篇#xff1a;
(5.1):THREE.Line2又一坑#xff1a;镜像后不见了
本文将解答上篇提到的3个问题#xff0c;首先回答第二个问题#xff0c;如何获取全局的缩放值。
scaleWorld这个玩意儿呢#xff0c;three.js官方就没提供了。应该说#xff0c;一般的渲染引…本文紧接上篇
(5.1):THREE.Line2又一坑镜像后不见了
本文将解答上篇提到的3个问题首先回答第二个问题如何获取全局的缩放值。
scaleWorld这个玩意儿呢three.js官方就没提供了。应该说一般的渲染引擎都不会弄这个而是把所有的变换都统一由matrixWorld来提供。
从矩阵提取缩放值three.js也是提供了api叫decompose(position:THREE.Vector3, quaternion:THREE.Quaternion, scale:THREE.Vector3)
使用者需要在外部创建好两个Vector3和一个Quaternion对象四元数处理旋转的本文不展开聊然后将其传入到对应的参数中decompose方法将往这3个对象中写入值。
上文中的updateFace方法调整如下
function updateFace(){var position new THREE.Vector3();var rotation new THREE.Quaternion();//这个我们无视按类型要求传进去就是var scale new THREE.Vector3();container.updateMatrixWorld();line2.updateMatrixWorld();line2.matrixWorld.decompose(position, rotation, scale);line2Material.side scale.x * scale.y * scale.z 0 ? THREE.FrontSide : THREE.BackSide;
}
这样的做法比上文提到的要优雅一些至少它的编码没那么硬了。 在回答剩下的两个问题之前笔者先给大家简单介绍下原生WebGL处理正反面显示的两个重要的api
1 gl.cullFace(face)
渲染时需要剔除哪个面有效值为
gl.BACK绘制正面剔除背面默认,
gl.FRONT绘制背面剔除正面
gl.FRONT_AND_BACK正面和背面都剔除这是画了个寂寞本文不聊这个
如果要开启双面那就是什么都不剔除此处没有一个值而是用gl.enable(gl.CULL_FACE)和gl.disable(gl.CULL_FACE)代之。
2 gl.frontFace(clockwise)
WebGL底层通过三角面三个点在投影到屏幕上的顺逆时针顺序来定义正反面默认设置为顺时针代表正面逆时针代表反面。然后可以通过该api去修改这一设置。
clockwise参数的有效值为gl.CW顺时针默认gl.CCW逆时针
因为在上文的例子中Mesh也是做了负缩放也没开双面材质按道理它镜像后是不可见的所以three.js的底层会通过这两个处理正反面的api修复镜像后不能正确显示的问题。
笔者的摸索过程就不跟大家啰嗦了直接告诉大家定位到代码在哪一共3处 WebGLRenderer是渲染的核心类但却看到了object.isMesh这样的补丁打在上面所以架构上显得封装性不强也不健壮也许是性能使然先不纠结这事。我们可以看到当被渲染的对象是Mesh的时候正反面的逻辑依赖于世界矩阵的determinant函数返回值的符号。
determinant的实现代码如下 如果你的线性代数还没完全还给老师的话那这个式子你应该能看出来个所以然就是矩阵M的行列式数学概念叫秩记为detMdet是determinant的简写。
如果你是个数学学霸那大概还会记得正定矩阵和负定矩阵的概念。实际上它可以准确匹配到物体的正负缩放上。其证明过程笔者没有很轻易地通过搜索引擎获取得到所以后面笔者会单开一篇文章给大家推导一遍。
言归正传这里加了个补丁isMesh那是不是再加个isLine2问题就解决了
很遗憾事情没有想象中那么简单因为Line2就是Mesh的子类 那我们试试排除Line2 测试发现这样做的确可以把问题解决掉。
Line2之所以不应该跟随镜像调整正反面是因为Line2虽然也是个矩形面片但它的坐标值是动态计算的先把端点的位置通过世界矩阵投影矩阵算好到屏幕的2D画布上然后再向着固定的方向生成4个点所以不管Line2缩放的符号是什么4个点的绕序都是固定的。如果换成单面材质的PlaneMesh那么你会发现相机旋转个180度之后PlaneMesh就会看不见了而且“线”的粗细还会随着镜头的移动而发生变化。
var line2Geometry new THREE.PlaneGeometry(100, 5);
var line2Material new THREE.MeshBasicMaterial({color: 0xFF6600});
var line2 new THREE.Mesh(line2Geometry, line2Material);
line2.position.set(60, -15, 0);
line2.rotation.set(0, 0, Math.PI / 3);
container.add(line2); 综上所述就是先算全局点再按粗细偏移出来的Line2面片绕序不受矩阵影响。但是先把面片4个点确定下来再各自计算全局坐标的PlaneMesh绕序就受矩阵影响了。
好了这下笔者也打了补丁这下打得更离谱核心类去引用examples里面的特殊类型进行处理。笔者不忍心这样破坏它就给Material加了一个属性叫autoFlipFrontFace默认为true设置为false时不调整正反面的绕序。 然后给LineMaterial的这一属性设置为false。
写本文的时候笔者再次审视这里的代码认为更合理的做法是在LineMaterial中通过判断matrixWorld的determinant值来控制面片4个顶点的生成方向。此法对架构的破坏力最小但写起来相当麻烦想要把封装性做好还要以牺牲性能为代价放到博客上的可观赏性也很差就干脆偷个懒好了。
下面来小结一下本文包括上文
1 THREE.MeshTHREE.Line镜像后都能正常显示唯独THREE.Line2会消失
2 THREE.Line走的是原生画线api不受正反面问题的影响
3 THREE.Mesh和THREE.Line2都是面片
4 THREE.Mesh镜像后面片的点绕序会发生变更底层通过gl.frontFace进行修正
5 THREE.Line2镜像后面片的点绕序不发生变更但因为它继承了THREE.Mesh所以也被误修了
6 镜像后的负缩放判断除了用不地道的scale乘积还可以用更硬核的determinant方法数学上它是个行列式
小结完了下篇笔者会跟大家专门探讨determinant可用于判断负缩放的原因过程有点复杂笔者会先从2D开始循序渐进让大家的消化曲线趋于平缓。
明天就是除夕了提前祝大家新春快乐蛇全蛇美