“This is the second day of my participation in the First Challenge 2022, for more details: First Challenge 2022”.
PK creative Spring Festival, I am participating in the “Spring Festival creative submission contest”, please see: Spring Festival creative submission Contest
background
Welcome the Winter Olympics, together to the future! The 2022 Winter Olympics will start soon. This paper uses three.js + React technology stack to realize winter and Olympic elements and create a 3D page with the theme of winter Olympics full of fun and commemorative significance. The knowledge points involved in this paper mainly include: TorusGeometry Torus, MeshLambertMaterial Non-luster Surface Material, MeshDepthMaterial Depth Mesh Material, custromMaterial custom material, Points particles, PointsMaterial Point material and so on.
The effect
The realization effect is shown in the following 👇 GIF. The page is mainly composed of 2022 Winter Olympic Games mascot Bing Dwen Dwen, Olympic five rings, dancing flag 🚩, trees 🌲 and snow effect ❄️. Press and hold the left mouse button to move the camera to get different views.
👀 online preview: dragonir.github. IO /3d/#/olympi… (Deployed on GitHub, may be a bit slow to load 😓)
implementation
The introduction of resources
The libraries and external resources needed to develop the page are introduced first, OrbitControls for lens track control, TWEEN for TWEEN animation implementation, GLTFLoader for loading 3D models in GLB or GLTF format, and some other models, textures, 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';
// ...
Copy the code
Page DOM structure
The page DOM structure is very simple, just the #container container that renders 3D elements and the.olympic_loading element that shows the loading progress.
<div>
<div id="container"></div>
{this.state.loadingProcess === 100 ? ' ' : (
<div className="olympic_loading">
<div className="box">{this.state.loadingProcess} %</div>
</div>
)}
</div>
Copy the code
Scene initialization
Initialize render containers, scenes, and cameras. For details about this part of the content, you can refer to my previous article, this article will not repeat.
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));
Copy the code
Add a light source
In this example, two main light sources have been added: DirectionalLight for creating shadows, adjusting page brightness, and AmbientLight for rendering the ambient 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);
Copy the code
Load Progress Management
Use THREE.LoadingManager to manage the page model load progress and perform some methods related to the load progress in its callback function. The page loading progress in this example is done in onProgress. When the page loading progress reaches 100%, the TWEEN shot TWEEN animation is performed.
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 tween 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)}); }};Copy the code
Create the ground
The bumpy ground in this example was modeled using Blender and then exported to GLB format to load. Of course, you can also achieve a similar effect by just using the Three. Js flat mesh and bump map. The advantage of using your own model in Blender is that you can freely and visually adjust the rise and fall 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.;
/ / the 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);
});
Copy the code
Create the Winter Olympics mascot, Bing Dwen Dwen
Now add the lovely Winter Olympics mascot panda Bing Dwen Dwen 🐼, bing Dwen Dwen is also loaded using GLB format model. The original model came from here, which is free from this website. Now the original model was built using 3D Max and I found that it can’t be used directly on the web. I need to convert the model format in Blender and adjust the map normals of the model to restore the rendering effect.
The original model:
Ice block map:
Convert to a model supported by Blender and adjust the model map normals and add maps in Blender:
Export to GLB format:
📖 Adding texture to models in Blender Tutorial Portal: How to Map models in Blender
A closer look at Ice Dun 🐼 shows that it has a transparent plastic or glass texture shell on the outside. This effect can be achieved by modifying the transparency, metallicity, roughness and other material parameters of the model. Finally, the effect as shown in 👆 banner can be rendered, as shown in the code below.
loader.load(bingdundunModel, mesh= > {
mesh.scene.traverse(child= > {
if (child.isMesh) {
/ / internal
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);
});
Copy the code
Create the Olympic rings
The five Olympic rings are realized by the basic geometric model TorusGeometry, which creates five torus and adjusts their material colors and positions to form a blue, black, red, yellow and green five-ring structure. The fifth ring material is 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);
Copy the code
💡
TorusGeometry toroid
TorusGeometry A class for generating ring geometry.
Constructor:
TorusGeometry(radius: Float, tube: Float, radialSegments: Integer, tubularSegments: Integer, arc: Float)
Copy the code
radius
: Radius of the ring, from the center of the ring to the center of the pipe (cross section). The default value is1
.tube
: Radius of the pipe. The default value is0.4
.radialSegments
: Indicates the number of segments of a ring. The default value is8
.tubularSegments
: The number of segments for a pipe. The default value is6
.arc
: Central Angle of a ring, in radians. The default value isMath.PI * 2
.
💡
MeshLambertMaterial Non gloss surface material
A non – shiny surface material without specular highlights. The material uses a non-physical Lambertian model to calculate reflectivity. This mimics some surfaces well (such as untreated wood or stone), but not glossy surfaces with specular highlights (such as painted wood).
Constructor:
MeshLambertMaterial(parameters : Object)
Copy the code
parameters
(Optional) An object that defines the appearance of a material and has one or more properties. Any attributes of the material can be passed in from here.
Create a banner
The flag model was downloaded from Sketchfab, and you need a flag pole. You can add a column cube to Blender and adjust the length, width and height to fit the flag.
Flag map:
The flag adds an animation that needs to be performed in the code to play the animation frame.
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);
}
/ / the pole
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);
});
Copy the code
Create trees
In order to enrich the picture and create a wintry atmosphere, several pine trees 🌲 were added as decorations. It is important to use a trick when adding pine trees: we know that because the tree model is very complex and has a very large number of faces, too many faces can slow down page performance and cause lag. In this article, we use two intersecting faces as the base of the tree, as shown in 👇 below, so that the tree has only two faces. Using this technique can greatly optimize the page performance, and the tree 🌲 also looks 3D.
Texture map:
In order to make the tree transparent only in the transparent part of the map and opaque elsewhere, and to create tree shadows instead of rectangular shadows, we need to add the following two materials to the tree model: MeshPhysicalMaterial and MeshDepthMaterial. Both materials use the same texture map. Where MeshDepthMaterial is added to the custromMaterial property of the model.
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 two other trees
let tree2 = mesh.scene.clone();
tree2.position.set(10, -8, -15);
tree2.scale.set(18.18.18);
scene.add(tree2)
// ...
});
Copy the code
The effect can also be seen in the Banner image above 👆. I removed the tree shadow display to make the image look better.
📌 In 3D functional development, some non-important decorative models can adopt this strategy to optimize.
💡
MeshDepthMaterial Depth mesh material
A material that draws geometry by depth. Depth is based on the camera’s near and far plane, with white nearest and black farthest.
Constructor:
MeshDepthMaterial(parameters: Object)
Copy the code
parameters
(Optional) An object that defines the appearance of a material and has one or more properties. Any attributes of the material can be passed in from here.
Special attributes:
.depthPacking[Constant]
:depth packing
Coding. The default isBasicDepthPacking
..displacementMap[Texture]
Displacement maps affect the position of vertices in the mesh. Unlike other maps, which only affect the lighting and shadows of the material, displaced vertices can cast shadows, block other objects, and act as real geometry..displacementScale[Float]
: The effect of displacement map on mesh (black is no displacement, white is maximum displacement). If no displacement map is set, this value is not applied. The default value is1
..displacementBias[Float]
: Displacement Map offset at mesh vertices. If no displacement map is set, this value is not applied. The default value is0
.
💡
CustromMaterial Custom material
Add the custromMaterial custom material attribute to the mesh to shadow the content area of the transparent outer PNG image map.
Create snow
To create snowflakes ❄️, we need to use particle knowledge. THREE.Points is a class for creating Points and also for batch managing particles. In this example, 1,500 snowflake particles are created and assigned random coordinates in three dimensional space and random movement speeds in horizontal and vertical directions.
// 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 fades
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 speed
vertice.velocityY = 0.1 + Math.random() / 3;
// Lateral speed
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);
Copy the code
💡
Points particles
In three. js, common particles in life such as rain 🌧️, snow ❄️, cloud ☁️ and star ️ can be simulated using Points.
Constructor:
new THREE.Points(geometry, material);
Copy the code
- The constructor can take two parameters, a geometry and a material. The geometry parameter is used to specify the position of the particle, and the material parameter is used to format the particle.
- Can be based on simple geometry objects such as
BoxGeometry
,SphereGeometry
Etc as parameters of the particle system; - In general, you need to specify vertices to determine the positions of particles.
💡
PointsMaterial point material
Using three. PointsMaterial you can set the particle property parameter, which is the default material used by Points.
Constructor:
PointsMaterial(parameters : Object)
Copy the code
parameters
(Optional) An object that defines the appearance of a material and has one or more properties. Any attributes of the material can be passed in from here.
💡
Material properties. Blending
The. Blending theory attribute mainly controls the stacking mode of texture blending. The values of the. Blending theory attribute include:
THREE.NormalBlending
Default value:THREE.AdditiveBlending
: Add fusion modeTHREE.SubtractiveBlending
: Subtraction fusion modeTHREE.MultiplyBlending
: Multiplication fusion modeTHREE.CustomBlending
: Custom fusion mode, and.blendSrc
..blendDst
或.blendEquation
Attribute combination
💡
Material property.sizeProfile
Whether particle size is attenuated by camera depth. Default is true (perspective camera only).
💡
Three. Js vector
The multi-dimensional vector has several components, the two-dimensional vector Vector2 has x and Y components, the three-dimensional vector Vector3 has X, Y and Z components, and the four-dimensional vector Vector4 has X, Y, Z and W components.
The API:
Vector2
: two-dimensional vectorVector3
: three-dimensional vectorVector4
: four dimensional vector
Camera control, zoom adaptation, animation
controls = new OrbitControls(camera, renderer.domElement);
controls.target.set(0.0.0);
controls.enableDamping = true;
// Disable panning
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.;
Copy the code
window.addEventListener('resize'.() = > {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}, false);
Copy the code
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
controls && controls.update();
// Flag animation update
mixer && mixer.update(new THREE.Clock().getDelta());
// Camera animation
TWEEN && TWEEN.update();
// Rotation of the five rings
fiveCyclesGroup && (fiveCyclesGroup.rotation.y += .01);
// The vertex changes need to be updated, otherwise raindrop effects cannot be implemented
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;
});
}
Copy the code
🔗 Full code: github.com/dragonir/3d…
conclusion
💡 The main new knowledge points included in this article include:
TorusGeometry
toriMeshLambertMaterial
Non-shiny surface materialMeshDepthMaterial
Depth mesh materialcustromMaterial
Custom materialPoints
The particlePointsMaterial
Some material- The material properties
.blending
,.sizeAttenuation
Three.js
vector
Further optimized space:
- Add more interactive functions and further optimize the interface style;
- The mascot Ice Dwen Dwen adds a skeleton animation and can control its movement and interaction with mouse and keyboard.
Next period:
- “
Metahuman
Yuan human!Three.js
Portrait Optimization –
To learn more about scene initialization, lighting, shadows, base geometry, meshes, materials, and more about three.js, read my previous articles. If you think the article is helpful to you, don’t forget a key three link oh 👍.
The appendix
- [1]. 1000 powder! Create an exclusive 3D medal using three.js 🥇
- [2].three.js to achieve the Year of the Tiger Spring Festival 3D creative page
- [3].three.js to implement the 3D dynamic Logo of facebook metasomes
- [4].three.js to implement 3D panoramic detective game
- [5].three.js to achieve cool acid style 3D pages
- [6].3DX model conversion to Blender supported format