杨帆 发表于 2025-4-30 19:47

学习马老师的《宇宙》代码

本帖最后由 杨帆 于 2025-5-1 17:29 编辑 <br /><br /><!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>宇宙天体</title>
</head>
<body>
    <style>
      #pa {
            --state: running;
            margin: 30px 0;
            left: calc(50% - 81px);
            transform: translateX(-50%);
            width: clamp(600px, 90vw, 1400px);
            height:750px;
            aspect-ratio: 16/9;
            background: url('https://cccimg.com/view.php/3d0eb4b2ff23f9071ec9c181368a9e89.jpg') no-repeat center/cover;
            box-shadow: 2px 2px 10px rgba(0,0,0,.65);
            z-index: 1;
            position: relative;
      }
      #pa canvas {
            display: block;
      }
      #player {
            position: absolute;
            left: 20px;
            top: 20px;
            z-index: 9;
            clip-path: circle(45%);
            transition: filter .7s;
            cursor: pointer;
            animation: rot 20s linear infinite var(--state);
      }
      #player:hover {
            filter: hue-rotate(90deg);
      }
      @keyframes rot {
            to {
                transform: rotate(360deg);
            }
      }

   #fullscreen { position: absolute; top:5%; left:88%;color:#ffffff; filter:drop-shadow( 1px 1px 1px #000000);font: normal 2.1em华文新魏; opacity: 0; cursor: pointer; z-index: 10}
   #pa:hover #fullscreen{ color: #DC143C;opacity: 1;}
   #vid1 {
            position: absolute;
            left: 0px;
            top: 0px;
            width: 100%;
            height: 100%;
            mix-blend-mode: screen;
            -webkit-mask: linear-gradient(to right top, red 0%, transparent 90%, transparent);
            opacity: 0.8;   
            object-fit: cover;
            z-index: -1;
      }
      #vid2 {
            position: absolute;
            left: 0px;
            top: 0px;
            width: 100%;
            height: 100%;
            mix-blend-mode: screen;
            -webkit-mask: radial-gradient(transparent 20%, red);
            opacity: 0.8;   
            object-fit: cover;
            z-index: -1;
      }
    </style>

    <div id="pa">
      <img id="player" src="https://cccimg.com/view.php/b40d9865baa914789669dbe9f4f2ee42.png" width="10%" title="播放/暂停" />
      <audio id="aud" src="https://cccimg.com/view.php/e789ee539d15a3bac9ad56235ebf58b3.mp3" autoplay loop></audio>
      <video id="vid1" src="https://video.699pic.com/videos/80/78/90/a_gXPEAH4VSuId1554807890_10s.mp4" loop muted></video>
      <video id="vid2" src="https://bpic.588ku.com/video_listen/588ku_video/22/11/03/11/48/17/video63633a01b9973.mp4" loop muted></video>
      <span id="fullscreen">全屏观赏</span>
</div>

    <script type="module">
      import * as THREE from "https://esm.sh/three";
      import { OrbitControls } from "https://esm.sh/three/examples/jsm/controls/OrbitControls";

      const aud = document.querySelector("#aud");
      const player = document.querySelector("#player");
      const pa = document.querySelector("#pa");
      const vid1 = document.querySelector("#vid1");
      const vid2 = document.querySelector("#vid2");

      let w = pa.offsetWidth;
      let h = pa.offsetHeight;
      let raf;

      const scene = new THREE.Scene();

      const camera = new THREE.PerspectiveCamera(60, w / h, 0.001, 1000);
      camera.position.set(0, 3, 24);
      camera.lookAt(scene.position);

      const renderer = new THREE.WebGLRenderer({
            antialias: true,
            alpha: true,
      });
      renderer.setPixelRatio(window.devicePixelRatio);
      renderer.setSize(w, h);
      renderer.setClearAlpha(0.1);
      pa.appendChild(renderer.domElement);

      const controls = new OrbitControls(camera, renderer.domElement);
      const count1 = 50000;
      const count2 = 100000;

      const geometry = new THREE.BufferGeometry();
      const positions = [];
      const sizes = [];
      const shifts = [];
      for (let i = 0; i < count1 + count2; i++) {
            let theta = Math.random() * Math.PI * 2;
            let phi = Math.acos(Math.random() * 2 - 1);
            let angle = (Math.random() * 0.9 + 0.1) * Math.PI * 0.1;
            let strength = Math.random() * 0.9 + 0.1;
            shifts.push(theta, phi, angle, strength);

            let size = Math.random() * 1.5 + 0.5;
            sizes.push(size);

            if (i < count1) {
                let r = Math.random() * 0.5 + 9.5;
                let { x, y, z } = new THREE.Vector3()
                  .randomDirection()
                  .multiplyScalar(r);
                positions.push(x, y, z);
            } else {
                let r = 10;
                let R = 40;
                let rand = Math.pow(Math.random(), 1.5);
                let radius = Math.sqrt(R * R * rand + (1 - rand) * r * r);
                let { x, y, z } = new THREE.Vector3().setFromCylindricalCoords(
                  radius,
                  Math.random() * 2 * Math.PI,
                  (Math.random() - 0.5) * 2
                );
                positions.push(x, y, z);
            }
      }

      geometry.setAttribute(
            "position",
            new THREE.Float32BufferAttribute(positions, 3)
      );
      geometry.setAttribute("aSize", new THREE.Float32BufferAttribute(sizes, 1));
      geometry.setAttribute("aShift", new THREE.Float32BufferAttribute(shifts, 4));

      const vertexShader = `
            attribute float aSize;
            attribute vec4 aShift;

            uniform float uTime;

            varying vec3 vColor;

            const float PI = 3.141592653589793238;

            void main() {
                float d = length(abs(position) / vec3(40., 10., 40.));
                d = clamp(d, 0., 1.);
                vec3 color1 = vec3(227., 155., 0.);
                vec3 color2 = vec3(100., 50., 255.);

                vColor = mix(color1, color2, d) / 255.;

                vec3 transformed = position;

                float theta = mod(aShift.x + aShift.z * uTime, PI * 2.);
                float phi = mod(aShift.y + aShift.z * uTime, PI * 2.);
                transformed += vec3(sin(phi) * cos(theta), cos(phi), sin(phi) * sin(theta)) * aShift.w;

                vec4 mvPosition = modelViewMatrix * vec4(transformed, 1.0);
                gl_PointSize = aSize * 50.0 / -mvPosition.z;
                gl_Position = projectionMatrix * mvPosition;
            }
      `;

      const fragmentShader = `
            varying vec3 vColor;
            void main() {
                float d = length(gl_PointCoord.xy - 0.5);
                if (d > 0.5) discard;
                gl_FragColor = vec4(vColor, smoothstep(0.5, 0.1, d));
            }
      `;

      const material = new THREE.ShaderMaterial({
            vertexShader,
            fragmentShader,
            uniforms: {
                uTime: { value: 0 },
            },
            transparent: true,
            blending: THREE.AdditiveBlending,
            depthTest: false,
      });

      const mesh = new THREE.Points(geometry, material);
      mesh.rotation.order = "ZYX";
      mesh.rotation.z = 0.2;
      scene.add(mesh);

      let clock = new THREE.Clock();

      function render() {
            let time = clock.getElapsedTime();
            mesh.rotation.y = time * 0.01;
            material.uniforms.uTime.value = time;
            renderer.render(scene, camera);
            controls.update();
            raf = requestAnimationFrame(render);
      }

      function resize() {
            w = pa.offsetWidth;
            h = pa.offsetHeight;
            renderer.setSize(w, h);
            camera.aspect = w / h;
            camera.updateProjectionMatrix();
      }

      function mState() {
            if (aud.paused) {
                cancelAnimationFrame(raf);
                pa.style.setProperty("--state", "paused");
                vid1.pause();
                vid2.pause();
            } else {
                render();
                pa.style.setProperty("--state", "running");
                vid1.play().catch(e => console.error('Error playing video 1:', e));
                vid2.play().catch(e => console.error('Error playing video 2:', e));
            }
      }

      function play() {
            aud.paused ? aud.play().catch(e => console.error('Error playing audio:', e)) : aud.pause();
      }

      aud.addEventListener("playing", mState);
      aud.addEventListener("pause", mState);
      window.addEventListener("resize", resize);
      player.addEventListener("click", play);

    let fs = true;
      fullscreen.onclick = () => {
                fs ? (fullscreen.innerText = '退出全屏',pa.requestFullscreen()) : (fullscreen.innerText = '全屏欣赏', document.exitFullscreen());
                fs = !fs;
      };
</script>
</body>
</html>

红影 发表于 2025-4-30 20:12

这背景图也是黑黑的?哦,杨帆加了视频,更漂亮了{:4_187:}

红影 发表于 2025-4-30 20:13

欣赏杨帆好帖{:4_187:}

梦江南 发表于 2025-5-1 10:09

佳作欣赏,祝五一节快乐!{:4_199:}

杨帆 发表于 2025-5-1 14:36

红影 发表于 2025-4-30 20:12
这背景图也是黑黑的?哦,杨帆加了视频,更漂亮了

是的,特喜欢马老师的大佬级分享,祝五一快乐{:4_204:}

杨帆 发表于 2025-5-1 14:37

梦江南 发表于 2025-5-1 10:09
佳作欣赏,祝五一节快乐!

欢迎江南观赏,祝五一快乐{:4_187:}

红影 发表于 2025-5-1 16:33

杨帆 发表于 2025-5-1 14:36
是的,特喜欢马老师的大佬级分享,祝五一快乐

谢谢扬帆,也祝你节日快乐{:4_187:}

小辣椒 发表于 2025-5-1 16:38

问好杨帆,你把全屏欣赏去了就可惜了,这个宇宙效果最让人震撼的就是全屏欣赏

杨帆 发表于 2025-5-1 17:26

小辣椒 发表于 2025-5-1 16:38
问好杨帆,你把全屏欣赏去了就可惜了,这个宇宙效果最让人震撼的就是全屏欣赏

谢谢小辣椒,幸好可以加上。祝五一快乐{:4_187:}
页: [1]
查看完整版本: 学习马老师的《宇宙》代码