ThreeJs入门22-加载vtk模型

2,444 阅读4分钟

「这是我参与2022首次更文挑战的第26天,活动详情查看:2022首次更文挑战

示例代码采用three.js-r73版本: cdnjs.cloudflare.com/ajax/libs/t…

yuque_diagram.jpg

这一节,我们主要讲解怎么将模型导入three.js中,让three.js能够显示我们的模型。让我们来看看吧。

重构项目

由于我们现在所做的示例,有很多代码是不需要经常改的,但是代码量也不少,我们修改代码时找起来比较麻烦,所以对项目进行一次重构。

我们的项目整体分为以下几部分:

  • 初始化渲染器
  • 初始化场景
  • 初始化相机
  • 初始化灯光
  • 初始化轨迹球
  • 初始化GUI界面
  • 初始化性能监控
  • 初始化颜色选择器
  • 初始化物体
  • 渲染循环
  • 监听浏览器尺寸改变

我们经常需要修改的部分:

  • 相机
  • 灯光
  • gui界面
  • 物体
  • 渲染循环时数据的更新
  • 颜色改变的更新

稍后实现加载模型示例时,对项目按照功能划分进行重构。

threejs加载模型过程

threejs中模型是怎么加载的呢,为了让大家明白这个道理,我们先看一下threejs加载模型的简化过程。 image.png

  • 服务器上的模型文件以文本的方式存储
    • 服务器上的模型文件大多是存储的模型的顶点信息,不一定通过文本方式存储
  • 通过浏览器下载文件到本地
  • javascript通过各种loader来解析模型文件,生成Mesh模型
    • 通过loader我们可以解析各种格式的模型文件
    • Javascript解析文本文件并生成一个geometry,最终生成Mesh
  • 把模型添加到场景中,显示出来

加载vtk模型

  • Vtk模型是一种以文本方式表示的3D模型文件,其能够表示点面信息,而且能够以人类易读易懂的方式以文本的形式存储下来。在科学研究中,这种文件格式使用得非常多。
  • 我们要加载vtk模型,就需要准备vtk模型文件(上节已经用过了)和解析vtk模型文件的loader,我已经帮大家准备好了
  • vtk模型文件:bunny.vtk.zip(890 kB)
  • 解析vtk的loader:github.com/mrdoob/thre…
    • 所有的loader都在threejs源码目录的examples/js/loaders中,最好是找到threejs对应版本的loaders,不然容易出错

html页面

  • 把我们的各种js资源进行引入
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script src="../../libs/three.js"></script>
    <script src="../../libs/dat.gui.js"></script>
    <script src="../../libs/TrackballControls.js"></script>
    <script src="../../libs/jscolor.js"></script>
    <script src="../../libs/stats.js"></script>
    <!-- 检查浏览器是否支持webgl -->
    <script src="../../libs/Detector.js"></script>
    <script src="../../libs/loaders/VTKLoader.js"></script>
  </head>
  <style type="text/css">
    body {
      margin: 0;
      overflow: hidden;
    }
    div#canvas-frame {
      border: none;
      cursor: pointer;
      width: 100%;
      height: 600px;
      background-color: #eeeeee;
    }
    #color-picker {
      position: absolute;
      bottom: 50px;
      left: 50px;
    }
  </style>
  <body>
    <div id="canvas-frame"></div>
    <script src="./index.js"></script>
  </body>
</html>

初始化渲染器

  • 因为我们的模型是偏白色的,所以我们把画布设置为黑色
const clearColor = 0x000000; // 0xFFFFFF

function initRenderer() {
    width = document.getElementById('canvas-frame').clientWidth;
    height = document.getElementById('canvas-frame').clientHeight;
    renderer = new THREE.WebGLRenderer({
        antialias: true
    });
    renderer.setSize(width, height);
    document.getElementById('canvas-frame').appendChild(renderer.domElement);
    renderer.setClearColor(clearColor, 1.0);
}

初始化场景

function initScene() {
    scene = new THREE.Scene();
}

初始化相机

function initCamera() {
    camera = new THREE.PerspectiveCamera(60, width / height, near, far);
    camera.position.z = 0.2;
    scene.add(camera);
}

初始化灯光

function initLight() {
    light = new THREE.DirectionalLight(0xffffff);
    light.position.set(200, 200, 1000).normalize();
    camera.add(light);
    camera.add(light.target);
}
  • 我们把光添加到摄像机中,是为了模拟光是从相机里照射出来的

初始化物体

创建基础材质

// 初始化物体
function initObject() {
    var material = new THREE.MeshLambertMaterial({ color: 0xffffff, side: THREE.DoubleSide });
}

loader加载vtk模型

  • 我们通过THREE.VTKLoader可以加载vtk模型,返回一个geometry
var loader = new THREE.VTKLoader();
loader.load("https://threejs-models.vercel.app/models/vtk/bunny.vtk", function (geometry) {
    geometry.computeVertexNormals();
    var mesh = new THREE.Mesh(geometry, material);
    mesh.position.setY(- 0.09);
    scene.add(mesh);
});
  • loader加载完成之后,获取到geometry,我们就可以创建网格并添加到场景中了。

初始化各种控件

  • 这里初始化布控球、GUI界面、性能监控、颜色选择器,浏览器窗口变化时处理,之前都实战过了,就不一一介绍了
var param
function initGUI() {
    var ParamObj = function () {
        this.repeat = 4;
        this.wrap = 1000;
    }
    param = new ParamObj();
    const gui = new dat.GUI();
    // gui.add(param, "repeat", -100, 100).name('纹理重复')
    // gui.add(param, "wrap", 1000, 1002).name('纹理环绕').step(1)
}

var stats;
function initStats() {
    stats = new Stats();
    stats.showPanel(0); // 0: fps, 1: ms, 2: mb, 3+: custom
    // 将stats的界面对应左上角
    stats.domElement.style.position = 'absolute';
    stats.domElement.style.left = '0px';
    stats.domElement.style.top = '0px';
    document.body.appendChild(stats.dom);
}

function initColorPicker() {
    const colorPicker = document.createElement('div');
    colorPicker.id = 'color-picker'
    document.body.appendChild(colorPicker);
    const html = `<input
      data-jscolor="{value:'#FF0000', alpha:1}"
      onChange="colorPickerUpdate(this.jscolor, '#pr3')"
    />`
    const colorPickerDom = document.querySelector('#color-picker')
    colorPickerDom.innerHTML = html;

    jscolor.install(colorPickerDom)

    jscolor.presets.default = {
        width: 141,               // make the picker a little narrower
        position: 'top',        // position it to the right of the target
        previewPosition: 'right', // display color preview on the right
        previewSize: 40,          // make the color preview bigger
        format: 'hex',
        previewSize: 40,
        closeButton: true,
        closeText: '关闭',
        palette: [
            '#000000', '#7d7d7d', '#870014', '#ec1c23', '#ff7e26',
            '#fef100', '#22b14b', '#00a1e7', '#3f47cc', '#a349a4',
            '#ffffff', '#c3c3c3', '#b87957', '#feaec9', '#ffc80d',
            '#eee3af', '#b5e61d', '#99d9ea', '#7092be', '#c8bfe7',
        ],
    }
}

function initControls() {
    controls = new THREE.TrackballControls(camera);

    controls.rotateSpeed = 0.2;// 旋转速度
    controls.zoomSpeed = 0.2;// 缩放速度
    controls.panSpeed = 0.1;// 平controls
    controls.staticMoving = false;// 静止移动,为 true 则没有惯性
    controls.dynamicDampingFactor = 0.2;// 阻尼系数 越小 则滑动越大

    controls.minDistance = near; // 最小视角
    controls.maxDistance = far;// 最大视角 Infinity 无穷大
}

/* 窗口变动触发 */
function onWindowResize() {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);
}

渲染循环

  • 准备工作做的差不多了,就该进行渲染了
function animation() {
    renderer.render(scene, camera);
    requestAnimationFrame(animation);
    stats.update();
    controls.update();
    update()
}

/* 数据更新 */
function update() {
	// 各种参数更新
}

function threeStart() {
    // 初始化渲染器
    initRenderer()
    // 初始化场景
    initScene();
    // 初始化相机
    initCamera();
    // 初始化灯光
    initLight();
    // 初始化物体
    initObject();

    // 初始化布控球
    initControls();
    // 初始化GUI界面
    initGUI()
    // 初始化性能监控
    initStats()
    // 初始化颜色选择器
    initColorPicker()

    // 渲染循环
    animation();
    /* 监听事件 */
    window.addEventListener('resize', onWindowResize, false);
}

最终现实效果 加载模型.gif codepen示例代码

总结

这一节我们主要讲了以下内容:

  • 重构项目代码讲解
  • threejs加载模型过程
  • 加载vtk模型