• 前端小玩意儿:用three.js开发的手机太空穿越VR游戏,特效非常猛


    hello,今天给大家用three.js开发了一个手机太空穿越VR游戏,确实不容易,小编的头发又少了一大截。Ok,废话少说,先看效果。

    效果图

    首页index.html

    <!DOCTYPE html>
    <html lang="en" >
    
    <head>
    <meta charset="UTF-8">
    <title>Three.js Mobile VR Sonic</title>
    
    
    
    <link rel="stylesheet" href="css/style.css">
    
    
    </head>
    
    <body>
    
    <script src="js/jquery-1.12.4.min.js"></script>
    <script src="js/d3.v4.min.js"></script>
    <script src="js/three.min.js"></script>
    <script src="js/GLTFLoader.js"></script>
    
    
    <script src="js/TweenMax.min.js"></script>
    <script src="js/CSSPlugin.min.js"></script>
    <script src="js/EasePack.min.js"></script>
    
    
    
    <script src='js/AssimpJSONLoader.js'></script>
    <script src="js/DeviceOrientationControls.js"></script>
    <script src="js/OrbitControls.js"></script>
    <script src="js/StereoEffect.js"></script>
    <script src="js/tween.min.js"></script>
    <script src="js/dat.gui.min.js"></script>
    <!-- glowing effect scripts -->
    <script src="js/EffectComposer.js"></script>
    <script src="js/RenderPass.js"></script>
    <script src="js/MaskPass.js"></script>
    <script src="js/ShaderPass.js"></script>
    <script src="js/CopyShader.js"></script>
    <script src="js/FXAAShader.js"></script>
    <script src="js/ConvolutionShader.js"></script>
    <script src="js/LuminosityHighPassShader.js"></script>
    <!-- unreal bloom -->
    <script src="js/UnrealBloomPass.js"></script>
    
    
        <!-- VR Button -->
        <button id='VR' class='button toggleVR' onclick='toggleVR()' title='Toggle VR Mode for Mobile Devices Only'>
          <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" x="0px" y="0px" viewBox="0 0 62.7 52.375" enable-background="new 0 0 62.7 41.9" xml:space="preserve"><path d="M53.4,5.5h-44c-2.1,0-3.7,1.7-3.7,3.7v22.6c0,2.1,1.7,3.7,3.7,3.7h13.4c1.1,0,2.1-0.6,2.5-1.6l3-7.5c1.2-2.6,4.9-2.5,6,0.1  l2.6,7.3c0.4,1,1.4,1.7,2.5,1.7h13.9c2.1,0,3.7-1.7,3.7-3.7V9.3C57.2,7.2,55.5,5.5,53.4,5.5z M20.4,27c-3.2,0-5.7-2.6-5.7-5.7  s2.6-5.7,5.7-5.7s5.7,2.6,5.7,5.7S23.6,27,20.4,27z M42.4,27c-3.2,0-5.7-2.6-5.7-5.7s2.6-5.7,5.7-5.7s5.7,2.6,5.7,5.7  S45.6,27,42.4,27z"/><text x="0" y="56.9" fill="#000000" font-size="5px" font-weight="bold" font-family="'Helvetica Neue', Helvetica, Arial-Unicode, Arial, Sans-serif">Created by Nick Bluth</text><text x="0" y="61.9" fill="#000000" font-size="5px" font-weight="bold" font-family="'Helvetica Neue', Helvetica, Arial-Unicode, Arial, Sans-serif">from the Noun Project</text></svg>
        </button>
    
        <div id="info">SUPER!</div>
        <audio id="bflat" src="sonic_ring_sound.mp3"></audio>
      
      
    
        <script  src="js/index.js"></script>
    
    
    
    
    </body>
    
    </html>

    核心代码js

    //===================================================== full screen
    var requestFullscreen = function(ele) {
      if (ele.requestFullscreen) {
        ele.requestFullscreen();
      } else if (ele.webkitRequestFullscreen) {
        ele.webkitRequestFullscreen();
      } else if (ele.mozRequestFullScreen) {
        ele.mozRequestFullScreen();
      } else if (ele.msRequestFullscreen) {
        ele.msRequestFullscreen();
      } else {
        console.log("Fullscreen API is not supported.");
      }
    };
    var exitFullscreen = function(ele) {
      if (ele.exitFullscreen) {
        ele.exitFullscreen();
      } else if (ele.webkitExitFullscreen) {
        ele.webkitExitFullscreen();
      } else if (ele.mozCancelFullScreen) {
        ele.mozCancelFullScreen();
      } else if (ele.msExitFullscreen) {
        ele.msExitFullscreen();
      } else {
        console.log("Fullscreen API is not supported.");
      }
    };
    //===================================================== add canvas
    let renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
    renderer.setSize(window.innerWidth, window.innerHeight);
    renderer.setClearColor(0xd8e7ff, 0);
    document.body.appendChild(renderer.domElement);
    //===================================================== resize
    window.addEventListener("resize", function() {
      let width = window.innerWidth;
      let height = window.innerHeight;
      renderer.setSize(width, height);
      camera.aspect = width / height;
      camera.updateProjectionMatrix();
    });
    //===================================================== add Scene
    let scene = new THREE.Scene();
    //===================================================== add Camera
    let camera = new THREE.PerspectiveCamera(
      45,
      window.innerWidth / window.innerHeight,
      1,
      10000
    );
    let cameraTarget = new THREE.Vector3(0, 0, 0);
    //===================================================== add Group
    group = new THREE.Group();
    scene.add(group);
    //===================================================== add Controls
    var controls = new THREE.OrbitControls(camera, renderer.domElement);
    controls.enableDamping = true;
    controls.dampingFactor = 0.25;
    controls.enableZoom = true;
    //How far you can orbit vertically, upper and lower limits. The maximum is Pi / 2 (90deg). You wont see below the below the line of the horizon
    controls.maxPolarAngle = Math.PI / 2.1;
    //===================================================== add VR
    renderer.setPixelRatio(window.devicePixelRatio); //VR
    effect = new THREE.StereoEffect(renderer); //VR
    effect.setSize(window.innerWidth, window.innerHeight); //VR
    
    var VR = false;
    function toggleVR() {
      if (VR) {
        VR = false;
        controls = new THREE.OrbitControls(camera, renderer.domElement);
      } else {
        VR = true;
        controls = new THREE.DeviceOrientationControls(camera);
        requestFullscreen(document.documentElement);
      }
      renderer.setSize(window.innerWidth, window.innerHeight);
    }
    //===================================================== curve points exported from blender using python
    var points = [
      /*[2.873088836669922, 1.886053442955017, 2.807666063308716] ,
    [2.8677191734313965, 1.901498556137085, 0.9702248573303223] ,
    [4.261392116546631, 1.1370995044708252, 2.199495315551758] ,
    [5.4436726570129395, -0.46564579010009766, 1.1223225593566895] ,
    [4.683673858642578, -5.823459148406982, 0.29172343015670776] ,
    [4.683673858642578, -5.823459148406982, 0.29172343015670776] ,
    [1.0320820808410645, -8.896514892578125, -0.25123584270477295] ,
    [-6.009271621704102, -5.854086875915527, -1.619685411453247] ,
    [-5.8719940185546875, 1.664353609085083, 1.4598760604858398] ,
    [-3.664644718170166, 5.1350908279418945, 0.8891280889511108] ,
    [0.3946661949157715, 9.023353576660156, 2.472759246826172] ,
    [6.545413017272949, 7.975126266479492, 4.807941913604736] ,
    [9.380702018737793, 3.8875434398651123, -1.2000198364257812] ,
    [3.980130672454834, -3.3519601821899414, -1.5907882452011108] ,
    [0.10054226964712143, -4.0724077224731445, -2.6255977153778076] ,
    [0.11306309700012207, -4.062130451202393, -2.625786781311035] ,
    [-1.5915921926498413, 2.2223830223083496, 1.9054492712020874] ,
    [-0.7800552845001221, 2.729933738708496, 2.711679458618164] ,
    [2.2788195610046387, 1.5061609745025635, 3.6167585849761963] ,
    [3.330962896347046, -1.753382682800293, 3.5841569900512695] ,
    [3.3268532752990723, -1.7394065856933594, 3.584031343460083] ,
    [1.8003381490707397, -2.6099541187286377, 4.496334552764893] ,
    [1.809736728668213, -2.6042258739471436, 4.490817546844482] ,
    [-1.5139055252075195, -2.2446377277374268, 3.513293743133545] ,
    [-1.5139055252075195, -2.2446377277374268, 3.513293743133545] ,
    [-1.5139055252075195, -2.2446377277374268, 1.7960444688796997] ,
    [-1.9393141269683838, -0.623103678226471, 1.3167498111724854] ,
    [-1.5, 0.0, 0.0] ,
    [-1.0, 1.0, 0.0] ,
    [1.0, 1.0, 0.0] ,
    [1.5, 0.0, 0.0] ,*/
    
      [1.8117204904556274, 5.987488269805908, 0.29106736183166504],
      [6.005367279052734, 1.7647128105163574, -1.5591322183609009],
      [1.435487985610962, -6.016839504241943, 2.1336286067962646],
      [-4.118395805358887, -6.886471271514893, -0.7294682264328003],
      [-4.732693195343018, 3.405961036682129, 3.1304938793182373],
      [8.304193496704102, 7.593861103057861, 0.3412821292877197],
      [8.038525581359863, -4.391696453094482, 2.687108278274536],
      [1.488401174545288, -9.993440628051758, -2.2956111431121826],
      [-5.277090549468994, -8.481210708618164, -0.719127893447876],
      [-7.250330448150635, -0.9653520584106445, -0.3089699447154999],
      [-6.526705741882324, 5.678538799285889, 0.15560221672058105],
      [-0.885545015335083, 6.678538799285889, 1.5724562406539917],
      [1.614454984664917, 5.678538799285889, 0.24559785425662994],
      [1.8117204904556274, 5.987488269805908, 0.29106736183166504]
    ];
    
    var scale = 5;
    
    //Convert the array of points into vertices
    for (var i = 0; i < points.length; i++) {
      var x = points[i][0] * scale;
      var y = points[i][1] * scale;
      var z = points[i][2] * scale;
      points[i] = new THREE.Vector3(x, z, -y);
    }
    
    //Create a path from the points
    var carPath = new THREE.CatmullRomCurve3(points);
    var radius = 0.25;
    
    var geometry = new THREE.TubeGeometry(carPath, 600, radius, 10, false);
    
    //Set a different color on each face
    for (var i = 0, j = geometry.faces.length; i < j; i++) {
      geometry.faces[i].color = new THREE.Color(
        "hsl(" + Math.floor(Math.random() * 290) + ",50%,50%)"
      );
    }
    
    var material = new THREE.MeshBasicMaterial({
      side: THREE.BackSide,
      vertexColors: THREE.FaceColors,
      side: THREE.DoubleSide,
      transparent: true,
      opacity: 1
    });
    var tube = new THREE.Mesh(geometry, material);
    scene.add(tube);
    
    //===================================================== add lighta
    var light = new THREE.DirectionalLight(0xefefff, 1.25);
    light.position.set(1, 1, 1).normalize();
    scene.add(light);
    var light = new THREE.DirectionalLight(0xffefef, 1.25);
    light.position.set(-1, -1, -1).normalize();
    scene.add(light);
    
    //Create a point light in our scene. Makes everything gloomy
    var light = new THREE.PointLight(0xffffff, 1, 100);
    scene.add(light);
    
    //===================================================== particles
    var mergedGeometry = new THREE.Geometry();
    
    var boxGeometry = new THREE.TetrahedronGeometry(0.25, 0);
    
    var material = new THREE.MeshNormalMaterial({
      color: new THREE.Color("white")
    });
    
    for (var i = 0; i < 1000; i++) {
      var x = Math.random() * 125 - 75;
      var y = Math.random() * 125 - 75;
      var z = Math.random() * 125 - 75;
    
      boxGeometry.translate(x, y, z);
    
      mergedGeometry.merge(boxGeometry);
    
      boxGeometry.translate(-x, -y, -z);
    }
    
    var cubes = new THREE.Mesh(mergedGeometry, material);
    scene.add(cubes);
    
    //===================================================== Loader
    //3d model from
    var loader = new THREE.GLTFLoader();
    var model;
    loader.load(
      "sky-island.glb",
      function(gltf) {
        gltf.scene.traverse(function(node) {
          if (node instanceof THREE.Mesh) {
            node.castShadow = true;
            node.material.side = THREE.DoubleSide;
          }
        });
    
        model = gltf.scene;
        model.scale.set(3, 3, 3);
        model.position.set(0, -20, -10);
        //model.rotateY(Math.PI);
        scene.add(model);
      }
    );
    
    //===================================================== Loader
    //3d model from https://3dwarehouse.sketchup.com/user/0438052632930067253040161/wingedkoopa67?nav=models
    var clock = new THREE.Clock();
    var mixer = null;
    var firstObject;
    var loader = new THREE.GLTFLoader();
    loader.load(
      "sonic.glb",
      function(gltf) {
        gltf.scene.traverse(function(node) {
          if (node instanceof THREE.Mesh) {
            node.castShadow = true;
            node.material.side = THREE.DoubleSide;
          }
        });
    
        firstObject = gltf.scene;
        firstObject.scale.set(0.65, 0.65, 0.65);
        group.add(firstObject);
    
        console.log(gltf.animations); //shows all animations imported into the dopesheet in blender
    
        mixer = new THREE.AnimationMixer(firstObject);
        mixer.clipAction(gltf.animations[0]).play();
    
        document.body.addEventListener("click", jump);
        function jump() {
          mixer.clipAction(gltf.animations[0]).stop();
          mixer.clipAction(gltf.animations[1]).play();
          setTimeout(function() {
            mixer.clipAction(gltf.animations[1]).stop();
            mixer.clipAction(gltf.animations[0]).play();
          }, 900);
        }
      }
    );
    
    //===================================================== add model
    var size = 0.05;
    var meshList = [];
    
    for (var i = 0; i < carPath.points.length; i++) {
      var x = carPath.points[i].x;
      var y = carPath.points[i].y;
      var z = carPath.points[i].z;
    
      var geometry = new THREE.TorusGeometry(3, 0.5, 8, 50);
      var material = new THREE.MeshBasicMaterial({
        color: new THREE.Color("yellow")
      });
      var secondObject = new THREE.Mesh(geometry, material);
      secondObject.position.set(x, y + 0.75, z);
      secondObject.scale.set(size, size, size);
      meshList.push(secondObject);
      scene.add(secondObject);
    }
    
    //===================================================== Collision Detection
    function PlaySound() {
      bflat.play();
    }
    
    //calculate distance of the main object
    firstBB = new THREE.Box3().setFromObject(group);
    
    //calculate distance for all other objects
    for (var i = 0; i < meshList.length; i++) {
      secondBB = new THREE.Box3().setFromObject(meshList[i]);
    }
    
    var count = 0;
    function hit() {
      //recalculate distance for the main object
      firstBB = new THREE.Box3().setFromObject(group);
      //recalcuate distance for all the other objects
      for (var i = 0; i < meshList.length; i++) {
        secondBB = new THREE.Box3().setFromObject(meshList[i]);
    
        if (firstBB.isIntersectionBox(secondBB)) {
          PlaySound();
          info.style.color = "hsl(" + Math.floor(Math.random() * 290) + ",50%,50%)";
          info.innerHTML =
            Math.random() > 0.25
              ? "Superb!"
              : Math.random() > 0.25 ? "Oustanding!" : "Awesome!";
    
          TweenLite.to(info, 0.75, {
            css: { fontSize: "50px", opacity: 1 },
            ease: Power4.easeOut,
            onComplete: function() {
              TweenLite.to(info, 0.75, {
                css: { fontSize: "14px", opacity: 0 },
                ease: Power4.easeOut
              }); //end tween
            } //end onComplete
          }); //end tween
        } //end if
      } //end for
    } //end hit
    
    //===================================================== bloom
    var renderScene = new THREE.RenderPass(scene, camera);
    var shaderActive = "none";
    var gui = new dat.GUI();
    dat.GUI.toggleHide();
    var composer;
    
    var parameters = {
      x: 0,
      y: 30,
      z: 0,
      bloomStrength: 1.0,
      bloomRadius: 1.0,
      bloomThreshold: 0.45,
      useShaderNone: function() {
        setupShaderNone();
      },
      useShaderBloom: function() {
        setupShaderBloom();
      }
    };
    
    gui.add(parameters, "useShaderNone").name("Display Original Scene");
    
    var folderBloom = gui.addFolder("Bloom");
    var bloomStrengthGUI = folderBloom
      .add(parameters, "bloomStrength")
      .min(0.0)
      .max(2.0)
      .step(0.01)
      .name("Strength")
      .listen();
    bloomStrengthGUI.onChange(function(value) {
      setupShaderBloom();
    });
    var bloomRadiusGUI = folderBloom
      .add(parameters, "bloomRadius")
      .min(0.0)
      .max(5.0)
      .step(0.01)
      .name("Radius")
      .listen();
    bloomRadiusGUI.onChange(function(value) {
      setupShaderBloom();
    });
    var bloomThresholdGUI = folderBloom
      .add(parameters, "bloomThreshold")
      .min(0)
      .max(0.99)
      .step(0.01)
      .name("Threshold")
      .listen();
    bloomThresholdGUI.onChange(function(value) {
      setupShaderBloom();
    });
    folderBloom.add(parameters, "useShaderBloom").name("Use Bloom Shader");
    folderBloom.open();
    
    //===================================================== functions
    
    function setupShaderNone() {
      shaderActive = "none";
    }
    
    function setupShaderBloom() {
      composer = new THREE.EffectComposer(renderer);
      composer.addPass(new THREE.RenderPass(scene, camera));
    
      /*unreal bloom*/
      var effectFXAA = new THREE.ShaderPass(THREE.FXAAShader);
      effectFXAA.uniforms["resolution"].value.set(
        1 / window.innerWidth,
        1 / window.innerHeight
      );
    
      var copyShader = new THREE.ShaderPass(THREE.CopyShader);
      copyShader.renderToScreen = true;
    
      var bloomPass = new THREE.UnrealBloomPass(
        new THREE.Vector2(window.innerWidth, window.innerHeight),
        parameters.bloomStrength,
        parameters.bloomRadius,
        parameters.bloomThreshold
      );
    
      composer = new THREE.EffectComposer(renderer);
      composer.setSize(window.innerWidth, window.innerHeight);
      composer.addPass(renderScene);
      composer.addPass(effectFXAA);
      composer.addPass(bloomPass);
      composer.addPass(copyShader);
      shaderActive = "bloom";
    }
    
    function isShaderActive() {
      if (shaderActive == "none") {
        renderer.render(scene, camera);
      } else {
        composer.render();
      }
    }
    
    //active bloom on load
    setupShaderBloom();
    
    //===================================================== Animate
    var percentage = 0;
    var prevTime = Date.now();
    function POV() {
      percentage += 0.00045;
      var p1 = carPath.getPointAt(percentage % 1);
      var p2 = carPath.getPointAt((percentage + 0.01) % 1);
      var p3 = carPath.getPointAt((percentage + 0.01 / 2) % 1);
      var p4 = carPath.getPointAt((percentage + 0.01 / 4) % 1);
    
      camera.lookAt(p2);
     
    
      group.position.set(p3.x, p3.y + 0.25, p3.z);
      group.lookAt(p2);
      camera.position.x = p4.x + 2;
      camera.position.y = p4.y + 1;
      camera.position.z = p4.z + 2;
      camera.lookAt(group.position);
    }
    
    function animate() {
      POV();
      hit();
      var delta = clock.getDelta();
      if (mixer != null) mixer.update(delta);
    
      //VR
      if (VR) {
        effect.render(scene, camera);
      } else {
        //renderer.render( scene, camera );
        isShaderActive();
      }
    
      requestAnimationFrame(animate);
      controls.update();
    }
    animate();
    
    //set camera position
    camera.position.x = 40;
    camera.position.y = 50;
    camera.position.z = 50;

    更多学习内容:请关注我的知乎专栏    高级前端工程师前端学习教程,从基础到进阶,看完保证让你的薪资上升一个台阶,你也能成为阿里人(持续更新)

  • 相关阅读:
    jquery选择器中逗号的使用
    超大文件上传和断点续传的代码
    超大文件上传和断点续传的源代码
    超大文件上传和断点续传的源码
    超大文件上传和断点续传的控件
    超大文件上传和断点续传的插件
    杨辉三角
    pkg-config的用法
    c++类的实例化,有没有new的区别
    正则表达式中元字符
  • 原文地址:https://www.cnblogs.com/coderhf/p/12917297.html
Copyright © 2020-2023  润新知