效果

一、首先实现面与包围球的关系计算
1.1、三点定义一个平面,并实现点到平面的距离计算
import { BufferGeometry } from "./BufferGeometry.js";
import * as Vector3 from '../Math/vec3.js';
export default class PlaneGeometry extends BufferGeometry {
constructor(position0, position1, position2) {
super()
this._firstPosition = position0
this._normal = this.computeNormal(position0, position1, position2)
}
computeNormal(position0, position1, position2) {
const vector1 = Vector3.fromValues(position1[0] - position0[0], position1[1] - position0[1], position1[2] - position0[2])
const vector2 = Vector3.fromValues(position2[0] - position1[0], position2[1] - position1[1], position2[2] - position1[2])
const normalVector = Vector3.create()
Vector3.cross(normalVector, vector1, vector2)
const normalizeNormal = Vector3.create()
Vector3.normalize(normalizeNormal, normalVector)
return normalizeNormal
}
get normal() {
return this._normal
}
getFirstPosition() {
return this._firstPosition
}
getDistanceToPoint(point) {
const planePoint2Point = Vector3.create()
Vector3.subtract(planePoint2Point, point, this._firstPosition)
const normalizeVec = Vector3.create()
Vector3.normalize(normalizeVec, planePoint2Point)
const cosTheta = Vector3.dot(normalizeVec, this._normal)
const distance = Vector3.length(planePoint2Point) * cosTheta
return distance
}
}
1.2、在每一帧中,获取视锥体的世界坐标
this._positions = [
0, 0, 0,
rightNear, topNear, -this._near,
rightNear, bottomNear, -this._near,
leftNear, topNear, -this._near,
leftNear, bottomNear, -this._near,
rightFar, topFar, -this._far,
rightFar, bottomFar, -this._far,
leftFar, topFar, -this._far,
leftFar, bottomFar, -this._far,
]
getWorldPosition() {
const positions = this._positions
const worldPositions = []
for (let i = 0; i < positions.length; i += 3) {
const vector = { elements: [positions[i], positions[i + 1], positions[i + 2], 1.0] }
const worldVector = this._camera.inverseViewMatrix.multiplyVector4(vector)
worldPositions[i] = worldVector.elements[0]
worldPositions[i + 1] = worldVector.elements[1]
worldPositions[i + 2] = worldVector.elements[2]
}
return worldPositions
}
1.2、每一帧下根据世界坐标计算视锥体六个面
getNearPlane() {
const pointNearRightTop = Vector3.fromValues(this._worldPositions[3], this._worldPositions[4], this._worldPositions[5])
const pointNearRightBottom = Vector3.fromValues(this._worldPositions[6], this._worldPositions[7], this._worldPositions[8])
const pointNearLeftBottom = Vector3.fromValues(this._worldPositions[12], this._worldPositions[13], this._worldPositions[14])
const nearPlane = new PlaneGeometry(pointNearRightTop, pointNearRightBottom, pointNearLeftBottom)
return nearPlane
}
getLeftPlane() {
const pointFarLeftTop = Vector3.fromValues(this._worldPositions[21], this._worldPositions[22], this._worldPositions[23])
const pointNearLeftTop = Vector3.fromValues(this._worldPositions[9], this._worldPositions[10], this._worldPositions[11])
const pointNearLeftBottom = Vector3.fromValues(this._worldPositions[12], this._worldPositions[13], this._worldPositions[14])
const leftPlane = new PlaneGeometry(pointFarLeftTop, pointNearLeftTop, pointNearLeftBottom)
return leftPlane
}
getRightPlane() {
const pointFarRightTop = Vector3.fromValues(this._worldPositions[15], this._worldPositions[16], this._worldPositions[17])
const pointFarRightBottom = Vector3.fromValues(this._worldPositions[18], this._worldPositions[19], this._worldPositions[20])
const pointNearRightBottom = Vector3.fromValues(this._worldPositions[6], this._worldPositions[7], this._worldPositions[8])
const rightPlane = new PlaneGeometry(pointFarRightTop, pointFarRightBottom, pointNearRightBottom)
return rightPlane
}
getTopPlane() {
const pointNearLeftTop = Vector3.fromValues(this._worldPositions[9], this._worldPositions[10], this._worldPositions[11])
const pointFarLeftTop = Vector3.fromValues(this._worldPositions[21], this._worldPositions[22], this._worldPositions[23])
const pointFarRightTop = Vector3.fromValues(this._worldPositions[15], this._worldPositions[16], this._worldPositions[17])
const topPlane = new PlaneGeometry(pointNearLeftTop, pointFarLeftTop, pointFarRightTop)
return topPlane
}
getBottomPlane() {
const pointNearRightBottom = Vector3.fromValues(this._worldPositions[6], this._worldPositions[7], this._worldPositions[8])
const pointFarRightBottom = Vector3.fromValues(this._worldPositions[18], this._worldPositions[19], this._worldPositions[20])
const pointFarLeftBottom = Vector3.fromValues(this._worldPositions[24], this._worldPositions[25], this._worldPositions[26])
const bottomPlane = new PlaneGeometry(pointNearRightBottom, pointFarRightBottom, pointFarLeftBottom)
return bottomPlane
}
getFarPlane() {
const pointFarRightBottom = Vector3.fromValues(this._worldPositions[18], this._worldPositions[19], this._worldPositions[20])
const pointFarRightTop = Vector3.fromValues(this._worldPositions[15], this._worldPositions[16], this._worldPositions[17])
const pointFarLeftTop = Vector3.fromValues(this._worldPositions[21], this._worldPositions[22], this._worldPositions[23])
const farPlane = new PlaneGeometry(pointFarRightBottom, pointFarRightTop, pointFarLeftTop)
return farPlane
}
1.4、计算每一个面与包围球是否相交
isCulledMeshBySphere(sphere) {
let culled = false
const radius = sphere.radius
const center = sphere.center
const refCenter = this._camera.refCenter
const distPoint = Vector3.fromValues(center[0] - refCenter[0], center[1] - refCenter[1], center[2] - refCenter[2])
const frustGeometry = this
const nearPlane = frustGeometry.getNearPlane()
const distanceNear = nearPlane.getDistanceToPoint(distPoint)
if (distanceNear < -radius) {
console.log('近平面剔除')
culled = true
}
const leftPlane = frustGeometry.getLeftPlane()
const distanceLeft = leftPlane.getDistanceToPoint(distPoint)
if (distanceLeft < -radius) {
console.log('左平面剔除')
culled = true
}
const rightPlane = frustGeometry.getRightPlane()
const distanceRight = rightPlane.getDistanceToPoint(distPoint)
if (distanceRight < -radius) {
console.log('右平面剔除')
culled = true
}
const farPlane = frustGeometry.getFarPlane()
const distanceFar = farPlane.getDistanceToPoint(distPoint)
if (distanceFar < -radius) {
console.log('远平面剔除')
culled = true
}
const topPlane = frustGeometry.getTopPlane()
const distanceTop = topPlane.getDistanceToPoint(distPoint)
if (distanceTop < -radius) {
console.log('上平面剔除')
culled = true
}
const bottomPlane = frustGeometry.getBottomPlane()
const distanceBottom = bottomPlane.getDistanceToPoint(distPoint)
if (distanceBottom < -radius) {
console.log('下平面剔除')
culled = true
}
return culled
}