three.js - 3D Text

image.png
  • 最终效果如上图所示,实现一个3D Text,使用了MeshMatcapMaterialFontLoaderTorusGeometry
  • 首先创建一个带有坐标系的基础场景
  // 导入three.js
  import * as THREE from 'three'
  // 导入轨道控制器
  import { OrbitControls } from 'three/addons/controls/OrbitControls.js';

  /**
   * scene
  **/
  const scene = new THREE.Scene()

  /**
   * camera
  **/
  const camera = new THREE.PerspectiveCamera(
    45, 
    window.innerWidth / window.innerHeight, 
    0.1, 
    1000
  )
  camera.position.set(0, 0, 10)
  camera.lookAt(0, 0, 0)

  /**
   * renderer
  **/
  const renderer = new THREE.WebGLRenderer()
  renderer.setSize(window.innerWidth, window.innerHeight)
  document.body.appendChild(renderer.domElement)

  /**
   * axesHelper
  **/
  const axesHelper = new THREE.AxesHelper(5)
  scene.add(axesHelper)

  /**
   * controls
  **/
  const controls = new OrbitControls(camera, renderer.domElement)
  controls.enableDamping = true

  /**
   * render
  **/
  function animate () {
    controls.update()
    requestAnimationFrame(animate)
    renderer.render(scene, camera)
  }
  animate()
  • 导入 FontLoaderTextGeometryFontLoader是一个用于加载JSON格式字体的类,TextGeometry是一个将文本生成单一几何体的类,他们都是附件组件,必须显示导入
  // 导入FontLoader
  import { FontLoader } from 'three/addons/loaders/FontLoader.js';
  // 导入TextGeometry
  import { TextGeometry } from 'three/addons/geometries/TextGeometry.js';
  • 导入成功后,准备一个字体文件,我们这里用的是three.js自带的,将nodel_modules文件夹下'three/examples/fonts/'该路径下的helvetiker_regular.typeface.jsonLICENSE文件提取出来,放在我们项目的public路径下,一个通常用于存放静态文件的地方
    image.png
  • 使用FontLoader加载字体,并用TextGeometry生成几何体
    • 这里建议先使用MeshBasicMaterial,设置wireframe: true便于观察字体的相关属性,例如bevelThicknessbevelSizebevelSegments
    • 这一步完成后的效果如下图
  /*
    * font
  */
  const fontLoader = new FontLoader() 
  fontLoader.load('../public/font/helvetiker_regular.typeface.json', (font) => {
    const textGeometry = new TextGeometry('Hello Three.js', {
      font, // font实例
      size: 0.5, // 字体大小
      height: 0.2, // 文本厚度
      curveSegments: 6, // 曲线上点的数量
      bevelEnabled: true, // 开启斜角
      bevelThickness: 0.03, // 上斜角深度
      bevelSize: 0.02, // 斜角与原始文本之间的延伸距离
      bevelOffset: 0,
      bevelSegments: 3, // 斜角分段数
    })

    const material = new THREE.MeshBasicMaterial({
      wireframe: true
    })
    const text = new THREE.Mesh(textGeometry, material)
    scene.add(text)
  })
image.png
  • 如上图所示,当前字体在坐标系中不在居中的位置,three.js提供了简单的居中方法,手动居中的计算方式可参考以下注释的部分,同时可对比2次打印textGeometry.boundingBox的坐标数值
  fontLoader.load(..., (font) => {
    ...
    ...
    textGeometry.computeBoundingBox() // 计算物体边界
    // console.log(textGeometry.boundingBox)

    // 移动物体至坐标系中心
    // textGeometry.translate(
    //   - (textGeometry.boundingBox.max.x - 0.02) * 0.5, // 减去bevelSize
    //   - (textGeometry.boundingBox.max.y - 0.02) * 0.5, // 减去bevelSize
    //   - (textGeometry.boundingBox.max.z - 0.03) * 0.5, // 减去bevelThickness
    // )
    textGeometry.center()
    // console.log(textGeometry.boundingBox)
  })
  • 下一步添加torus的部分,这一步需要注意的是,我们将创建TorusGeometry的部分放在for循环外面,避免频繁创建所造成的性能消耗,可以通过console.time观察不同的写法所消耗的时间
  fontLoader.load(..., (font) => {
    ...
    ...
    ...
    // console.time('donuts')

    // torus
    const donutGeometry = new THREE.TorusGeometry(0.3, 0.2, 20, 45)
    for(let i = 0; i < 100; i++) {
      const donut = new THREE.Mesh(donutGeometry, material)

      donut.position.x = (Math.random() - 0.5) * 10
      donut.position.y = (Math.random() - 0.5) * 10
      donut.position.z = (Math.random() - 0.5) * 10

      donut.rotation.x = Math.random() * Math.PI
      donut.rotation.y = Math.random() * Math.PI

      const scale = Math.random()
      donut.scale.set(scale, scale, scale)
      // 这里特别注意,不能写成这样,xyz坐标值需要保持统一变化
      // donut.scale.set(Math.random(), Math.random(), Math.random()) 
 
      scene.add(donut)
    }
    // console.timeEnd('donuts')
  })
  • 最后,通过TextureLoader加载新的纹理,替换material,删除坐标系
  /*
    * texture
  */
  const textureLoader = new THREE.TextureLoader()
  const matcapTexture = textureLoader.load('../public/imgs/mat-cap-4.jpg')
  fontLoader.load(..., (font) => {
    ...
    ...
    // const material = new THREE.MeshBasicMaterial({
    //   wireframe: true
    // })
    const material = new THREE.MeshMatcapMaterial({
      matcap: matcapTexture
    })
  })
  /**
   * axesHelper
  **/
  const axesHelper = new THREE.AxesHelper(5)
  // scene.add(axesHelper)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,843评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,538评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,187评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,264评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,289评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,231评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,116评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,945评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,367评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,581评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,754评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,458评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,068评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,692评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,842评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,797评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,654评论 2 354