如何计算 three.js 中闭合形状的法线?
How can I calculate normals of closed shape in three.js?
我正在尝试为自己的文件格式编写自己的网格导入器。在我的文件格式中没有正常数据。所以我正在尝试计算接近形状的法线并将这些法线应用于网格。
我的解决方案是为几何体的每个面计算法向量,并从这些面的中间沿法向量的方向创建光线投射。如果该射线投射到某物(另一个平面),则表示该方向在内部。在那种情况下,我会正常翻转,如果它没有击中某些东西,我就这样离开。
虽然我用这个逻辑编写了一个函数,但法线根本没有改变。
function calculateNormals(object){
for (var i = 0; i < object.geometry.faces.length; i++) {
var vertices= object.geometry.vertices;
var face=object.geometry.faces[i];
var a=vertices[face.a];
var b=vertices[face.b];
var c=vertices[face.c];
console.log(face.a+" "+face.b+" "+face.c+" "+face.normal.z);
console.log(face);
console.log(face[4]);
var edge0=new THREE.Vector3(0,0,0);
edge0.subVectors(a,b);
var edge1=new THREE.Vector3(0,0,0);
edge1.subVectors(b,c);
var planeNormal=new THREE.Vector3(0,0,0)
planeNormal.crossVectors(edge0,edge1);
// console.log(planeNormal);
//Raycast from middle point towards plane nrmal direction
//If it hits anything it means normal direction is wrong
var midPoint=calculateMiddlePoint([a,b,c]);
var raycaster = new THREE.Raycaster(midPoint,planeNormal);
var intersects = raycaster.intersectObjects([object]);
if(intersects.length==0){
console.log("Normal is true");
face.normal=planeNormal;
}else{
console.log("Normal is wrong, you should flip normal direction, length: "+intersects.length);
console.log("Old face");
console.log(face.normal);
var newNormal=new THREE.Vector3(-1*planeNormal.x,-1*planeNormal.y,-1*planeNormal.z);
console.log(newNormal);
face.normal=newNormal;
console.log("new face");
console.log(face.normal);
console.log(face);
}
object.geometry.faces[i]=face;
// console.log(object.geometry.faces);
};
return object;
}
Matey 提出了一个很好的观点。缠绕顺序决定了面法线,这意味着哪一侧被认为是 "front." 顶点法线用于着色(例如 MeshPhongMaterial
)。如果你的顶点法线指向与你的面法线相反的方向,你最终会得到意想不到的结果(从糟糕的阴影到全黑的脸)。
综上所述,Geometry
具有计算法线的辅助函数。
Geometry.computeFaceNormals
(根据缠绕顺序)
Geometry.computeFlatVertexNormals
(将顶点法线设置为与关联的面法线相同)
Geometry.computeVertexNormals
(设置顶点法线为其周围面法线的平均值)
计算完法线后,您可以进行第二次尝试并更正它们,方法是重新排序顶点(以更正面法线),或者自己重新计算顶点法线。
我正在尝试为自己的文件格式编写自己的网格导入器。在我的文件格式中没有正常数据。所以我正在尝试计算接近形状的法线并将这些法线应用于网格。
我的解决方案是为几何体的每个面计算法向量,并从这些面的中间沿法向量的方向创建光线投射。如果该射线投射到某物(另一个平面),则表示该方向在内部。在那种情况下,我会正常翻转,如果它没有击中某些东西,我就这样离开。
虽然我用这个逻辑编写了一个函数,但法线根本没有改变。
function calculateNormals(object){
for (var i = 0; i < object.geometry.faces.length; i++) {
var vertices= object.geometry.vertices;
var face=object.geometry.faces[i];
var a=vertices[face.a];
var b=vertices[face.b];
var c=vertices[face.c];
console.log(face.a+" "+face.b+" "+face.c+" "+face.normal.z);
console.log(face);
console.log(face[4]);
var edge0=new THREE.Vector3(0,0,0);
edge0.subVectors(a,b);
var edge1=new THREE.Vector3(0,0,0);
edge1.subVectors(b,c);
var planeNormal=new THREE.Vector3(0,0,0)
planeNormal.crossVectors(edge0,edge1);
// console.log(planeNormal);
//Raycast from middle point towards plane nrmal direction
//If it hits anything it means normal direction is wrong
var midPoint=calculateMiddlePoint([a,b,c]);
var raycaster = new THREE.Raycaster(midPoint,planeNormal);
var intersects = raycaster.intersectObjects([object]);
if(intersects.length==0){
console.log("Normal is true");
face.normal=planeNormal;
}else{
console.log("Normal is wrong, you should flip normal direction, length: "+intersects.length);
console.log("Old face");
console.log(face.normal);
var newNormal=new THREE.Vector3(-1*planeNormal.x,-1*planeNormal.y,-1*planeNormal.z);
console.log(newNormal);
face.normal=newNormal;
console.log("new face");
console.log(face.normal);
console.log(face);
}
object.geometry.faces[i]=face;
// console.log(object.geometry.faces);
};
return object;
}
Matey 提出了一个很好的观点。缠绕顺序决定了面法线,这意味着哪一侧被认为是 "front." 顶点法线用于着色(例如 MeshPhongMaterial
)。如果你的顶点法线指向与你的面法线相反的方向,你最终会得到意想不到的结果(从糟糕的阴影到全黑的脸)。
综上所述,Geometry
具有计算法线的辅助函数。
Geometry.computeFaceNormals
(根据缠绕顺序)
Geometry.computeFlatVertexNormals
(将顶点法线设置为与关联的面法线相同)
Geometry.computeVertexNormals
(设置顶点法线为其周围面法线的平均值)
计算完法线后,您可以进行第二次尝试并更正它们,方法是重新排序顶点(以更正面法线),或者自己重新计算顶点法线。