­

虚拟现实前传-Three.js实现管壳式换热器3D模型在线查看

  • 2019 年 10 月 6 日
  • 筆記

BGM:

使用threejs实现3D模型加载和显示。3D编程就像拍大片,几个关键东西如下所列:

  1. 场景师Sence,类似于舞台,重要性不言而喻,把一些杂七杂八的东西摆在舞台上(含演员),看起来像在现场一样;
  2. 灯光师Light,包括太阳这个顶级灯光,否则什么都看不到;
  3. 摄影师Camera,负责拍摄成像;
  4. 演员(含群众演员),例如各类静态Mesh(群演)、武打演员(动画[主演])等等;
  5. 道具如Texture、Material,负责演员衣服/化妆、场景纹理喷涂等;
  6. 武术动作指导:物理引擎Physics Engine;
  7. 导演兼视频编辑Renderer,负责协调摄影师、灯光师、场景师和演员道具等等;

好了,现在实现加载管壳式换热器stl模型。修改threejs官方stl加载案例源码(https://threejs.org/examples/?q=stl#webgl_loader_stl),代码如下:

<!DOCTYPE html>  <html lang="en">    <head>      <title>three.js webgl - 管壳式换热器.STL</title>      <meta charset="utf-8">      <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">      <link type="text/css" rel="stylesheet" href="css/main.css">      <script src="js/three.min.js"></script>      <script src="js/OrbitControls.js"></script>      <script src="js/STLLoader.js"></script>      <script src="js/stats.min.js"></script>    </head>    <body>      <script type="module">        var container, stats, camera, scene, renderer;          init();        animate();          function init() {            container = document.createElement( 'div' );          document.body.appendChild( container );            camera = new THREE.PerspectiveCamera( 35, window.innerWidth / window.innerHeight, 1, 15 );          camera.position.set( 3, 0.15, 3 );            scene = new THREE.Scene();          scene.background = new THREE.Color( 0x72645b );          scene.fog = new THREE.Fog( 0x72645b, 2, 15 );          // Ground          var plane = new THREE.Mesh(            new THREE.PlaneBufferGeometry( 40, 40 ),            new THREE.MeshPhongMaterial( { color: 0x999999, specular: 0x101010 } )          );          plane.rotation.x = - Math.PI / 2;          plane.position.y = - 0.5;          scene.add( plane );            plane.receiveShadow = true;            // STLfile          var loader = new THREE.STLLoader();          loader.load( './models/stl/demo.stl', function ( geometry ) {            geometry.center();              var material = new THREE.MeshPhongMaterial( { color: 0xff5533, specular: 0x111111, shininess: 200 } );            var mesh = new THREE.Mesh( geometry, material );              mesh.scale.set( 0.001, 0.001, 0.001 );              mesh.castShadow = true;            mesh.receiveShadow = true;              scene.add( mesh );          } );            // Lights 玉帝说要有光,于是便有了光!          scene.add( new THREE.HemisphereLight( 0x443333, 0x111122 ) );            addShadowedLight( 1, 1, 1, 0xffffff, 1.35 );          addShadowedLight( 0.5, 1, - 1, 0xffaa00, 1 );            // renderer          renderer = new THREE.WebGLRenderer( { antialias: true } );          renderer.setPixelRatio( window.devicePixelRatio );          renderer.setSize( window.innerWidth, window.innerHeight );            renderer.gammaInput = true;          renderer.gammaOutput = true;            renderer.shadowMap.enabled = true;            container.appendChild( renderer.domElement );            // stats          stats = new Stats();          container.appendChild( stats.dom );            //          window.addEventListener( 'resize', onWindowResize, false );            var controls = new THREE.OrbitControls( camera, renderer.domElement );          controls.addEventListener( 'change', render );        }          function addShadowedLight( x, y, z, color, intensity ) {          var directionalLight = new THREE.DirectionalLight( color, intensity );          directionalLight.position.set( x, y, z );          scene.add( directionalLight );            directionalLight.castShadow = true;            var d = 1;          directionalLight.shadow.camera.left = - d;          directionalLight.shadow.camera.right = d;          directionalLight.shadow.camera.top = d;          directionalLight.shadow.camera.bottom = - d;            directionalLight.shadow.camera.near = 1;          directionalLight.shadow.camera.far = 4;            directionalLight.shadow.bias = - 0.002;        }          function onWindowResize() {          camera.aspect = window.innerWidth / window.innerHeight;          camera.updateProjectionMatrix();            renderer.setSize( window.innerWidth, window.innerHeight );        }          function animate() {          requestAnimationFrame( animate );            render();          stats.update();        }          function render() {          renderer.render( scene, camera );        }  </script>    </body>  </html>

程序分为几个步骤:

  1. 摄影师上场了,由于上个摄影师早上刚辞职,随便找了群众演员9527做临时摄影师,但貌似9527很愣,一动不动;
  2. 场景师布景,其实也没做什么,给导演做做样子;
  3. 演员上场,也就是今天主角:管壳式换热器水路计算域;但由于经费紧张,场景师只给了一件用纸糊的但金光闪闪的一次性外衣MeshPhongMaterial;
  4. 灯光师看见主演来了,不紧不慢上场,打开摄影棚窗户,随即又点亮两盏灯,并把灯光都给了主角管壳式换热器;
  5. 导演看人齐了,吩咐心腹小弟OrbitControls督促好摄影师不要愣站着,要摄影师满足各位看官大爷的请求。吩咐完,开始了拍摄工作…

出于安全,只能服务器上跑该程序,需要手工搭建简单服务器,使用nodejs(安装说明见2.5 node.js回首望,需要安装库static-server)搭建之,脚本如下:

var StaticServer = require('static-server');    var server = new StaticServer({    rootPath: './',    port: 3000  });    server.start(function () {    console.log('Server started on port ' + server.port);  });

将该服务器脚本保存为server.js,与刚刚的网页保存为index.html,并置于同一目录下,在该目录下进入命令行工具,输入node server,启动服务器,浏览器输入127.0.0.1:3000,显示效果如下: