показать переход 3D сцены при прокрутке на одном холсте, threejs - PullRequest
0 голосов
/ 08 мая 2020

Я новичок в обращении с трехмерными объектами, а точнее с тремя JS. Я создал холст с 3 сценами, используя this , поэтому в основном мой проект состоит из 3 сцен, которые принимают полную ширину и высоту окна просмотра. Я хотел получить этот эффект прокрутки , но когда я добавляю элементы сцены в этот демонстрационный файл с прокруткой, возникают его ошибки бросания, поэтому я застрял в этом. Как еще можно это сделать? Будем очень признательны за любые указатели

function main() {
  var textureLoader,
    cloudMesh,
    starGeo,
    stars,
    light,
    camera,
    cloudParticles = [];
  var canvas = $("#c")[0];
  var renderer = new THREE.WebGLRenderer({
    canvas,
    alpha: true,
    antialias: true
  });

  const sceneElements = [];

  // add scene
  function addScene(elem, fn) {
    sceneElements.push({ elem, fn });
  }

  // make scene
  function makeScene() {
    var scene = new THREE.Scene();
    const fov = 55;
    const aspect = 2;
    const near = 0.1;
    const far = 5;

    camera = new THREE.PerspectiveCamera(
      75,
      window.innerWidth / window.innerHeight,
      1,
      1000
    );
    camera.position.z = 1;
    camera.rotation.x = 1.16;
    camera.rotation.y = -0.12;
    camera.rotation.z = 0.27;

    // create directional light
    const color = "white";
    const intensity = 1;
    light = new THREE.DirectionalLight(color, intensity);
    light.position.set(-1, 2, 4);
    scene.add(light);

    return { scene, camera };
  }

  {
    const elem = $("#ocean_box")[0];
    const { scene, camera } = makeScene();
    var waterGeometry = new THREE.PlaneBufferGeometry(10000, 10000);

    water = new THREE.Water(waterGeometry, {
      textureWidth: 512,
      textureHeight: 512,
      waterNormals: new THREE.TextureLoader().load(
        "images/waternormals.jpg",
        function (texture) {
          texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
        }
      ),
      alpha: 1.0,
      sunDirection: light.position.clone().normalize(),
      sunColor: 0xffffff,
      waterColor: 0x001e0f,
      distortionScale: 3.7,
      fog: scene.fog !== undefined
    });

    water.rotation.x = -Math.PI / 2;

    scene.add(water);

    // clouds
    const objLoader = new THREE.OBJLoader();
    objLoader.setPath("/blender_files/");

    const mtlLoader = new THREE.MTLLoader();
    mtlLoader.setPath("/blender_files/");

    new Promise(resolve => {
      mtlLoader.load("Cloud.mtl", material => {
        resolve(material);
      });
    }).then(material => {
      material.preload();
      objLoader.setMaterials(material);
      objLoader.load("Cloud.obj", cubeObj => {
        cube = cubeObj;
        cube.scale.set(5, 5, 5);
        cube.position.set(-1.5, 100, 100);
        // cube.addColor("white");

        // scene.add(cube);
      });
    });

    textureLoader = new THREE.TextureLoader();
    textureLoader.load("images/smoke.png", function (texture) {
      cloudGeo = new THREE.PlaneGeometry(150, 150);
      cloudMaterial = new THREE.MeshLambertMaterial({
        map: texture,
        transparent: true,
        emissive: "white"
      });

      // create lots of clouds
      for (let i = 0; i < 100; i++) {
        cloudMesh = new THREE.Mesh(cloudGeo, cloudMaterial);
        cloudMesh.position.set(
          Math.random() * 800 - 500,
          200,
          Math.random() * 800 - 500
        );
        // cloudMesh.rotation.x = 1.16;
        // cloudMesh.rotation.y = -0.2;
        // cloudMesh.rotation.z = Math.random() * 2 * Math.PI;
        cloudMesh.material.opacity = 0.5;
        cloudParticles.push(cloudMesh);
        scene.add(cloudMesh);
      }
      scene.add(cloudMesh);
    });

    var sky = new THREE.Sky();

    var uniforms = sky.material.uniforms;

    uniforms["turbidity"].value = 10;
    uniforms["rayleigh"].value = 2;
    uniforms["luminance"].value = 1;
    uniforms["mieCoefficient"].value = 0.005;
    uniforms["mieDirectionalG"].value = 0.8;

    var parameters = {
      distance: 400,
      inclination: 0.4158,
      azimuth: 0.215
    };

    var cubeCamera = new THREE.CubeCamera(0.1, 1, 512);
    cubeCamera.renderTarget.texture.generateMipmaps = true;
    cubeCamera.renderTarget.texture.minFilter = THREE.LinearMipmapLinearFilter;

    scene.background = cubeCamera.renderTarget;

    function updateSun() {
      var theta = Math.PI * (parameters.inclination - 0.5);
      var phi = 2 * Math.PI * (parameters.azimuth - 0.5);

      light.position.x = parameters.distance * Math.cos(phi);
      light.position.y = parameters.distance * Math.sin(phi) * Math.sin(theta);
      light.position.z = parameters.distance * Math.sin(phi) * Math.cos(theta);

      sky.material.uniforms["sunPosition"].value = light.position.copy(
        light.position
      );
      water.material.uniforms["sunDirection"].value
        .copy(light.position)
        .normalize();

      cubeCamera.update(renderer, sky);
    }

    updateSun();

    controls = new THREE.OrbitControls(camera, renderer.domElement);
    controls.maxPolarAngle = Math.PI * 0.495;
    controls.target.set(0, 10, 0);
    controls.minDistance = 10.0;
    controls.maxDistance = 200.0;
    controls.update();

    addScene(elem, (time, rect) => {
      camera.aspect = rect.width / rect.height;
      camera.updateProjectionMatrix();
      // cloudMesh.rotation.y = time * 0.1;
      cloudParticles.forEach(p => {
        // p.rotation.z -= 0.001;
      });
      renderer.render(scene, camera);
    });
  }

  {
    // const elem = $("#nebula_box")[0];
    // const { scene, camera } = makeScene();
    // scene.background = new THREE.Color("#b6e1fc");
    // console.log("scene", scene);
    // scene.fog = new THREE.FogExp2("#87cefa", 0.001);
    // renderer.setClearColor(scene.fog.color);
    // // create a loader for loading smoke effect
    // textureLoader = new THREE.TextureLoader();
    // textureLoader.load("images/smoke.png", function (texture) {
    //   console.log(texture);
    //   cloudGeo = new THREE.PlaneGeometry(500, 500);
    //   cloudMaterial = new THREE.MeshLambertMaterial({
    //     map: texture,
    //     emissive: "white",
    //     transparent: true
    //   });
    //   // create lots of clouds
    //   for (let i = 0; i < 30; i++) {
    //     cloudMesh = new THREE.Mesh(cloudGeo, cloudMaterial);
    //     cloudMesh.position.set(
    //       Math.random() * 800 - 500,
    //       200,
    //       Math.random() * 800 - 500
    //     );
    //     cloudMesh.rotation.x = 1.16;
    //     cloudMesh.rotation.y = -0.12;
    //     cloudMesh.rotation.z = Math.random() * 2 * Math.PI;
    //     cloudMesh.material.opacity = 0.55;
    //     cloudParticles.push(cloudMesh);
    //     scene.add(cloudMesh);
    //   }
    //   scene.add(cloudMesh);
    //   // add lighting
    //   let directionalLight = new THREE.DirectionalLight(0xff8c19);
    //   directionalLight.position.set(0, 0, 1);
    //   scene.add(directionalLight);
    //   // now add differet point lights
    //   // blue light
    //   let blueLight = new THREE.PointLight(0x3677ac, 50, 450, 1.7);
    //   blueLight.position.set(300, 300, 200);
    //   scene.add(blueLight);
    //   // red light
    //   let greyLight = new THREE.PointLight("#808080", 50, 450, 1.7);
    //   greyLight.position.set(100, 300, 100);
    //   scene.add(greyLight);
    //   // orange light
    //   let orangeLight = new THREE.PointLight("#808080", 50, 450, 1.7);
    //   orangeLight.position.set(200, 300, 100);
    //   scene.add(orangeLight);
    //   addScene(elem, (time, rect) => {
    //     camera.aspect = rect.width / rect.height;
    //     camera.updateProjectionMatrix();
    //     // cloudMesh.rotation.y = time * 0.1;
    //     cloudParticles.forEach(p => {
    //       p.rotation.z -= 0.01;
    //     });
    //     renderer.render(scene, camera);
    //   });
    // });
  }

  // cloud
  {
    const elem = $("#cloud_box")[0];
    const { scene, camera } = makeScene();
    scene.background = new THREE.Color("#b6e1fc");

    // scene.fog = new THREE.FogExp2("#87cefa", 0.001);
    // renderer.setClearColor(scene.fog.color);

    // create a loader for loading smoke effect
    textureLoader = new THREE.TextureLoader();

    textureLoader.load("images/smoke.png", function (texture) {
      cloudGeo = new THREE.PlaneGeometry(100, 100);
      cloudMaterial = new THREE.MeshLambertMaterial({
        map: texture,
        emissive: "white",
        transparent: true
      });

      // create lots of clouds
      for (let i = 0; i < 800; i++) {
        cloudMesh = new THREE.Mesh(cloudGeo, cloudMaterial);
        cloudMesh.position.set(
          Math.random() * 900 - 400,
          300,
          Math.random() * 900 - 500
        );
        cloudMesh.rotation.x = 1.16;
        cloudMesh.rotation.y = -0.12;
        cloudMesh.rotation.z = Math.random() * 9 * Math.PI;
        cloudMesh.material.opacity = 0.9;
        cloudParticles.push(cloudMesh);
        scene.add(cloudMesh);
      }
      scene.add(cloudMesh);

      // add lighting
      let directionalLight = new THREE.DirectionalLight(0xff8c19);
      directionalLight.position.set(0, 0, 1);
      scene.add(directionalLight);

      // now add differet point lights

      // blue light
      let blueLight = new THREE.PointLight(0x3677ac, 50, 450, 1.7);
      blueLight.position.set(300, 300, 200);
      // scene.add(blueLight);

      addScene(elem, (time, rect) => {
        camera.aspect = rect.width / rect.height;
        camera.updateProjectionMatrix();
        // cloudMesh.rotation.y = time * 0.1;
        cloudParticles.forEach(p => {
          p.rotation.z -= 0.02;
        });
        renderer.render(scene, camera);
      });
    });
  }

  {
    const elem = $("#star_space_box")[0];
    const { scene, camera } = makeScene();
    scene.background = new THREE.Color("black");

    // create Geometery
    starGeo = new THREE.Geometry();
    for (i = 0; i < 6000; i++) {
      star = new THREE.Vector3(
        Math.random() * 800 - 500,
        Math.random() * 800 - 500,
        Math.random() * 800 - 500
      );

      // add animation
      star.velocity = 0;
      star.acceleration = 0.002;
      starGeo.vertices.push(star);
    }

    var sprite = new THREE.TextureLoader().load("images/star.jpg");
    var starMaterial = new THREE.PointsMaterial({
      color: "yellow",
      size: 0.9,
      map: sprite
    });

    stars = new THREE.Points(starGeo, starMaterial);
    scene.add(stars);

    addScene(elem, (time, rect) => {
      camera.aspect = rect.width / rect.height;
      camera.updateProjectionMatrix();
      // cloudMesh.rotation.y = time * 0.1;
      starGeo.vertices.forEach(p => {
        p.velocity += p.acceleration;
        p.y -= p.velocity;

        if (p.y < -200) {
          p.y = 200;
          p.velocity = 0;
        }
      });
      starGeo.verticesNeedUpdate = true;
      renderer.render(scene, camera);
    });
  }

  console.log(sceneElements);

  function resizeRendererToDisplaySize(renderer) {
    const canvas = renderer.domElement;
    const width = canvas.clientWidth;
    const height = canvas.clientHeight;
    const needResize = canvas.width !== width || canvas.height !== height;
    if (needResize) {
      renderer.setSize(width, height, false);
    }
    return needResize;
  }

  // now render
  function render(time) {
    time *= 0.001;

    // this is to allow object to transform along with the scrolling
    const transform = "translateY(" + window.scrollY + "px)";

    renderer.domElement.style.transform = transform;

    resizeRendererToDisplaySize(renderer);

    renderer.setScissorTest(false);
    renderer.setClearColor("black", 0);
    renderer.clear(true, true);
    renderer.setScissorTest(true);

    // animate water
    water.material.uniforms["time"].value += 1.0 / 60.0;

    // loop the sceneElements

    sceneElements.forEach((sceneObj, index) => {
      console.log("index", index);

      const { elem, fn } = sceneObj;

      // this fetch top, right, width, height etc of that elem
      const cube = elem.getBoundingClientRect();
      const { left, right, top, bottom, width, height } = cube;

      // check if the cube is offscreen
      const isOffScreen =
        bottom < 0 ||
        top > renderer.domElement.clientHeight ||
        right < 0 ||
        left > renderer.domElement.clientWidth;

      // if it is not offscreen
      if (!isOffScreen) {
        const positiveYUpBottom = renderer.domElement.clientHeight - bottom;
        renderer.setScissor(left, positiveYUpBottom, width, height);
        renderer.setViewport(left, positiveYUpBottom, width, height);
        fn(time, cube);
      }
    });

    // request animation
    requestAnimationFrame(render);
  }
  requestAnimationFrame(render);
}

main();
#c {
  position: absolute;
  left: 0;
  top: 0;
  width: 100vw;
  height: 100vh;
  display: block;
  z-index: -1;
}
.diagram {
  width: 100%;
  height: 100%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<html>
  <head>
    <title>183degrees</title>
    <!-- The default layout, resets and and text styling -->
    <link href="stylesheets/main.css" rel="stylesheet" />
    <!-- Custom styling -->
    <link href="stylesheets/scroll.css" rel="stylesheet" />
    <link href="stylesheets/custom.css" rel="stylesheet" />
  </head>
  <body>
    <canvas id="c"></canvas>
    <div id="ocean_box" class="diagram"></div>
    <div id="cloud_box" class="diagram"></div>
    <div id="star_space_box" class="diagram"></div>

    <script src="./javascripts/jquery-3.5.0.min.js"></script>
    <!-- <script src="./javascripts/rellax.min.js"></script> -->
    <script src="javascripts/three.min.js"></script>
    <script src="javascripts/postprocessing.min.js"></script>
    <script src="javascripts/Water.js"></script>
    <script src="javascripts/OrbitControls.js"></script>
    <script src="javascripts/Sky.js"></script>
    <script src="javascripts/OBJLoader.js"></script>
    <script src="javascripts/MTLLoader.js"></script>
    

    <script src="./javascripts/animation.js"></script>

    
  </body>
</html>

Я использовал приведенный выше код, я просто хочу добавить в него эффект прокрутки.

...