用户注册



邮箱:

密码:

用户登录


邮箱:

密码:
记住登录一个月忘记密码?

发表随想


还能输入:200字
云代码 - javascript代码库

3D冰墩墩源码 3D模型 + Three.js + React

2022-03-23 作者: Python自学举报

[javascript]代码库

在线预览:https://dragonir.github.io/3d/#/olympic

详情步骤看Readme

冰墩墩外面有一层透明塑料或玻璃质感外壳,这个效果可以通过修改模型的透明度、金属度、粗糙度等材质参数
/* eslint-disable */
import React from 'react';
import Stats from "three/examples/jsm/libs/stats.module";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { TWEEN } from "three/examples/jsm/libs/tween.module.min.js";
import Animations from '../../assets/utils/animations';
import landModel from './models/land.glb';
import treeModel from './models/tree.gltf';
import bingdwendwenModel from './models/bingdwendwen.glb';
import xuerongrongModel from './models/xuerongrong.glb';
import flagModel from './models/flag.glb';
import skyTexture from './images/sky.jpg';
import snowTexture from './images/snow.png';
import treeTexture from './images/tree.png';
import flagTexture from './images/flag.png';
require('./libs/GLTFLoader.js');
import './index.css';

export default class Olympic extends React.Component {

  constructor(props) {
    super(props);
  }

  state = {
    loadingProcess: 0
  }

  componentDidMount () {
    this.initThree()
  }

  initThree = () => {
    var container, controls, stats, mixer;
    var camera, scene, renderer, light, land = null, meshes = [], points = [];
    var fiveCyclesGroup = new THREE.Group(), clock = new THREE.Clock();
    var mouseX = 0, mouseY = 0;
    var windowHalfX = window.innerWidth / 2;
    var windowHalfY = window.innerHeight / 2;
    var _this = this;

    init();
    animate();

    function init () {
      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.type = THREE.PCFSoftShadowMap; // // 0: THREE.BasicShadowMap, 1: THREE.PCFShadowMap, 2: THREE.PCFSoftShadowMap

      container.appendChild(renderer.domElement);

      scene = new THREE.Scene();
      scene.background = new THREE.TextureLoader().load(skyTexture);
      scene.fog = new THREE.Fog(0xffffff, 10, 100);

      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));

      // 性能工具
      // stats = new Stats();
      // document.documentElement.appendChild(stats.dom);

      const cubeGeometry = new THREE.BoxGeometry(0.001, 0.001, 0.001);
      const cubeMaterial = new THREE.MeshLambertMaterial({ color: 0xdc161a });

      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(16, 16, 8);
      light.castShadow = true;
      light.target = cube;
      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);

      // const lightHelper = new THREE.DirectionalLightHelper(light, 1, 'red');
      // scene.add(lightHelper);
      // const lightCameraHelper = new THREE.CameraHelper(light.shadow.camera);
      // scene.add(lightCameraHelper);

      var ambientLight = new THREE.AmbientLight(0xcfffff);
      ambientLight.intensity = 1;

      scene.add(ambientLight);

      const manager = new THREE.LoadingManager();

      manager.onStart = (url, loaded, total) => { };

      manager.onLoad = () => { console.log('Loading complete!') };

      manager.onProgress = async (url, loaded, total) => {
        if (Math.floor(loaded / total * 100) === 100) {
          _this.loadingProcessTimeout && clearTimeout(_this.loadingProcessTimeout);

          _this.loadingProcessTimeout = setTimeout(() => {
            _this.setState({ loadingProcess: Math.floor(loaded / total * 100) });
            Animations.animateCamera(camera, controls, { x: 0, y: -1, z: 20 }, { x: 0, y: 0, z: 5 }, 3600, () => { });
          }, 800);
        } else {
          _this.setState({ loadingProcess: Math.floor(loaded / total * 100) });
        }
      };

      // 添加地面
      var loader = new THREE.GLTFLoader(manager);
      loader.load(landModel, function (mesh) {
        mesh.scene.traverse(function (child) {
          if (child.isMesh) {
            meshes.push(child)
            child.material.metalness = .1;
            child.material.roughness = .8;
            // 地面
            if (child.name === 'Mesh_2') {
              child.material.metalness = .5;
              child.receiveShadow = true;
            }
            // 围巾
            if (child.name === 'Mesh_17') {
              child.material.metalness = .2;
              child.material.roughness = .8;
            }
            // 帽子
            if (child.name === 'Mesh_17') { }
          }
        });

        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);
      });

      // 旗帜
      loader.load(flagModel, mesh => {
        mesh.scene.traverse(child => {
          if (child.isMesh) {
            meshes.push(child)
            child.castShadow = true;

            // 旗帜
            if (child.name === 'mesh_0001') {
              child.material.metalness = .1;
              child.material.roughness = .1;
              child.material.map = new THREE.TextureLoader().load(flagTexture);
            }

            // 旗杆
            if (child.name === '柱体') {
              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);

        // 动画
        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);
      });

      // bingdwendwen
      loader.load(bingdwendwenModel, function (mesh) {
        mesh.scene.traverse(function (child) {
          if (child.isMesh) {
            meshes.push(child)

            if (child.name === '皮肤') {
              child.material.metalness = .3;
              child.material.roughness = .8;
            }

            if (child.name === '外壳') {
              child.material.transparent = true;
              child.material.opacity = .4;
              child.material.metalness = .4;
              child.material.roughness = 0;
              child.material.refractionRatio = 1.6;
              child.castShadow = true;
              child.material.envMap = new THREE.TextureLoader().load(skyTexture);
              child.material.envMapIntensity = 1;
            }

            if (child.name === '围脖') {
              child.material.transparent = true;
              child.material.opacity = .6;
              child.material.metalness = .4;
              child.material.roughness = .6;
            }
          }
        });

        mesh.scene.rotation.y = Math.PI / 24;
        mesh.scene.position.set(-5, -11.5, 0);
        mesh.scene.scale.set(24, 24, 24);

        scene.add(mesh.scene);
      });

      // xuerongrong
      loader.load(xuerongrongModel, function (mesh) {
        mesh.scene.traverse(function (child) {
          if (child.isMesh) {
            child.castShadow = true;
            meshes.push(child)
          }
        });

        mesh.scene.rotation.y = Math.PI / 3;
        mesh.scene.position.set(-20, -10.8, 0);
        mesh.scene.scale.set(12, 12, 12);

        scene.add(mesh.scene);
      });

      // 添加树
      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, function (mesh) {
        mesh.scene.traverse(function (child) {
          if (child.isMesh) {
            meshes.push(child)
            child.material = treeMaterial;
            child.custromMaterial = treeCustomDepthMaterial;
          }
        });

        mesh.scene.position.set(14, -9, 0);
        mesh.scene.scale.set(16, 16, 16);
        scene.add(mesh.scene);

        let tree2 = mesh.scene.clone();
        tree2.position.set(10, -8, -15);
        tree2.scale.set(18, 18, 18);
        scene.add(tree2)

        let tree3 = mesh.scene.clone();
        tree3.position.set(-18, -8, -16);
        tree3.scale.set(22, 22, 22);
        scene.add(tree3)
      });

      // 创建五环
      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);

      // 创建雪花
      let texture = new THREE.TextureLoader().load(snowTexture);
      let geometry = new THREE.Geometry();
      let pointsMaterial = new THREE.PointsMaterial({
        size: 1,
        transparent: true,
        opacity: 0.8,
        map: texture,
        blending: THREE.AdditiveBlending,
        sizeAttenuation: true,
        depthTest: false
      });

      let range = 100;
      let vertices = []
      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);
        // 纵向移动速度
        vertice.velocityY = 0.1 + Math.random() / 3;
        // 横向移动速度
        vertice.velocityX = (Math.random() - 0.5) / 3;
        // 将顶点加入几何
        geometry.vertices.push(vertice);
      }

      geometry.center();
      points = new THREE.Points(geometry, pointsMaterial);
      points.position.y = -30;
      scene.add(points);

      controls = new OrbitControls(camera, renderer.domElement);
      controls.target.set(0, 0, 0);
      controls.enableDamping = true;
      controls.enablePan = false;
      controls.enableZoom = false;

      // 垂直旋转角度限制
      controls.minPolarAngle = 1.4;
      controls.maxPolarAngle = 1.8;

      // 水平旋转角度限制
      controls.minAzimuthAngle = -.8;
      controls.maxAzimuthAngle = .8;

      window.addEventListener('resize', onWindowResize, false);
    }

    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();
      fiveCyclesGroup && (fiveCyclesGroup.rotation.y += .01);
      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;
      });

      // 顶点变动之后需要更新,否则无法实现雨滴特效
      points.geometry.verticesNeedUpdate = true;

      let time = clock.getDelta();
      mixer && mixer.update(time);
    }

    // 增加点击事件,声明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(meshes);

      if (intersects.length > 0) {
        console.log(intersects[0].object)
      }
    }

    window.addEventListener('click', onMouseClick, false);
  }

  render () {
    return (
      <div>
        <div id="container"></div>
        {this.state.loadingProcess === 100 ? '' : (
          <div className="olympic_loading">
            <div className="box">{this.state.loadingProcess} %</div>
          </div>
        )
        }
      </div>
    )
  }
}

[代码运行效果截图]


3D冰墩墩源码 3D模型 + Three.js + React

[源代码打包下载]




网友评论    (发表评论)

共2 条评论 1/1页

发表评论:

评论须知:

  • 1、评论每次加2分,每天上限为30;
  • 2、请文明用语,共同创建干净的技术交流环境;
  • 3、若被发现提交非法信息,评论将会被删除,并且给予扣分处理,严重者给予封号处理;
  • 4、请勿发布广告信息或其他无关评论,否则将会删除评论并扣分,严重者给予封号处理。


扫码下载

加载中,请稍后...

输入口令后可复制整站源码

加载中,请稍后...