经过前面2篇WebGL射线拾取模型的文章,相信大家对射线和模型面片相交的原理已经有所了解,那么今天我们再深入探究关于射线拾取的一个问题,那就是遍历场景中的所有与射线相交的模型的优化问题。首先我们来复习一下射线拾取模型的原理,请看下图。

  我们从上图中可以看到,在frustum视棱台区域中只有一个模型就是triangle2三角形2,那么遍历整个scene场景我们也只能取到一个geometry,取出该geometry后我们通过空间变换矩阵得到该三角形2在场景中的位置坐标以及姿态信息,再将空间中的这个已知位置和姿态的geometry去和射线P1P3求交点,如果有交点就代表鼠标拾取到了该triangle2三角形2,这就是raycaster射线拾取的基本原理,前两篇文章已经叙述过了,这里复习一下。接下来我们引入今天的话题,射线碰撞raycaster遍历场景中模型的优化。首先我们来看下图。

             我们看到在视棱台范围内存在2个模型geometry,他们分别是triangle2,triangle3两个三角形,而鼠标和相机构成的射线明显不经过triangle3这个模型,但是我们还是要遍历场景中所有的模型包括这个triangle3,遍历到的每个模型都要和射线P1P3求交点,当求得的交点数大于零则表示射线碰撞到该模型,交点数等于零表示射线没有碰到该模型,即鼠标未拾取到该模型。这就引出了我们今天的话题,那就是优化这个遍历所有模型的过程。作者:ccentry/葱烤河鲫鱼。 作为程序,我们追求的是高性能的算法,如果每次做射线拾取都要从头遍历一遍所有的模型geometry,那么系统将无疑会很卡,尤其当场景中模型的数量级达到一定的高度时,卡顿会尤其明显。那么我们就动脑筋想一个问题,我们能否不去遍历scene场景下的所有模型,只遍历必要的一部分模型,进而减少线段和三角面相交判断的计算量。答案是可以,具体怎么操作,请看下图。

  我们看上图,我们将视棱台划分成8个区域,分别从区域1到区域8,所有场景中的模型geometry都分布在这8个区域中,现在我们就通过这8个区域缩小射线碰撞的遍历geometry模型的范围。具体的操作很简单,那就是先让射线和这8个区域的棱台几何体进行射线相交计算,只有与射线产生交点的棱台几何体区域才是射线检测的模型空间范围,其余和射线不产生交点的区域中的geometry模型就不必参与到raycaster检测中来,这样就极大的缩小了遍历geometry的数量,从而优化了raycaster的功能。我们来看看上图中依照8叉树优化逻辑进行的raycaster步骤。首先,射线只交2个区域的棱台他们分别是区域7和区域3,那么区域1,2,4,5,6,8中的所有geometry就都不用参与raycaster射线碰撞检测了,一下子我们就排除了Triangle3三角形3,因为他处于区域4中,不在检测区域范围内,是不是就减少了后面线段和面相交的计算量,优化了raycaster整体的性能。这是非常好的一个做法,直接缩小了检测范围,而且还能继续递归细分下去,比如区域3还能细分成8个小区域,将检测范围缩得更小,进一步排除检测区域外的多余模型,进一步减少计算量,这就是8叉树在raycaster中的优化算法,看上去很简单,其实非常的高效。

  好了,也许有同学会问,你怎么知道模型geometry处在哪个区域中,对于这个问题,一句话告诉你,判断geometry的中心,即geometry模型包围盒的中心position坐标是否在区域棱台范围中即可得到答案。简单实用,对于跨区域,跨多个区域的模型也有效。那就有人抬杠了,万一跨区域的模型中心不在检测区域内,但该模型有部分在检测区域里怎么办,其实针对这种情况也很好办,那就是用模型包围盒的6个面去和检测区域的棱台几何(其实在世界坐标系下也是包围盒)的6个面去进行碰撞检测(不在本篇中论述),碰到即在检测区域内。

  以上论述是对raycaster进行8叉树优化的理论描述,下面给出部分关键代码。

/*

 */
let Intersector = require('./Intersector');
let LineSegmentIntersection = require('./Intersection').LineSegmentIntersection;
let Vec3 = require('./Vec3');
let Mat4 = require('./Mat4');
let Algorithm = require('./Algorithm'); let LineSegmentIntersector = function () {
Intersector.call(this); //原始的起始点和临界值,初始化设置的数据,保留作为参照,设置后不再变动
this._orginStart = Vec3.new();//线段起点
this._orginEnd = Vec3.new();//线段终点
this._orginThreshold = 0.0;//点和线求相交时的临界值,完全相交是很难求到的 //临时存储,每次求交都可能会变动的数据
//对于有变换的几何求交,不会变换几何顶点而是变换起始点和临界值
this._start = Vec3.new();//线段起点
this._end = Vec3.new();//线段终点
this._threshold = 0.0;//点和线求相交时的临界值,完全相交是很难求到的 this._direction = Vec3.new();
this._length = ;
this._inverseLength = ;
this._matrix = Mat4.new();
}; LineSegmentIntersector.prototype = Object.create(Intersector.prototype);
LineSegmentIntersector.prototype.constructor = LineSegmentIntersector;
Object.assign(LineSegmentIntersector.prototype, {
init: function (start, end, threshold) {
Vec3.copy(this._orginStart, start);
Vec3.copy(this._orginEnd, end);
Vec3.copy(this._start, start);
Vec3.copy(this._end, end); if (threshold !== undefined) {
this._orginThreshold = threshold;
this._threshold = threshold;
}
},
intersect: function (drawable) {
//先使用包围盒子
if (!drawable.getBoundingBox().intersectLineSegment(this._orginStart, this._orginEnd)) {
return;
} this._drawable = drawable;
let geometry = drawable.getGeometry();
let vertexbuffer = geometry.getBufferArray('Vertex');
this._vertices = vertexbuffer.getArrayBuffer();
//没有顶点数据不处理直接返回
if (!this._vertices) return; //没有图元不处理直接返回
let primitive = geometry.getPrimitive();
if (!primitive) return; //初始化求相交的各种数据
let matrix = drawable.getTransform();
if (this._transform !== matrix) {//如果不一样,需要计算新的起始点以及各种临时数据
this._transform = matrix;
Mat4.invert(this._matrix, matrix); //根据矩阵计算新的临界值
if (this._orginThreshold > 0.0) {
let tmp = this._start;
Mat4.getScale(tmp, this._matrix);
let x = tmp[];
let y = tmp[];
let z = tmp[];
this._threshold = this._orginThreshold * (x > y ? (x > z ? x : z) : y > z ? y : z);
}
//根据矩阵计算新的起始点
Vec3.transformMat4(this._start, this._orginStart, this._matrix);
Vec3.transformMat4(this._end, this._orginEnd, this._matrix); //根据新的起始点计算各种临时数据
Vec3.sub(this._direction, this._end, this._start);
this._length = Vec3.length(this._direction);//长度
this._inverseLength = this._length <= Algorithm.EPSILON ? 0.0 : 1.0 / this._length;
Vec3.scale(this._direction, this._direction, this._inverseLength);//求单位向量
}//如果变换与上次一样,直接使用上次的数据求相交 //求相交
primitive.operate(this);
},
intersectPoint: function (vertex) {
// https://www.geometrictools.com/GTEngine/Include/Mathematics/GteDistPointSegment.h
//起点指向绘制点,向量M
let m = Vec3.MemoryPool.alloc();
Vec3.sub(m, vertex, this._start);
//起点指向终点,向量N
let n = Vec3.MemoryPool.alloc();
Vec3.sub(n, this._end, this._start); //求M在N上的投影比例值
//|m|*|n|*cos / \n\*\n\ = |m|*cos/\n\
let r = Vec3.dot(m, n) * this._inverseLength * this._inverseLength; //计算绘制点到线段的距离
let sqrdist = 1.0;
if (r < 0.0) {//夹角超过90度,绘制点在当前线段起点后面,求绘制点与起点的距离
sqrdist = Vec3.sqrLen(m);
} else if (r > 1.0) {//绘制点在当前线段终点后面,求绘制点与终点的距离
sqrdist = Vec3.sqrDist(vertex, this._end);
} else {//在0到1之间
//m - n * r 如果平行或者接近于平行,结果接近于0,相交
sqrdist = Vec3.sqrLen(Vec3.scaleAndAdd(m, m, n, -r));
} let intersection = undefined;
if (sqrdist > this._threshold * this._threshold) {//超过了临界值,没有相交返回 } else {
//相交
intersection = new LineSegmentIntersection();
//intersection._i1 = index;
//intersection._r1 = 1.0;
Vec3.scaleAndAdd(intersection._point, this._start, n, r);
intersection._ratio = r;
}
Vec3.MemoryPool.free(m);
Vec3.MemoryPool.free(n);
return intersection;
},
intersectLine: function (vertex0, vertex1) {
// https://www.geometrictools.com/GTEngine/Samples/Geometrics/DistanceSegments3/DistanceSegments3.cpp
//let epsilon = 0.00000001; //起点到终点的向量
let u = Vec3.MemoryPool.alloc();
Vec3.sub(u, vertex1, vertex0);
let v = Vec3.MemoryPool.alloc();
Vec3.sub(v, this._end, this._start);
let w = Vec3.MemoryPool.alloc();
Vec3.sub(w, vertex0, this._start); let a = Vec3.dot(u, u);
let b = Vec3.dot(u, v);
let c = Vec3.dot(v, v);
let d = Vec3.dot(u, w);
let e = Vec3.dot(v, w);
let D = a * c - b * b;
let sN;
let tN;
let sD = D;
let tD = D; // compute the line parameters of the two closest points
if (D < Algorithm.EPSILON) {//平行
// the lines are almost parallel
sN = 0.0; // force using point P0 on segment S1
sD = 1.0; // to prevent possible division by 0.0 later
tN = e;
tD = c;
} else {
// get the closest points on the infinite lines
sN = b * e - c * d;
tN = a * e - b * d;
if (sN < 0.0) {
// sc < 0 => the s=0 edge is visible
sN = 0.0;
tN = e;
tD = c;
} else if (sN > sD) {
// sc > 1 => the s=1 edge is visible
sN = sD;
tN = e + b;
tD = c;
}
} if (tN < 0.0) {
// tc < 0 => the t=0 edge is visible
tN = 0.0;
// recompute sc for this edge
if (-d < 0.0) sN = 0.0;
else if (-d > a) sN = sD;
else {
sN = -d;
sD = a;
}
} else if (tN > tD) {
// tc > 1 => the t=1 edge is visible
tN = tD;
// recompute sc for this edge
if (-d + b < 0.0) sN = ;
else if (-d + b > a) sN = sD;
else {
sN = -d + b;
sD = a;
}
}
// finally do the division to get sc and tc
let sc = Math.abs(sN) < Algorithm.EPSILON ? 0.0 : sN / sD;
let tc = Math.abs(tN) < Algorithm.EPSILON ? 0.0 : tN / tD; // get the difference of the two closest points
let closest0 = Vec3.MemoryPool.alloc();
let closest1 = Vec3.MemoryPool.alloc();
Vec3.scaleAndAdd(closest0, vertex0, u, sc);
Vec3.scaleAndAdd(closest1, this._start, v, tc); let sqrDistance = Vec3.sqrDist(closest0, closest1);
Vec3.MemoryPool.free(closest0);
Vec3.MemoryPool.free(closest1); let intersection = undefined;
if (sqrDistance > this._threshold * this._threshold) { } else {
//相交
intersection = new LineSegmentIntersection();
// intersection._i1 = index0;
// intersection._i2 = index1;
// intersection._r1 = 1.0 - tc;
// intersection._r2 = tc;
Vec3.copy(intersection._point, closest1);
intersection._ratio = tc;
}
Vec3.MemoryPool.free(u);
Vec3.MemoryPool.free(v);
Vec3.MemoryPool.free(w);
return intersection;
},
intersectTriangle: function (vertex0, vertex1, vertex2) {
let e2 = Vec3.MemoryPool.alloc();
Vec3.sub(e2, vertex2, vertex0);
let e1 = Vec3.MemoryPool.alloc();
Vec3.sub(e1, vertex1, vertex0);
let pvec = Vec3.MemoryPool.alloc();
Vec3.cross(pvec, this._direction, e2); let intersection = undefined;
//线段与三角面点积
let det = Vec3.dot(pvec, e1);
//判断三角形所在的平面与线段是否平行,如果平行铁定不相交,面片没有厚度
if (Math.abs(det) < Algorithm.EPSILON) {
//return undefined;
}else{
let invDet = 1.0 / det;
let tvec = Vec3.MemoryPool.alloc();
Vec3.sub(tvec, this._start, vertex0);
let u = Vec3.dot(pvec, tvec) * invDet;
//三角面超出了线段两个点范围外面,铁定不相交
if (u < 0.0 || u > 1.0) {
//return undefined;
}else{
let qvec = Vec3.MemoryPool.alloc();
Vec3.cross(qvec, tvec, e1);
let v = Vec3.dot(qvec, this._direction) * invDet;
//
if (v < 0.0 || u + v > 1.0) {
//return undefined;
}else{
let t = Vec3.dot(qvec, e2) * invDet;
if (t < Algorithm.EPSILON || t > this._length) {
//return undefined;
}else{
//相交
intersection = new LineSegmentIntersection(); //求相交点
let r0 = 1.0 - u - v;
let r1 = u;
let r2 = v;
let r = t * this._inverseLength;
let interX = vertex0[] * r0 + vertex1[] * r1 + vertex2[] * r2;
let interY = vertex0[] * r0 + vertex1[] * r1 + vertex2[] * r2;
let interZ = vertex0[] * r0 + vertex1[] * r1 + vertex2[] * r2;
// intersection._i1 = index0;
// intersection._i2 = index1;
// intersection._i3 = index2;
// intersection._r1 = r0;
// intersection._r2 = r1;
// intersection._r3 = r2; //这里的点没有经过变换,不是真实的世界坐标点
Vec3.set(intersection._point, interX, interY, interZ);
Vec3.transformMat4(intersection._point, intersection._point, this._transform); //求法向量,法向量未变换,如果有用途也要变换
let normal = intersection._normal;
Vec3.cross(normal, e1, e2);
Vec3.normalize(normal, normal);
//比例,在相交线段上的比例,不需要变换
intersection._ratio = r;
}
}
Vec3.MemoryPool.free(qvec);
}
Vec3.MemoryPool.free(tvec);
}
Vec3.MemoryPool.free(e1);
Vec3.MemoryPool.free(e2);
Vec3.MemoryPool.free(pvec);
return intersection;
// http://gamedev.stackexchange.com/questions/54505/negative-scale-in-matrix-4x4
// https://en.wikipedia.org/wiki/Determinant#Orientation_of_a_basis
// you can't exactly extract scale of a matrix but the determinant will tell you
// if the orientation is preserved
//intersection._backface = mat4.determinant(intersection._matrix) * det < 0;
},
intersectBoundingBox: function (box) {
return box.intersectLineSegment(this._orginStart, this._orginEnd);
},
}); module.exports = LineSegmentIntersector; // setDrawable: function (drawable) {
// this._geometry = drawable.getGeometry();
// this._vertices = this._geometry.getBufferArray('Vertex');
//
// let matrix = drawable.getTransform();
// if (this._transform === matrix) {//如果与上次的一样,不再处理
// return;
// }
//
// //如果不一样,需要计算新的起始点已经各种临时数据
// this._transform = matrix;
// Mat4.invert(this._matrix, matrix);
//
// //根据矩阵计算新的临界值
// if (this._orginThreshold > 0.0) {
// let tmp = this._start;
// Mat4.getScale(tmp, this._matrix);
// let x = tmp[0];
// let y = tmp[1];
// let z = tmp[2];
// this._threshold = this._orginThreshold * (x > y ? (x > z ? x : z) : y > z ? y : z);
// }
// //根据矩阵计算新的起始点
// Vec3.transformMat4(this._start, this._orginStart, this._matrix);
// Vec3.transformMat4(this._end, this._orginEnd, this._matrix);
//
// //根据新的起始点计算各种临时数据
// Vec3.sub(this._direction, this._end, this._start);
// this._length = Vec3.length(this._direction);//长度
// this._inverseLength = this._length <= Algorithm.EPSILON ? 1.0 / this._length : 0.0;
// Vec3.scale(this._direction, this._direction, this._inverseLength);//求单位向量
// },
// setGeometry: function (geometry, matrix) {
// Intersector.prototype.setGeometry.call(this, geometry, matrix);
//
// //如果不一样,需要计算新的起始点已经各种临时数据
// Mat4.invert(this._matrix, matrix);
//
// //根据矩阵计算新的临界值
// if (this._orginThreshold > 0.0) {
// let tmp = this._start;
// Mat4.getScale(tmp, this._matrix);
// let x = tmp[0];
// let y = tmp[1];
// let z = tmp[2];
// this._threshold = this._orginThreshold * (x > y ? (x > z ? x : z) : y > z ? y : z);
// }
// //根据矩阵计算新的起始点
// Vec3.transformMat4(this._start, this._orginStart, this._matrix);
// Vec3.transformMat4(this._end, this._orginEnd, this._matrix);
//
// //根据新的起始点计算各种临时数据
// Vec3.sub(this._direction, this._end, this._start);
// this._length = Vec3.length(this._direction);//长度
// this._inverseLength = this._length <= Algorithm.EPSILON ? 1.0 / this._length : 0.0;
// Vec3.scale(this._direction, this._direction, this._inverseLength);//求单位向量
// },
// setGeometry: function (geometry) {
// //没有顶点数据不处理直接返回
// let vertexbuffer = geometry.getBufferArray('Vertex');
// if(!vertexbuffer) return;
//
// //没有图元不处理直接返回
// let primitive = geometry.getPrimitive();
// if (primitive)
// primitive.operate(this);
// },
/*
相交遍历器,暂时摈弃
这里的遍历有局限性,遍历的是RenderNode级别的对象,无法知道哪些被场景剔除,除了隐藏以外
所以如果不需要考虑被剔除的对象,没必要使用此遍历器
*/
let NodeVisitor = require('./NodeVisitor');
let Mat4 = require('./Mat4'); let IntersectVisitor = function () {
NodeVisitor.call(this); this._intersector = undefined; this._matrixStack = [];//模型变换矩阵栈
this._matrixStack.push(Mat4.new());
}; IntersectVisitor.prototype = Object.create(NodeVisitor.prototype);
IntersectVisitor.prototype.constructor = IntersectVisitor;
Object.assign(IntersectVisitor.prototype, {
setIntersector: function (i) {
this._intersector = i;
},
getIntersector: function () {
return this._intersector;
},
//重载
apply: function (node) {
let TransformSetting = require('../core/TransformSetting');
let Geometry = require('../core/Geometry');
if(node instanceof TransformSetting){
this.applyTransform(node);
}else if(node instanceof Geometry){
this.applyGeometry(node);
}else{
this.traverse(node);
}
},
applyTransform: function(t) {
if (!this._intersector.valid(g)) return; let m = Mat4.new();
Mat4.copy(m, this.getMatrix()); //TransformSetting的子类都包含该函数
t.computeLocalToWorldMatrix(m);
this.pushMatrix(m);
this.traverse(t);
this.popMatrix();
},
applyGeometry: function (g) {
if (!this._intersector.valid(g)) return; this._intersector.setGeometry(g, this.getMatrix());
this._intersector.intersect(g);
},
getMatrix: function () {
return this._matrixStack.back();
},
pushMatrix: function (m) {
this._matrixStack.push(m);
},
popMatrix: function () {
this._matrixStack.pop();
},
}); module.exports = IntersectVisitor;
/*

 */
//let Primitives = require('../core/Primitives');
let Vec3 = require('./Vec3'); let Intersector = function () {
//图元码,确定哪些图元需要求交,默认全都求
this._primitiveMask = Intersector.ALL_PRIMITIVES;
//
//this._intersectLimit = intersectionEnums.NO_LIMIT; //相交的结果
this._intersections = []; //临时数据
this._drawable = undefined;
//this._geometry = undefined;
this._vertices = undefined;
this._transform = undefined;//上一次的变换,如果一样,不再处理
this._primitiveIndex = ;
}; // Intersector.NO_LIMIT = 0;
// Intersector.LIMIT_ONE_PER_DRAWABLE = 1;
// Intersector.LIMIT_ONE = 2; //PrimitiveMask
Intersector.POINT_PRIMITIVES = << ;
Intersector.LINE_PRIMITIVES = << ;
Intersector.TRIANGLE_PRIMITIVES = << ;
Intersector.ALL_PRIMITIVES = ( << ) | ( << ) | ( << ); let sortBackToFrontFunction = function (a, b) {//从大往小排序,从后向前
return b.getRatio() - a.getRatio();
};
let sortFrontToBackFunction = function (a, b) {//从小往大排序,从前向后
return a.getRatio() - b.getRatio();
}; Object.assign(Intersector.prototype, {
reset: function() {
//this._hit = false;
this._drawable = undefined;
this._vertices = undefined;
this._primitiveIndex = ;
},
intersect: function (drawable) {
// //没有顶点数据不处理直接返回
// if(!this._vertices) return;
//
// //没有图元不处理直接返回
// let primitive = this._geometry.getPrimitive();
// if (primitive)
// primitive.operate(this);
},
getIntersections: function () {//相交的结果集
this._intersections.sort(sortFrontToBackFunction);
return this._intersections;
},
operatePoint: function (index) {
//if (this._limitOneIntersection && this._hit) return;
if ((this._primitiveMask & Intersector.POINT_PRIMITIVES) === ) return; let vertex = Vec3.MemoryPool.alloc();
let vertices = this._vertices;
Vec3.set(vertex, vertices[ * index], vertices[ * index + ], vertices[ * index + ]); let intersection = this.intersectPoint(vertex);
Vec3.MemoryPool.free(vertex);
if(intersection){
intersection._primitiveIndex = this._primitiveIndex;
intersection._drawable = this._drawable;
this._intersections.push(intersection);
//this._hit = true;
}
this._primitiveIndex++;
},
operateLine: function (index0, index1) {
//if (this._limitOneIntersection && this._hit) return;
if ((this._primitiveMask & Intersector.LINE_PRIMITIVES) === )
return; let vertex0 = Vec3.MemoryPool.alloc();
let vertex1 = Vec3.MemoryPool.alloc(); let vertices = this._vertices;
Vec3.set(vertex0, vertices[ * index0], vertices[ * index0 + ], vertices[ * index0 + ]);
Vec3.set(vertex1, vertices[ * index1], vertices[ * index1 + ], vertices[ * index1 + ]); let intersection = this.intersectLine(vertex0, vertex1);
Vec3.MemoryPool.free(vertex0);
Vec3.MemoryPool.free(vertex1); if(intersection){
intersection._primitiveIndex = this._primitiveIndex;
intersection._drawable = this._drawable;
this._intersections.push(intersection);
//this._hit = true;
}
this._primitiveIndex++;
},
operateTriangle: function (index0, index1, index2) {
//if (this._limitOneIntersection && this._hit) return;
if ((this._primitiveMask & Intersector.TRIANGLE_PRIMITIVES) === ) return; let vertex0 = Vec3.MemoryPool.alloc();
let vertex1 = Vec3.MemoryPool.alloc();
let vertex2 = Vec3.MemoryPool.alloc();
let vertices = this._vertices;
Vec3.set(vertex0, vertices[ * index0], vertices[ * index0 + ], vertices[ * index0 + ]);
Vec3.set(vertex1, vertices[ * index1], vertices[ * index1 + ], vertices[ * index1 + ]);
Vec3.set(vertex2, vertices[ * index2], vertices[ * index2 + ], vertices[ * index2 + ]); let intersection = this.intersectTriangle(vertex0, vertex1, vertex2);
Vec3.MemoryPool.free(vertex0);
Vec3.MemoryPool.free(vertex1);
Vec3.MemoryPool.free(vertex2); if(intersection){
intersection._primitiveIndex = this._primitiveIndex;
intersection._drawable = this._drawable;
this._intersections.push(intersection);
//this._hit = true;
}
this._primitiveIndex++;
},
intersectPoint: function (/*v0, p0*/) {
},
intersectLine: function (/*v0, v1, p0, p1*/) {
},
intersectTriangle: function (/*v0, v1, v2, p0, p1, p2*/) {
},
}); module.exports = Intersector; // setDrawable: function (drawable) {
// this._drawable = drawable;
//
// let vertexbuffer = drawable.getGeometry().getBufferArray('Vertex');
// this._vertices = vertexbuffer.getArrayBuffer();
//
// let matrix = drawable.getTransform();
// if (this._transform === matrix) {//如果与上次的一样,不再处理
// return;
// }
//
// //如果不一样,需要计算新的起始点已经各种临时数据
// this._transform = matrix;
// },
// setGeometry: function (geometry, matrix) {
// this._geometry = geometry;
// this._vertices = geometry.getBufferArray('Vertex');
//
// if (this._transform === matrix) {//如果与上次的一样,不再处理
// return;
// }
//
// //如果不一样,需要计算新的起始点已经各种临时数据
// this._transform = matrix;
// },
/*
相交的结果
*/
let Vec3 = require('./Vec3'); let Intersection = function () {
this._drawable = undefined;
this._primitiveIndex = undefined;
}; Object.assign(Intersection.prototype, {
getDrawable: function () {
return this._drawable;
},
}); let LineSegmentIntersection = function () {
Intersection.call(this); this._ratio = 0.0;
this._point = Vec3.new();
this._normal = Vec3.create(, , );//相交点的法向量
}; LineSegmentIntersection.prototype = Object.create(Intersection.prototype);
LineSegmentIntersection.prototype.constructor = LineSegmentIntersection;
Object.assign(LineSegmentIntersection.prototype, {
getRatio: function () {
return this._ratio;
},
getPoint: function () {
return this._point;
},
}); module.exports.Intersection = Intersection;
module.exports.LineSegmentIntersection = LineSegmentIntersection;

   好了,到今天为止对射线拾取raycaster的讨论就差不多都结束了,后续如有勘误会陆续补充,谢谢同学们的耐心阅读,再次感谢连俊,风哥的指导解惑,也谢谢群里各位大佬的不吝指正。我会继续努力学习,今天到这里告一段落,下周再见。

  本文系 ccentry/葱烤河鲫鱼 原创,如需引用请注明出处:https://www.cnblogs.com/ccentry/p/10011730.html

WebGL射线拾取模型——八叉树优化的更多相关文章

  1. 拾取模型的原理及其在THREE.JS中的代码实现

    1. Three.js中的拾取  1.1. 从模型转到屏幕上的过程说开 由于图形显示的基本单位是三角形,那就先从一个三角形从世界坐标转到屏幕坐标说起,例如三角形abc 乘以模型视图矩阵就进入了视点坐标 ...

  2. threeJS射线拾取机制及案例

    前言 在浏览器中浏览三维图形的时候,有时想要与三维图形之间做一些点击事件和交互操作,其中比较常用的一个解决方案就是使用Raycaster对象来实现(射线拾取). 基础知识 世界坐标系:webGL中,世 ...

  3. UE4 射线拾取&三维画线

    虽然有人建议UE4使用C++创建VR项目,能避免一些坑爹的错误,但是我用C++创建,竟然问题更多,还存在创建不了的情况,也不知道是不是我的操作问题,快疯了. 于是我还是选择了蓝图创建VR项目,但是.. ...

  4. MapReduce计算模型的优化

    MapReduce 计算模型的优化涉及了方方面面的内容,但是主要集中在两个方面:一是计算性能方面的优化:二是I/O操作方面的优化.这其中,又包含六个方面的内容. 1.任务调度 任务调度是Hadoop中 ...

  5. Django【第26篇】:中介模型以及优化查询以及CBV模式

    中介模型以及优化查询以及CBV模式 一.中介模型:多对多添加的时候用到中介模型 自己创建的第三张表就属于是中介模型 class Article(models.Model): ''' 文章表 ''' t ...

  6. TensorFlow学习笔记9-深度模型的优化

    深度模型的优化 回顾概念: 代价函数时训练集上损失函数的平均: \[J(\theta)=E_{(x,y)\sim \hat{p}_{data}}L(f(x;\theta),y) \tag{1}\] 引 ...

  7. 天猫精灵业务如何使用机器学习PAI进行模型推理优化

    引言 天猫精灵(TmallGenie)是阿里巴巴人工智能实验室(Alibaba A.I.Labs)于2017年7月5日发布的AI智能语音终端设备.天猫精灵目前是全球销量第三.中国销量第一的智能音箱品牌 ...

  8. 【翻译】借助 NeoCPU 在 CPU 上进行 CNN 模型推理优化

    本文翻译自 Yizhi Liu, Yao Wang, Ruofei Yu.. 的  "Optimizing CNN Model Inference on CPUs" 原文链接: h ...

  9. NVIDIA GPUs上深度学习推荐模型的优化

    NVIDIA GPUs上深度学习推荐模型的优化 Optimizing the Deep Learning Recommendation Model on NVIDIA GPUs 推荐系统帮助人在成倍增 ...

随机推荐

  1. wfst讲解

    一.比较好的博客 1.0 官方网站 1.1 语音识别解码器(1)—自动机与半环 1.2 走进语音识别中的WFST 1.3Kaldi WFST 构图 学习 二.比较好的paper 三.开源项目 3.1 ...

  2. LeetCode刷题(数据库)---- 交换工资

    题:给定一个工资表,如下所示,m=男性 和 f=女性 .交换所有的 f 和 m 值(例如,将所有 f 值更改为 m,反之亦然).要求使用一个更新查询,并且没有中间临时表. 例如: | id | nam ...

  3. ThinkPHP里面用原生SQL

    public function rewardlog(){ $adminNav = C('ADMIN_NAV'); $adminNav[1]['class'] = 'cur'; $this->as ...

  4. On Java 8中文版 英雄召集令

    这是该项目的GITHUB地址:https://github.com/LingCoder/OnJava8 广招天下英雄,为开源奉献!让我们一起来完成这本书的翻译吧! 如果您在阅读本书的过程中有发现不明白 ...

  5. HDU 1165 公式推导题

    题目链接: acm.hdu.edu.cn/showproblem.php?pid=1165 Eddy's research II Time Limit: 4000/2000 MS (Java/Othe ...

  6. 解决dpdk中出现IOMMU not found的问题

    问题 在使用VFIO前,需要在BIOS打开VT-x和VT-d,想把一个PCIe网卡透传到虚拟机中,发现虚拟机启动失败,提示IOMMU没有找到. 输入以下命令确定vt-d开启 dmesg | grep ...

  7. git删除指定文件夹

    1.在本地仓库删除指定文件 git rm 文件名名称 2.在本地仓库删除指定文件夹 git rm -r 文件夹/ 3.提交修改 git commit -m"删除文件夹" 4.推送到 ...

  8. oo第二次总结作业

    OO电梯作业总结 这三周的作业和课堂内容以及OS的课上内容都相同,都是关于多线程方面的知识.在这次作业中由浅入深布置了三项多线程电梯方面的作业,让我们在实践中感受了多线程的工作原理以及各项需要注意的要 ...

  9. 解决win10安装MySQL数据库出现服务无法启动的问题

    安装mysql的时候一直出现这个问题,在网上找了很多种方法,终于解决了这个问题. 我在官网下载的安装包解压后没有my.ini文件,需要自己添加(红字不要复制) [mysql]# 设置mysql客户端默 ...

  10. 11.1 js中级,数据类型、数据储存方式、作用域内存空间的区别以及例识别。

    一. 基本数据类型和引用数据类型的区别. 1.基本数据类型:基本数据类型就是简单的操作值. 2.引用数据类型:就是把引用的地址赋给变量. 堆内存: 就是存放代码块的,存放形式有两种 1)对象以键值对的 ...