虛擬現實前傳-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,顯示效果如下: