马黑黑 发表于 2025-4-30 12:27

宇宙

本帖最后由 马黑黑 于 2025-4-30 21:19 编辑 <br /><br /><style>
        #pa { --state: running; margin: 30px 0; left: calc(50% - 81px); transform: translateX(-50%); width: clamp(600px, 90vw, 1600px); height: auto; aspect-ratio: 16/9; background: url('https://bayimg.com/49690c5dec662f3800fe484eb0f2c75ae1897191.jpg') no-repeat center/cover; box-shadow: 2px 2px 10px rgba(0,0,0,.65); z-index: 1; position: relative; }
        #pa canvas { display: block; }
        #pa canvas { position: absolute; display: block; z-index: 5; }
        #player { position: absolute; left: 20px; top: 20px; z-index: 9; clip-path: circle(45%); transition: filter .7s; cursor: pointer; animation: rot 10s linear infinite var(--state); }
        #player:hover { filter: hue-rotate(90deg); }
        #btnFs { right: 30px; top: 30px; color: #eee; }
        #btnFs:hover { color: red; }
        #pa video { position: absolute; width: 100%; height: 100; mask: radial-gradient(circle, transparent, red); opacity: .5; }
        @keyframes rot { to { transform: rotate(360deg); } }
</style>

<div id="pa">
        <img id="player" src="https://638183.freep.cn/638183/small/mufuz2.jpg" width="10%" title="播放/暂停" />
        <audio id="aud" src="https://music.163.com/song/media/outer/url?id=450041137" autoplay loop></audio>
        <video src="https://bpic.588ku.com/video_listen/588ku_video/23/08/29/17/08/52/video64edb5a4341d3.mp4" autoplay loop muted></video>
</div>

<script type="module">

import * as THREE from "https://esm.sh/three";
import { OrbitControls } from "https://esm.sh/three/examples/jsm/controls/OrbitControls";
import { FS } from 'https://638183.freep.cn/638183/web/ku/fscreen.js';

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

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

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.3);
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();
        requestAnimationFrame(render);
}

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


window.addEventListener("resize", resize);

render();

FS(pa, player);

</script>

马黑黑 发表于 2025-4-30 12:29

本帖最后由 马黑黑 于 2025-4-30 21:20 编辑

帖子代码(基于 three.js 的核心代码作者为 古柳 大佬):
<style>
        #pa { --state: running; margin: 30px 0; left: calc(50% - 81px); transform: translateX(-50%); width: clamp(600px, 90vw, 1600px); height: auto; aspect-ratio: 16/9; background: url('https://bayimg.com/49690c5dec662f3800fe484eb0f2c75ae1897191.jpg') no-repeat center/cover; box-shadow: 2px 2px 10px rgba(0,0,0,.65); z-index: 1; position: relative; }
        #pa canvas { display: block; }
        #pa canvas { position: absolute; display: block; z-index: 5; }
        #player { position: absolute; left: 20px; top: 20px; z-index: 9; clip-path: circle(45%); transition: filter .7s; cursor: pointer; animation: rot 10s linear infinite var(--state); }
        #player:hover { filter: hue-rotate(90deg); }
        #btnFs { right: 30px; top: 30px; color: #eee; }
        #btnFs:hover { color: red; }
        #pa video { position: absolute; width: 100%; height: 100; mask: radial-gradient(circle, transparent, red); opacity: .5; }
        @keyframes rot { to { transform: rotate(360deg); } }
</style>

<div id="pa">
        <img id="player" src="https://638183.freep.cn/638183/small/mufuz2.jpg" width="10%" title="播放/暂停" />
        <audio id="aud" src="https://music.163.com/song/media/outer/url?id=450041137" autoplay loop></audio>
        <video src="https://bpic.588ku.com/video_listen/588ku_video/23/08/29/17/08/52/video64edb5a4341d3.mp4" autoplay loop muted></video>
</div>

<script type="module">

import * as THREE from "https://esm.sh/three";
import { OrbitControls } from "https://esm.sh/three/examples/jsm/controls/OrbitControls";
import { FS } from 'https://638183.freep.cn/638183/web/ku/fscreen.js';

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

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

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.3);
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();
        requestAnimationFrame(render);
}

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


window.addEventListener("resize", resize);

render();

FS(pa, player);

</script>

马黑黑 发表于 2025-4-30 12:31

古柳大佬全名 牛衣古柳,他的展示作品地址:

https://codepen.io/prisoner849/pen/RwyzrVj

马黑黑 发表于 2025-4-30 12:32

three.js 使用 GLSL 渲染,比较吃显卡。如果帖子呈现的粒子不正常,刷新一下。

马黑黑 发表于 2025-4-30 12:36

帖子的粒子系统在画布上渲染出来,它原本是覆盖一切的,帖子背景不可见。

我启用了 alpha: true,,并给 renderer 对象添加一个设置:

renderer.setClearAlpha(0.3);

这样canvas画布之下的帖子容器才得以呈现。设置 为 0 也行,帖子背景就完全呈现了。

马黑黑 发表于 2025-4-30 12:44

粒子整体可以使用鼠标滑轮调整尺寸

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

马黑黑 发表于 2025-4-30 12:44
粒子整体可以使用鼠标滑轮调整尺寸

是的,进来就发现了。可以无限变大变小呢。太神奇了{:4_199:}

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

黑黑又带来了奇妙的效果,背景配得特别好,还让背景得到了显示。
如此浩渺的无限宇宙{:4_199:}

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

想起黑黑以前带来的太空蜘蛛等好几个大佬的设计,真的很让人开眼界呢{:4_187:}

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

马黑黑 发表于 2025-4-30 12:44
粒子整体可以使用鼠标滑轮调整尺寸

暂停的时候尺寸也不能调整了{:4_173:}

梦江南 发表于 2025-4-30 15:07

我看不到画面

杨帆 发表于 2025-4-30 15:23

开眼界、长见识了,谢谢马老师带来的大佬级分享{:4_191:}

小辣椒 发表于 2025-4-30 16:35

大气磅礴,宇宙之巅,美不胜收{:4_178:}

小辣椒 发表于 2025-4-30 16:36

自己去电脑预览换背景看看效果{:4_205:}

马黑黑 发表于 2025-4-30 17:00

梦江南 发表于 2025-4-30 15:07
我看不到画面

需要浏览器支持,太低版本的浏览器无法支持 three.js 3d 场景的渲染

梦江南 发表于 2025-4-30 17:45

马黑黑 发表于 2025-4-30 17:00
需要浏览器支持,太低版本的浏览器无法支持 three.js 3d 场景的渲染

现在能看到了,全都是粒子组成的。

马黑黑 发表于 2025-4-30 20:53

梦江南 发表于 2025-4-30 17:45
现在能看到了,全都是粒子组成的。

有背景图、有视频、有音频控制按钮

马黑黑 发表于 2025-4-30 20:53

小辣椒 发表于 2025-4-30 16:36
自己去电脑预览换背景看看效果

这个应该需要本地虚拟服务器程序支持。three.js 有一些功能相对独特

马黑黑 发表于 2025-4-30 20:54

小辣椒 发表于 2025-4-30 16:35
大气磅礴,宇宙之巅,美不胜收

现在加了个视频,还加了全屏按钮、键盘控制

马黑黑 发表于 2025-4-30 20:54

杨帆 发表于 2025-4-30 15:23
开眼界、长见识了,谢谢马老师带来的大佬级分享

{:4_190:}
页: [1] 2 3 4
查看完整版本: 宇宙