WebGL射线拾取模型——八叉树优化

2023-10-27

  经过前面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 = 0;
    this._inverseLength = 0;
    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[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 ? 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 = 0;
            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[0] * r0 + vertex1[0] * r1 + vertex2[0] * r2;
                        let interY = vertex0[1] * r0 + vertex1[1] * r1 + vertex2[1] * r2;
                        let interZ = vertex0[2] * r0 + vertex1[2] * r1 + vertex2[2] * 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 = 0;
};

// Intersector.NO_LIMIT = 0;
// Intersector.LIMIT_ONE_PER_DRAWABLE = 1;
// Intersector.LIMIT_ONE = 2;

//PrimitiveMask
Intersector.POINT_PRIMITIVES = 1 << 0;
Intersector.LINE_PRIMITIVES = 1 << 1;
Intersector.TRIANGLE_PRIMITIVES = 1 << 2;
Intersector.ALL_PRIMITIVES = (1 << 0) | (1 << 1) | (1 << 2);

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 = 0;
    },
    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) === 0) return;

        let vertex = Vec3.MemoryPool.alloc();
        let vertices = this._vertices;
        Vec3.set(vertex, vertices[3 * index], vertices[3 * index + 1], vertices[3 * index + 2]);

        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) === 0)
            return;

        let vertex0 = Vec3.MemoryPool.alloc();
        let vertex1 = Vec3.MemoryPool.alloc();

        let vertices = this._vertices;
        Vec3.set(vertex0, vertices[3 * index0], vertices[3 * index0 + 1], vertices[3 * index0 + 2]);
        Vec3.set(vertex1, vertices[3 * index1], vertices[3 * index1 + 1], vertices[3 * index1 + 2]);

        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) === 0) return;

        let vertex0 = Vec3.MemoryPool.alloc();
        let vertex1 = Vec3.MemoryPool.alloc();
        let vertex2 = Vec3.MemoryPool.alloc();
        let vertices = this._vertices;
        Vec3.set(vertex0, vertices[3 * index0], vertices[3 * index0 + 1], vertices[3 * index0 + 2]);
        Vec3.set(vertex1, vertices[3 * index1], vertices[3 * index1 + 1], vertices[3 * index1 + 2]);
        Vec3.set(vertex2, vertices[3 * index2], vertices[3 * index2 + 1], vertices[3 * index2 + 2]);

        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(1, 0, 0);//相交点的法向量
};

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                       

转载于:https://www.cnblogs.com/ccentry/p/10011730.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

  • ChatGPT fine tune微调+prompt介绍

    目录 1 如何使用ChatGPT 1 1 图片生成 image generation 1 2 对话 chat 1 3 中文纠错 Chinese Spelling Correct 1 4 关键词提取 keyword extract 1 5 抽
  • Blender的一些使用小结,在辣椒酱基础上加了点,会持续更新

    Blender的一些使用小结 在辣椒酱基础上加了点 会持续更新 操作 快捷键 边线折痕 编辑模式 SHIFT E 点倒角 编辑模式 Ctrl Shift B 按P选择内外 加选 编辑模式 Ctrl 减选 编辑模式 Ctrl 分块选择 编辑模
  • 机器学习如何提高训练模型结果的准确度

    提高输出结果的准确度可以从以下几个方面入手 1 调整超参数 可以尝试调整模型的超参数 比如学习率 迭代次数 隐层维度 卷积核大小等 找到最优的超参数组合来提高准确度 2 增加训练数据 增加训练数据可以让模型更好地学习到数据的特征 从而提高准
  • sql分别从两个表取出_SQL--多表查询

    前几篇文章中 我们查询的数据都是在一张表中进行操作的 而在实际工作中 我们经常需要在不同的表中查询数据 今天我们来学习从多张表中获取数据的方法 多表查询 表的加法 表的联结 用SQL联结解决业务问题 case 表达式 1 表的加法 unio
  • 机器学习三-xmeans

    能力有限 只是表面上理解了公式和图所表达的内容 论文参见 点击打开链接 已有文章分析参见 点击打开链接 具体java代码实现 点击打开链接 核心应该是 分割前BIC分数 gt 分割后BIC分数 结论是 原簇可分割为两个新簇 图示如下 公式如
  • 模板观念和函数模板

    模板 模板是C 的一种特性 允许函数或类 对象 通过泛型的形式表现或运行 模板可以使函数或类在对应不同的型别的时候正常工作 无需为每一个型别都写一份代码 两种模板 类模板 使用泛型参数的类 函数模板 使用泛型参数的函数 模板实例化 模板的声
  • AcWing.102. 最佳牛围栏(二分&&双指针&&前缀和)

    输入样例 10 6 6 4 2 10 3 8 5 9 4 1 输出样例 6500 解析 1 由题意可知答案位于 1 2000以内 所以可以二分这个区间 2 对于每个mid 我们要看是否存在一个区间 这个区间的平均值大于mid 如果存在返回t
  • 使用Spring StateMachine框架实现状态机

    Spring StateMachine框架可能对于大部分使用Spring的开发者来说还比较生僻 该框架目前差不多也才刚满一岁多 它的主要功能是帮助开发者简化状态机的开发过程 让状态机结构更加层次化 前几天刚刚发布了它的第三个Release版
  • 程序媛怎样规划自己的人生

    上个月我跟一个很优秀的程序媛聊天 她感到职业上有些迷茫 希望向我寻求答案 我让她先自己想一想 说这个月专门写一篇文章来讲一讲 其实我在等一个我自己的答案 但是这个月还是没等到 但是说过的话要算数 我还是写一篇来聊聊这个问题 我不能回答其他任
  • 自定义QDockWidget的标题栏

    左侧为自定义 右侧为原始 通过QDockWidget setTitleBarWidget 实现标题栏自定义 MDockWidget cpp include MDockWidget h include
  • Elasticsearch 安装配置 外网访问 及 后台启动

    本文转自http www jianshu com p 658961f707d8 作者 咪博士 感谢咪博士分享 Elasticsearch的安装总体来说还是相当简单的 当然中间也会有些小坑 不过大家也不必太过担心 咪博士将给大家详细演示如何在
  • qq批量登录软件_QQ账号永久冻结

    昨天上午我的QQ被永久冻结了 理由是批量加好友批量登录啥的 这个理由看的我也是一脸懵逼 我一个学生QQ的好友也就一百多个 我的QQ主要也就是打打游戏跟朋友养个花火聊天 怎么就批量加好友了呢 封号前一天我还跟朋友打游戏打到后半夜 第二天直接给
  • Python--爬取天气网站天气数据并进行数据分析

    目的 从天气网站中爬取数据 生成excel表格 里面存储南昌市近十一年的天气情况 并对爬取产生的数据进行数据分析 第一步 编写代码进行数据爬取 首先 导入 requests 模块 并调用函数 requests get 从天气的网站上面获 取
  • 【满分】【华为OD机试真题2023 JS】获得完美走位

    华为OD机试真题 2023年度机试题库全覆盖 刷题指南点这里 获得完美走位 时间限制 1s 空间限制 256MB 限定语言 不限 题目描述 在第一人称射击游戏中 玩家通过键盘的A S D W四个按键控制游戏人物分别向左 向后 向右 向前进行
  • xpath解析页面中文乱码

    res requests get url headers headers html etree HTML res text 这样写后面进行处理时得到的内容乱码了 改为以下形式 可以正常显示中文 html etree HTML res con
  • 前端小游戏2048(一步步详解附带源代码,源码上传到csdn,可以免费下载)

    2048小游戏 2048是前端开发必经的一个小游戏 2048小游戏包含了HTML CSS和JavaScript 简介 2048 是一款益智小游戏 这款游戏是由年仅19岁的意大利程序员加布里勒希鲁尼 Gabriele Cirulli 开发出来
  • python随机数模块——random

    近期遇到了一个很常见又不是很注意的随机数问题 随机数 gt 伪随机数 在这里特地的写下来作为提醒 随机数一般情况下我们直接使用random 但是random这个随机数有点假 伪随机 解析如下 Python3实现随机数 random是用于生成
  • JavaScript简写技巧

    声明变量 Longhand let x let y 20 Shorthand let x y 20 给多个变量赋值 我们可以使用数组解构来在一行中给多个变量赋值 Longhand let a b c a 5 b 8 c 12 Shortha
  • HTML5样式指南和代码约定

    目录 HTML 代码约定 智能且有保证 请使用正确的文档类型 请使用小写元素名 关闭所有 HTML 元素 关闭空的 HTML 元素 使用小写属性名 属性值加引号 必需的属性 空格和等号 避免长代码行 空行和缩进 元数据 HTML 注释 样式
  • BH1750光强度传感器Stm32f103驱动(已测试ok)

    1 实验 Stm32f103 驱动 bh1750采集光照强度 串口打印采集到的数据 2 实验准备器材 开发版 stm32f103c8t6 器件 bh1750 GY 302 开发环境 win10 KILE4 下载程序的软件 FlyMcu ex

随机推荐