Disclaimer: The graphic and model materials involved in this article are only for personal study, research and appreciation. Please do not re-modify, illegally spread, reprint, publish, commercialize, or conduct other profit-making activities.
background
In the Three.js Journey
course example , an example of the 3D
text suspension effect implemented using the Three.js
built-in method is provided. This article uses the React + Three.js
technology stack to achieve a similar effect with reference to the example. Article related to knowledge include: CSS
grid background, MeshNormalMaterial
normal material, FontLoader
font loader, TextGeometry
text buffer geometry, TorusBufferGeometry
ring buffer geometry, ConeBufferGeometry
cone buffer geometry, OctahedronBufferGeometry
octahedral cushion geometry, Three.js
late rendering , GlitchPass
channel, Element.requestFullscreen
, Document.exitFullscreen
, etc.
Effect
The effect is shown in the 👆
banner
diagram. The main body of the page is composed of the text mesh model in the center and the surrounding torus, cone and octahedron . As the 🖱
mouse is moved or clicked on the page, the model moves with it. There are 2
buttons in the upper right corner of the page, which can switch the background color of the page and switch fault style and post-production effects. Double tap the screen to enter or exit full screen.
👀
Online preview: https://3d-dragonir.vercel.app/#/floating
Adapted:
💻
PC
end📱
Mobile terminal
accomplish
resource introduction
First introduce the module resources required for development, of which FontLoader
is used to load font files, TextGeometry
is used to create 3D
font grid, EffectComposer
, RenderPass
and GlitchPass
are used for post-effect rendering.
import * as THREE from "three";
import { FontLoader } from "three/examples/jsm/loaders/FontLoader";
import { TextGeometry } from 'three/examples/jsm/geometries/TextGeometry';
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js';
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js';
import { GlitchPass } from 'three/examples/jsm/postprocessing/GlitchPass.js';
DOM structure
The structure of the page DOM
is very simple, the container #canvas
is used for scene rendering, .color_pick
is used to switch the page background color, and .pass_button
is used to switch the glitch style post-rendering.
<div className='floating_page' style={{ backgroundColor: this.state.backgroundColor }}>
<div id="canvas"></div>
<input className='color_pick' type="color" onChange={this.handleInputChange} value={this.state.backgroundColor} />
<button className='pass_button' onClick={this.handleRenderChange}>特效<span className='highlight'>{this.state.renderGlithPass ? '开' : '关'}</span></button>
</div>
set state
backgroundColor
indicates the background color of the current page, and renderGlithPass
indicates whether to enable the later state. The self-test found that in the iOS Safari
browser, the post-rendering of the faulty style will cause the model to have mold-piercing problems 😱
, so use this parameter to control the default close of post-effects on the mobile phone and open by default on pc
.
state = {
backgroundColor: '#164CCA',
renderGlithPass: !(window.navigator.userAgent.toLowerCase().indexOf('mobile') > 0)
}
grid background
Use the pure CSS
attribute linear-gradient
to implement a grid background to beautify the page 🎏
.
background-image: linear-gradient(rgba(3, 192, 60, .3) 1px, transparent 1px), linear-gradient(90deg, rgba(3, 192, 60, .3) 1px, transparent 1px);
background-size: 1em 1em;
scene initialization
Initialize the rendering container, scene, and camera. The position of the camera can be adjusted according to your needs. render
Open alpha
and set .setClearAlpha(0)
to set the background color to transparent.
canvas = document.getElementById('canvas');
renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setPixelRatio(Math.min(2, window.devicePixelRatio));
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearAlpha(0);
canvas.appendChild(renderer.domElement);
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, .1, 10000);
camera.position.set(-2 * 10000, 0, 780);
Create material
All mesh models of in this article will use the same material MeshNormalMaterial
, and applying its properties can make mesh models produce color gradients. It is created once globally, and subsequent development does not require repeated creation, which is conducive to improving page performance.
const material = new THREE.MeshNormalMaterial();
💡
MeshNormalMaterial
It is a material that maps the normal vector to the color of RGB
. You can check whether the surface of the model is flat by observing whether the gradient color of the surface of the model is continuous.
constructor :
MeshNormalMaterial(parameters : Object)
parameters
: Optional, an object that defines the appearance of the material, with one or more properties.
special attribute :
.normalMap[Texture]
: Used to create normal map textures, theRGB
value affects the surface normal for each pixel fragment and changes the way the color is illuminated..normalMapType[Integer]
: Type of normal map, options areTHREE.TangentSpaceNormalMap
(default) andTHREE.ObjectSpaceNormalMap
..normalScale[Vector2]
: How much the normal map affects the material. The range is0-1
and the default isVector2
set to(1, 1)
..flatShading[Boolean]
: Defines whether the material is rendered with flat shading, default isfalse
..morphNormals[Boolean]
: Defines whether to usemorphNormals
. Set totrue
to pass themorphNormal
attribute fromgeometry
toshader
. The default value isfalse
..morphTargets[Boolean]
: Defines whether the material usesmorphTargets
, the default isfalse
.
Create text models
Load the FontLoader
font JSON
file with fontface
and create the text geometry model with TextGeometry
.
const loader = new FontLoader();
loader.load('./fonts/helvetiker_regular.typeface.json', font => {
textMesh.geometry = new TextGeometry('@dragonir\nfantastic\nthree.js\nart work', {
font: font,
size: 100,
height: 40,
curveSegments: 12,
bevelEnabled: true,
bevelThickness: 30,
bevelSize: 8,
bevelOffset: 1,
bevelSegments: 12
});
textMesh.material = material;
scene.add(textMesh);
});
💡
FontLoader Font Loader
Use a class for loading fonts in JSON
format, return Font
, the return value is an array of type Shape
representing the font, which internally uses FileLoader
to load the file.
constructor :
FontLoader(manager: LoadingManager)
manager
: 06228032a90f91 used by theloadingManager
, default isTHREE.DefaultLoadingManager
.
method:
.load
fromURL
and passes the loadedtexture
toonLoad
..load(url: String, onLoad: Function, onProgress: Function, onError: Function): null
。url
: The URL or path of the file, orData URI
.onLoad
: Will be called when loading is complete. The callback parameter istexture
to be loaded.onProgress
: Will be called during loading. The parameter is an instance ofXMLHttpRequest
, containingtotal
andloaded
bytes.onError
: Called on load error.
.parse
is parsed inJSON
format and returns aFont
..parse (json: Object ): Font
。json
:JSON
structure for parsing.
💡
TextGeometry Text geometry
A class for generating text into a single geometry constructed from a given string of text and parameters consisting of the loaded Font
font and the settings in the ExtrudeGeometry
parent class of that geometry.
constructor :
TextGeometry(text: String, parameters: Object)
text
: The text that will be displayed.parameters
:font[Font]
:THREE.Font
instance.size[Float]
: font size, default is100
.height[Float]
: The thickness of the extruded text, the default value is50
.curveSegments[Integer]
: The number of points on the curve representing the text, the default value is12
.bevelEnabled[Boolean]
: Whether to enable bevel, the default isfalse
.bevelThickness[Float]
: The depth of the text bevel, the default value is20
.bevelSize[Float]
: The extension distance between the bevel and the original text outline, the default is8
.bevelSegments[Integer]
: Number of segments for the bevel, default is3
.
🔗
can convertThree.js
supported by online using facetype.js 16228032a91341.
Create geometry model
Decorate the page with the other 3 built-in geometry models Torus, Cone and Octahedron . The number of decorative geometry is relatively large. In order to effectively improve page performance, the following two points need to be paid attention to:
⭐
usesTHREE.Group
manage all geometry.⭐
UseBufferAttribute
when creating geometry, e.g. use ConeBufferGeometry instead of ConeGeometry , which can pass data toGPU
more efficiently.
// 批量创建模型方法
generateRandomMesh = (geometry, material, count) => {
for (let i = 0; i < count; i++) {
let mesh = new THREE.Mesh(geometry, material);
let dist = farDist / 3;
let distDouble = dist * 2;
// 设置随机的位置和旋转角度
mesh.position.x = Math.random() * distDouble - dist;
mesh.position.y = Math.random() * distDouble - dist;
mesh.position.z = Math.random() * distDouble - dist;
mesh.rotation.x = Math.random() * 2 * Math.PI;
mesh.rotation.y = Math.random() * 2 * Math.PI;
mesh.rotation.z = Math.random() * 2 * Math.PI;
// 手动控制何时重新计算3D变换以获得更好的性能
mesh.matrixAutoUpdate = false;
mesh.updateMatrix();
group.add(mesh);
}
}
// 创建100个八面体
const octahedronGeometry = new THREE.OctahedronBufferGeometry(80);
generateRandomMesh(octahedronGeometry, material, 100);
// 创建200个圆环面
const torusGeometry = new THREE.TorusBufferGeometry(40, 25, 16, 40);
generateRandomMesh(torusGeometry, material, 200);
// 创建100个圆锥
const coneGeometry = new THREE.ConeBufferGeometry(40, 80, 80);
generateRandomMesh(coneGeometry, material, 100);
scene.add(group);
💡
TorusBufferGeometry Torus buffer geometry
Class for generating torus geometry.
constructor :
TorusBufferGeometry(radius: Float, tube: Float, radialSegments: Integer, tubularSegments: Integer, arc: Float)
radius
: The radius of the ring, from the center of the ring to the center of the pipe cross section, the default is1
.tube
: The radius of the pipe, the default is0.4
.radialSegments
: The number of segments of the ring, the default value is8
.tubularSegments
: The number of segments for the pipeline, the default is6
.arc
: The central angle of the ring, in radians, the default value isMath.PI * 2
.
💡
ConeBufferGeometry Cone Buffer Geometry
Class for generating cone geometry.
constructor :
ConeBufferGeometry(radius: Float, height: Float, radialSegments: Integer, heightSegments: Integer, openEnded: Boolean, thetaStart: Float, thetaLength: Float)
radius
: The radius of the base of the cone, the default value is1
.height
: The height of the cone, default is1
.radialSegments
: Number of segments around the sides of the cone, defaults to8
.heightSegments
: The number of segments for the sides of the cone along its height, default is1
.openEnded
: Indicates whether the base of the cone is open or capped. The default value isfalse
, which means the bottom surface is capped by default.thetaStart
: The starting angle of the first segment, defaults to0
.thetaLength
: The central angle of the circular sector of the base of the cone, commonly referred to asθ
. The default is2*PI
, making it a full cone.
💡
OctahedronBufferGeometry Octahedron buffer geometry
Class for creating octahedrons.
constructor :
OctahedronBufferGeometry(radius: Float, detail: Integer)
radius
: The radius of the octahedron, the default value is1
.detail
: The default value is0
, setting this value to a number greater than0
will add some vertices to it, making it no longer an octahedron.
mouse event listener
Add listener methods for mouse movement and touch movement events by converting between 🖱
mouse movement coordinates and model coordinates.
const mouseFX = {
windowHalfX: window.innerWidth / 2,
windowHalfY: window.innerHeight / 2,
coordinates: (coordX, coordY) => {
mouseX = (coordX - mouseFX.windowHalfX) * 5;
mouseY = (coordY - mouseFX.windowHalfY) * 5;
},
onMouseMove: e => { mouseFX.coordinates(e.clientX, e.clientY) },
onTouchMove: e => { mouseFX.coordinates(e.changedTouches[0].clientX, e.changedTouches[0].clientY)}
};
document.addEventListener('mousemove', mouseFX.onMouseMove, false);
document.addEventListener('touchmove', mouseFX.onTouchMove, false);
background color switch
Use a input[type='color']
tag to toggle the background color.
handleInputChange = e => {
this.setState({ backgroundColor: e.target.value });
}
post rendering
For a more punchy visual effect 💥
, I added a glitch style post-render effect and used a button switch ⚙
to turn the effect on and off.
composer = new EffectComposer(renderer);
composer.addPass( new RenderPass(scene, camera));
glitchPass = new GlitchPass();
composer.addPass(glitchPass);
handleRenderChange = () => {
this.setState({ renderGlithPass: !this.state.renderGlithPass });
}
💡
Post rendering
Three.js
Post-rendering processing is the process of achieving the desired visual effect by superimposing rendering channels. The implementation process is as follows:
- Create Effects Compositor: The Effects Compositor is the entry point for the various processing channels, use the
EffectComposer
object to create an Effects Compositor. - Add Channel: Add
RenderPass
channel which will render a new scene based on the specified scene and camera. - Compositor update: In the animation loop, call the
render
method of the effects compositor and the channel generation effects will be output in the scene.
💡
GlitchPass glitch style pass
GlitchPass
channel produces an analog glitch style effect, it has only one optional configuration parameter:
goWild
This property accepts a boolean value that specifies whether the electromagnetic storm effect persists.
📌
Three.js
provides many post-processing channels, which can be used directly. TheShaderPass
channel is also provided, which supports the use of customShader
to create advanced custom post-processing channels.
animation
Updated scene, camera, and post render passes in requestAnimationFrame
.
function animate() {
requestAnimationFrame(animate);
camera.position.x += (mouseX - camera.position.x) * 0.05;
camera.position.y += (mouseY * -1 - camera.position.y) * 0.05;
camera.lookAt(scene.position);
// 给场景中的立方体网格和字体网格添加自转动画
const t = Date.now() * 0.001;
const rx = Math.sin(t * 0.7) * 0.5;
const ry = Math.sin(t * 0.3) * 0.5;
const rz = Math.sin(t * 0.2) * 0.5;
group.rotation.x = rx;
group.rotation.y = ry;
group.rotation.z = rz;
textMesh.rotation.x = rx;
textMesh.rotation.y = ry;
textMesh.rotation.z = rx;
renderer.render(scene, camera);
// 更新后期渲染通道
composer.render();
}
zoom fit
renderer
and composer
should be resized at the same time.
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
composer.setSize( window.innerWidth, window.innerHeight );
}, false);
Double tap to full screen
Monitor the page 🖱
double-click dblclick
event, and enter or exit the full-screen state by calling requestFullscreen
and exitFullscreen
.
window.addEventListener('dblclick', () => {
let fullscreenElement = document.fullscreenElement || document.webkitFullscreenElement;
if (!fullscreenElement) {
if (canvas.requestFullscreen) {
canvas.requestFullscreen();
} else if (canvas.webkitRequestFullscreen) {
canvas.webkitRequestFullscreen();
}
console.log('进入全屏')
} else {
if (document.exitFullscreen) {
document.exitFullscreen();
} else if (document.webkitExitFullscreen) {
document.webkitExitFullscreen();
}
console.log('退出全屏')
}
})
💡
Element.requestFullscreen
Element.requestFullscreen
method is used to make an asynchronous request to put the element into full screen mode. Calling this API
does not guarantee that the element will be able to enter fullscreen mode . If the element is allowed to enter full screen mode, the returned Promise
will be resolve
, and the element will receive a fullscreenchange
event notifying it that it has entered full screen mode. If the fullscreen request is rejected, the returned promise
becomes rejected
and the element receives a fullscreenerror
event. If the element has been detached from the original document, the document will receive these events.
syntax :
var Promise = Element.requestFullscreen(options);
options
: Optional, aFullscreenOptions
object that provides control options for switching to fullscreen mode.
📌
This method can only be called when the user interacts or the device orientation changes, otherwise it will fail.FullscreenOptions
The only option currently isnavigationUI
, which controls whether the navbar is shown when the element is in fullscreen modeUI
. The default value isauto
, indicating that it will be up to the browser to display the navigation bar.
💡
Document.exitFullscreen
Document.exitFullscreen
method is used to take the current document out of full screen mode. Calling this method will roll back the document to the state it was in before the last call to the Element.requestFullscreen
method to enter full screen mode.
Syntax :
document.exitFullscreen();
At this point, all the functions of the example page are completed, you can visit the following link to view the complete code 😀
.
🔗
full code: https://github.com/dragonir/3d/tree/master/src/containers/Floating
Summarize
The new knowledge mainly included in the knowledge points of this article:
CSS
grid backgroundMeshNormalMaterial
normal materialFontLoader
font loaderTextGeometry
Text Buffer GeometryTorusBufferGeometry
Ring buffer geometryConeBufferGeometry
Conical buffer geometryOctahedronBufferGeometry
Buffer GeometryThree.js
post renderingGlitchPass
channelElement.requestFullscreen
Document.exitFullscreen
To learn about scene initialization, lighting, shadows, basic geometry, meshes, materials, and other Three.js related knowledge, you can read my previous articles. Please indicate the original address and author . If you think the article is helpful to you, don't forget to one-click three links 👍 .
appendix
- [1]. Three.js realizes the 3D effect of two-dimensional pictures
- [2]. Three.js Realize the 2022 Winter Olympics theme 3D fun page, Bing Dun
- [3]. Three.js makes an exclusive 3D medal
- [4]. Three.js to realize the 3D creative page of the Chinese New Year of the Tiger
- [5]. Three.js implements Facebook Metaverse 3D dynamic logo
- [6]. Three.js implements 3D panoramic detective game
- [7]. Three.js implements cool acid style 3D pages
- [8]. www.ilithya.rocks
- [9]. MDN requestFullScreen
- [10]. MDN exitFullscreen
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。