1
头图

background

I recently learned WebGL and Three.js , so I want to decorate my homepage in combination with the acidic design style that is popular recently, and at the same time summarize some of the knowledge learned. This article mainly introduces that by using the React + three.js technology stack, loading 3D model, adding 3D text, adding animation, click interaction, etc., with style design, to achieve the 🤢 acid style page full of design sense.

Basic knowledge

Three.js

Three.js 3D engine based on the native WebGL package and running in the browser. It can be used to create various 3D scenes, including cameras, light and shadow, materials and other objects. It is a very widely used 3D engine. You can learn more three.js official Chinese document

Acid design

term acid design is translated from Acid Graphics , which originated from acid house music, electronic dance music and hippie culture in the 1990s. In the field of design, this acidic aesthetic carries a free proposition, grotesque graphics, bold and vivid colors, special material texture, and a variety of fonts to form a unique acidic design style.

In short, bright and highly saturated color combination; black-gray base and highly saturated fluorescent color colorful black dotted with the screen; full of futuristic, cool and technological sense liquid metal, glass, aluminum foil plastic And other materials; random elements, graphic layout; repeating, cutting, combining geometric figures, etc. are all acidic design styles. Acidic style has gradually become popular in music album covers, visual posters, book and movie covers, and web design.

Achieve effect

Online preview: https://tricell.fun

accomplish

3D model

Scene initialization

🌏 Create scene
scene = new THREE.Scene();
📷 Initialize the camera

PerspectiveCamera's 4 parameters respectively refer to: field of view, aspect ratio, near surface, and far surface.

camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 1000);
// 设置相机位置
camera.position.set(600, 20, -200);
// 相机聚焦到屏幕中央
camera.lookAt(new THREE.Vector3(0, 0, 0));
💡 Initialize the light source

Add HemisphereLight: Create a more natural light source for outdoor effects

light = new THREE.HemisphereLight(0xffffff, 0x444444);
light.position.set(0, 20, 0);
scene.add(light);
light = new THREE.DirectionalLight(0xffffff);
light.position.set(0, 20, 10);
light.castShadow = true;
scene.add(light);

Add AmbientLight:

var ambiColor = '#0C0C0C';
var ambientLight = new THREE.AmbientLight(ambiColor);
scene.add(ambientLight);

Add auxiliary tools (optional)

📦 Add auxiliary grid

GridHelper can be used to add grid auxiliary lines, can also be used for decoration, through GridHelper(size, divisions, colorCenterLine, colorGrid) achieve.

  • size : Grid width, the default value is 10 .
  • divisions : equal fraction, the default value is 10 .
  • colorCenterLine : Center line color, the default value is 0x444444 .
  • colorGrid : Grid line color, the default value is 0x888888 .
var grid = new THREE.GridHelper(1000, 100, 0x000000, 0x000000);
grid.material.opacity = 0.1;
grid.material.transparent = true;
grid.position.set(0, -240, 0);
scene.add(grid);
📦 Add camera controls

Through the camera control OrbitControls you can zoom, translate, and rotate the three-dimensional scene. What essentially changes is not the scene, but the parameters of the camera. OrbitControls.js needs to be introduced separately during development.

controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.target.set(0, 0, 0);
controls.update();
📦 add performance view plugin

stats is an Three.js , which is mainly used to detect the number of frames when the animation is running. stats.js also needs to be introduced separately.

stats = new Stats();
container.appendChild(stats.dom);

Load the model

discus thrower statue 3D model used in the example in this article is threedscans.com and can be free at 16153a636408c5. There are multiple free model download sites at the end of this article. There are free models with more than 200 pages. If you are interested, you can choose your own Download and use the favorite model. Of course, students with modeling ability can also use professional modeling software such as blender and 3dmax

Load obj or fbx model

It is necessary to separately import FBXLoader.js or OBJLoader.js . The model loading method of .fbx and .obj

// var loader = new THREE.FBXLoader();
var loader = new THREE.OBJLoader();
loader.load(model, function (object) {
  object.traverse(function (child) {
    if (child.isMesh) {
      child.castShadow = true;
      child.receiveShadow = true;
    }
  });
  object.rotation.y = Math.PI / 2;
  object.position.set(0, -200, 0);
  object.scale.set(0.32, 0.32, 0.32);
  model = object;
  scene.add(object);
});
Load gltf model

GLTFLoader.js needs to be introduced separately, and the .gltf format model is slightly different. It should be noted that the traversal object of the model and the final added to the scene are object.scene instead of object .

var loader = new THREE.GLTFLoader();
loader.load(model, function (object) {
  object.scene.traverse(function (child) {
    if (child.isMesh) {
      child.castShadow = true;
      child.receiveShadow = true;
    }
  });
  object.scene.rotation.y = Math.PI / 2;
  object.scene.position.set(0, -240, 0);
  object.scene.scale.set(0.33, 0.33, 0.33);
  model = object.scene;
  scene.add(object.scene);
});

The effect after adding the grid and loading the model is shown in the figure below.

Add turntable animation

Add the turntable animation effect by requestAnimationFrame window.requestAnimationFrame() tells the browser that it wants to execute an animation, and requires the browser to call the specified callback function to update the animation before the next redraw. This method needs to pass in a callback function as a parameter, and the callback function will be executed before the next browser redraw.

function animate () {
  requestAnimationFrame(animate);
  // 随着页面重绘不断改变场景的rotation.y来实现旋转
  scene.rotation.y -= 0.015;
  renderer.render(scene, camera);
}

Add click interaction

In the Three.js scene, we need to click on a model to get its information, or do some other operations, we need to use Raycaster (ray casting), the principle is to emit a beam of rays at the position where you click with the mouse, and the objects in the ray are all Was recorded. The basic syntax is Raycaster(origin, direction, near, far) , where:

  • origin : the starting point vector of the ray.
  • direction : The direction vector of the ray.
  • near : All returned results should be farther near The value cannot be negative, the default value is 0 .
  • far : All returned results should be closer than far . It cannot be smaller than near , and the default value is infinity.

The basic steps of the code implementation are: get the coordinates of the mouse on the screen screen coordinates to standard device coordinates standard device coordinates to world coordinates get the world coordinates of the mouse in the scene according to the world coordinates and the camera to generate the ray projection direction unit vector according to The ray casting direction unit vector creates a ray projector object.

//声明raycaster和mouse变量
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
onMouseClick = event => {
  // 将鼠标点击位置的屏幕坐标转成threejs中的标准坐标,以屏幕中心为原点,值的范围为-1到1.
  mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
  mouse.y = - (event.clientY / window.innerHeight) * 2 + 1;
  // 通过鼠标点的位置和当前相机的矩阵计算出raycaster
  raycaster.setFromCamera(mouse, camera);
  // 获取raycaster直线和所有模型相交的数组集合
  let intersects = raycaster.intersectObjects(scene.children);
  if (intersects.length > 0) {
    alert('HELLO WORLD')
    // 可以通过遍历实现点击不同mesh触发不同交互,如:
    let selectedObj = intersects[0].object;
    if (selectedObj.name === 'car') {
      alert('汽车🚗')
    }
  }
}
window.addEventListener('click', onMouseClick, false);

Add 3D text

Use TextGeometry(text : String, parameters : Object) add 3D text, the following is the description of the attributes that can be set:

  • size : Font size, generally the height of a capital letter.
  • height : The thickness of the text.
  • weight : The value is normal or bold , indicating whether it is bold or not.
  • font : Font, the default is helvetiker , which needs to correspond to the referenced font file.
  • style : The value is normal or italics , indicating whether it is italic
  • bevelThickness : Chamfer thickness.
  • bevelSize : Chamfer width.
  • curveSegments : The number of arc segments, making the curve of the text smoother.
  • bevelEnabled : Boolean value, whether to use chamfer or not, meaning to cut at the edge.
var loader = new THREE.FontLoader();
loader.load('gentilis_regular.typeface.json', function (font) {
  var textGeo = new THREE.TextGeometry('HELLO WORLD', {
    font: font,
    size: .8,
    height: .8,
    curveSegments: .05,
    bevelThickness: .05,
    bevelSize: .05,
    bevelEnabled: true
  });
  var textMaterial = new THREE.MeshPhongMaterial({ color: 0x03c03c });
  var mesh = new THREE.Mesh(textGeo, textMaterial);
  mesh.position.set(0, 3.8, 0);
  scene.add(mesh);
});

optimization

Now the model loading has been basically completed, but 3D model is generally relatively large. After deployment, I found that the web page loads very slowly, which affects the user experience. It is very necessary to reduce the model size. After looking for a compression tool on the Internet for a long time, I found If you need to install the large-scale 3D modeling software, using obj2gltf can convert the larger OBJ format model to the gltf model, effectively optimizing the model size and improving the web page loading speed.

Install
npm install obj2gltf --save
Copy the obj model to the following directory
node_modules\obj2gltf\bin
Execute transcoding instructions
node obj2gltf.js -i demo.obj -o demo.gltf

FIG appear similar to the above, transcoding is complete, the file size before and after conversion contrast, in the present embodiment kas.obj initial file size 9.7M converted file kas.gltf only 4.6M , the volume reduced by half, then the model is loaded onto the page transformed , The naked eye can hardly see the change of the model effect, and the page loading speed has been significantly improved.

obj2gltf can also be used as a library, through the node service real-time conversion model, interested students can learn more through the link at the end of the article.
May be used 3D modeling software such as blender other hand by reducing the model number of faces and reduce the volume compression optimization model for other ways, such optimization is more effective.

Complete code

var model = require('@/assets/models/kas.gltf');
var container, stats, controls;
var camera, scene, renderer, light, model;
class Kas extends React.Component {
  render () {
    return (
      <div id="kas"></div>
    )
  }
  componentDidMount () {
    this.initThree();
  }
  initThree () {
    init();
    animate();
    function init () {
      container = document.getElementById('kas');
      scene = new THREE.Scene();
      scene.fog = new THREE.Fog(0xa0a0a0, 200, 1000);
      // 透视相机:视场、长宽比、近面、远面
      camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 1000);
      camera.position.set(600, 20, -200);
      camera.lookAt(new THREE.Vector3(0, 0, 0));
      // 半球光源:创建室外效果更加自然的光源
      light = new THREE.HemisphereLight(0xffffff, 0x444444);
      light.position.set(0, 20, 0);
      scene.add(light);
      light = new THREE.DirectionalLight(0xffffff);
      light.position.set(0, 20, 10);
      light.castShadow = true;
      scene.add(light);
      // 环境光
      var ambiColor = '#0C0C0C';
      var ambientLight = new THREE.AmbientLight(ambiColor);
      scene.add(ambientLight);
      // 网格
      var grid = new THREE.GridHelper(1000, 100, 0x000000, 0x000000);
      grid.material.opacity = 0.1;
      grid.material.transparent = true;
      grid.position.set(0, -240, 0);
      scene.add(grid);
      // 加载gltf模型
      var loader = new THREE.GLTFLoader();
      loader.load(model, function (object) {
        object.scene.traverse(function (child) {
          if (child.isMesh) {
            child.castShadow = true;
            child.receiveShadow = true;
          }
        });
        object.scene.rotation.y = Math.PI / 2;
        object.scene.position.set(0, -240, 0);
        object.scene.scale.set(0.33, 0.33, 0.33);
        model = object.scene;
        scene.add(object.scene);
      });
      renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
      renderer.setPixelRatio(window.devicePixelRatio);
      renderer.setSize(window.innerWidth, window.innerHeight);
      renderer.setClearAlpha(0);
      renderer.shadowMap.enabled = true;
      container.appendChild(renderer.domElement);
      window.addEventListener('resize', () => {
        camera.aspect = window.innerWidth / window.innerHeight;
        camera.updateProjectionMatrix();
        renderer.setSize(window.innerWidth, window.innerHeight);
      }, false);
      stats = new Stats();
      container.appendChild(stats.dom);
    }
    function animate () {
      var clock = new THREE.Clock()
      requestAnimationFrame(animate);
      var delta = clock.getDelta();
      scene.rotation.y -= 0.015;
      renderer.render(scene, camera);
      stats.update();
    }
    // 增加点击事件
    //声明raycaster和mouse变量
    var raycaster = new THREE.Raycaster();
    var mouse = new THREE.Vector2();
    function onMouseClick(event) {
      // 通过鼠标点击位置计算出raycaster所需要点的位置,以屏幕中心为原点,值的范围为-1到1.
      mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
      mouse.y = - (event.clientY / window.innerHeight) * 2 + 1;
      // 通过鼠标点的位置和当前相机的矩阵计算出raycaster
      raycaster.setFromCamera(mouse, camera);
      // 获取raycaster直线和所有模型相交的数组集合
      var intersects = raycaster.intersectObjects(scene.children);
      if (intersects.length > 0) {
        alert('HELLO WORLD')
      }
    }
    window.addEventListener('click', onMouseClick, false);
  }
}

Other design elements

This article mainly introduces 3D elements. Due to the limited length and time of the article ( bloggers are too lazy 😂) The implementation of other elements will not be explained in detail (maybe later there will be time to summarize and organize maybe ) interested students can expand and read the following other Excellent article by Great God.

Fluid background

Static liquid background image can be achieved by SVG filter , you can read "Creating Patterns With SVG Filters" , to achieve dynamic fluid background, you can use Three.js combined with native GLSL to achieve, you can refer to "CodePen Shader Templatebc" .

Acidic fonts such as metal, neon, glitch effects, etc. can read my other article "Cyberpunk 2077 style visual effects in just a few steps with CSS" , or you can use design generation, due to time constraints, this project The metal effect text and the text in the banner header of this article are all online artistic font generation website . Interested students can try to design by themselves.

Further optimization in the future

  • #todo Acidic style liquid background realization.
  • #todo 3D model liquid metal effect.

three.js Excellent case recommendation

Finally, I recommend a few very amazing three.js projects to experience and learn together. Whether it is page interaction, visual design or performance optimization, they have achieved the ultimate, and you can learn a lot from them.

Reference

address of this article: 16153a636418bb https://segmentfault.com/a/1190000040753633

dragonir
1.8k 声望3.9k 粉丝

Accepted ✔