我有一个非常简单的代码,用于基于伪四叉树算法从不同缩放级别的图块可视化地形。
由于跨域限制和StackOverflow限制,我发布代码仅供参考,实时演示位于http://vault.vkuchinov.co.uk/lod/
旅行时出现故障,请检查YouTube video
似乎我弄乱了结构,但是我不知道那里。
有人可以帮助我解决此故障吗?
var folder = "17_50438_37354_50534_37450/satellite/";
var levels = [0x0B132B, 0x1C2541, 0x3A506B, 0x5BC0BE, 0x6FFFE9];
var mouseDown = false;
var renderer, scene, camera, controls, loader, terrain, glsl, uniforms, root, tree;
var colors = [0xFFFFFF, 0xFF0000, 0x00FF00, 0x0000FF, 0xFFFF00, 0xFF00FF];
class Node{
constructor(level_, index_, centerX_, centerY_, width_, height_, resolution_){
this.treeId = null;
this.level = level_;
this.index = index_;
this.w = width_;
this.h = height_;
this.x = centerX_;
this.y = centerY_;
this.resolution = resolution_;
}
}
class Quadtree{
constructor(root_, levels_, distance_){
var this_ = this;
this.levels = levels_;
this.distance = distance_;
this.root = root_;
this.tree = [ {id: "1", n: root_, active: true, children: null } ];
this.generateLevels(this.tree[0]);
this.lastTree = [...this.tree];
this.tiles = {};
this.generateTiles(this.tree[0]);
}
generateTiles = function(node_){
if(node_.children == null){
if(node_.active = true){ this.createTile(node_.n); }
}
else{
this.generateTiles(node_.children[0]);
this.generateTiles(node_.children[1]);
this.generateTiles(node_.children[2]);
this.generateTiles(node_.children[3]);
}
}
generateLevels = function(parent_){
if(parent_.n.level < this.levels && this.sqrtDistance(parent_.n) < this.distance){
parent_.children = [];
var parentID = parent_.id + ".";
parent_.children.push({id: parentID + 1, n: new Node(parent_.n.level + 1, { x: parent_.n.index.x * 2, y: parent_.n.index.y * 2 }, parent_.n.x - parent_.n.w / 4, parent_.n.y - parent_.n.h / 4, parent_.n.w / 2, parent_.n.h / 2, parent_.n.resolution / 2), active: true, children: null});
parent_.children.push({id: parentID + 2, n: new Node(parent_.n.level + 1, { x: parent_.n.index.x * 2, y: parent_.n.index.y * 2 + 1 }, parent_.n.x + parent_.n.w / 4, parent_.n.y - parent_.n.h / 4, parent_.n.w / 2, parent_.n.h / 2, parent_.n.resolution / 2), active: true, children: null});
parent_.children.push({id: parentID + 3, n: new Node(parent_.n.level + 1, { x: parent_.n.index.x * 2 + 1, y: parent_.n.index.y * 2 }, parent_.n.x - parent_.n.w / 4, parent_.n.y + parent_.n.h / 4, parent_.n.w / 2, parent_.n.h / 2, parent_.n.resolution / 2), active: true, children: null});
parent_.children.push({id: parentID + 4, n: new Node(parent_.n.level + 1, { x: parent_.n.index.x * 2 + 1, y: parent_.n.index.y * 2 + 1 }, parent_.n.x + parent_.n.w / 4, parent_.n.y + parent_.n.h / 4, parent_.n.w / 2, parent_.n.h / 2, parent_.n.resolution / 2), active: true, children: null});
this.generateLevels(parent_.children[0]);
this.generateLevels(parent_.children[1]);
this.generateLevels(parent_.children[2]);
this.generateLevels(parent_.children[3]);
parent_.active = false;
}
}
binToDec = function (bstr) { return parseInt((bstr + '').replace(/[^01]/gi, ''), 2); }
update = function(){
var this_ = this;
this.tree = [ {id: "1", n: this.root, active: true, children: null } ];
this.generateLevels(this.tree[0]);
var prev = {};
this_.bfs(this.lastTree[0], "children", prev);
var next = {};
this_.bfs(this.tree[0], "children", next);
Object.keys(next).forEach(function(key_){
if(!prev.hasOwnProperty(key_)){
if(next[key_].active = true) { this_.createTile(next[key_].n); }
}
})
//remove old
Object.keys(prev).forEach(function(key_){
if(!next.hasOwnProperty(key_)){
this_.deleteTile(prev[key_].n);
}
})
this.lastTree = [...this.tree];
}
bfs = function(tree, key, collection) {
if (!tree[key] || tree[key].length === 0) return;
for (var i=0; i < tree[key].length; i++) {
var child = tree[key][i]
collection[child.id] = child;
this.bfs(child, key, collection);
}
return;
}
getIndicesList = function(node_){
var out = [];
return out;
}
updateTree = function(node_, state_){
if(!state_) {
var id = "tile" + node_.n.index.x + "_" + node_.n.index.y;
if(!this.validateNodeByIndex(this.tree, node_.id)){
var tile = scene.getObjectByName(id);
if(tile != undefined){
tile.geometry.dispose();
tile.material.dispose();
scene.remove(tile);
}
}
}else{
var id = node_.n.index.x + "_" + node_.n.index.y;
if(!this.validateNodeByIndex(this.lastTree, node_.id)){
//this.createTile(node_.n);
}
}
if(node_.children != null){
this.updateTree(node_.children[0]);
this.updateTree(node_.children[1]);
this.updateTree(node_.children[2]);
this.updateTree(node_.children[3]);
}
}
getNodeByIndex = function(tree_, index_){
var n = index_.split(".");
n = n.map(function(f_){ return Number(f_); })
if( n.length == 2) { return tree_[0].children[n[1]]; }
else if ( n.length == 3) { return tree_[0].children[n[1] - 1].children[n[2] - 1]; }
else if ( n.length == 4) { return tree_[0].children[n[1] - 1].children[n[2] - 1].children[n[3] - 1]; }
else if ( n.length == 5) { return tree_[0].children[n[1] - 1].children[n[2] - 1].children[n[3] - 1].children[n[4] - 1]; }
else if ( n.length == 6) { return tree_[0].children[n[1] - 1].children[n[2] - 1].children[n[3] - 1].children[n[4] - 1].children[n[5] - 1]; }
}
validateNodeByIndex = function(tree_, index_){
var out = false;
var nodes = tree_[0]
return out;
}
findNode = function(index_){
var out = false;
for (let node_ of this.nodes) {
if(node_.index.x + "_" + node_.index.y == index_) { out = node_; break; }
}
return out;
}
createTile = function(parent_){
var id = parent_.index.x + "_" + parent_.index.y;
var url = folder + "level" + parent_.level + "/" + parent_.index.x + "_" + parent_.index.y + ".jpg";
var geometry = new THREE.PlaneGeometry(parent_.w, parent_.h, parent_.resolution, parent_.resolution);
var material = new THREE.MeshBasicMaterial({ map: new THREE.TextureLoader().load(url) });
var tile = new THREE.Mesh(geometry, material);
tile.name = "tile" + parent_.index.x + "_" + parent_.index.y;
tile.rotation.set(-Math.PI / 2, 0, 0);
tile.position.set(parent_.x, 0, parent_.y);
scene.add(tile);
}
deleteTile = function(parent_){
var name = "tile" + parent_.index.x + "_" + parent_.index.y;
var tile = scene.getObjectByName(name);
if(tile != undefined){
scene.remove(tile);
tile.geometry.dispose();
tile.material.dispose();
}
}
bin_to_dec = function (bstr) { return parseInt((bstr + '').replace(/[^01]/gi, ''), 2); }
splitNode = function(index_, parent_, tree_, check_){
if((parent_.level < this.levels && this.sqrtDistance(parent_) < this.distance) || !check_){
tree_.children = [];
var lt = new Node(parent_.level + 1, { x: parent_.index.x * 2, y: parent_.index.y * 2 }, parent_.x - parent_.w / 4, parent_.y - parent_.h / 4, parent_.w / 2, parent_.h / 2, parent_.resolution / 2);
var rt = new Node(parent_.level + 1, { x: parent_.index.x * 2, y: parent_.index.y * 2 + 1 }, parent_.x + parent_.w / 4, parent_.y - parent_.h / 4, parent_.w / 2, parent_.h / 2, parent_.resolution / 2);
var lb = new Node(parent_.level + 1, { x: parent_.index.x * 2 + 1, y: parent_.index.y * 2 }, parent_.x - parent_.w / 4, parent_.y + parent_.h / 4, parent_.w / 2, parent_.h / 2, parent_.resolution / 2);
var rb = new Node(parent_.level + 1, { x: parent_.index.x * 2 + 1, y: parent_.index.y * 2 + 1 }, parent_.x + parent_.w / 4, parent_.y + parent_.h / 4, parent_.w / 2, parent_.h / 2, parent_.resolution / 2);
tree_.children[0] = {id: lt.index.x + "_" + lt.index.y, n: lt, children: null };
tree_.children[1] = {id: rt.index.x + "_" + rt.index.y, n: rt, children: null };
tree_.children[2] = {id: lb.index.x + "_" + lb.index.y, n: lb, children: null };
tree_.children[3] = {id: rb.index.x + "_" + rb.index.y, n: rb, children: null };
lt.treeId = tree_.children[0];
rt.treeId = tree_.children[1];
lb.treeId = tree_.children[2];
rb.treeId = tree_.children[3];
return [lt, rt, lb, rb];
}
return [parent_];
}
getDifference = function(t1_, t2_) {
function getD(object, parent) {
function getKeys({ where, ...object }) {
return Object.keys(object).flatMap(k => [k, ...(object[k] ? getKeys(object[k]) : [])]);
}
var types;
Object
.entries(object)
.forEach(([k, { where, ...o }]) => {
if (where) {
let keys = getKeys(o);
if (!types) types = {};
if (!types[where]) types[where] = [];
types[where].push(keys.length ? [k, keys] : k);
} else {
getD(o, k);
}
});
if (types) {
if (-1 in types) result.push([types[-1], parent]);
if (1 in types) result.push([parent, types[1]]);
}
}
var temp = {},
add = (inc, tree) => ({ id, children }) => {
tree[id] = tree[id] || { where: 0 };
tree[id].where += inc;
if (children) children.forEach(add(inc, tree[id]));
},
result = [];
t1_.forEach(add(-1, temp));
t2_.forEach(add(1, temp));
getD(temp);
return result;
}
sqrtDistance = function(node_){
var target = new THREE.Vector2(camera.position.x, camera.position.z).lerp(new THREE.Vector2(controls.target.x, controls.target.z), 1.0);
var x1 = node_.x - node_.w / 2.0;
var y1 = node_.y - node_.h / 2.0;
var x2 = node_.x + node_.w / 2.0;
var y2 = node_.y + node_.h / 2.0;
var rx = (x1 + x2) / 2.0;
var ry = (y1 + y2) / 2.0;
var rwidth = node_.w;
var rheight = node_.h;
var dx = Math.max(Math.abs(target.x - rx) - rwidth / 2, 0);
var dy = Math.max(Math.abs(target.y - ry) - rheight / 2, 0);
return Math.sqrt(dx * dx + dy * dy);
}
draw = function(){
this.nodes.forEach(function(node_){ node_.draw(); })
}
}
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0x000000);
document.body.appendChild(renderer.domElement);
scene = new THREE.Scene();
loader = new THREE.TextureLoader();
loader.crossOrigin = "";
camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 51200);
camera.position.set(-2048, 2048, -2048);
controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.05;
controls.screenSpacePanning = false;
controls.minDistance = 8;
controls.maxDistance = 5120;
controls.maxPolarAngle = Math.PI / 2;
camera.position.set(208.48355078304965, 45.28894677815297, 310.34089790619583);
controls.target.set(233.437242880138, -1.1266992511037067e-14, 279.779814968453);
root = new Node(0, {x: 0, y: 0}, 0, 0, 2048, 2048, 64);
tree = new Quadtree(root, 5, 2048.0 / 16.0);
animate();
document.addEventListener("mousedown", function(){ mouseDown = true; }, false);
document.addEventListener("mouseup", function(){ mouseDown = false; }, false);
document.addEventListener("mousemove", onMouseUpdate, false);
renderer.domElement.addEventListener("wheel", onMouseUpdate, false);
function animate(){
console.log(scene.children.length);
controls.update();
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
function onMouseUpdate(e_){
tree.update();
}
body { margin: 0; }
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>GLSL Intersection</title>
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
<script src="https://unpkg.com/[email protected]/build/three.min.js"></script>
<script src="https://unpkg.com/[email protected]/examples/js/controls/OrbitControls.js"></script>
<script src="stats.min.js"></script>
</head>
<body>
</body>
</html>
您的地形图块在不同缩放级别之间为z-fighting。在第二个缩放级别加载之前,这更明显,因为它们是黑色的,并显示了深度缓冲区的明显迹象,试图优先占据两个占据相同空间的平面。
您的YouTube视频中发生了同样的事情,但那时地形纹理已加载。在代码中的某个地方,deleteTile()
或`updateTree()函数中的旧图块都没有被破坏(不确定为什么您有两个函数执行相同的操作)。