【问题标题】:THREE.JS Level of Detail Texture LoadingTHREE.JS 细节层次纹理加载
【发布时间】:2020-09-07 12:49:29
【问题描述】:

这是带有 THREE.LOD() 对象的 Three.JS 小草图。 如您所见,共有 4 个级别具有独特的纹理。

现在,所有这些纹理都是在启动时预加载的。

有什么方法可以在放大时动态加载 1、2、3 级纹理?

是的,我可以在没有 THREE.LOD() 的情况下做同样的事情,只需编写我自己的自定义算法,该算法会在缩放时生成/删除平面,但我对内置的 THREE.LOD() 非常感兴趣。

    
var folder = "http://vault.vkuchinov.co.uk/test/assets";
var levels = [0xF25E6B, 0x4EA6A6, 0x8FD9D1, 0xF2B29B, 0xF28E85];  
    
var renderer, scene, camera, controls, loader, lod, glsl, uniforms;
    
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;

lod = new THREE.LOD();
lod.name = "0,0";
generateTiles(lod, 2048, 2048, 2048, 0, 0x00FFFF);
  
scene.add(lod);
    
animate();
        
function animate(){
         
    controls.update();
    renderer.render(scene, camera);
    
    requestAnimationFrame(animate);

}

function generateTiles(parent_, width_, height_, zoom_, level_, hex_){
    
    var id = parent_.name.split(",");
    var colors = [0xFFFF00, 0xFF000, 0x00FF00, 0x0000FF, 0xFF00FF, 0xF0F0F0];
    
    var group = new THREE.Group(), geometry, plane;

    var dx = 0, dy  = 0;
    dy *= Math.pow(2, level_); dx *= Math.pow(2, level_); 
    
    var url = folder + "/textures/level" + level_ + "/" + id[0] + "_" + id[1] + ".jpg";
    
    if(level_ < 3){
        
        var uniforms = {

            satellite: {
                type: "t",
                value: loader.load(url)
            }
            
        };

        var glsl = new THREE.ShaderMaterial({

        uniforms: uniforms,
        vertexShader: document.getElementById("vertexTerrain").textContent,
        fragmentShader: document.getElementById("fragmentTerrain").textContent,
        lights: false,
        fog: false,
        transparent: true

        });

        glsl.extensions.derivatives = true;
        
        geometry = new THREE.PlaneGeometry(width_, height_, 256, 256);
        plane = new THREE.Mesh(geometry, glsl); 
        plane.rotation.set(-Math.PI / 2, 0, 0);
        parent_.addLevel(plane, zoom_);

        geometry = new THREE.PlaneGeometry(width_ / 2, height_ / 2, 128, 128);

        var ix = (Number(id[0]) * 2);
        var iy  = (Number(id[1]) * 2);

        var lod1 = new THREE.LOD();
        var url1 = getURL(ix + "," + iy, width_ / 2, height_ / 2, zoom_ / 2, level_ + 1);
        
        var uniforms1 = {

            satellite: {
                type: "t",
                value: loader.load(url1)
            }
            
        };

        var glsl1 = new THREE.ShaderMaterial({

        uniforms: uniforms1,
        vertexShader: document.getElementById("vertexTerrain").textContent,
        fragmentShader: document.getElementById("fragmentTerrain").textContent,
        lights: false,
        fog: false,
        transparent: true

        });

        glsl1.extensions.derivatives = true;
        
        plane = new THREE.Mesh(geometry, glsl1);
        plane.rotation.set(-Math.PI / 2, 0, 0);
        lod1.addLevel(plane, zoom_ / 2);
        lod1.position.set(-width_ / 4, 0, -height_ / 4);
        lod1.name = ix + "," + iy;
        group.add(lod1);

        var lod2 = new THREE.LOD();
        var url2 = getURL(ix + "," + (iy + 1), width_ / 2, height_ / 2, zoom_ / 2, level_ + 1);
        
        var uniforms2 = {

            satellite: {
                type: "t",
                value: loader.load(url2)
            }
            
        };

        var glsl2 = new THREE.ShaderMaterial({

        uniforms: uniforms2,
        vertexShader: document.getElementById("vertexTerrain").textContent,
        fragmentShader: document.getElementById("fragmentTerrain").textContent,
        lights: false,
        fog: false,
        transparent: true

        });

        glsl2.extensions.derivatives = true;
        
        plane = new THREE.Mesh(geometry, glsl2);
        plane.rotation.set(-Math.PI / 2, 0, 0);
        lod2.addLevel(plane, zoom_ / 2);
        lod2.position.set(width_ / 4, 0, -height_ / 4);
        lod2.name = ix + "," + (iy + 1);
        group.add(lod2);

        var lod3 = new THREE.LOD();
        var url3 = getURL((ix + 1) + "," + iy, width_ / 2, height_ / 2, zoom_ / 2, level_ + 1);
        
        var uniforms3 = {

            satellite: {
                type: "t",
                value: loader.load(url3)
            }
            
        };

        var glsl3 = new THREE.ShaderMaterial({

        uniforms: uniforms3,
        vertexShader: document.getElementById("vertexTerrain").textContent,
        fragmentShader: document.getElementById("fragmentTerrain").textContent,
        lights: false,
        fog: false,
        transparent: true

        });

        glsl3.extensions.derivatives = true;
        
        plane = new THREE.Mesh(geometry, glsl3);
        plane.rotation.set(-Math.PI / 2, 0, 0);
        lod3.addLevel(plane, zoom_ / 2);
        lod3.position.set(-width_ / 4, 0, height_ / 4);
        lod3.name = (ix + 1) + "," + iy;
        group.add(lod3);

        var lod4 = new THREE.LOD();
        var url4 = getURL((ix + 1) + "," + (iy + 1), width_ / 2, height_ / 2, zoom_ / 2, level_ + 1);
        
        var uniforms4 = {

            satellite: {
                type: "t",
                value: loader.load(url4)
            }
            
        };

        var glsl4 = new THREE.ShaderMaterial({

        uniforms: uniforms4,
        vertexShader: document.getElementById("vertexTerrain").textContent,
        fragmentShader: document.getElementById("fragmentTerrain").textContent,
        lights: false,
        fog: false,
        transparent: true

        });

        glsl4.extensions.derivatives = true;
        
        plane = new THREE.Mesh(geometry, glsl4);
        plane.rotation.set(-Math.PI / 2, 0, 0);
        lod4.addLevel(plane, zoom_ / 2);
        lod4.position.set(width_ / 4, 0, height_ / 4);
        lod4.name = (ix + 1) + "," + (iy + 1);
        group.add(lod4);

        parent_.addLevel(group, zoom_ / 2);

        generateTiles(lod1, width_ / 2, height_ / 2, zoom_ / 2, level_ + 1, colors[level_]);
        generateTiles(lod2, width_ / 2, height_ / 2, zoom_ / 2, level_ + 1, colors[level_]);
        generateTiles(lod3, width_ / 2, height_ / 2, zoom_ / 2, level_ + 1, colors[level_]);
        generateTiles(lod4, width_ / 2, height_ / 2, zoom_ / 2, level_ + 1, colors[level_]);
        
    }

}
    
function getURL(name_, width_, height_, zoom_, level_){
    
    var id = name_.split(",");  
    return folder + "/textures/level" + level_ + "/" + id[0] + "_" + id[1] + ".jpg"; 

}
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/three@0.116.0/build/three.min.js"></script>
    <script src="https://unpkg.com/three@0.116.0/examples/js/controls/OrbitControls.js"></script>

</head>
<body>

<script id="vertexTerrain" type="x-shader/x-vertex">

uniform sampler2D satellite;
varying vec2 vUv;

void main() {

  vUv = uv;
  vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
  gl_Position = projectionMatrix * mvPosition;


}

</script>
    
<script id="fragmentTerrain" type="x-shader/x-fragment">

precision highp float;
precision highp int;

uniform sampler2D satellite;

varying vec2 vUv;

void main() {

    gl_FragColor = texture2D(satellite, vUv);

}
        
</script>

    
</body>
</html>

【问题讨论】:

    标签: three.js textures zooming levels


    【解决方案1】:

    看看the code,你可以扫描你的lods,看看它们的当前等级是多少,然后检查它是否已经加载?

    body {
      margin: 0;
    }
    #c {
      width: 100vw;
      height: 100vh;
      display: block;
    }
    <canvas id="c"></canvas>
    <script type="module">
    import * as THREE from 'https://threejsfundamentals.org/threejs/resources/threejs/r115/build/three.module.js';
    import {OrbitControls} from 'https://threejsfundamentals.org/threejs/resources/threejs/r115/examples/jsm/controls/OrbitControls.js';
    
    function main() {
      const canvas = document.querySelector('#c');
      const renderer = new THREE.WebGLRenderer({canvas});
    
      const fov = 75;
      const aspect = 2;  // the canvas default
      const near = 0.1;
      const far = 500;
      const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
      camera.position.z = 2;
    
      const controls = new OrbitControls(camera, canvas);
      controls.update();
    
      const scene = new THREE.Scene();
    
      {
        const color = 0xFFFFFF;
        const intensity = 1;
        const light = new THREE.DirectionalLight(color, intensity);
        light.position.set(-1, 2, 4);
        scene.add(light);
      }
    
      const numLevels = 4;
      const lodInfos = [];
      function createLod(pos) {
        const lod = new THREE.LOD();
        lod.position.set(...pos);
        scene.add(lod);
        for (let level = 0; level < numLevels; ++level) {
          const obj = new THREE.Object3D();
          lod.addLevel(obj, 3 + Math.pow(2, level));
        }
        lodInfos.push({
          lod,
          levels: [],
        });
      }
      
      createLod([0, 0, 0]);
      
      function scanLods() {
        for (const {lod, levels} of lodInfos) {
          const level = lod.getCurrentLevel();
          if (!levels[level]) {
            // this level is not loaded
            levels[level] = true; // mark it as loaded
            
            // load it
            loadLodLevel(level, lod.levels[level].object);
            
            // optimization: if all levels are loaded 
            // remove this from the lodInfos
          }
        }
      }
      
      function loadLodLevel(level, obj) {
        // obviously I'd use some kind of data structure but just to
        // get something working
        let geometry;
        let material;
        switch(level) {
          case 0:
            geometry = new THREE.BoxBufferGeometry(1, 1, 1);
            material = new THREE.MeshPhongMaterial({color: 'red'});
            break;
          case 1:
            geometry = new THREE.SphereBufferGeometry(0.5, 12, 6);
            material = new THREE.MeshPhongMaterial({color: 'yellow'});
            break;
          case 2:
            geometry = new THREE.ConeBufferGeometry(0.5, 1, 12);
            material = new THREE.MeshPhongMaterial({color: 'green'});
            break;
          case 3:
            geometry = new THREE.CylinderBufferGeometry(0.5, 0.5, 1, 12);
            material = new THREE.MeshPhongMaterial({color: 'purple'});
            break;
        }
        const lodMesh = new THREE.Mesh(geometry, material);
        obj.add(lodMesh);
      }
    
      function resizeRendererToDisplaySize(renderer) {
        const canvas = renderer.domElement;
        const width = canvas.clientWidth;
        const height = canvas.clientHeight;
        const needResize = canvas.width !== width || canvas.height !== height;
        if (needResize) {
          renderer.setSize(width, height, false);
        }
        return needResize;
      }
    
      function render(time) {
        time *= 0.001;
    
        if (resizeRendererToDisplaySize(renderer)) {
          const canvas = renderer.domElement;
          camera.aspect = canvas.clientWidth / canvas.clientHeight;
          camera.updateProjectionMatrix();
        }
    
        scanLods();
    
        renderer.render(scene, camera);
    
        requestAnimationFrame(render);
      }
    
      requestAnimationFrame(render);
    }
    
    main();
    </script>

    上面的解决方案为每个 lod 添加了一个THREE.Object3D,然后在它可见时为其添加一个网格。

    你也可以替换 THREE.Object3D 所以而不是

    obj.add(lodMesh);
    

    应该是这样的

    obj.levels[level].object = lodMesh;
    obj.parent.add(lodMesh);
    obj.parent.remove(obj);
    

    【讨论】:

    • 这有点工作,但我需要将其调整为递归版本。无论如何,非常感谢。
    猜你喜欢
    • 2014-03-10
    • 1970-01-01
    • 1970-01-01
    • 2013-07-28
    • 2012-10-03
    • 2013-06-28
    • 2014-12-28
    • 2020-07-23
    • 2015-05-30
    相关资源
    最近更新 更多