three.js 示例学习 | 04-3D场景中的文字

896 阅读2分钟

前言

本篇是学习three.js官网上geometry_text_shapes、geometry_text_stroke 和 geometry_text 示例,了解如何在3D 空间内展示文本内容。

实现

添加基本代码结构

import * as THREE from 'three';

let camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 10000 );
camera.position.set( 0, - 400, 600 );

let scene = new THREE.Scene();
scene.background = new THREE.Color( 0xf0f0f0 );

renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );

const consorl = new OrbitControls(camera, renderer.domElement);
consorl.target.set(0,0,0);
consorl.update();

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

WebGLRenderer 参数 antialias 为抗锯齿,默认为false,当开启时可以柔滑物体外形,提升画面质量。

OrbitControls(轨道控制器)的 target 属性是指控制器的焦点,即轨道会围绕着这个点而运动。

import { FontLoader } from 'three/examples/jsm/loaders/FontLoader.js';

const loader = new FontLoader();
loader.load('font/helvetiker_regular.typeface.json', function (font) {
  ......
});

FontLoader 用于加载JSON格式的字体。

.load ( url : String, onLoad : Function, onProgress : Function, onError : Function ) : undefined

属性描述
url文件的URL或者路径
onLoad将在加载完成时调用。参数是将要被加载的font。
onProgress将在加载过程中调用。
onError将在加载错误时调用。

加载完成返回的字体为 Font 类型。其中 generateShapes 方法可以将文本加载为shape数组。

generateShapes(text: string, size: number): Shape[];

属性描述
text要加载的文本
size文本大小

在loader.load 的onLoad 回调中继续添加代码:

const color = new THREE.Color(0x006699);
const matLite = new THREE.MeshBasicMaterial({
    color: color,
    transparent: true,
    opacity: 0.4,
    side: THREE.DoubleSide,
});

const message = '   Three.js\nSimple text.';
const shapes = font.generateShapes(message, 100);
const geometry = new THREE.ShapeGeometry(shapes);
geometry.computeBoundingBox();
const xMid = - 0.5 * (geometry.boundingBox.max.x - geometry.boundingBox.min.x);
geometry.translate(xMid, 0, 0);

const text = new THREE.Mesh(geometry, matLite);
text.position.z = -150;
scene.add(text);

MeshBasicMaterial 是基础网格材质,不受光照影响。

color材质颜色
transparent材质是否透明
opacity在0.0 - 1.0的范围内的浮点数,表明材质的透明度。值0.0表示完全透明,1.0表示完全不透明。
side定义渲染那一面,默认只渲染正面(THREE.FrontSide),其他选项有背面(THREE.BackSide),双面(THREE.DoubleSide)。

将要渲染的文本通过 font.generateShapes 方法得到shape(二维形状平面)数组。再生成ShapeGeometry(形状缓冲几何体)。

computeBoundingBox() 方法是计算出 geometry 的 boundingBox(外边界矩形),通过计算和 translate 方法将 geometry 的中心移动到原点上。

生成mesh 添加到场景中。

text1.gif

添加线性文字

const matDark = new THREE.LineBasicMaterial({
    color: color,
    side: THREE.DoubleSide
});

const linePoints: THREE.Vector2[][] = [];
for (let i = 0;i < shapes.length;i++) {
    const shape = shapes[i];
    linePoints.push(shape.getPoints());
    if (shape.holes && shape.holes.length > 0 && i !== 4) {
        for (let j = 0 ; j < shape.holes.length;j++) {
            const hole = shape.holes[j];
            linePoints.push(hole.getPoints());
        }
    }
}

const lineText = new THREE.Object3D();
for (let i = 0 ; i < linePoints.length;i++) {
    const points = linePoints[i];
    const geometry = new THREE.BufferGeometry().setFromPoints(points);
    geometry.translate(xMid, 0,0);
    const lineMesh = new THREE.Line(geometry, matDark);
    lineText.add(lineMesh);
}
scene.add(lineText);

LineBasicMaterial 是一种线框样式材质。

对 shapes 数组进行遍历,对shape 通过 getPoints 获取组成图形点的序列。有些文字字母上会有 holes 的存在,也同样获取 getPoints 。holes 是图形上的洞,当我们想要用线来描述一个图形时,这些洞就需要单独处理,类似于下图:

1658318992471.jpg

Object3D(三维物体)是three.js 中大部分物体的基类,提供了许多三维空间内的物体操控方法,此处是用于文字生成的各个 geometry 的父对象。

将linePoints 遍历 通过setFromPoints() 生成 geometry 添加到 Object3D 父对象,最后把父对象添加到场景中。

text2.gif

添加svg文字

const style = SVGLoader.getStrokeStyle( 5, color.getStyle() );
const strokeText = new THREE.Group();
for (let i = 0 ; i < linePoints.length; i++) {
    const points = linePoints[i];
    const geometry = SVGLoader.pointsToStroke( points, style );
    geometry.translate( xMid, 0, 0 );
    const strokeMesh = new THREE.Mesh( geometry, matDark );
    strokeText.add( strokeMesh );
}
strokeText.position.z = 150;
scene.add(strokeText);

SVGLoader 用来加载SVG 二维矢量图形的加载器。

text3.gif

添加文字几何体

let textGeo = new TextGeometry(message, {
    font: font,
    size: 100,
    height: 20,
    curveSegments: 4,
    bevelThickness: 2,
    bevelSize: 1.5,
    bevelEnabled: true
});

let textMesh1 = new THREE.Mesh( textGeo, matLite );

textMesh1.position.x = xMid;
textMesh1.position.y = 0;
textMesh1.position.z = 300;

scene.add(textMesh1);

TextGeometry 是将文本生成几何体。

属性描述
font字体,HREE.Font的实例。
size字体大小,默认值为100。
height挤出文本的厚度。默认值为50。
curveSegments文本曲线上点的数量。默认值为12。
bevelThickness文本上斜角的深度,默认值为20。
bevelSize斜角与原始文本轮廓之间的延伸距离。默认值为8。
bevelEnabled是否开启斜角,默认为false。
bevelSegments斜角的分段数。默认值为3。

text4.gif

中文文字

在three.js 中展示中文需要引入其他字体文件,上面字体是three.js 官方示例中提供的字体文件,对中文无法识别展示。

windows 电脑 C:\Windows\Fonts 目录之下是计算机安装的字体文件,选择想要的字体文件(.ttf)转为 .json 格式。

转换工具:gero3.github.io/facetype.js…

将转换后的json文件替换上面Font引入代码,再将展示文本换成中文就不会出现乱码了。

text6.gif

结语

本篇是实现3D场景中文字的实现,参考three.js官网示例:

threejs.org/examples/#w…

threejs.org/examples/#w…

threejs.org/examples/#w…

本篇以上代码也已放到了github上:github.com/wenxuan12/3… ,可供做印证参考。