<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      基于three.js的Instanced Draw+LOD+Frustum Cull的改進實現

      大家好,本文在上文的基礎上,優化了Instanced Draw+LOD+Frustum Cull的性能,性能提升了3倍以上

      關鍵詞:three.js、Instanced Draw、大場景、LOD、Frustum Cull、優化、Web3D、WebGL、開源

      上文:
      three.js使用Instanced Draw+Frustum Cull+LOD來渲染大場景(開源)

      相對于上文的改進點

      相對于上文的Octree,本文的Octree直接遍歷世界矩陣而不是Mesh,從而提高了性能

      相對于上文的Instanced LOD,本文的Instanced LOD簡化了數據結構,并且不再通過交換來實現cull,從而提高了性能

      本文改進的代碼

      調用代碼:

      let first = new THREE.Group()
      ...
      first.add(mesh)
      
      let details = [
      	//第一級LOD
      	{
      		group: first,
      		level: "l0",
      		distance: 800,
      	},
      	//第二級LOD...
      	{
      		group: second,
      		level: "l1",
      		distance: 1000,
      	},
      	...
      ]
      
      let octree = new Octree(boundingBox, 5, 0)
      
      
      let camera = 獲得當前相機
      
      let instancedlod = new InstancedLOD(staticGroup, camera, "lod")
      
      instancedlod.setOctree(octree);
      instancedlod.setLevels(details, true);
      instancedlod.setPopulation();
      
      ...
      
      
      在主循環中調用:
      instancedlod.update()
      
      

      Octree

      import * as THREE from "three";
      
      class Octree {
      	public box
      	public capacity
      	public divided
      	public transforms
      	public children
      	public depth
      
      	constructor(box3, n, depth) {
      		this.box = box3;
      		this.capacity = n;
      		this.divided = false;
      		this.transforms = [];
      		this.children = [];
      		this.depth = depth;
      	}
      
      	subdivide() {
      		const { box, capacity, depth } = this;
      		let size = new THREE.Vector3().subVectors(box.max, box.min).divideScalar(2);
      		let arr = [
      			[0, 0, 0],
      			[size.x, 0, 0],
      			[0, 0, size.z],
      			[size.x, 0, size.z],
      			[0, size.y, 0],
      			[size.x, size.y, 0],
      			[0, size.y, size.z],
      			[size.x, size.y, size.z],
      		];
      		for (let i = 0; i < 8; i++) {
      			let min = new THREE.Vector3(
      				box.min.x + arr[i][0],
      				box.min.y + arr[i][1],
      				box.min.z + arr[i][2]
      			);
      			let max = new THREE.Vector3().addVectors(min, size);
      			let newbox = new THREE.Box3(min, max);
      			this.children.push(new Octree(newbox, capacity, depth + 1));
      		}
      		this.divided = true;
      	}
      
      	insert(transform) {
      		const { box, transforms, capacity, divided, children } = this;
      		if (
      			!box.containsPoint(new THREE.Vector3().setFromMatrixPosition(transform))
      		)
      			return false;
      		if (transforms.length < capacity) {
      			transforms.push(transform);
      			return true;
      		} else {
      			if (!divided) this.subdivide();
      			for (let i = 0; i < children.length; i++) {
      				if (children[i].insert(transform)) return true;
      			}
      		}
      	}
      
      	queryByBox(boxRange, found = []) {
      		if (!this.box.intersectsBox(boxRange)) {
      			return found;
      		} else {
      			for (let transform of this.transforms) {
      				if (
      					boxRange.containsPoint(
      						new THREE.Vector3().setFromMatrixPosition(transform)
      					)
      				) {
      					found.push(transform);
      				}
      			}
      			if (this.divided) {
      				this.children.forEach((child) => {
      					child.queryByBox(boxRange, found);
      				});
      			}
      			return found;
      		}
      	}
      
      	queryBySphere(
      		sphereRange,
      		boundingBox = sphereRange.getBoundingBox(new THREE.Box3()),
      		found = []
      	) {
      		if (!this.box.intersectsBox(boundingBox)) {
      			return found;
      		} else {
      			for (let transform of this.transforms) {
      				if (
      					sphereRange.containsPoint(
      						new THREE.Vector3().setFromMatrixPosition(transform)
      					)
      				) {
      					found.push(transform);
      				}
      			}
      			if (this.divided) {
      				this.children.forEach((child) => {
      					child.queryBySphere(sphereRange, boundingBox, found);
      				});
      			}
      			return found;
      		}
      	}
      
      	queryByFrustum(frustum, found = []) {
      		if (!frustum.intersectsBox(this.box)) {
      			return found;
      		} else {
      			for (let transform of this.transforms) {
      				if (
      					frustum.containsPoint(
      						new THREE.Vector3().setFromMatrixPosition(transform)
      					)
      				) {
      					found.push(transform);
      				}
      			}
      			if (this.divided) {
      				this.children.forEach((child) => {
      					child.queryByFrustum(frustum, found);
      				});
      			}
      			return found;
      		}
      	}
      
      	display(scene) {
      		// 葉子結點
      		if (!this.divided && this.transforms.length > 0) {
      			scene.add(new THREE.Box3Helper(this.box, 0x00ff00));
      			return;
      		}
      		this.children.forEach((child) => {
      			child.display(scene);
      		});
      	}
      }
      
      export { Octree };
      
      

      Contract(用于契約檢查)

      export let buildAssetMessage = (expect:string, actual = "not as expect") => {
          return `expect ${expect}, but actual ${actual}`;
      }
      
      export let test = (message: string, func: () => boolean): void => {
          if (func() !== true) {
              throw new Error(message);
          }
      }
      
      export let requireCheck = (func: () => void, isTest: boolean): void => {
          if (!isTest) {
              return;
          }
      
          func();
      }
      
      export function ensureCheck<T extends any>(returnVal: T, func: (returnVal: T) => void, isTest: boolean): T {
          if (!isTest) {
              return returnVal;
          }
      
          func(returnVal);
      
          return returnVal;
      }
      
      export function assertPass() {
          return true;
      }
      
      export function assertTrue(source: boolean) {
          return source === true;
      }
      
      export function assertFalse(source: boolean) {
          return source === false;
      }
      
      function _isNullableExist<T extends any>(source: T): T extends null ? never : T extends undefined ? never : boolean;
      function _isNullableExist(source:any) {
          return source !== undefined && source !== null;
      };
      
      export let assertNullableExist = _isNullableExist;
      
      // export function assertEqual<S extends any, T extends any>(source: S, target: T): S extends T ? true : false;
      export function assertEqual<S extends number, T extends number>(source: S, target: T): S extends T ? true : false;
      export function assertEqual<S extends string, T extends string>(source: S, target: T): S extends T ? true : false;
      export function assertEqual<S extends boolean, T extends boolean>(source: S, target: T): S extends T ? true : false;
      export function assertEqual<S extends number | string | boolean, T extends number | string | boolean>(source: S, target: T): false;
      export function assertEqual(source:any, target:any) {
          return source == target;
      }
      
      export function assertNotEqual<S extends number, T extends number>(source: S, target: T): S extends T ? false : true;
      export function assertNotEqual<S extends string, T extends string>(source: S, target: T): S extends T ? false : true;
      export function assertNotEqual<S extends boolean, T extends boolean>(source: S, target: T): S extends T ? false : true;
      export function assertNotEqual<S extends number | string | boolean, T extends number | string | boolean>(source: S, target: T): true;
      export function assertNotEqual(source:any, target:any) {
          return source != target;
      }
      
      export function assertGt(source: number, target: number) {
          return source > target;
      }
      
      export function assertGte(source: number, target: number) {
          return source >= target;
      }
      
      export function assertLt(source: number, target: number) {
          return source < target;
      }
      
      export function assertLte(source: number, target: number) {
          return source <= target;
      }
      

      InstancedLOD

      import * as THREE from "three";
      import { requireCheck, test } from "./Contract";
      
      let count = 0
      class InstancedLOD {
      	public treeSpecies
      	public numOfLevel
      	public scene
      	public camera
      	public levels
      	public instancedMeshOfAllLevel: Array<
      		{
      			meshes: Array<THREE.Mesh>,
      			count: number,
      			matrix4: Array<THREE.Matrix4>,
      			castShadow: boolean,
      			receiveShadow: boolean
      		}>
      	public groupOfInstances
      
      
      	public octree
      
      	public frustum
      	public worldProjectionMatrix
      	public obj_position
      	public cur_dist
      	public cur_level
      
      	constructor(scene, camera, treeSpecies) {
      		this.treeSpecies = treeSpecies;
      		this.numOfLevel = 0;
      		this.scene = scene;
      		this.camera = camera;
      		this.levels;
      		this.instancedMeshOfAllLevel;
      		this.groupOfInstances;
      
      		this.frustum = new THREE.Frustum();
      		this.worldProjectionMatrix = new THREE.Matrix4();
      		this.obj_position = new THREE.Vector3();
      		this.cur_dist = 0;
      		this.cur_level = 0;
      	}
      
      	setOctree(octree) {
      		this.octree = octree;
      	}
      
      	extractMeshes(group) {
      		return group.children
      	}
      
      	setLevels(array, isDebug) {
      		requireCheck(() => {
      			let group = array[0].group
      
      			test("meshs should be first level children", () => {
      				return group.children.reduce((result, child: THREE.Mesh) => {
      					if (!result) {
      						return result
      					}
      
      					return child.isMesh && child.children.length == 0
      				}, true)
      			})
      			test("transform should be default", () => {
      				return group.children.reduce((result, child: THREE.Mesh) => {
      					if (!result) {
      						return result
      					}
      
      					return child.position.equals(new THREE.Vector3(0, 0, 0)) && child.rotation.equals(new THREE.Euler(0, 0, 0)) && child.scale.equals(new THREE.Vector3(1, 1, 1))
      				}, true)
      			})
      		}, isDebug)
      
      		this.numOfLevel = array.length;
      		this.levels = new Array(this.numOfLevel);
      		this.instancedMeshOfAllLevel = new Array(this.numOfLevel); // array of { mesh:[], count, matrix4:[] }
      		this.groupOfInstances = new Array(this.numOfLevel); // array of THREE.Group(), each Group -> tree meshes in each level
      		for (let i = 0; i < this.numOfLevel; i++) {
      			this.levels[i] = array[i].distance;
      			let group = array[i].group
      			this.instancedMeshOfAllLevel[i] = {
      				meshes: this.extractMeshes(group),
      				count: 0,
      				matrix4: [],
      				castShadow: group.castShadow,
      				receiveShadow: group.receiveShadow,
      			};
      		}
      	}
      
      	setPopulation() {
      		for (let i = 0; i < this.numOfLevel; i++) {
      			const group = new THREE.Group();
      
      			let { meshes, castShadow, receiveShadow } = this.instancedMeshOfAllLevel[i]
      
      			meshes.forEach((m) => {
      				const instancedMesh = new THREE.InstancedMesh(
      					m.geometry,
      					m.material,
      					15000
      				);
      				instancedMesh.castShadow = castShadow;
      				instancedMesh.receiveShadow = receiveShadow;
      
      				group.add(instancedMesh);
      			});
      			this.groupOfInstances[i] = group;
      			this.scene.add(group);
      		}
      	}
      
      	getDistanceLevel(dist) {
      		const { levels } = this;
      		const length = levels.length;
      		for (let i = 0; i < length; i++) {
      			if (dist <= levels[i]) {
      				return i;
      			}
      		}
      		return -1
      	}
      
      	getLastLevel() {
      		return this.levels.length - 1;
      	}
      
      	getSpecies() {
      		return this.treeSpecies;
      	}
      
      	expandFrustum(frustum, offset) {
      		frustum.planes.forEach((plane) => {
      			plane.constant += offset;
      		});
      	}
      
      	/* update函數每幀都要進行,內存交換越少越好,計算時間越短越好 */
      	// render() {
      	update() {
      		count++
      		let {
      			instancedMeshOfAllLevel,
      			groupOfInstances,
      			numOfLevel,
      			camera,
      			frustum,
      			octree,
      			worldProjectionMatrix,
      			obj_position,
      			cur_dist,
      			cur_level,
      		} = this;
      		// clear
      		for (let i = 0; i < numOfLevel; i++) {
      			instancedMeshOfAllLevel[i].count = 0;
      			instancedMeshOfAllLevel[i].matrix4 = [];
      		}
      		// update camera frustum
      		worldProjectionMatrix.identity(); // reset as identity matrix
      		frustum.setFromProjectionMatrix(
      			worldProjectionMatrix.multiplyMatrices(
      				camera.projectionMatrix,
      				camera.matrixWorldInverse
      			)
      		);
      
      		this.expandFrustum(frustum, 25);
      		let found = octree.queryByFrustum(frustum);
      		found.forEach((matrix) => {
      			obj_position.setFromMatrixPosition(matrix);
      			cur_dist = obj_position.distanceTo(camera.position);
      			cur_level = this.getDistanceLevel(cur_dist);
      			if (cur_level != -1) {
      				instancedMeshOfAllLevel[cur_level].count++;
      				instancedMeshOfAllLevel[cur_level].matrix4.push(matrix); // column-major list of a matrix
      			}
      		});
      
      		for (let i = 0; i < numOfLevel; i++) {
      			const obj = instancedMeshOfAllLevel[i]; // obj: { meshes:[], count, matrix4:[] }
      			for (let j = 0; j < groupOfInstances[i].children.length; j++) {
      				let instancedMesh = groupOfInstances[i].children[j];
      
      				if (instancedMesh.count >= obj.count) {
      					instancedMesh.count = obj.count;
      					for (let k = 0; k < obj.count; k++) {
      						instancedMesh.instanceMatrix.needsUpdate = true;
      						instancedMesh.setMatrixAt(k, obj.matrix4[k]);
      					}
      				} else {
      					let new_instancedMesh = new THREE.InstancedMesh(
      						obj.meshes[j].geometry,
      						obj.meshes[j].material,
      						obj.count
      					);
      					for (let k = 0; k < obj.count; k++) {
      						new_instancedMesh.setMatrixAt(k, obj.matrix4[k]);
      					}
      					new_instancedMesh.castShadow = obj.castShadow;
      					new_instancedMesh.receiveShadow = obj.receiveShadow;
      					groupOfInstances[i].children[j] = new_instancedMesh;
      				}
      			}
      		}
      	}
      }
      
      export { InstancedLOD };
      
      posted @ 2024-05-24 08:38  楊元超  閱讀(373)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 内地偷拍一区二区三区| 福利一区二区在线观看| 在线看国产精品自拍内射| 东京热无码国产精品| 国产精品午夜福利免费看| 亚洲精品人成网线在播放VA| 无套内谢少妇一二三四| 亚洲国产精品自产拍久久| 泾源县| 亚洲综合一区二区三区视频| 久久人人97超碰爱香蕉| 亚洲超碰97无码中文字幕| 亚洲国产精品久久久天堂麻豆宅男| 亚洲人成小说网站色在线| 四虎国产精品久久免费精品| 韩日午夜在线资源一区二区| av天堂午夜精品一区| 婷婷六月色| 无码h黄肉动漫在线观看| 狠狠色婷婷久久综合频道日韩| 国产毛片精品av一区二区| 国产免费无遮挡吃奶视频| 国产中文字幕一区二区| 伊人久久大香线蕉AV网禁呦| 余江县| 少妇午夜福利一区二区三区| 国产果冻豆传媒麻婆| 亚洲欧洲∨国产一区二区三区 | 欲色欲色天天天www| 97欧美精品系列一区二区| 亚洲中文字幕国产综合| 狠狠色噜噜狠狠狠狠av不卡| 97久久超碰国产精品2021| 久久综合婷婷成人网站| 国产肉丝袜在线观看| 国产午夜成人久久无码一区二区| 一本av高清一区二区三区| 肥臀浪妇太爽了快点再快点| 另类专区一区二区三区| 安国市| 精品自拍自产一区二区三区|