THREE.JS:四叉树地形故障

问题描述 投票:0回答:1

我有一个非常简单的代码,用于基于伪四叉树算法从不同缩放级别的图块可视化地形。

由于跨域限制和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>
three.js terrain quadtree tiling level-of-detail
1个回答
0
投票

您的地形图块在不同缩放级别之间为z-fighting。在第二个缩放级别加载之前,这更明显,因为它们是黑色的,并显示了深度缓冲区的明显迹象,试图优先占据两个占据相同空间的平面。

enter image description here

您的YouTube视频中发生了同样的事情,但那时地形纹理已加载。在代码中的某个地方,deleteTile()或`updateTree()函数中的旧图块都没有被破坏(不确定为什么您有两个函数执行相同的操作)。

© www.soinside.com 2019 - 2024. All rights reserved.