author :dragonir
source :https://segmentfault.com/a/1190000041363089
background
Welcome the Winter Olympics , Together to the future !2022 The Winter Olympics It's about to start , This article USES the Three.js + React Technology stack , Realize winter and Olympic elements , Made a Winter Olympic theme full of fun and commemorative significance 3D page .
The knowledge points involved in this paper mainly include :TorusGeometry Torus 、MeshLambertMaterial Non gloss surface material 、MeshDepthMaterial Depth mesh material 、custromMaterial Custom materials 、Points The particle 、PointsMaterial Point material, etc .
effect
The effect is as follows: The diagram shows , The page is mainly composed of 2022 The mascot of the Winter Olympics Ice mound 、 Olympic rings 、 Dancing flag 、 tree And the effect of snow ️ Other components . Press and hold the left mouse button to move to change the camera position , Get different views .

The online preview :https://dragonir.github.io/3d/#/olympic( Deployed in
GitHub, The loading speed may be a little slow)
Realization
Introduce resources
First, introduce the library and external resources needed to develop the page ,OrbitControls For lens track control 、TWEEN It is used to realize the animation of making up room 、GLTFLoader Used for loading glb or gltf Format 3D Model 、 And some other models 、 Map and other resources .
import React from 'react';
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { TWEEN } from "three/examples/jsm/libs/tween.module.min.js";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import bingdundunModel from './models/bingdundun.glb';
// ...
page DOM structure
page DOM The structure is very simple , Only rendering 3D Elemental #container Container and display of loading progress .olympic_loading Elements .
<div>
<div id="container"></div>
{this.state.loadingProcess === 100 ? '' : (
<div className="olympic_loading">
<div className="box">{this.state.loadingProcess} %</div>
</div>
)}
</div>
Scene initialization
Initialize render container 、 scene 、 The camera . Detailed knowledge points about this part , You can refer to my previous articles , It will not be repeated in this article .
container = document.getElementById('container');
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
container.appendChild(renderer.domElement);
scene = new THREE.Scene();
scene.background = new THREE.TextureLoader().load(skyTexture);
camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 30, 100);
camera.lookAt(new THREE.Vector3(0, 0, 0));
Add light source
In this example, two kinds of light sources are mainly added :DirectionalLight Used to produce shadows , Adjust page brightness 、AmbientLight Used to render the environment atmosphere .
// Direct light
const light = new THREE.DirectionalLight(0xffffff, 1);
light.intensity = 1;
light.position.set(16, 16, 8);
light.castShadow = true;
light.shadow.mapSize.width = 512 * 12;
light.shadow.mapSize.height = 512 * 12;
light.shadow.camera.top = 40;
light.shadow.camera.bottom = -40;
light.shadow.camera.left = -40;
light.shadow.camera.right = 40;
scene.add(light);
// The ambient light
const ambientLight = new THREE.AmbientLight(0xcfffff);
ambientLight.intensity = 1;
scene.add(ambientLight);
Loading schedule management
Use THREE.LoadingManager Manage page model loading progress , Execute some methods related to the loading progress in its callback function . The page loading progress in this example is onProgress Done in , When the page loading progress is 100% when , perform TWEEN Camera room animation .
const manager = new THREE.LoadingManager();
manager.onStart = (url, loaded, total) => {};
manager.onLoad = () => { console.log('Loading complete!')};
manager.onProgress = (url, loaded, total) => {
if (Math.floor(loaded / total * 100) === 100) {
this.setState({ loadingProcess: Math.floor(loaded / total * 100) });
// Camera room animation
Animations.animateCamera(camera, controls, { x: 0, y: -1, z: 20 }, { x: 0, y: 0, z: 0 }, 3600, () => {});
} else {
this.setState({ loadingProcess: Math.floor(loaded / total * 100) });
}
};
Create the ground
In this example, the uneven ground is used Blender Build the model , Then export glb Format load created . Of course, you can just use Three.js A similar effect can be achieved with its own plane mesh and bump map . Use Blender The advantage of self built model is that it can freely and visually adjust the fluctuation effect of the ground .
var loader = new THREE.GLTFLoader(manager);
loader.load(landModel, function (mesh) {
mesh.scene.traverse(function (child) {
if (child.isMesh) {
child.material.metalness = .1;
child.material.roughness = .8;
// ground
if (child.name === 'Mesh_2') {
child.material.metalness = .5;
child.receiveShadow = true;
}
});
mesh.scene.rotation.y = Math.PI / 4;
mesh.scene.position.set(15, -20, 0);
mesh.scene.scale.set(.9, .9, .9);
land = mesh.scene;
scene.add(land);
});

Bing dwen dwen Winter Olympic mascot
Now add the lovely Winter Olympic mascot, panda ice pier. , Bing dwen dwen is also used. glb Format model loaded . Its original model comes from here , Free from this website now after the model , The original model is using 3D max I found that it can not be directly used in web pages , Need to be in Blender Convert model format in , You also need to adjust the map normals of the model , To restore the render effect .
The original model :

Map of ice pier pier :

convert to Blender Supported models , And in Blender Adjust model map normals 、 And add a map :

export glb Format :

Observe carefully Ice mound You can find , It has a floor outside Transparent plastic or glass shell , This effect can be achieved by modifying the transparency of the model 、 Metallicity 、 Realization of material parameters such as roughness , Finally, you can render such as banner chart The effect shown , The details are shown in the following code .
loader.load(bingdundunModel, mesh => {
mesh.scene.traverse(child => {
if (child.isMesh) {
// Inside
if (child.name === 'oldtiger001') {
child.material.metalness = .5
child.material.roughness = .8
}
// Translucent shell
if (child.name === 'oldtiger002') {
child.material.transparent = true;
child.material.opacity = .5
child.material.metalness = .2
child.material.roughness = 0
child.material.refractionRatio = 1
child.castShadow = true;
}
}
});
mesh.scene.rotation.y = Math.PI / 24;
mesh.scene.position.set(-8, -12, 0);
mesh.scene.scale.set(24, 24, 24);
scene.add(mesh.scene);
});
Create the Olympic rings
The Olympic rings consist of a circular torus with a basic geometric model TorusGeometry To achieve , Create five torus , And adjust their material color and position to form Blue black red yellow green Sequential five ring structure . The five rings are made of MeshLambertMaterial.
const fiveCycles = [
{ key: 'cycle_0', color: 0x0885c2, position: { x: -250, y: 0, z: 0 }},
{ key: 'cycle_1', color: 0x000000, position: { x: -10, y: 0, z: 5 }},
{ key: 'cycle_2', color: 0xed334e, position: { x: 230, y: 0, z: 0 }},
{ key: 'cycle_3', color: 0xfbb132, position: { x: -125, y: -100, z: -5 }},
{ key: 'cycle_4', color: 0x1c8b3c, position: { x: 115, y: -100, z: 10 }}
];
fiveCycles.map(item => {
let cycleMesh = new THREE.Mesh(new THREE.TorusGeometry(100, 10, 10, 50), new THREE.MeshLambertMaterial({
color: new THREE.Color(item.color),
side: THREE.DoubleSide
}));
cycleMesh.castShadow = true;
cycleMesh.position.set(item.position.x, item.position.y, item.position.z);
meshes.push(cycleMesh);
fiveCyclesGroup.add(cycleMesh);
});
fiveCyclesGroup.scale.set(.036, .036, .036);
fiveCyclesGroup.position.set(0, 10, -8);
scene.add(fiveCyclesGroup);
TorusGeometry Torus
TorusGeometry A class for generating torus geometry .
Constructors :
TorusGeometry(radius: Float, tube: Float, radialSegments: Integer, tubularSegments: Integer, arc: Float)
radius: The radius of the ring , From the center of the ring to the pipe ( cross section ) Center of . The default value is1.tube: The radius of the pipe , The default value is0.4.radialSegments: Number of segments of the ring , The default value is8.tubularSegments: The number of segments of the pipe , The default value is6.arc: The central angle of the ring ( The unit is radians ), The default value isMath.PI * 2.
MeshLambertMaterial Non gloss surface material
A material with a non glossy surface , No specular highlights . The material uses non physical based Lambertian Model to calculate reflectivity . This can simulate some surfaces very well ( For example, untreated wood or stone ), But it cannot simulate a glossy surface with specular highlights ( For example, painted wood ).
Constructors :
MeshLambertMaterial(parameters : Object)
parameters:( Optional ) The object that defines the appearance of the material , Having one or more properties . Any property of the material can be passed in from here .
Create flag
The flag model is from sketchfab Download the , And a flagpole , Can be in Blender A cylindrical cube is added to the , And adjust the appropriate length, width and height to combine with the flag surface .

Flag map :

The flag surface has been animated , Animation frame playback needs to be performed in the code .
loader.load(flagModel, mesh => {
mesh.scene.traverse(child => {
if (child.isMesh) {
child.castShadow = true;
// The flag
if (child.name === 'mesh_0001') {
child.material.metalness = .1;
child.material.roughness = .1;
child.material.map = new THREE.TextureLoader().load(flagTexture);
}
// flagpole
if (child.name === ' Cylinder ') {
child.material.metalness = .6;
child.material.roughness = 0;
child.material.refractionRatio = 1;
child.material.color = new THREE.Color(0xeeeeee);
}
}
});
mesh.scene.rotation.y = Math.PI / 24;
mesh.scene.position.set(2, -7, -1);
mesh.scene.scale.set(4, 4, 4);
// Animation
let meshAnimation = mesh.animations[0];
mixer = new THREE.AnimationMixer(mesh.scene);
let animationClip = meshAnimation;
let clipAction = mixer.clipAction(animationClip).play();
animationClip = clipAction.getClip();
scene.add(mesh.scene);
});
Create trees
To enrich the picture , Create a winter atmosphere , So a few pine trees were added As a decoration . It is very important to use a technique when adding pine trees : We know because the tree model is very complex , There are a lot of faces , Too many faces will reduce page performance , Create a Caton . Two are used in this article, as shown in the figure below The two intersecting faces shown are used as the base of the tree , In this case, the tree has only two faces , Using this technique can greatly optimize page performance , And trees It looks like 3D Sensible .

Texture mapping :

To make the tree Transparent only in the transparent part of the map 、 Opaque elsewhere , And you can Produces tree shadows instead of box shadows , You need to add the following to the tree model MeshPhysicalMaterial、MeshDepthMaterial Two materials , Both materials use the same texture map , among MeshDepthMaterial Model added to custromMaterial Attribute .
let treeMaterial = new THREE.MeshPhysicalMaterial({
map: new THREE.TextureLoader().load(treeTexture),
transparent: true,
side: THREE.DoubleSide,
metalness: .2,
roughness: .8,
depthTest: true,
depthWrite: false,
skinning: false,
fog: false,
reflectivity: 0.1,
refractionRatio: 0,
});
let treeCustomDepthMaterial = new THREE.MeshDepthMaterial({
depthPacking: THREE.RGBADepthPacking,
map: new THREE.TextureLoader().load(treeTexture),
alphaTest: 0.5
});
loader.load(treeModel, mesh => {
mesh.scene.traverse(child =>{
if (child.isMesh) {
child.material = treeMaterial;
child.custromMaterial = treeCustomDepthMaterial;
}
});
mesh.scene.position.set(14, -9, 0);
mesh.scene.scale.set(16, 16, 16);
scene.add(mesh.scene);
// Clone the other two trees
let tree2 = mesh.scene.clone();
tree2.position.set(10, -8, -15);
tree2.scale.set(18, 18, 18);
scene.add(tree2)
// ...
});
The effect can also be achieved from above Banner You can see that in the picture , For a better picture , I canceled the shadow display of the tree .
stay3DFunction development in progress , Some unimportant decoration models can adopt this strategy to optimize .
MeshDepthMaterial Depth mesh material
A material that draws geometry by depth . The depth is based on the near far plane of the camera , White recently , Black farthest .
Constructors :
MeshDepthMaterial(parameters: Object)
parameters:( Optional ) The object that defines the appearance of the material , Having one or more properties . Any property of the material can be passed in from here .
Special properties :
.depthPacking[Constant]:depth packingThe coding . The default isBasicDepthPacking..displacementMap[Texture]: Displacement mapping affects the position of mesh vertices , Unlike other maps that affect only the material's lighting and shadows , Shifted vertices can cast shadows , Block other objects , And act as real geometry ..displacementScale[Float]: How much the displacement map affects the mesh ( Black is no displacement , White is the maximum displacement ). If no displacement map is set , This value is not applied . The default value is1..displacementBias[Float]: The offset of the displacement map on the mesh vertices . If no displacement map is set , This value is not applied . The default value is0.
custromMaterial Custom materials
Add to grid custromMaterial Custom material properties , Transparent peripherals can be achieved png Content area shadow of picture map .
Create snowflakes
Create snowflakes ️, You use Particle knowledge .THREE.Points Is a class used to create points , It's also used to batch manage particles . In this example, a 1500 Snowflake particles , The random coordinates defining the three-dimensional space and the horizontal and vertical random moving speeds are set for them .
// Snowflake map
let texture = new THREE.TextureLoader().load(snowTexture);
let geometry = new THREE.Geometry();
let range = 100;
let pointsMaterial = new THREE.PointsMaterial({
size: 1,
transparent: true,
opacity: 0.8,
map: texture,
// Background fusion
blending: THREE.AdditiveBlending,
// Depth of field attenuation
sizeAttenuation: true,
depthTest: false
});
for (let i = 0; i < 1500; i++) {
let vertice = new THREE.Vector3(Math.random() * range - range / 2, Math.random() * range * 1.5, Math.random() * range - range / 2);
// Longitudinal velocity
vertice.velocityY = 0.1 + Math.random() / 3;
// Lateral velocity
vertice.velocityX = (Math.random() - 0.5) / 3;
// Add to geometry
geometry.vertices.push(vertice);
}
geometry.center();
points = new THREE.Points(geometry, pointsMaterial);
points.position.y = -30;
scene.add(points);
Points The particle
Three.js in , rain ️、 snow ️、 cloud ️、 Stars And other common particles in life can be used Points To simulate the implementation .
Constructors :
new THREE.Points(geometry, material);
- The constructor can accept two parameters , A geometry and a material , Geometry parameters are used to determine the position coordinates of particles , Material parameters are used to format particles ;
- Can be based on simple geometry objects such as
BoxGeometry、SphereGeometryAnd so on as the parameters of the particle system ; - In general , You need to assign vertices yourself to determine the position of particles .
PointsMaterial Point material
adopt THREE.PointsMaterial You can set the attribute parameters of particles , yes Points Default material used .
Constructors :
PointsMaterial(parameters : Object)
parameters:( Optional ) The object that defines the appearance of the material , Having one or more properties . Any property of the material can be passed in from here .
Material properties .blending
The material .blending Attribute mainly controls the superposition mode of texture fusion ,.blending The values of the property include :
THREE.NormalBlending: The default value isTHREE.AdditiveBlending: Additive fusion modeTHREE.SubtractiveBlending: Subtraction fusion modeTHREE.MultiplyBlending: Multiplicative fusion modeTHREE.CustomBlending: Custom fusion mode , And.blendSrc,.blendDstor.blendEquationAttribute combination
Material properties .sizeAttenuation
Is the depth of the particles attenuated by the camera , The default is true( Perspective cameras only ).
Three.js vector
A few dimensional vector has several components , Two dimensional vector Vector2 Yes x and y Two components , Three dimensional vector Vector3 Yes x、y、z The three components , four-dimensional vector Vector4 Yes x、y、z、w Four components .
relevant API:
Vector2: Two dimensional vectorVector3: Three dimensional vectorVector4: four-dimensional vector
Lens control 、 Zoom fit 、 Animation
controls = new OrbitControls(camera, renderer.domElement);
controls.target.set(0, 0, 0);
controls.enableDamping = true;
// Disable translation
controls.enablePan = false;
// Disable scaling
controls.enableZoom = false;
// Vertical rotation angle limit
controls.minPolarAngle = 1.4;
controls.maxPolarAngle = 1.8;
// Horizontal rotation angle limit
controls.minAzimuthAngle = -.6;
controls.maxAzimuthAngle = .6;
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}, false);
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
controls && controls.update();
// Flag animation update
mixer && mixer.update(new THREE.Clock().getDelta());
// Lens animation
TWEEN && TWEEN.update();
// Five ring rotation
fiveCyclesGroup && (fiveCyclesGroup.rotation.y += .01);
// The vertex needs to be updated after it changes , Otherwise, the raindrop effect cannot be realized
points.geometry.verticesNeedUpdate = true;
// Snowflake animation update
let vertices = points.geometry.vertices;
vertices.forEach(function (v) {
v.y = v.y - (v.velocityY);
v.x = v.x - (v.velocityX);
if (v.y <= 0) v.y = 60;
if (v.x <= -20 || v.x >= 20) v.velocityX = v.velocityX * -1;
});
}
Complete code :https://github.com/dragonir/3d/tree/master/src/containers/Olympic
summary
The new knowledge points mainly included in this paper include :
TorusGeometryTorusMeshLambertMaterialNon gloss surface materialMeshDepthMaterialDepth mesh materialcustromMaterialCustom materialsPointsThe particlePointsMaterialPoint material- Material properties
.blending、.sizeAttenuation Three.jsvector
Space for further optimization :
- Add more interactive features 、 The interface style is further optimized ;
- Mascot adds bone animation to ice pier pier. , And its movement and interaction can be controlled by mouse and keyboard .
Recent hot article recommends :
1.1,000+ Avenue Java Arrangement of interview questions and answers (2022 The latest version )
2. Explode !Java Xie Cheng is coming ...
3.Spring Boot 2.x course , It's too complete !
4.20w Programmer red envelope cover , Get it quickly ...
5.《Java Development Manual ( Song Mountain version )》 The latest release , Download it quickly !
I think it's good , Don't forget to like it + Forward !








