“This is the first day of my participation in the First Challenge 2022. For details: First Challenge 2022.”

background

Break the defense 😭! I suddenly found that the number of fans on the SegmentFault platform has exceeded 1000. It is the first one among my three blogging platforms, Nuggets, Blog Garden and SegmentFault, to exceed 1000 fans. Therefore, I designed and developed this page to commemorate it. Thank you very much for your attention to 🙏. In the future, I will pay more attention to the sorting and sharing of front-end knowledge and write more high-quality articles. (Hope other platforms will soon break thousands 😂)

In this paper, React + three. js technology stack is used to achieve the 3D memorial page of fans exceeding 1000. The main knowledge points include: Three. Js light source, DirectionLight parallel light, light sources, AmbientLight HemisphereLight hemisphere environment, and MEDALS material generated, map knowledge, MeshPhysicalMaterial physical material, TWEEN Shot tween animation, CSS fireworks animation, etc.

The effect

The implementation effect is shown in the article 👆 Banner. The page is composed of medal 🥇 and 1000+ Followers model with my personal information. You can preview it in real time through the link below: 🤣.

👀 online preview: dragonir.github. IO /3d/#/ Segmen…

implementation

The introduction of resources

The libraries required for development are introduced first, including FBXLoader for adding 1000+ font model, OrbitControls for lens track control, TWEEN for generating TWEEN animation, Stats for performance viewing at development time.

import * as THREE from "three";
import { FBXLoader } from "three/examples/jsm/loaders/FBXLoader";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { TWEEN } from "three/examples/jsm/libs/tween.module.min.js";
import Stats from "three/examples/jsm/libs/stats.module";
Copy the code

Scene initialization

This section is mainly used to initialize scenarios and parameters. You can click the link at the end of this article to read my previous 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;
renderer.shadowMap.needsUpdate = true;
container.appendChild(renderer.domElement);
/ / the scene
scene = new THREE.Scene();
// Create a nice background for the scene
scene.background = new THREE.TextureLoader().load(backgroundTexture);
/ / the camera
camera = new THREE.PerspectiveCamera(60.window.innerWidth / window.innerHeight, 0.1.1000);
camera.position.set(0.0.0);
camera.lookAt(new THREE.Vector3(0.0.0));
/ / controller
controls = new OrbitControls(camera, renderer.domElement);
controls.target.set(0.0.0);
controls.enableDamping = true;
controls.enableZoom = false;
controls.enablePan = false;
controls.rotateSpeed = 2.;
Copy the code

📌 For better visual effects, OrbitControls have zooming disabled, panning disabled and default rotation speed reduced

Lighting effects

To simulate a real physical scene, three light sources are used in this example.

/ / direct light
const cubeGeometry = new THREE.BoxGeometry(0.001.0.001.0.001);
const cubeMaterial = new THREE.MeshLambertMaterial({ color: 0xffffff });
const cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
cube.position.set(0.0.0);
light = new THREE.DirectionalLight(0xffffff.1);
light.intensity = 1;
light.position.set(18.20.60);
light.castShadow = true;
light.target = cube;
light.shadow.mapSize.width = 512 * 12;
light.shadow.mapSize.height = 512 * 12;
light.shadow.camera.top = 80;
light.shadow.camera.bottom = -80;
light.shadow.camera.left = -80;
light.shadow.camera.right = 80;
scene.add(light);
/ / half light
const ambientLight = new THREE.AmbientLight(0xffffff);
ambientLight.intensity = 8.;
scene.add(ambientLight);
/ / the ambient light
const hemisphereLight = new THREE.HemisphereLight(0xffffff.0xfffc00);
hemisphereLight.intensity = 3.;
scene.add(hemisphereLight);
Copy the code

💡The light source provided by three.js

The three.js library provides a list of light sources, and none of them has a specific behavior or purpose. These sources include:

Name of the light source describe
AmbientLightThe ambient light This is a base light source whose color is added to the current color of the entire scene and all objects
PointLightA point source A point in space that emits light in all directions
SpotLightSpot light source This light source has the effect of concentrating light, similar to a lamp, a ceiling chandelier, or a flashlight
DirectionLightParallel light Also known as infinite light. The light from this source can look parallel. For example, sunlight
HemishpereLightHemisphere light This is a special light source that can be used to create a more natural outdoor light, mimicking a light surface and a dim sky
AreaLightSurface light source Use this light source to specify the plane from which the light emanates, rather than a point in space
LensFlareThe glare of the lens It’s not a light source, but throughLensFlareYou can add a glare effect to the light source in the scene

💡THREE. DirectionLight parallel light

DirectionLight can be thought of as a light that is very far away, and all the rays it emits are parallel to each other. An example of parallel light is sunlight. The entire region illuminated by parallel light receives the same intensity of light.

Constructor:

new THREE.DirectionLight(color);
Copy the code

Attribute Description:

  • position: Position of the light source in the scene.
  • target: the target. Its direction is important. usetargetProperty that allows you to point a light source at a specific object or location in the scene. One is required for this propertyTHREE.Object3DObject.
  • intensity: Intensity of light source, default value:1.
  • castShadow: projection, if set totrue, the light source will create a shadow.
  • onlyShadow: Shadow only, if this property is set totrue, the light source only generates shadows and does not add any light to the scene.
  • shadow.camera.near: shadow near point, which indicates the position from the light source where the shadow begins to form.
  • shadow.camera.far: projection far point, indicating the position to which shadow can be generated from the light source.
  • shadow.camera.left: Projection left edge.
  • shadow.camera.right: Projection the right edge.
  • shadow.camera.top: Projection upper boundary.
  • shadow.camera.bottom: Projection lower boundary.
  • shadow.map.widthshadow.map.height: Shadow map width and shadow map height Determines how many pixels are used to create the shadow. This value can be increased when the shadow has jagged edges or does not look smooth. Cannot be changed after the scene has been rendered. The default values for both are:512.

💡Hemispherical light sources

Using a hemispherical light source creates a more natural lighting effect.

Constructor:

new THREE.HeimsphereLight(groundColor, color, intensity);
Copy the code

Attribute Description:

  • groundColor: Color of light emitted from the ground.
  • Color: Color of light emitted from the sky.
  • intensity: Intensity of light exposure.

💡THREE. AmbientLight ambient light

When creating THREE.AmbientLight, the color is applied globally. The light source has no particular direction of origin and does not produce shadows.

Constructor:

new THREE.AmbientLight(color);
Copy the code

Usage suggestions:

  • It is usually not possible toTHREE.AmbientLightAct as the only light source in the scene, as it will render all objects in the scene the same color.
  • Use other light sources such asTHREE.SpotLightTHREE.DirectionLightThe purpose is to weaken the shadows or add some extra color to the scene.
  • Due to theTHREE.AmbientLightThe light source does not need to be located and is applied globally, so just specify a color and add it to the scene.

Add mesh and ground

The mesh was added for ease of development, allowing you to adjust the appropriate relative position of the model. In this case, the mesh was retained for a 3D depth of field effect on the page. The transparent floor is designed to show the shadow of the model.

/ / grid
const grid = new THREE.GridHelper(200.200.0xffffff.0xffffff);
grid.position.set(0, -30, -50);
grid.material.transparent = true;
grid.material.opacity = 0.1;
scene.add(grid);
// Create ground, transparent material to show shadows
var planeGeometry = new THREE.PlaneGeometry(200.200);
var planeMaterial = new THREE.ShadowMaterial({ opacity: . 5 });
var plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.rotation.x = -0.5 * Math.PI;
plane.position.set(0, -30, -50);
plane.receiveShadow = true;
scene.add(plane);
Copy the code

Create a medal

Due to time constraints, this sample medal model is directly implemented using the basic cube model Three. BoxGeometry that comes with Three.js. You can also use other cubes such as spheres, beads, or even create your own medal shapes using professional modeling software such as Blender. (PS: PERSONALLY, I think the cube is also pretty good 😂)

💡Medal UI material generated

🥇 Below and side textures:

In order to generate a gold texture for the medal, this example uses the texture 👇 to create a blinding 24K gold effect 🤑.

🥇 Front and back stickers:

The texture used on the front and back of the medal is a screenshot of the SegmentFault personal center page. For a more metallic effect, I added a border with rounded corners using the metal texture texture above 👆.

Photoshop to generate rounded metal border specific method: add metal layer on the screenshot -> use the Box Selection Tool box to select the content to delete -> Select -> Click Modify -> Click Smooth -> Enter the appropriate rounded corner size -> Delete selection -> Merge layer -> Finish and export the image.

The final front and back material maps are as shown in 👇 below. In order to display more clearly, I modified the contrast and saturation of the image in Photoshop and added the SegmentFault Logo on it.

🥇 Phase map for front and back of medal:

To generate a bump texture, you need to add a phase map to the model. Using the front and back material maps already generated at 👆, you can use the online tool to automatically generate phase maps. When generating, you can fine-tune the style by adjusting Strength, Level, Blur and other parameters as needed, and preview in real time. Click Download to Download it.

🚪 NormalMap Online tool portal: NormalMap-Online

After many times of adjustment and optimization, the final phase map used is shown in 👇 below.

Using the material generated above, now build the medal model. Use personal information material on the front and back, and metal material on the other sides. Then iterate over all surfaces to adjust the metallicity and roughness patterns.

let segmentMap = new THREE.MeshPhysicalMaterial({map: new THREE.TextureLoader().load(segmentTexture), normalMap: new THREE.TextureLoader().load(normalMapTexture) });
let metalMap = new THREE.MeshPhysicalMaterial({map: new THREE.TextureLoader().load(metalTexture)});
// Create a texture array
const boxMaps = [metalMap, metalMap, metalMap, metalMap, segmentMap, segmentMap];
// 💡 The cube length, width and height ratio should be the same as the texture size ratio, the thickness can be arbitrary
box = new THREE.Mesh(new THREE.BoxGeometry(297.456.12), boxMaps);
box.material.map(item= > {
  // Adjust the material style
  item.metalness = . 5;
  item.roughness = 4.;
  item.refractionRatio = 1;
  return item;
});
box.scale.set(0.085.0.085.0.085);
box.position.set(-22.2.0);
box.castShadow = true;
meshes.push(box);
scene.add(box);
Copy the code

👆 the four renderings above correspond to:

  • Figure 1: Create one without texturesBoxGeometryIt’s just a white cube.
  • Figure 2: Cube addTexture mappingAt this time,No bump effect.
  • Figure 3: Cube addThe method for the mapAt this time,Bump effect.
  • Figure 4.: Adjust the cube materialDegree of metal,roughnessreflectivity, more realistic.

💡Maps in three.js

Map type
  • map: Texture map
  • normalMap: Normal map
  • bumpMap: bump map
  • envMap: Environment map
  • specularMap: Specular map
  • lightMap: Illumination map
Mapping principle

Use TextureLoader() to create a new texture object, and then call the load() method to load an image. This will return a texture object, which can be used as the value of the model material color map property. After the material’s color map property map is set, the model collects pixel values from the texture map.

💡MeshPhysicalMaterial Indicates the physical material

The MeshPhysicalMaterial class is a PBR physical material that can better simulate lighting calculations and render more realistically than the MeshPhongMaterial, a high-light mesh material.

If you want to show a product, choose this material for a more realistic rendering. If you want to show a game, choose PBR material MeshPhysicalMaterial instead of specular material MeshPhongMaterial.

Special attributes
  • .metalnessMetallicity property: Indicates the degree to which the material looks like metal. Non-metallic materials, such as wood or stone, are used0.0Metal use1.0, there is no (usually). Default0.5. 0.01.0Values between can be used for rusty metal appearance. If roughness maps are also provided.metalnessMap, then both values are multiplied.
  • .roughnessRoughness property: Indicates the roughness of the material.0.0Represents smooth specular reflection,1.0Represents complete diffuse reflection. The default0.5If roughness map is also provided.roughnessMap, then the two values are multiplied.
  • .metalnessMapMetallicity map: The blue channel of the texture is used to change the metallicity of the material.
  • .roughnessMapRoughness map: The green channel of the texture is used to change the roughness of the material.

📌 Note When using physical materials, you need to set the environment map. EnvMap.

Load 1000+ text model

The 1000+ model is loaded using three. LoadingManager and FBXLoader. Detailed usage methods are not described in this article, please refer to the link at the end of the article to see my other articles, which have detailed descriptions. 😁

const manager = new THREE.LoadingManager();
manager.onProgress = async(url, loaded, total) => {
  if (Math.floor(loaded / total * 100) = = =100) {
    // Set the loading progress
    _this.setState({ loadingProcess: Math.floor(loaded / total * 100)});// Load the camera moving tween animation
    Animations.animateCamera(camera, controls, { x: 0.y: 4.z: 60 }, { x: 0.y: 0.z: 0 }, 3600.() = > {});
  } else {
    _this.setState({ loadingProcess: Math.floor(loaded / total * 100)}); }};const fbxLoader = new FBXLoader(manager);
fbxLoader.load(textModel, mesh= > {
  mesh.traverse(child= > {
    if (child.isMesh) {
      // Generate a shadow
      child.castShadow = true;
      // Style adjustment
      child.material.metalness = 1;
      child.material.roughness = 2.; meshes.push(mesh); }}); mesh.position.set(16, -4.0);
  mesh.rotation.x = Math.PI / 2
  mesh.scale.set(. 08.. 08.. 08);
  scene.add(mesh);
});
Copy the code

Filling between animation

When the page is opened, the model is loaded and the animation from big to small is realized through TWEEN.

animateCamera: (camera, controls, newP, newT, time = 2000, callBack) = > {
  var tween = new TWEEN.Tween({
    x1: camera.position.x, / / camera x
    y1: camera.position.y, Y / / cameras
    z1: camera.position.z, Z / / cameras
    x2: controls.target.x, // The center of the control point is x
    y2: controls.target.y, // The center of the control point is y
    z2: controls.target.z, // The center of the control point z
  });
  tween.to({
    x1: newP.x,
    y1: newP.y,
    z1: newP.z,
    x2: newT.x,
    y2: newT.y,
    z2: newT.z,
  }, time);
  tween.onUpdate(function (object) {
    camera.position.x = object.x1;
    camera.position.y = object.y1;
    camera.position.z = object.z1;
    controls.target.x = object.x2;
    controls.target.y = object.y2;
    controls.target.z = object.z2;
    controls.update();
  });
  tween.onComplete(function () {
    controls.enabled = true;
    callBack();
  });
  tween.easing(TWEEN.Easing.Cubic.InOut);
  tween.start();
}
Copy the code

Animation updates

Finally, don’t forget to update the scene, the track controller, TWEEN, and the rotation of the model 🌍 in the requestAnimationFrame.

// Listen for page zooming, update camera and render
function onWindowResize() {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(window.innerWidth, window.innerHeight);
}
function animate() {
  requestAnimationFrame(animate);
  renderer.render(scene, camera);
  stats && stats.update();
  controls && controls.update();
  TWEEN && TWEEN.update();
  // Medal model rotation
  box && (box.rotation.y += . 04);
}
Copy the code

Fireworks animation

Finally, add 🎉 bloom effect to the page with box-shadow and simple CSS animation to create 🎅 celebration atmosphere!

<div className="firework_1"></div>
<div className="firework_2"></div>
<! -... -->
<div className="firework_10"></div>
Copy the code

Style animation:

[class^=firework_] {
  position: absolute;
  width: 0.1 rem;
  height: 0.1 rem;
  border-radius: 50%;
  transform: scale(8)}.firework_1 {
  animation: firework_lg 2s both infinite;
  animation-delay: 0.3 s;
  top: 5%;
  left: 5%;
}
@keyframes firework_lg {
  0%.100% {
    opacity: 0;
  }
  10%.70% {
    opacity: 1;
  }
  100% {
    box-shadow: -0.9 rem 0rem 0 #fff.0.9 rem 0rem 0 #fff.0rem -0.9 rem 0 #fff.0rem 0.9 rem 0 #fff.0.63 rem -0.63 rem 0 #fff.0.63 rem 0.63 rem 0 #fff, -0.63 rem -0.63 rem 0 #fff, -0.63 rem 0.63 rem 0 #fff; }}Copy the code

Effect:

🔗 Full code github.com/dragonir/3d…

conclusion

The knowledge points mainly involved in this paper include:

  • Three.jsSupplied light source
  • THREE.DirectionLightParallel light
  • THREE.HemisphereLightHemispherical light source
  • THREE.AmbientLightThe ambient light
  • The MEDALSUIMaterial generated
  • Three.jsIn the map
  • MeshPhysicalMaterialPhysical material
  • TWEENCamera tween animation
  • CSSFireworks animation

To learn more about scene initialization, lighting, shadows, and more about three.js, read my other articles. If you think the article is helpful to you, don’t forget a key three link 👍.

The appendix

  • [1].three.js to achieve the Year of the Tiger Spring Festival 3D creative page
  • [2].three.js to implement the 3D dynamic Logo of facebook metasomes
  • [3].three.js to implement 3D panoramic detective game
  • [4]. Use three.js to create cool acid style 3D pages
  • [5]. Environment texture source: Dribbble
  • [6]. Font Model source: SketchFab