wwqdrh

简介

对于webar中,3D模型资源是很重要的一个组成部分。可以通过建模或者使用threejs搭建一些简单的3D场景

一个人的能力始终是有边界的,所以对于复杂专业的建模软件的学习,暂时不进行展开,要么使用现成的3D模型进行学习使用,或者对于一些简单的3D场景,学习使用threejs来完成

安装

我这里采用vite构建工具,并且开发框架使用svelte对于一些小型的页面我比较喜欢使用svelte

npm create vite@latest 3d-assets -- --template svelte

cd 3d-assets

pnpm install --save three

pnpm run dev

然后在入口js中添加核心资源, 注意由于这些是客户端资源,所以必须在onMount挂载后初始化

import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { onMount } from "svelte";

开发示例

主要对象

  • 场景
  • 相机
  • 形状
  • 材质

import * as THREE from 'three';

let threescene;

  onMount(() => {
    const scene = new THREE.Scene();
    const camera = new THREE.PerspectiveCamera(
      75,
      window.innerWidth / window.innerHeight,
      0.1,
      1000
    );
    const renderer = new THREE.WebGLRenderer();
    renderer.setSize(window.innerWidth, window.innerHeight);
    threescene.appendChild(renderer.domElement);
  });

一个旋转的立方体

创建形状以及材质

const geometry = new THREE.BoxGeometry( 1, 1, 1 );
const material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
const cube = new THREE.Mesh( geometry, material );
scene.add( cube );

camera.position.z = 5;

渲染出结果

当我们在animate的回调中不断改变cube这个正方体的rotation值,就会看上去像旋转一样

function animate() {
    requestAnimationFrame( animate );

    cube.rotation.x += 0.01;
    cube.rotation.y += 0.01;

    renderer.render( scene, camera );
}
animate();

线

const material = new THREE.LineBasicMaterial({ color: 0x0000ff });
const points = [];
points.push(new THREE.Vector3(-10, 0, 0));
points.push(new THREE.Vector3(0, 10, 0));
points.push(new THREE.Vector3(10, 0, 0));
const geometry = new THREE.BufferGeometry().setFromPoints(points);
const line = new THREE.Line(geometry, material);

3D文字

需要为three安装FontLoader以及TextGeometry两个扩展 如果是使用npm安装的three那么就直接有了,不需要额外操作

对于字体来说,需要将ttf格式转换成json文件才能导入使用, 在线转化

import * as THREE from 'three';
import { FontLoader } from "three/examples/jsm/loaders/FontLoader.js";
import { TextGeometry } from "three/examples/jsm/geometries/TextGeometry.js";

function loadFont() {
    const loader = new FontLoader();
    loader.load( 'fonts/' + fontName + '_' + fontWeight + '.typeface.json', function ( response ) {

        font = response;

        refreshText();

    } );

}

function createText() {
    textGeo = new TextGeometry( text, {
        font: font,
        size: size,
        height: height,
        curveSegments: curveSegments,
        bevelThickness: bevelThickness,
        bevelSize: bevelSize,
        bevelEnabled: bevelEnabled
    } );

    textGeo.computeBoundingBox();

    const centerOffset = - 0.5 * ( textGeo.boundingBox.max.x - textGeo.boundingBox.min.x );

    textMesh1 = new THREE.Mesh( textGeo, materials );

    textMesh1.position.x = centerOffset;
    textMesh1.position.y = hover;
    textMesh1.position.z = 0;

    textMesh1.rotation.x = 0;
    textMesh1.rotation.y = Math.PI * 2;

    group.add( textMesh1 );
}

function refreshText() {
    group.remove( textMesh1 );
    if ( mirror ) group.remove( textMesh2 );

    if ( ! text ) return;

    createText();
}

GUI

import { GUI } from "three/examples/jsm/libs/lil-gui.module.min.js";

const params = {
    changeColor: function () {
        pointLight.color.setHSL(Math.random(), 1, 0.5);
    },
    changeFont: function () {
        fontIndex++;

        fontName =
            reverseFontMap[fontIndex % reverseFontMap.length];

        loadFont();
    },
    changeWeight: function () {
        if (fontWeight === "bold") {
            fontWeight = "regular";
        } else {
            fontWeight = "bold";
        }

        loadFont();
    },
    changeBevel: function () {
        bevelEnabled = !bevelEnabled;

        refreshText();
    },
};

//

const gui = new GUI();

gui.add(params, "changeColor").name("change color");
// gui.add(params, "changeFont").name("change font");
// gui.add(params, "changeWeight").name("change weight");
gui.add(params, "changeBevel").name("change bevel");
gui.open();

3D模型

GLTF(Graphics Language Transmission Format)是一种标准的3D模型文件格式,它以JSON的形式存储3D模型信息,例如模型的层次结构、材质、动画、纹理等。

模型中依赖的静态资源,比如图片,可以通过外部URI的方式来引入,也可以转成base64直接插入在GLTF文件中。 它包含两种形式的后缀,分别是.gltf(JSON/ASCII)和.glb(Binary)。.gltf是以JSON的形式存储信息。.glb则是.gltf的扩展格式,它以二进制的形式存储信息,因此导出的模型体积也更小一些。如果我们不需要通过JSON对.gltf模型进行直接修改,建议使用.glb模型,它更小、加载更快。

import GLTFLoader from 'GLTFLoader'
const loader = new GLTFLoader()
loader.load('path/to/gallery.glb',
  gltf => {
    scene.add(gltf.scene) // 添加到场景中
  } 
)