threejs使用外部模型

news2024/11/18 18:21:29

个人博客地址: https://cxx001.gitee.io

前面我们都是用Threejs提供的几何体来创建网格,对于简单几何体(如球体和方块)来说非常有效,但当你想要创建复杂的三维模型时,这不是最好的方法。通常情况下,你可以使用三维建模工具(如Blender和3D Studio Max)来创建复杂几何体。

本节就来学习如何加载和展示由这些三维建模工具所创建的模型。

网格对象组合与合并

在学习使用外部三维建模工具所创建的模型前,我们先了解两个基本操作:将对象组合在一起,以及将多个网格合并为一个网格。

1. 网格组合

这个不是什么新东西了,前面我们很多示例其实早就使用了。就是把多个网格对象添加到一个对象里(THREE.Group),对这1个对象移动、缩放、旋转变换操作其子对象会一起变换。

<!-- chapter-08-01.html -->
<!DOCTYPE html>
<html>
<head>
    <title>Group</title>
    <script type="text/javascript" src="../libs/three.js"></script>
    <script type="text/javascript" src="../libs/stats.js"></script>
    <script type="text/javascript" src="../libs/dat.gui.js"></script>
    <style>
        body {
            margin: 0;
            overflow: hidden;
        }
    </style>
</head>
<body>

<div id="Stats-output">
</div>
<div id="WebGL-output">
</div>

<script type="text/javascript">
    function init() {
        var stats = initStats();
        var scene = new THREE.Scene();
        var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);

        var webGLRenderer = new THREE.WebGLRenderer();
        webGLRenderer.setClearColor(new THREE.Color(0xEEEEEE, 1.0));
        webGLRenderer.setSize(window.innerWidth, window.innerHeight);
        webGLRenderer.shadowMapEnabled = true;

        camera.position.x = 30;
        camera.position.y = 30;
        camera.position.z = 30;
        camera.lookAt(new THREE.Vector3(0, 0, 0));

        var ground = new THREE.PlaneGeometry(100, 100, 50, 50);
        var groundMesh = THREE.SceneUtils.createMultiMaterialObject(ground,
                [new THREE.MeshBasicMaterial({wireframe: true, overdraw: true, color: 000000}),
                    new THREE.MeshBasicMaterial({color: 0x00ff00, transparent: true, opacity: 0.5}
                    )
                ]);
        groundMesh.rotation.x = -0.5 * Math.PI;
        scene.add(groundMesh);

        document.getElementById("WebGL-output").appendChild(webGLRenderer.domElement);

        var step = 0.03;
        var sphere;
        var cube;
        var group;
        var controls = new function () {
            this.cubePosX = 0;
            this.cubePosY = 3;
            this.cubePosZ = 10;

            this.spherePosX = 10;
            this.spherePosY = 5;
            this.spherePosZ = 0;

            this.groupPosX = 10;
            this.groupPosY = 5;
            this.groupPosZ = 0;

            this.grouping = false;
            this.rotate = false;

            this.groupScale = 1;
            this.cubeScale = 1;
            this.sphereScale = 1;

            this.redraw = function () {
                scene.remove(group);
                sphere = createMesh(new THREE.SphereGeometry(5, 10, 10));
                cube = createMesh(new THREE.BoxGeometry(6, 6, 6));
                sphere.position.set(controls.spherePosX, controls.spherePosY, controls.spherePosZ);
                cube.position.set(controls.cubePosX, controls.cubePosY, controls.cubePosZ);
				
                // 将球体和立方体网格添加到组合对象中
                group = new THREE.Group();
                group.add(sphere);
                group.add(cube);
                scene.add(group);
				
                // 在group组合对象中心位置标志一个箭头
                var arrow = new THREE.ArrowHelper(new THREE.Vector3(0, 1, 0), group.position, 10, 0x0000ff);
                scene.add(arrow);
            };
        };

        var gui = new dat.GUI();
        var sphereFolder = gui.addFolder("sphere");
        sphereFolder.add(controls, "spherePosX", -20, 20).onChange(function (e) {
            sphere.position.x = e;
        });
        sphereFolder.add(controls, "spherePosZ", -20, 20).onChange(function (e) {
            sphere.position.z = e;
        });
        sphereFolder.add(controls, "spherePosY", -20, 20).onChange(function (e) {
            sphere.position.y = e;
        });
        sphereFolder.add(controls, "sphereScale", 0, 3).onChange(function (e) {
            sphere.scale.set(e, e, e);
        });

        var cubeFolder = gui.addFolder("cube");
        cubeFolder.add(controls, "cubePosX", -20, 20).onChange(function (e) {
            cube.position.x = e;
        });
        cubeFolder.add(controls, "cubePosZ", -20, 20).onChange(function (e) {
            cube.position.z = e;
        });
        cubeFolder.add(controls, "cubePosY", -20, 20).onChange(function (e) {
            cube.position.y = e;
        });
        cubeFolder.add(controls, "cubeScale", 0, 3).onChange(function (e) {
            cube.scale.set(e, e, e);
        });

        var cubeFolder = gui.addFolder("group");
        cubeFolder.add(controls, "groupPosX", -20, 20).onChange(function (e) {
            group.position.x = e;
        });
        cubeFolder.add(controls, "groupPosZ", -20, 20).onChange(function (e) {
            group.position.z = e;
        });
        cubeFolder.add(controls, "groupPosY", -20, 20).onChange(function (e) {
            group.position.y = e;
        });
        cubeFolder.add(controls, "groupScale", 0, 3).onChange(function (e) {
            group.scale.set(e, e, e);
        });

        gui.add(controls, "grouping");
        gui.add(controls, "rotate");
        controls.redraw();
        render();

        function createMesh(geom) {
            var meshMaterial = new THREE.MeshNormalMaterial();
            meshMaterial.side = THREE.DoubleSide;
            var wireFrameMat = new THREE.MeshBasicMaterial();
            wireFrameMat.wireframe = true;
            var plane = THREE.SceneUtils.createMultiMaterialObject(geom, [meshMaterial, wireFrameMat]);
            return plane;
        }

        function render() {
            stats.update();
            if (controls.grouping && controls.rotate) {
                group.rotation.y += step;
            }

            if (controls.rotate && !controls.grouping) {
                sphere.rotation.y += step;
                cube.rotation.y += step;
            }

            requestAnimationFrame(render);
            webGLRenderer.render(scene, camera);
        }

        function initStats() {
            var stats = new Stats();
            stats.setMode(0); // 0: fps, 1: ms
            stats.domElement.style.position = 'absolute';
            stats.domElement.style.left = '0px';
            stats.domElement.style.top = '0px';
            document.getElementById("Stats-output").appendChild(stats.domElement);
            return stats;
        }
	};

    window.onload = init;
</script>
</body>
</html>

在这里插入图片描述

2. 网格合并

通过THREE.Geometry.merge()函数可以将多个网格对象合并成一个。如果场景中网格太多是有性能瓶颈的,合并它们可以提升渲染效率。但是注意合并后你就不能再单独操作某个网格了。

<!-- chapter-08-02.html -->
<!DOCTYPE html>
<html>
<head>
    <title>Merge objects</title>
    <script type="text/javascript" src="../libs/three.js"></script>
    <script type="text/javascript" src="../libs/stats.js"></script>
    <script type="text/javascript" src="../libs/dat.gui.js"></script>
    <style>
        body {
            margin: 0;
            overflow: hidden;
        }
    </style>
</head>
<body>

<div id="Stats-output">
</div>
<div id="WebGL-output">
</div>

<script type="text/javascript">
    function init() {
        var stats = initStats();
        var scene = new THREE.Scene();
        var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 500);

        var renderer = new THREE.WebGLRenderer();
        renderer.setClearColor(new THREE.Color(0x00000, 1.0));
        renderer.setSize(window.innerWidth, window.innerHeight);
        renderer.shadowMapEnabled = true;

        camera.position.x = 0;
        camera.position.y = 40;
        camera.position.z = 50;
        camera.lookAt(scene.position);
        document.getElementById("WebGL-output").appendChild(renderer.domElement);

        var step = 0;
        var cubeMaterial = new THREE.MeshNormalMaterial({color: 0x00ff00, transparent: true, opacity: 0.5});
    
        var controls = new function () {
            this.combined = false;
            this.numberOfObjects = 500;

            this.redraw = function () {
                var toRemove = [];
                // traverse遍历场景对象是不能增、删操作
                scene.traverse(function (e) {
                    if (e instanceof THREE.Mesh) toRemove.push(e);
                });
                toRemove.forEach(function (e) {
                    scene.remove(e)
                });

                if (controls.combined) {
                    // 将所有网格对象合并到geometry一个对象中
                    var geometry = new THREE.Geometry();
                    for (var i = 0; i < controls.numberOfObjects; i++) {
                        var cubeMesh = addCube();
                        cubeMesh.updateMatrix(); // 变换矩阵,保证合并后正确定位和旋转
                        geometry.merge(cubeMesh.geometry, cubeMesh.matrix); // 添加合并网格
                    }
                    scene.add(new THREE.Mesh(geometry, cubeMaterial));

                } else {
                    // 不合并,网格对象一个个添加到场景中
                    for (var i = 0; i < controls.numberOfObjects; i++) {
                        scene.add(addCube());
                    }
                }
            };
        };

        var gui = new dat.GUI();
        gui.add(controls, 'numberOfObjects', 0, 20000);
        gui.add(controls, 'combined').onChange(controls.redraw);
        gui.add(controls, 'redraw');
        
    	controls.redraw();
        render();
		
    	// 添加立方体
        function addCube() {
            var cubeSize = 1.0;
            var cubeGeometry = new THREE.BoxGeometry(cubeSize, cubeSize, cubeSize);
            var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
            cube.position.x = -60 + Math.round((Math.random() * 100));
            cube.position.y = Math.round((Math.random() * 10));
            cube.position.z = -150 + Math.round((Math.random() * 175));
            return cube;
        }
    	
    	var rotation = 0;
        function render() {
            rotation += 0.005;
            stats.update();
            camera.position.x = Math.sin(rotation) * 50;
            camera.position.z = Math.cos(rotation) * 50;
            camera.lookAt(scene.position);
            requestAnimationFrame(render);
            renderer.render(scene, camera);
        }

        function initStats() {
            var stats = new Stats();
            stats.setMode(0); // 0: fps, 1: ms
            stats.domElement.style.position = 'absolute';
            stats.domElement.style.left = '0px';
            stats.domElement.style.top = '0px';
            document.getElementById("Stats-output").appendChild(stats.domElement);
            return stats;
        }
    }
    window.onload = init;
</script>
</body>
</html>

创建2W个网格直接添加进场景,看帧率降到14了。

同样2W个,合并后,帧率正常了。


从外部资源加载网格

threejs支持多种三维文件格式,可以读取并从中导入几何体和网格。下面是threejs支持的文件格式:

下面依次介绍这些三维文件格式在Threejs中怎么导入/导出的。

1. 以Threejs的JSON格式保存和加载

你可以在两种情形下使用Threejs的JSON文件格式:用它来保存和加载单个THREE.Mesh(网格),或者用它来保存和加载整个场景。

  1. 保存和加载THREE.Mesh

保存:通过mesh.toJSON()可以将网格转换为json对象,后面就是js的常规保存了。

加载:Threejs提供了一个叫THREE.ObjectLoader的辅助对象,使用它可以将JSON转换成THREE.Mesh对象。

<!-- chapter-08-03.html -->
<!DOCTYPE html>
<html>
<head>
    <title>Save & Load Mesh</title>
    <script type="text/javascript" src="../libs/three.js"></script>
    <script type="text/javascript" src="../libs/stats.js"></script>
    <script type="text/javascript" src="../libs/dat.gui.js"></script>
    <style>
        body {
            margin: 0;
            overflow: hidden;
        }
    </style>
</head>
<body>

<div id="Stats-output">
</div>
<div id="WebGL-output">
</div>

<script type="text/javascript">
    function init() {
        var stats = initStats();
        var scene = new THREE.Scene();
        var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);

        var webGLRenderer = new THREE.WebGLRenderer();
        webGLRenderer.setClearColor(new THREE.Color(0xEEEEEE, 1.0));
        webGLRenderer.setSize(window.innerWidth, window.innerHeight);
        webGLRenderer.shadowMapEnabled = true;
		
        var knot = createMesh(new THREE.TorusKnotGeometry(10, 1, 64, 8, 2, 3, 1));
        scene.add(knot);

        camera.position.x = -30;
        camera.position.y = 40;
        camera.position.z = 50;
        camera.lookAt(new THREE.Vector3(-20, 0, 0));
        document.getElementById("WebGL-output").appendChild(webGLRenderer.domElement);

        var loadedMesh;
        var controls = new function () {
            this.radius = knot.geometry.parameters.radius;
            this.tube = 0.3;
            this.radialSegments = knot.geometry.parameters.radialSegments;
            this.tubularSegments = knot.geometry.parameters.tubularSegments;
            this.p = knot.geometry.parameters.p;
            this.q = knot.geometry.parameters.q;
            this.heightScale = knot.geometry.parameters.heightScale;

            this.redraw = function () {
                scene.remove(knot);
                knot = createMesh(new THREE.TorusKnotGeometry(controls.radius, controls.tube, Math.round(controls.radialSegments), Math.round(controls.tubularSegments), Math.round(controls.p), Math.round(controls.q), controls.heightScale));
                scene.add(knot);
            };
			
            // 保存
            this.save = function () {
                // 网格对象转换为JSON对象
                var result = knot.toJSON();
                // 调用HTML5本地保存数据接口
                localStorage.setItem("json", JSON.stringify(result));
            };
			
            // 加载
            this.load = function () {
                scene.remove(loadedMesh);
                // 调用HTML5本地读取数据接口
                var json = localStorage.getItem("json");
                if (json) {
                    // JSON字符串转换为json对象
                    var loadedGeometry = JSON.parse(json);
                    // JSON对象转换为网格对象
                    var loader = new THREE.ObjectLoader();
                    loadedMesh = loader.parse(loadedGeometry);
                    loadedMesh.position.x -= 50;
                    scene.add(loadedMesh);
                }
            }
        };

        var gui = new dat.GUI();
        var ioGui = gui.addFolder('Save & Load');
        ioGui.add(controls, 'save').onChange(controls.save);
        ioGui.add(controls, 'load').onChange(controls.load);
        var meshGui = gui.addFolder('mesh');
        meshGui.add(controls, 'radius', 0, 40).onChange(controls.redraw);
        meshGui.add(controls, 'tube', 0, 40).onChange(controls.redraw);
        meshGui.add(controls, 'radialSegments', 0, 400).step(1).onChange(controls.redraw);
        meshGui.add(controls, 'tubularSegments', 1, 20).step(1).onChange(controls.redraw);
        meshGui.add(controls, 'p', 1, 10).step(1).onChange(controls.redraw);
        meshGui.add(controls, 'q', 1, 15).step(1).onChange(controls.redraw);
        meshGui.add(controls, 'heightScale', 0, 5).onChange(controls.redraw);

        render();

        function createMesh(geom) {
            var meshMaterial = new THREE.MeshBasicMaterial({
                vertexColors: THREE.VertexColors,
                wireframe: true,
                wireframeLinewidth: 2,
                color: 0xaaaaaa
            });
            meshMaterial.side = THREE.DoubleSide;
            var mesh = new THREE.Mesh(geom, meshMaterial);
            return mesh;
        }

        function render() {
            stats.update();
            knot.rotation.y += 0.01;
            requestAnimationFrame(render);
            webGLRenderer.render(scene, camera);
        }

        function initStats() {
            var stats = new Stats();
            stats.setMode(0); // 0: fps, 1: ms
            stats.domElement.style.position = 'absolute';
            stats.domElement.style.left = '0px';
            stats.domElement.style.top = '0px';
            document.getElementById("Stats-output").appendChild(stats.domElement);
            return stats;
        }
    }
    window.onload = init;
</script>
</body>
</html>

  1. 保存和加载场景

使用Threejs提供的导出器和加载器: THREE.SceneExporterTHREE.SceneLoader。也支持从URL地址加载。

<!-- chapter-08-04.html -->
<!DOCTYPE html>
<html>
<head>
    <title>Load and save scene</title>
    <script type="text/javascript" src="../libs/three.js"></script>
    <script type="text/javascript" src="../libs/SceneLoader.js"></script>
    <script type="text/javascript" src="../libs/SceneExporter.js"></script>
    <script type="text/javascript" src="../libs/stats.js"></script>
    <script type="text/javascript" src="../libs/dat.gui.js"></script>
    <style>
        body {
            margin: 0;
            overflow: hidden;
        }
    </style>
</head>
<body>

<div id="Stats-output">
</div>
<div id="WebGL-output">
</div>

<script type="text/javascript">
    function init() {
        var stats = initStats();
        var scene = new THREE.Scene();
        var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);

        var renderer = new THREE.WebGLRenderer();
        renderer.setClearColor(new THREE.Color(0xEEEEEE, 1.0));
        renderer.setSize(window.innerWidth, window.innerHeight);

        var planeGeometry = new THREE.PlaneGeometry(60, 20, 1, 1);
        var planeMaterial = new THREE.MeshLambertMaterial({color: 0xffffff});
        var plane = new THREE.Mesh(planeGeometry, planeMaterial);

        plane.rotation.x = -0.5 * Math.PI;
        plane.position.x = 15;
        plane.position.y = 0;
        plane.position.z = 0;
        scene.add(plane);

        var cubeGeometry = new THREE.BoxGeometry(4, 4, 4);
        var cubeMaterial = new THREE.MeshLambertMaterial({color: 0xff0000});
        var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
        cube.position.x = -4;
        cube.position.y = 3;
        cube.position.z = 0;
        scene.add(cube);

        var sphereGeometry = new THREE.SphereGeometry(4, 20, 20);
        var sphereMaterial = new THREE.MeshLambertMaterial({color: 0x7777ff});
        var sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
        sphere.position.x = 20;
        sphere.position.y = 0;
        sphere.position.z = 2;
        scene.add(sphere);

        camera.position.x = -30;
        camera.position.y = 40;
        camera.position.z = 30;
        camera.lookAt(scene.position);

        var ambientLight = new THREE.AmbientLight(0x0c0c0c);
        scene.add(ambientLight);

        var spotLight = new THREE.PointLight(0xffffff);
        spotLight.position.set(-40, 60, -10);
        scene.add(spotLight);
        document.getElementById("WebGL-output").appendChild(renderer.domElement);

        var controls = new function () {
            // 导出场景
            this.exportScene = function () {
                var exporter = new THREE.SceneExporter();
                var sceneJson = JSON.stringify(exporter.parse(scene));
                localStorage.setItem('scene', sceneJson);
            };
			
            // 清理场景
            this.clearScene = function () {
                scene = new THREE.Scene();
            };
			
            // 导入场景
            this.importScene = function () {
                var json = (localStorage.getItem('scene'));
                var sceneLoader = new THREE.SceneLoader();
                sceneLoader.parse(JSON.parse(json), function (e) {
                    scene = e.scene;
                }, '.'); // 最后参数是外部纹理资源路径,这个示例没有使用外部资源,所以传入当前目录即可。
            }
        };

        var gui = new dat.GUI();
        gui.add(controls, "exportScene");
        gui.add(controls, "clearScene");
        gui.add(controls, "importScene");

        render();
        function render() {
            stats.update();
            requestAnimationFrame(render);
            renderer.render(scene, camera);
        }

        function initStats() {
            var stats = new Stats();
            stats.setMode(0); // 0: fps, 1: ms
            stats.domElement.style.position = 'absolute';
            stats.domElement.style.left = '0px';
            stats.domElement.style.top = '0px';
            document.getElementById("Stats-output").appendChild(stats.domElement);
            return stats;
        }
    }
    window.onload = init;
</script>
</body>
</html>

2. 使用Blender导出JSON格式加载

有很多三维软件可以用来创建复杂的网格。其中一个流行的开源的软件叫作Blender(www.blender.org)。

Threejs库目前已经提供了支持Blender以及Maya和3D Studio Max的导出器(插件扩展的形式),可以直接将文件导出为Threejs的JSON格式。

注:怎么安装和使用Blender支持导出json格式的插件这里不详细介绍了,详情参考《Three.js开发指南-第八章》。

<!-- chapter-08-05.html -->
<!DOCTYPE html>
<html>
<head>
    <title>Load blender model </title>
    <script type="text/javascript" src="../libs/three.js"></script>
    <script type="text/javascript" src="../libs/stats.js"></script>
    <script type="text/javascript" src="../libs/dat.gui.js"></script>
    <style>
        body {
            margin: 0;
            overflow: hidden;
        }
    </style>
</head>
<body>

<div id="Stats-output">
</div>
<div id="WebGL-output">
</div>

<script type="text/javascript">
    function init() {
        var stats = initStats();
        var scene = new THREE.Scene();
        var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);

        var webGLRenderer = new THREE.WebGLRenderer();
        webGLRenderer.setClearColor(new THREE.Color(0xEEEEEE, 1.0));
        webGLRenderer.setSize(window.innerWidth, window.innerHeight);
        webGLRenderer.shadowMapEnabled = true;

        camera.position.x = -30;
        camera.position.y = 40;
        camera.position.z = 50;
        camera.lookAt(new THREE.Vector3(0, 10, 0));

        var spotLight = new THREE.SpotLight(0xffffff);
        spotLight.position.set(0, 50, 30);
        spotLight.intensity = 2;
        scene.add(spotLight);
        document.getElementById("WebGL-output").appendChild(webGLRenderer.domElement);

        var mesh;
    	// 加载Blender导出的JSON模型文件
        var loader = new THREE.JSONLoader();
    	/* 参数:
    	 * JSON模型文件
    	 * 回调函数,返回几何体和材质数组
    	 * 材质所在路径,即JSON中mapDiffuse字段图片路径
    	*/
        loader.load('../assets/models/misc_chair01.js', function (geometry, mat) {
            mesh = new THREE.Mesh(geometry, mat[0]);
            mesh.scale.x = 15;
            mesh.scale.y = 15;
            mesh.scale.z = 15;
            scene.add(mesh);
        }, '../assets/models/');

        render();
        function render() {
            stats.update();
            if (mesh) {
                mesh.rotation.y += 0.02;
            }
            requestAnimationFrame(render);
            webGLRenderer.render(scene, camera);
        }

        function initStats() {
            var stats = new Stats();
            stats.setMode(0); // 0: fps, 1: ms
            stats.domElement.style.position = 'absolute';
            stats.domElement.style.left = '0px';
            stats.domElement.style.top = '0px';
            document.getElementById("Stats-output").appendChild(stats.domElement);
            return stats;
        }
    }
    window.onload = init;
</script>
</body>
</html>

在这里插入图片描述

3. 加载OBJ/MTL格式模型

OBJ和MTL是相互配合的两种格式,经常一起使用。OBJ文件定义几何体,MTL文件定义所用材质。

<!-- chapter-08-06.html -->
<!DOCTYPE html>
<html>
<head>
    <title>Load OBJ and MTL </title>
    <script type="text/javascript" src="../libs/three.js"></script>
    <script type="text/javascript" src="../libs/OBJLoader.js"></script>
    <script type="text/javascript" src="../libs/MTLLoader.js"></script>
    <script type="text/javascript" src="../libs/OBJMTLLoader.js"></script>
    <script type="text/javascript" src="../libs/stats.js"></script>
    <script type="text/javascript" src="../libs/dat.gui.js"></script>
    <style>
        body {
            margin: 0;
            overflow: hidden;
        }
    </style>
</head>
<body>

<div id="Stats-output">
</div>
<div id="WebGL-output">
</div>

<script type="text/javascript">
    function init() {
        var stats = initStats();
        var scene = new THREE.Scene();
        var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);

        var webGLRenderer = new THREE.WebGLRenderer();
        webGLRenderer.setClearColor(new THREE.Color(0xaaaaff, 1.0));
        webGLRenderer.setSize(window.innerWidth, window.innerHeight);
        webGLRenderer.shadowMapEnabled = true;

        camera.position.x = -30;
        camera.position.y = 40;
        camera.position.z = 50;
        camera.lookAt(new THREE.Vector3(0, 10, 0));

        var spotLight = new THREE.SpotLight(0xffffff);
        spotLight.position.set(0, 40, 30);
        spotLight.intensity = 2;
        scene.add(spotLight);
        document.getElementById("WebGL-output").appendChild(webGLRenderer.domElement);

        var mesh;
    	// 加载OBJ/MTL格式
        var loader = new THREE.OBJMTLLoader();
        loader.load('../assets/models/butterfly.obj', '../assets/models/butterfly.mtl', function (object) {
            // 对加载的网格模型材质属性微调
            var wing1 = object.children[4].children[0];
            var wing2 = object.children[5].children[0];
            wing1.material.opacity = 0.6;
            wing1.material.transparent = true;
            wing1.material.depthTest = false;
            wing1.material.side = THREE.DoubleSide;

            wing2.material.opacity = 0.6;
            wing2.material.depthTest = false;
            wing2.material.transparent = true;
            wing2.material.side = THREE.DoubleSide;

            object.scale.set(140, 140, 140);
            mesh = object;
            scene.add(mesh);

            object.rotation.x = 0.2;
            object.rotation.y = -1.3;
        });

        render();
        function render() {
            stats.update();
            if (mesh) {
                mesh.rotation.y += 0.006;
            }
            requestAnimationFrame(render);
            webGLRenderer.render(scene, camera);
        }

        function initStats() {
            var stats = new Stats();
            stats.setMode(0); // 0: fps, 1: ms
            stats.domElement.style.position = 'absolute';
            stats.domElement.style.left = '0px';
            stats.domElement.style.top = '0px';
            document.getElementById("Stats-output").appendChild(stats.domElement);
            return stats;
        }
    }
    window.onload = init;
</script>
</body>
</html>

在这里插入图片描述

4. 加载Collada模型(.dae)

这是另一种非常通用的格式,不仅可以定义模型(网格和材质),还可以定义场景以及动画。

加载这种格式,使用上和加载OBJ/MTL模型步骤基本一样。主要区别是回调函数的返回结构不同:

var result = {
    ...
    scene: scene,
    morphs: morphs,
    skins: skins,
    animations: animData,
    dae: {
        ...
    }
    ...
}

还一个需要注意的点是,导出.dae格式模型,如果描述文件中纹理是用的.tga格式,那么需要把它转换为.png,并对应修改.dae模型文件的XML元素,指向转换后的.png文件。因为WebGL不支持.tga格式的纹理。

<!-- chapter-08-07.html -->
<!DOCTYPE html>
<html>
<head>
    <title>Load collada model </title>
    <script type="text/javascript" src="../libs/three.js"></script>
    <script type="text/javascript" src="../libs/ColladaLoader.js"></script>
    <script type="text/javascript" src="../libs/stats.js"></script>
    <script type="text/javascript" src="../libs/dat.gui.js"></script>
    <style>
        body {
            margin: 0;
            overflow: hidden;
        }
    </style>
</head>
<body>

<div id="Stats-output">
</div>
<div id="WebGL-output">
</div>

<script type="text/javascript">
    function init() {
        var stats = initStats();
        var scene = new THREE.Scene();
        var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);

        var webGLRenderer = new THREE.WebGLRenderer();
        webGLRenderer.setClearColor(new THREE.Color(0xcccccc, 1.0));
        webGLRenderer.setSize(window.innerWidth, window.innerHeight);
        webGLRenderer.shadowMapEnabled = true;

        camera.position.x = 150;
        camera.position.y = 150;
        camera.position.z = 150;
        camera.lookAt(new THREE.Vector3(0, 20, 0));

        var spotLight = new THREE.SpotLight(0xffffff);
        spotLight.position.set(150, 150, 150);
        spotLight.intensity = 2;
        scene.add(spotLight);
        document.getElementById("WebGL-output").appendChild(webGLRenderer.domElement);

    	var mesh;
    	// 加载.dae模型文件
        var loader = new THREE.ColladaLoader();
        loader.load("../assets/models/dae/Truck_dae.dae", function (result) {
            // 模型中找到我们需要的网格对象
            mesh = result.scene.children[0].children[0].clone();
            mesh.scale.set(4, 4, 4);
            scene.add(mesh);
        });

        render();
        function render() {
            stats.update();
            requestAnimationFrame(render);
            webGLRenderer.render(scene, camera);
        }

        function initStats() {
            var stats = new Stats();
            stats.setMode(0); // 0: fps, 1: ms
            stats.domElement.style.position = 'absolute';
            stats.domElement.style.left = '0px';
            stats.domElement.style.top = '0px';
            document.getElementById("Stats-output").appendChild(stats.domElement);
            return stats;
        }
    }
    window.onload = init;
</script>
</body>
</html>

5. 加载STL、CTM、VTK、AWD、Assimp、VRML和Babylon模型

这些使用都基本相同,就不一一列完整示例了,下面是它们的加载方式:

// STL
var loader = new THREE.STLLoader();
loader.load("../assets/models/SolidHead_2_lowPoly_42k.stl", function (geometry) {
    console.log(geometry);
    var mat = new THREE.MeshLambertMaterial({color: 0x7777ff});
    var mesh = new THREE.Mesh(geometry, mat);
    mesh.rotation.x = -0.5 * Math.PI;
    mesh.scale.set(0.6, 0.6, 0.6);
    scene.add(mesh);
});

// CTM
var loader = new THREE.CTMLoader();
loader.load("../assets/models/auditt_wheel.ctm", function (geometry) {
    var mat = new THREE.MeshLambertMaterial({color: 0xff8888});
    var group = new THREE.Mesh(geometry, mat);
    group.scale.set(20, 20, 20);
    scene.add(group);
}, {});

// VTK
var loader = new THREE.VTKLoader();
loader.load("../assets/models/moai_fixed.vtk", function (geometry) {
    var mat = new THREE.MeshLambertMaterial({color: 0xaaffaa});
    var group = new THREE.Mesh(geometry, mat);
    group.scale.set(9, 9, 9);
    scene.add(group);
});

// AWD
var loader = new THREE.AWDLoader();
loader.load("../assets/models/awd/PolarBear.awd", function (model) {
    console.log(model);
    model.traverse(function (child) {
        if (child instanceof THREE.Mesh) {
            child.material = new THREE.MeshLambertMaterial({color: 0xaaaaaa});
            console.log(child.geometry);
        }
    });
    model.scale.set(0.1, 0.1, 0.1);
    scene.add(model);
});

// Assimp
var loader = new THREE.AssimpJSONLoader();
loader.load("../assets/models/assimp/spider.obj.assimp.json", function (model) {
    console.log(model);
    model.traverse(function (child) {
        if (child instanceof THREE.Mesh) {
            // child.material = new THREE.MeshLambertMaterial({color:0xaaaaaa});
            console.log(child.geometry);
        }
    });
    model.scale.set(0.1, 0.1, 0.1);
    scene.add(model);
});

// VRML
var loader = new THREE.VRMLLoader();
loader.load("../assets/models/vrml/tree.wrl", function (model) {
    console.log(model);
    model.traverse(function (child) {
        if (child instanceof THREE.Mesh) {
            // child.material = new THREE.MeshLambertMaterial({color:0xaaaaaa});
            console.log(child.geometry);
        }
    });
    model.scale.set(10, 10, 10);
    scene.add(model);
});

// Babylon
var loader = new THREE.BabylonLoader();
loader.load("../assets/models/babylon/skull.babylon", function (loadedScene) {
    console.log(loadedScene.children[1].material = new THREE.MeshLambertMaterial());
    scene = loadedScene;
});

6. 加载PDB模型(分子结构)

这是一种特殊的模型,用于显示分子结构。

<!-- chapter-08-08.html -->
<!DOCTYPE html>
<html>
<head>
    <title>Load pdb model </title>
    <script type="text/javascript" src="../libs/three.js"></script>
    <script type="text/javascript" src="../libs/PDBLoader.js"></script>
    <script type="text/javascript" src="../libs/stats.js"></script>
    <script type="text/javascript" src="../libs/dat.gui.js"></script>
    <style>
        body {
            margin: 0;
            overflow: hidden;
        }
    </style>
</head>
<body>

<div id="Stats-output">
</div>
<div id="WebGL-output">
</div>

<script type="text/javascript">
    function init() {
        var stats = initStats();
        var scene = new THREE.Scene();
        var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);

        var webGLRenderer = new THREE.WebGLRenderer();
        webGLRenderer.setClearColor(new THREE.Color(0x000, 1.0));
        webGLRenderer.setSize(window.innerWidth, window.innerHeight);
        webGLRenderer.shadowMapEnabled = true;

        camera.position.x = 6;
        camera.position.y = 6;
        camera.position.z = 6;
        camera.lookAt(new THREE.Vector3(0, 0, 0));

        var dir1 = new THREE.DirectionalLight(0.4);
        dir1.position.set(-30, 30, -30);
        scene.add(dir1);

        var dir2 = new THREE.DirectionalLight(0.4);
        dir2.position.set(-30, 30, 30);
        scene.add(dir2);

        var dir3 = new THREE.DirectionalLight(0.4);
        dir3.position.set(30, 30, -30);
        scene.add(dir3);

        var spotLight = new THREE.SpotLight(0xffffff);
        spotLight.position.set(30, 30, 30);
        scene.add(spotLight);

        document.getElementById("WebGL-output").appendChild(webGLRenderer.domElement);

        var mesh;
        var loader = new THREE.PDBLoader();
        var group = new THREE.Object3D();
        loader.load("../assets/models/aspirin.pdb", function (geometry, geometryBonds) {
        //loader.load("../assets/models/diamond.pdb", function (geometry, geometryBonds) {			
            // 在分子结构顶点处创建圆点
            var i = 0;
            geometry.vertices.forEach(function (position) {
                var sphere = new THREE.SphereGeometry(0.2);
                var material = new THREE.MeshPhongMaterial({color: geometry.colors[i++]});
                var mesh = new THREE.Mesh(sphere, material);
                mesh.position.copy(position);
                group.add(mesh);
            });
			
            // 分子圆点之间的键创建连接管
            for (var j = 0; j < geometryBonds.vertices.length; j += 2) {
                var path = new THREE.SplineCurve3([geometryBonds.vertices[j], geometryBonds.vertices[j + 1]]);
                var tube = new THREE.TubeGeometry(path, 1, 0.04);
                var material = new THREE.MeshPhongMaterial({color: 0xcccccc});
                var mesh = new THREE.Mesh(tube, material);
                group.add(mesh);
            }

            scene.add(group);
        });

        render();
        function render() {
            stats.update();
            if (group) {
                group.rotation.y += 0.006;
                group.rotation.x += 0.006;
            }
            requestAnimationFrame(render);
            webGLRenderer.render(scene, camera);
        }

        function initStats() {
            var stats = new Stats();
            stats.setMode(0); // 0: fps, 1: ms
            stats.domElement.style.position = 'absolute';
            stats.domElement.style.left = '0px';
            stats.domElement.style.top = '0px';
            document.getElementById("Stats-output").appendChild(stats.domElement);
            return stats;
        }
    }
    window.onload = init;
</script>
</body>
</html>

7. 把加载的外部模型创建为粒子系统

这里用PLY格式模型举例,其加载流程都差不多,没什么要说的。我们做一些不一样的操作,将使用加载的模型信息来创建一个粒子系统。

<!-- chapter-08-09.html -->
<!DOCTYPE html>
<html>
<head>
    <title>Load ply model </title>
    <script type="text/javascript" src="../libs/three.js"></script>
    <script type="text/javascript" src="../libs/PLYLoader.js"></script>
    <script type="text/javascript" src="../libs/stats.js"></script>
    <script type="text/javascript" src="../libs/dat.gui.js"></script>
    <style>
        body {
            margin: 0;
            overflow: hidden;
        }
    </style>
</head>
<body>

<div id="Stats-output">
</div>
<div id="WebGL-output">
</div>

<script type="text/javascript">
    function init() {
        var stats = initStats();
        var scene = new THREE.Scene();
        var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);

        var webGLRenderer = new THREE.WebGLRenderer();
        webGLRenderer.setClearColor(new THREE.Color(0x000, 1.0));
        webGLRenderer.setSize(window.innerWidth, window.innerHeight);
        webGLRenderer.shadowMapEnabled = true;

        camera.position.x = 10;
        camera.position.y = 10;
        camera.position.z = 10;
        camera.lookAt(new THREE.Vector3(0, -2, 0));

        var spotLight = new THREE.SpotLight(0xffffff);
        spotLight.position.set(20, 20, 20);
        scene.add(spotLight);
        document.getElementById("WebGL-output").appendChild(webGLRenderer.domElement);

        var loader = new THREE.PLYLoader();
        var group = new THREE.Object3D();
        loader.load("../assets/models/test.ply", function (geometry) {
            // 粒子系统的点云材质
            var material = new THREE.PointCloudMaterial({
                color: 0xffffff,
                size: 0.4,
                opacity: 0.6,
                transparent: true,
                blending: THREE.AdditiveBlending,
                map: generateSprite()  // 外部纹理信息
            });

            group = new THREE.PointCloud(geometry, material);
            group.sortParticles = true;
            scene.add(group);
        });
        render();
    
    	// 获取画布的纹理信息
        function generateSprite() {
            var canvas = document.createElement('canvas');
            canvas.width = 16;
            canvas.height = 16;

            var context = canvas.getContext('2d');
            var gradient = context.createRadialGradient(canvas.width / 2, canvas.height / 2, 0, canvas.width / 2, canvas.height / 2, canvas.width / 2);
            gradient.addColorStop(0, 'rgba(255,255,255,1)');
            gradient.addColorStop(0.2, 'rgba(0,255,255,1)');
            gradient.addColorStop(0.4, 'rgba(0,0,64,1)');
            gradient.addColorStop(1, 'rgba(0,0,0,1)');

            context.fillStyle = gradient;
            context.fillRect(0, 0, canvas.width, canvas.height);

            var texture = new THREE.Texture(canvas);
            texture.needsUpdate = true;
            return texture;
        }

        function render() {
            stats.update();
            if (group) {
                group.rotation.y += 0.006;
            }
            requestAnimationFrame(render);
            webGLRenderer.render(scene, camera);
        }

        function initStats() {
            var stats = new Stats();
            stats.setMode(0); // 0: fps, 1: ms
            stats.domElement.style.position = 'absolute';
            stats.domElement.style.left = '0px';
            stats.domElement.style.top = '0px';
            document.getElementById("Stats-output").appendChild(stats.domElement);
            return stats;
        }
    }
    window.onload = init;
</script>
</body>
</html>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/706797.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

android实现启动未声明的Activity

实现原理&#xff1a;首先创建一个占位StubActivity&#xff0c;这个Activity必须要添加声明&#xff0c;用来代替目标的Activity&#xff0c;然后在ActivityThread中的Handler回调中替换掉原来的Callback&#xff0c;改为自己的Callback&#xff0c;并在此修改成自己要启动的真…

Appium自动化测试 —— 断言

&#x1f60f;作者简介&#xff1a;博主是一位测试管理者&#xff0c;同时也是一名对外企业兼职讲师。 &#x1f4e1;主页地址&#xff1a;【Austin_zhai】 &#x1f646;目的与景愿&#xff1a;旨在于能帮助更多的测试行业人员提升软硬技能&#xff0c;分享行业相关最新信息。…

六.函数的定义与调用

目录 一.内置函数&#xff1a; 二.标准库函数 三、自定义函数 1、函数定义 2、函数调用 3、函数参数 值传递&#xff1a; 引用传值&#xff1a; 4、函数返回多个值 5、defer语句 6、init函数&#xff1a; 一.内置函数&#xff1a; Go 语言拥有一些不需要进行导入操…

实现java代码加密,jar\war加密

Springboot 项目代码加密&#xff0c;对你的代码进行加密&#xff0c;市面工具无法实现反编译。加密 Class 文件中每个方法的 Java 字节码&#xff0c;运行时在 JVM实现动态解密。 支持的部署环境Windows/Linux/macOS支持的框架SpringMVC、SpringBoot、Maven场景java加固&…

JAVA-编程基础-08-try-catch性能探究

Lsion <dreamlison163.com>, v1.0.0, 2023.04.01 JAVA-编程基础-08-try-catch性能探究 文章目录 JAVA-编程基础-08-try-catch性能探究try-catch会影响性能吗&#xff1f; try-catch会影响性能吗&#xff1f; 在 for 循环里面搞了个 try-catch&#xff0c;不知道try-cat…

Java的SPI

JavaSPI&#xff0c;全称是ServiceProviderInterface。 它是一种基于接口的动态扩展机制&#xff0c;相当于Java里面提供了一套接口。然后第三方可以实现这个接口来完成功能的扩展和实现。 举个简单的例子。 在Java的SDK里面&#xff0c;提供了一个数据库驱动的接口java.sql.Dr…

ModaHub魔搭社区:向量数据库Milvus性能优化问题(一)

目录 性能优化问题 为什么重启 Milvus 服务端之后&#xff0c;第一次搜索时间非常长&#xff1f; 为什么搜索的速度非常慢&#xff1f; 如何进行性能调优&#xff1f; 应如何设置 IVF 索引的 nlist 和 nprobe 参数&#xff1f; 性能优化问题 为什么重启 Milvus 服务端之后…

如何创建你的第一个西门子200PLC程序

更多关于西门子S7-200PLC内容请查看&#xff1a;西门子200系列PLC学习课程大纲 创建西门子200PLC程序分五步&#xff1a;1.打开Micro/WIN软件&#xff1b;2.新建工程&#xff1b;3.打开程序编辑器&#xff1b;4.输入程序指令&#xff1b;5.保存程序。 我们以下图程序为例讲解西…

Unable to reload Maven project

原因就是IDEA的版本与Maven的版本冲突。 IDEA的日志报错如下&#xff1a; 我当时IDEA是2020版&#xff0c;Maven是3.8.5. 后来把Maven换成3.6.3版本就可以了。

途乐证券|沪深两市震荡整理 机器人概念成市场新热点

周四&#xff0c;A股三大指数维持窄幅震荡整理走势&#xff0c;沪深两市成交额仍维持在9000亿元下方&#xff0c;北向资金净卖出超70亿元。盘面上&#xff0c;减速器、机器人概念持续爆发&#xff0c;煤炭、电力板块领跌。 香港途乐证券有限公司&#xff08;191883.com&#xf…

【每日一题】Leetcode - 剑指 Offer 43. 1~n 整数中 1 出现的次数

题目 Leetcode - 剑指 Offer 43. 1&#xff5e;n 整数中 1 出现的次数 解题思路 分解数字中的每一位&#xff0c;判断记录 结果 class Solution {public int countDigitOne(int n) {int count 0;for (int i 1; i < n; i) {int localI i;while (localI / 10 ! 0) {in…

Leetcode-每日一题【143.重排链表】

题目 给定一个单链表 L 的头节点 head &#xff0c;单链表 L 表示为&#xff1a; 请将其重新排列后变为&#xff1a; 不能只是单纯的改变节点内部的值&#xff0c;而是需要实际的进行节点交换。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5]输出&#xff1a;[1,5,2,…

Ubuntu 23.10 现在由Linux内核6.3提供支持

对于那些希望在Ubuntu上尝试最新的Linux 6.3内核系列的人来说&#xff0c;今天有一个好消息&#xff0c;因为即将发布的Ubuntu 23.10&#xff08;Mantic Minotaur&#xff09;已经重新基于Linux内核6.3。 Ubuntu 23.10的开发工作于4月底开始&#xff0c;基于目前的临时版本Ubu…

光模块封装类型有哪些?光模块“皮肤”大揭秘

什么是光模块&#xff1f; 光模块&#xff08;Optical Transceiver&#xff09;全称为光收发一体模块&#xff0c;它是光通信中的核心器件&#xff0c;能够完成信号的光-电/电-光转换过程&#xff0c;它由光电子器件、功能电路和光接口等部件组成&#xff0c;其中的光电子器件…

【STL】iterator adapters_反向迭代器的实现

放在专栏【C知识总结】&#xff0c;会持续更新&#xff0c;期待支持 本章相关文章&#xff1a; 【STL】容器适配器 【STL】list的模拟实现 【STL】vector的模拟实现 1、反向迭代器介绍 1.1、前言 在前文中我们已经讲过STL中的适配器概念&#xff0c;即在底层将一个类的接口转…

centos7卸载自带jdk(openjdk)

前提&#xff1a;root模式 1、查看自带jdk 首先确定我们有自带的jdk&#xff0c;然后搜索jdk java -version rpm -qa | grep jdk 2、使用如下命令卸载openjdk rpm -e --nodeps [name] 复制带有openjdk的那两行&#xff0c;有的人可能有多行。一条条执行。 最后java -ver…

win10/11环境解决fastboot模式看不到设备

问题描述&#xff1a; C:\Users\good>adb devices * daemon not running. starting it now on port 5037 * * daemon started successfully * List of devices attached ? device C:\Users\good>fastboot devices fastboot devices 不是内部或外部命令&#xff0…

茶油生产加工MES质量溯源平台源码(spring boot+mybatis+easyui+mysql+h5)

一、生产加工MES&#xff08;Manufacturing Execution System&#xff0c;简称MES&#xff09;是一种面向车间的生产过程管理与实时信息系统。它主要负责监控生产过程&#xff0c;管理生产资源&#xff0c;优化生产流程&#xff0c;提高生产效率和质量。MES系统需要与ERP系统、…

【阻塞队列】阻塞队列DelayedWorkQueue源码详解

目录 一、前言 二、ScheduledThreadPoolExecutor线程池 三、DelayedWorkQueue延迟阻塞队列 四、工作原理 五、源码分析 5.1 定义 5.2 成员属性 5.3 构造函数 5.4 入队方法 5.4.1 offer添加元素 5.4.2 扩容grow() 5.4.3 向上堆化siftUp 5.5 出队方法 5.5.1 take()…

LangChain 基于 ChatGPT 构建本地知识库问答应用

一、使用本地知识库构建问答应用 上篇文章基于 LangChain 的Prompts 提示管理构建特定领域模型&#xff0c;如果看过应该可以感觉出来 ChatGPT 还是非常强大的&#xff0c;但是对于一些特有领域的内容让 GPT 回答的话还是有些吃力的&#xff0c;比如让 ChatGPT 介绍下什么是 L…