Three.js - 建立文字



通常您需要在場景中新增文字。在本章中,讓我們看看如何在場景中新增 2D 和 3D 文字。

繪製文字到畫布並用作紋理

這是在場景中新增 2D 文字最簡單的方法。您可以使用 JavaScript 建立畫布並將其新增到 DOM 中。

const canvas = document.createElement('canvas')
const context = canvas.getContext('2d')

上面的程式碼建立了一個畫布元素,我們將上下文設定為 2d。canvas.getContext() 方法返回一個物件,該物件提供用於在畫布上繪製的方法和屬性,它可以使用這些方法和屬性繪製文字、線條、框、圓圈等。

context.fillStyle = 'green'
context.font = '60px sans-serif
context.fillText('Hello World!', 0, 60)

fillText() 是 2D 繪製上下文的一種方法。fillText() 方法允許您在具有您提供的 fillStyle 派生的填充(顏色)的座標處繪製文字字串。您可以使用 font 屬性設定文字的字型。

以上程式碼將字型設定為 60 畫素高的 sans-serif,填充樣式設定為綠色。文字“Hello, World!”從座標 (0, 60) 開始繪製。

// canvas contents are used for a texture
const texture = new THREE.Texture(canvas)
texture.needsUpdate = true

要從畫布元素建立紋理,我們需要建立一個新的 THREE.Texture 例項並傳入我們建立的畫布元素。上面的程式碼使用畫布(在本例中為我們的文字)建立紋理。紋理的 needsUpdate 引數設定為 true。它通知 Three.js 我們的畫布紋理已更改,需要在下一次渲染場景時更新。

現在,建立一個平面幾何體並將其作為紋理新增到材質中。

var material = new THREE.MeshBasicMaterial({
   map: texture,
   side: THREE.DoubleSide,
})
material.transparent = true
var mesh = new THREE.Mesh(new THREE.PlaneGeometry(50, 10), material)

使用文字幾何體

THREE.TextGeometry 是另一種幾何體型別,它將文字生成單個幾何體。它接受兩個引數,text - 您要渲染的文字,以及其他引數。

引數

  • font − 這是字型的名稱。

  • size − 文字的大小。預設為 100。

  • height − height 屬性定義文字的深度;換句話說,文字突出顯示以使其成為 3D 的程度。預設為 50。

  • curveSegments − 曲線上點的數量。預設為 12。

  • bevelEnabled − 倒角提供從文字正面到側面的平滑過渡。如果將此值設定為 true,則會向渲染的文字新增倒角。預設情況下,它是 false。

  • bevelThickness − 如果您已將 bevelEnabled 設定為 true,則它定義倒角的深度。預設為 10。

  • bevelSize − 它決定倒角的高度。預設為 8。

  • bevelOffset − 倒角距文字輪廓多遠開始。預設為 0。

  • bevelSegments − 倒角段的數量。預設為 3。

您需要使用 THREE.FontLoader 從其 typeface.json 檔案載入字型。

const loader = new THREE.FontLoader()
loader.load('fonts/helvetiker_regular.typeface.json', function (font) {
   const geometry = new THREE.TextGeometry('Hello Three.js!', {
      font: font,
      size: 3,
      height: 0.2,
      curveSegments: 12,
      bevelEnabled: false,
      bevelThickness: 0.5,
      bevelSize: 0.3,
      bevelOffset: 0,
      bevelSegments: 5,
   })
})

現在,您應該向其新增一些材質並建立一個網格。

const material = new THREE.MeshFaceMaterial([
   new THREE.MeshPhongMaterial({
      color: 0xff22cc,
      flatShading: true,
   }), // front
   new THREE.MeshPhongMaterial({
      color: 0xffcc22
   }), // side
])
const mesh = new THREE.Mesh(geometry, material)
mesh.name = 'text'
scene.add(mesh)

注意 − 在使用 THREE.TextGeometry 和材質時,您需要注意一件事。它可以將兩個材質作為陣列:一個用於渲染文字的正面,另一個用於文字的側面。如果您只傳入一個材質,它將應用於正面和側面。

示例

現在,您可以看到渲染到場景中的文字。檢視以下示例。

2d-text.html

<!DOCTYPE html>
<html>
   <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>Three.js - 2d text</title>
      <style>
         * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: -applesystem, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu,
               Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
         }
         html,
         body {
            height: 100vh;
            width: 100vw;
         }
         #threejs-container {
            position: block;
            width: 100%;
            height: 100%;
         }
      </style>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.7/dat.gui.js"></script>
   </head>
   <body>
      <div id="threejs-container"></div>
      <script type="module">
         // Adding 2d text to Three.js scene
         // Writing on canvas and then adding the canvas as a texture to material
         // GUI
         const gui = new dat.GUI()

         // sizes
         let width = window.innerWidth
         let height = window.innerHeight
         const size = 256
         const container = document.querySelector('#threejs-container')
         const canvas = document.createElement('canvas'),
         ctx = canvas.getContext('2d')
         function changeCanvas() {
            ctx.font = '20pt Arial'
            ctx.fillStyle = 'white'
            ctx.fillRect(0, 0, canvas.width, canvas.height)
            ctx.fillStyle = 'black'
            ctx.textAlign = 'center'
            ctx.textBaseline = 'middle'
            ctx.fillText('Tutorialspoint!', canvas.width / 2, canvas.height / 2)
         }
         // scene
         const scene = new THREE.Scene()
         scene.background = new THREE.Color(0x262626)
         // lights
         const ambientLight = new THREE.AmbientLight(0xffffff, 1)
         scene.add(ambientLight)
         const pointLight = new THREE.PointLight(0xffffff, 0.5)
         pointLight.position.x = 20
         pointLight.position.y = 30
         pointLight.position.z = 40
         scene.add(pointLight)
         // camera
         const camera = new THREE.PerspectiveCamera(70, width / height, 1, 1000)
         camera.position.z = 500
         scene.add(camera)
         // renderer
         const renderer = new THREE.WebGL1Renderer({ antialias: true })
         renderer.setSize(width, height)
         renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
         container.append(renderer.domElement)
         renderer.render(scene, camera)
         // cube
         const texture = new THREE.Texture(canvas)
         const material = new THREE.MeshStandardMaterial({ map: texture })
         const geometry = new THREE.BoxGeometry(200, 200, 200)
         const mesh = new THREE.Mesh(geometry, material)
         scene.add(mesh)
         canvas.width = canvas.height = size
         // responsiveness
         window.addEventListener('resize', () => {
            width = window.innerWidth
            height = window.innerHeight
            camera.aspect = width / height
            camera.updateProjectionMatrix()
            renderer.setSize(window.innerWidth, window.innerHeight)
            renderer.render(scene, camera)
         })
         // animation
         function animate() {
            requestAnimationFrame(animate)
            changeCanvas()
            texture.needsUpdate = true
            mesh.rotation.y += 0.01
            renderer.render(scene, camera)
         }
         animate()
      </script>
   </body>
</html>

輸出

示例

現在讓我們再舉一個例子,看看如何在場景中新增 3D 文字。

3d-text.html

<!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>Three.js - 3d text</title>
      <style>
         * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: -applesystem, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu,
               Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
         }
         html,
         body {
            height: 100vh;
            width: 100vw;
         }
         #threejs-container {
            position: block;
            width: 100%;
            height: 100%;
         }
      </style>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.7/dat.gui.js"></script>
   <head>
   <body>
      <div id="threejs-container"></div>
      <script type="module">
         // Creating 3d text using Text Geometry in Three.js
         // GUI
         const gui = new dat.GUI()
         // sizes
         let width = window.innerWidth
         let height = window.innerHeight
         // scene
         const scene = new THREE.Scene()
         scene.background = new THREE.Color(0x262626)
         // lights
         const ambientLight = new THREE.AmbientLight(0xffffff, 1)
         scene.add(ambientLight)
         const pointLight = new THREE.PointLight(0xffffff, 0.5)
         pointLight.position.x = 20
         pointLight.position.y = 30
         pointLight.position.z = 40
         scene.add(pointLight)
         // camera
         const camera = new THREE.PerspectiveCamera(30, width / height, 0.1, 1000)
         camera.position.set(0, 0, 50)
         const camFolder = gui.addFolder('Camera')
         camFolder.add(camera.position, 'z').min(10).max(500).step(10)
         camFolder.open()
         function createMaterial() {}
         const loader = new THREE.FontLoader()
         // promisify font loading
         function loadFont(url) {
            return new Promise((resolve, reject) => {
               loader.load(url, resolve, undefined, reject)
            })
         }
         async function doit() {
            const font = await loadFont('https://threejs.org/examples/fonts/helvetiker_regular.typeface.json')
            let text = 'Hello World !'
            const geometry = new THREE.TextGeometry(text, {
               font: font,
               size: 3,
               height: 0.2,
               curveSegments: 12,
               bevelEnabled: true,
               bevelOffset: 0,
               bevelThickness: 0.5,
               bevelSize: 0.3,
               bevelSegments: 5
            })
            const material = [
               new THREE.MeshPhongMaterial({
                  color: 0xff22cc,
               flatShading: true
               }), // front
               new THREE.MeshPhongMaterial({
               color: 0xffcc22
               }) // side
            ]
            const mesh = new THREE.Mesh(geometry, material)
            geometry.computeBoundingBox()
            geometry.computeVertexNormals()
            geometry.boundingBox.getCenter(mesh.position).multiplyScalar(-1)
            mesh.position.x = -geometry.boundingBox.max.x / 2
            const parent = new THREE.Object3D()
            parent.add(mesh)
            scene.add(parent)
            const opts = geometry.parameters.options
            console.log(opts)
            const geoProps = {
               font: opts.font,
               size: opts.size,
               height: opts.height,
               curveSegments: opts.curveSegments,
               bevelEnabled: opts.bevelEnabled,
               bevelOffset: opts.bevelOffset,
               bevelThickness: opts.bevelThickness,
               bevelSize: opts.bevelSize,
               bevelSegments: opts.bevelSegments
            }
            console.log(geoProps)
            // GUI for exporimenting cube properties
            const props = gui.addFolder('Properties')
            props
               .add(geoProps, 'size', 1, 30)
               .step(1)
               .onChange(redraw)
               .onFinishChange(() => console.dir(mesh.geometry))
            props.add(geoProps, 'height', 0, 30).step(0.1).onChange(redraw)
            props.add(geoProps, 'curveSegments', 1, 30).step(1).onChange(redraw)
            props.add(geoProps, 'bevelEnabled').onChange(redraw)
            props.add(geoProps, 'bevelOffset', 0, 1).onChange(redraw)
            props.add(geoProps, 'bevelThickness', 0, 3).onChange(redraw)
            props.add(geoProps, 'bevelSize', 0, 3).onChange(redraw)
            props.add(geoProps, 'bevelSegments', 1, 8).step(1).onChange(redraw)
            props.open()
            function redraw() {
               camera.position.set(0, 0, 80)
               let newGeometry = new THREE.TextGeometry(text, {
                  font: geoProps.font,
                  size: geoProps.size,
                  height: geoProps.height,
                  curveSegments: geoProps.curveSegments,
                  bevelEnabled: geoProps.bevelEnabled,
                  bevelOffset: geoProps.bevelOffset,
                  bevelThickness: geoProps.bevelThickness,
                  bevelSize: geoProps.bevelSize,
                  bevelSegments: geoProps.bevelSegments
               })
               mesh.geometry.dispose()
               mesh.geometry = newGeometry
               mesh.geometry.parameters.options.depth = 0.2
               console.log(mesh.geometry.parameters.options)
            }
         }
         doit()
         // responsiveness
         window.addEventListener('resize', () => {
            width = window.innerWidth
            height = window.innerHeight
            camera.aspect = width / height
            camera.updateProjectionMatrix()
            renderer.setSize(window.innerWidth, window.innerHeight)
            renderer.render(scene, camera)
         })
         // renderer
         const renderer = new THREE.WebGL1Renderer({ antialias: true })
         renderer.setSize(width, height)
         renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
         // animation
         function animate() {
            requestAnimationFrame(animate)
            renderer.render(scene, camera)
         }
         // rendering the scene
         const container = document.querySelector('#threejs-container')
         container.append(renderer.domElement)
         renderer.render(scene, camera)
         animate()
      </script>
   </body>
</html>

輸出

廣告