在 three.js 中提高光线投射精度
improving raycasting precision in three.js
我正在使用重铸将光线从网格的每个面中点投射到网格本身,以找到局部厚度。有趣的是,由于网格排列,有时光线会在三角形之间穿过,并且不会显示正确的厚度。有人向我提到浮点精度的问题。我想知道是否有人知道如何改进我的代码。
谢谢。
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 100000);
camera.position.setScalar(500);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var controls = new THREE.OrbitControls(camera, renderer.domElement);
var loader = new THREE.STLLoader();
loader.load('https://sos-ch-dk-2.exo.io/stl-upload-test/uploads/stl/5bd15fae89ff9a1cb7816af9/stl/hexagon.STL', function(geometry) {
var material = new THREE.MeshBasicMaterial({ side: THREE.DoubleSide, vertexColors: THREE.VertexColors});
var mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
colorMesh(mesh);
});
function colorMesh(mesh) {
//var minDist = 0.6;
var colorArray = [];
var raycaster = new THREE.Raycaster();
var intersects = [];
var pos = mesh.geometry.attributes.position;
var ori = new THREE.Vector3();
var dir = new THREE.Vector3();
var trc = new THREE.Vector3();
var a = new THREE.Vector3(),
b = new THREE.Vector3(),
c = new THREE.Vector3(),
midPoint = new THREE.Vector3(),
tri = new THREE.Triangle();
var closest = new THREE.Vector3();
var faces = pos.count / 3;
for (let i = 0; i < faces; i++) {
a.fromBufferAttribute(pos, i * 3 + 0);
b.fromBufferAttribute(pos, i * 3 + 1);
c.fromBufferAttribute(pos, i * 3 + 2);
tri.set(a, b, c);
tri.getMidpoint(ori);
tri.getNormal(dir)
raycaster.set(ori, dir.negate());
intersects = raycaster.intersectObject(mesh);
//minDist = Math.min(minDist, intersects[intersects.length > 1 ? 1 : 0].distance);
minDist = intersects[intersects.length > 1 ? 1 : 0].distance;
console.log(minDist);
var col = new THREE.Vector3(Math.floor((10/minDist)*255),Math.floor(0.01*minDist*255),0);
//var col = new THREE.Vector3(0,0,0);
for (var j = 0;j<3;j++) {
colorArray.push(col.x);
colorArray.push(col.y);
colorArray.push(col.z);
}
console.log(col);
}
var colors = new Uint8Array( colorArray );
mesh.geometry.addAttribute( 'color', new THREE.BufferAttribute( colors, 3,true ) );
//helper = new THREE.VertexNormalsHelper( objects[0], 2, 0x00ff00, 1 );
// scene.add(helper);
//return minDist >= 0.6;
}
renderer.setAnimationLoop(() => {
renderer.render(scene, camera)
});
body {
overflow: hidden;
margin: 0;
}
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
<script src="https://threejs.org/examples/js/loaders/STLLoader.js"></script>
问题是Ray.js没有考虑所有可能的情况。事实上,它 returns 要么是交点,要么是 null,这不足以处理你的边缘问题。
问题:
当光线与网格相交时,有很多可能的特殊情况:
射线正好穿过网格的一个顶点 --> 在当前的实现中,这将被报告为零或一或二或最多 N交点(取决于浮点精度如何作用于基于该顶点的 N 个面中的每一个)
光线正好沿着其中一个面,
- 光线沿着面走,最后与顶点相交,
...以及更多类似的情况,但您明白了。
有一种方法可以显着降低陷入这些情况的风险:不要使用面中点,而是在面上取一个随机点。
例如:
// tri.getMidpoint(ori);
// get some random point on face
let r0 = 0.33 * Math.random();
let r1 = 0.33 * Math.random();
let r2 = 1.0 - r0 - r1;
let ori = new THREE.Vector3(
r0*a.x + r1*b.x + r2*c.x,
r0*a.y + r1*b.y + r2*c.y,
r0*a.z + r1*b.z + r2*c.z
);
我正在使用重铸将光线从网格的每个面中点投射到网格本身,以找到局部厚度。有趣的是,由于网格排列,有时光线会在三角形之间穿过,并且不会显示正确的厚度。有人向我提到浮点精度的问题。我想知道是否有人知道如何改进我的代码。 谢谢。
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 100000);
camera.position.setScalar(500);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var controls = new THREE.OrbitControls(camera, renderer.domElement);
var loader = new THREE.STLLoader();
loader.load('https://sos-ch-dk-2.exo.io/stl-upload-test/uploads/stl/5bd15fae89ff9a1cb7816af9/stl/hexagon.STL', function(geometry) {
var material = new THREE.MeshBasicMaterial({ side: THREE.DoubleSide, vertexColors: THREE.VertexColors});
var mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
colorMesh(mesh);
});
function colorMesh(mesh) {
//var minDist = 0.6;
var colorArray = [];
var raycaster = new THREE.Raycaster();
var intersects = [];
var pos = mesh.geometry.attributes.position;
var ori = new THREE.Vector3();
var dir = new THREE.Vector3();
var trc = new THREE.Vector3();
var a = new THREE.Vector3(),
b = new THREE.Vector3(),
c = new THREE.Vector3(),
midPoint = new THREE.Vector3(),
tri = new THREE.Triangle();
var closest = new THREE.Vector3();
var faces = pos.count / 3;
for (let i = 0; i < faces; i++) {
a.fromBufferAttribute(pos, i * 3 + 0);
b.fromBufferAttribute(pos, i * 3 + 1);
c.fromBufferAttribute(pos, i * 3 + 2);
tri.set(a, b, c);
tri.getMidpoint(ori);
tri.getNormal(dir)
raycaster.set(ori, dir.negate());
intersects = raycaster.intersectObject(mesh);
//minDist = Math.min(minDist, intersects[intersects.length > 1 ? 1 : 0].distance);
minDist = intersects[intersects.length > 1 ? 1 : 0].distance;
console.log(minDist);
var col = new THREE.Vector3(Math.floor((10/minDist)*255),Math.floor(0.01*minDist*255),0);
//var col = new THREE.Vector3(0,0,0);
for (var j = 0;j<3;j++) {
colorArray.push(col.x);
colorArray.push(col.y);
colorArray.push(col.z);
}
console.log(col);
}
var colors = new Uint8Array( colorArray );
mesh.geometry.addAttribute( 'color', new THREE.BufferAttribute( colors, 3,true ) );
//helper = new THREE.VertexNormalsHelper( objects[0], 2, 0x00ff00, 1 );
// scene.add(helper);
//return minDist >= 0.6;
}
renderer.setAnimationLoop(() => {
renderer.render(scene, camera)
});
body {
overflow: hidden;
margin: 0;
}
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
<script src="https://threejs.org/examples/js/loaders/STLLoader.js"></script>
问题是Ray.js没有考虑所有可能的情况。事实上,它 returns 要么是交点,要么是 null,这不足以处理你的边缘问题。
问题: 当光线与网格相交时,有很多可能的特殊情况:
射线正好穿过网格的一个顶点 --> 在当前的实现中,这将被报告为零或一或二或最多 N交点(取决于浮点精度如何作用于基于该顶点的 N 个面中的每一个)
光线正好沿着其中一个面,
- 光线沿着面走,最后与顶点相交,
...以及更多类似的情况,但您明白了。
有一种方法可以显着降低陷入这些情况的风险:不要使用面中点,而是在面上取一个随机点。
例如:
// tri.getMidpoint(ori);
// get some random point on face
let r0 = 0.33 * Math.random();
let r1 = 0.33 * Math.random();
let r2 = 1.0 - r0 - r1;
let ori = new THREE.Vector3(
r0*a.x + r1*b.x + r2*c.x,
r0*a.y + r1*b.y + r2*c.y,
r0*a.z + r1*b.z + r2*c.z
);