Three.js 着色器驱动的 3D Vortex

2025-05-27 0 933

使用 Three.js 的 WebGL 小实验。一个发光的 3D 漩涡,带有脉动的圆环、旋转的球体和动态粒子轨迹,由 Three.js、WebGL 和 GLSL 着色器提供支持,具有泛光、GPU 动画和交互式 OrbitControls。

Three.js 着色器驱动的 3D Vortex
实现代码
<style>    * {        margin: 0;        padding: 0;        overflow: hidden;        background-color: #000;    }    canvas {        width: 100%;        height: 100vh;        display: block;        position: fixed;        top: 50%;        left: 50%;        transform: translate(-50%, -50%);    }</style>
<script type="importmap">{    "imports": {        "three""https://unpkg.com/three@0.162.0/build/three.module.js",        "three/addons/controls/OrbitControls.js""https://unpkg.com/three@0.162.0/examples/jsm/controls/OrbitControls.js",        "three/addons/postprocessing/EffectComposer.js""https://unpkg.com/three@0.162.0/examples/jsm/postprocessing/EffectComposer.js",        "three/addons/postprocessing/RenderPass.js""https://unpkg.com/three@0.162.0/examples/jsm/postprocessing/RenderPass.js",        "three/addons/postprocessing/UnrealBloomPass.js""https://unpkg.com/three@0.162.0/examples/jsm/postprocessing/UnrealBloomPass.js"    }}</script>
<script type="module">import * as THREE from 'three';import { OrbitControls } from 'three/addons/controls/OrbitControls.js';import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js';
const scene new THREE.Scene();const camera new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.11000);const renderer new THREE.WebGLRenderer({ antialiastrue, alphatrue });renderer.setSize(window.innerWidth, window.innerHeight);renderer.setPixelRatio(window.devicePixelRatio);document.body.appendChild(renderer.domElement);
const controls new OrbitControls(camera, renderer.domElement);controls.enableDamping = true;controls.dampingFactor = 0.05;camera.position.set(0520);controls.update();
const nebulaGeometry new THREE.SphereGeometry(1006464);const nebulaMaterial new THREE.ShaderMaterial({    uniforms: {        time: { value0 }    },    vertexShader: `        varying vec3 vPosition;        void main() {            vPosition = position;            gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);        }    `,    fragmentShader: `        uniform float time;        varying vec3 vPosition;        float noise(vec3 p) {            return fract(sin(dot(p, vec3(127.1311.774.7))) * 43758.5453);        }        void main() {            vec3 p = normalize(vPosition) * 10.0;            float n = noise(p + vec3(time * 0.1));            vec3 color = mix(vec3(0.20.10.4), vec3(0.10.40.3), n);            float glow = pow(1.0 - length(vPosition) / 100.02.0);            gl_FragColor = vec4(color * glow, 0.3);        }    `,    side: THREE.BackSide,    transparenttrue,    blending: THREE.AdditiveBlending});const nebula new THREE.Mesh(nebulaGeometry, nebulaMaterial);scene.add(nebula);
const torusGeometry new THREE.TorusGeometry(61.532100);const torusMaterial new THREE.ShaderMaterial({    uniforms: {        time: { value0 }    },    vertexShader: `        varying vec3 vNormal;        varying vec3 vPosition;        uniform float time;        void main() {            vNormal = normal;            vPosition = position;            vec3 pos = position;            pos *= 1.0 + sin(time * 2.0 + length(position)) * 0.15;            gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);        }    `,    fragmentShader: `        uniform float time;        varying vec3 vNormal;        varying vec3 vPosition;        void main() {            float glow = sin(time * 2.5 + vPosition.x * 1.5) * 0.5 + 0.5;            vec3 baseColor = vec3(1.00.20.8);            vec3 pulseColor = vec3(0.40.11.0);            vec3 color = mix(baseColor, pulseColor, glow);            float edge = smoothstep(0.40.6abs(vNormal.z));            gl_FragColor = vec4(color * edge, 1.0);        }    `,    side: THREE.DoubleSide});const torusCore new THREE.Mesh(torusGeometry, torusMaterial);scene.add(torusCore);
const innerSphereGeometry new THREE.SphereGeometry(23232);const innerSphereMaterial new THREE.ShaderMaterial({    uniforms: {        time: { value0 }    },    vertexShader: `        varying vec3 vNormal;        varying vec2 vUv;        void main() {            vNormal = normal;            vUv = uv;            gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);        }    `,    fragmentShader: `        uniform float time;        varying vec3 vNormal;        varying vec2 vUv;
        void main() {            vec2 uv = vUv;            float t = time * 2.0;
            float pattern = sin(uv.x * 20.0 + t) * sin(uv.y * 20.0 - t);            pattern += sin(uv.x * 15.0 - t * 1.5) * sin(uv.y * 15.0 + t * 1.5) * 0.5;
            float fresnel = pow(1.0 - abs(dot(vNormal, vec3(0.00.01.0))), 2.0);
            vec3 baseColor = vec3(0.40.11.0);            vec3 glowColor = vec3(1.00.20.8);            vec3 finalColor = mix(baseColor, glowColor, pattern);
            float alpha = (pattern * 0.5 + fresnel * 0.7) * 0.8;            gl_FragColor = vec4(finalColor, alpha);        }    `,    transparent: true,    blending: THREE.AdditiveBlending});const innerSphere new THREE.Mesh(innerSphereGeometry, innerSphereMaterial);scene.add(innerSphere);
const sphereCount 12;const spheres = [];const trails = [];const sphereGeometry new THREE.SphereGeometry(0.83232);const sphereMaterial new THREE.ShaderMaterial({    uniforms: {        time: { value0 }    },    vertexShader: `        varying vec3 vPosition;        void main() {            vPosition = position;            gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);        }    `,    fragmentShader: `        uniform float time;        varying vec3 vPosition;        void main() {            float pulse = sin(time * 4.0 + vPosition.y * 2.0) * 0.5 + 0.5;            vec3 color = vec3(0.10.80.2) * (pulse * 0.9 + 0.4);            float edge = smoothstep(0.30.5length(vPosition) / 0.8);            gl_FragColor = vec4(color * edge, 0.9);        }    `,    transparent: true,    blending: THREE.AdditiveBlending});
for (let i = 0; i < sphereCount; i++) {    const sphere new THREE.Mesh(sphereGeometry, sphereMaterial);    const trailGeometry new THREE.BufferGeometry();    const trailPositions new Float32Array(100 * 3);    const trailMaterial new THREE.LineBasicMaterial({         color0x33ff66        transparenttrue        opacity0.3,        blending: THREE.AdditiveBlending     });    const trail new THREE.Line(trailGeometry, trailMaterial);
    sphere.userData = {        orbitAngle: Math.random() * Math.PI * 2,        orbitSpeed: 0.05 + Math.random() * 0.03,        orbitRadiusX: 10 + Math.random() * 4,        orbitRadiusZ: 8 + Math.random() * 3,        trailPositions: trailPositions,        trailIndex: 0    };    spheres.push(sphere);    trails.push(trail);    scene.add(sphere);    scene.add(trail);}
const vortexCount 600;const vortices new THREE.Group();const vortexGeometry new THREE.BufferGeometry();const vortexPositions new Float32Array(vortexCount * 3);const vortexColors new Float32Array(vortexCount * 3);const vortexSizes new Float32Array(vortexCount);for (let i = 0; i < vortexCount; i++) {    vortexPositions[i * 3] = 0;    vortexPositions[i * 3 + 1] = 0;    vortexPositions[i * 3 + 2] = 0;    const color new THREE.Color().setHSL(Math.random(), 0.70.6);    vortexColors[i * 3] = color.r;    vortexColors[i * 3 + 1] = color.g;    vortexColors[i * 3 + 2] = color.b;    vortexSizes[i] = 0.3 + Math.random() * 0.2;}vortexGeometry.setAttribute('position'new THREE.BufferAttribute(vortexPositions, 3));vortexGeometry.setAttribute('customColor'new THREE.BufferAttribute(vortexColors, 3));vortexGeometry.setAttribute('size'new THREE.BufferAttribute(vortexSizes, 1));const vortexMaterial new THREE.ShaderMaterial({    uniforms: {        time: { value0 }    },    vertexShader: `        attribute vec3 customColor;        attribute float size;        varying vec3 vColor;        varying float vAlpha;        void main() {            vColor = customColor;            vAlpha = 1.0 - length(position) / 20.0;            gl_PointSize = size * (400.0 / -modelViewMatrix[3][2]);            gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);        }    `,    fragmentShader: `        uniform float time;        varying vec3 vColor;        varying float vAlpha;        void main() {            vec2 uv = gl_PointCoord - 0.5;            float dist = length(uv);            if (dist > 0.5) discard;            float shimmer = sin(time * 8.0 + dist * 15.0) * 0.3 + 0.7;            float glow = exp(-dist * 5.0);            gl_FragColor = vec4(vColor * shimmer, glow * vAlpha * 0.8);        }    `,    transparent: true,    blending: THREE.AdditiveBlending});const vortexSystem new THREE.Points(vortexGeometry, vortexMaterial);vortices.add(vortexSystem);scene.add(vortices);
const vortexData Array(vortexCount).fill().map(() => ({    angle: Math.random() * Math.PI * 2,    speed: 0.06 + Math.random() * 0.04,    distance: 0,    maxDistance: 15 + Math.random() * 5,    twist: 0.4 + Math.random() * 0.3}));
const ambientLight new THREE.AmbientLight(0xffffff0.4);scene.add(ambientLight);const pointLight1 new THREE.PointLight(0xffe6ff2.0100);pointLight1.position.set(151515);scene.add(pointLight1);const pointLight2 new THREE.PointLight(0xe6ffec1.6100);pointLight2.position.set(-15, -15, -15);scene.add(pointLight2);const pointLight3 new THREE.PointLight(0xe6f0ff1.2100);pointLight3.position.set(020, -10);scene.add(pointLight3);
const composer new EffectComposer(renderer);const renderPass new RenderPass(scene, camera);composer.addPass(renderPass);const bloomPass new UnrealBloomPass(new THREE.Vector2(window.innerWidth, window.innerHeight), 1.40.60.85);bloomPass.threshold = 0.2;bloomPass.strength = 1.6;bloomPass.radius = 0.6;composer.addPass(bloomPass);
let time = 0;function animate() {    requestAnimationFrame(animate);    time += 0.01;
    nebulaMaterial.uniforms.time.value = time;
    torusCore.rotation.y += 0.005;    torusCore.rotation.z += 0.003;    torusMaterial.uniforms.time.value = time;
    innerSphereMaterial.uniforms.time.value = time;
    spheres.forEach((sphere, i) => {        const data = sphere.userData;        data.orbitAngle += data.orbitSpeed;        sphere.position.x = Math.cos(data.orbitAngle) * data.orbitRadiusX;        sphere.position.z = Math.sin(data.orbitAngle) * data.orbitRadiusZ;        sphere.position.y = Math.sin(data.orbitAngle * 1.5 + time) * 3;        sphere.material.uniforms.time.value = time;
        const trail = trails[i];        const pos = data.trailPositions;        const idx = data.trailIndex * 3;        pos[idx] = sphere.position.x;        pos[idx + 1] = sphere.position.y;        pos[idx + 2] = sphere.position.z;        data.trailIndex = (data.trailIndex + 1) % 100;        trail.geometry.setAttribute('position'new THREE.BufferAttribute(pos, 3));        trail.geometry.attributes.position.needsUpdate = true;    });
    const vortexPosArray = vortexSystem.geometry.attributes.position.array;    for (let i = 0; i < vortexCount; i++) {        const data = vortexData[i];        data.distance += data.speed;        const angle = data.angle + time * data.twist;        vortexPosArray[i * 3] = Math.cos(angle) * data.distance * (1.0 + data.twist);        vortexPosArray[i * 3 + 1] = Math.sin(angle) * data.distance * 0.6;        vortexPosArray[i * 3 + 2] = Math.sin(angle + data.twist) * data.distance;        if (data.distance > data.maxDistance) {            data.distance = 0;        }    }    vortexSystem.geometry.attributes.position.needsUpdate = true;    vortexMaterial.uniforms.time.value = time;
    controls.update();    composer.render();}
animate();
window.addEventListener('resize', () => {    camera.aspect = window.innerWidth / window.innerHeight;    camera.updateProjectionMatrix();    renderer.setSize(window.innerWidth, window.innerHeight);    composer.setSize(window.innerWidth, window.innerHeight);});</script>

源码:

https://codepen.io/VoXelo/pen/GgRoaow

体验:

https://codepen.io/VoXelo/full/GgRoaow

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

免责声明 1、百创网作为第三方中介平台,依据交易合同(商品描述、交易前商定的内容)来保障交易的安全及买卖双方的权益; 2、非平台线上交易的项目,出现任何后果均与百创网无关;无论卖家以何理由要求线下交易的,请联系管理举报。 3. 百创网网站的资源均由店家上传出售,本站无法判断和识别资源的版权等合法性属性。如果您对本网站上传的信息资源的版权存有异议,请您及时联系 我们。如果需要删除链接,请下载下面的附件,正确填写信息后并发给我们,本站核实信息真实性后,在24小时内对商品进行删除处理。 联系邮箱:baicxx@baicxx.com (相关事务请发函至该邮箱)

百创网-源码交易平台_网站源码_商城源码_小程序源码 行业资讯 Three.js 着色器驱动的 3D Vortex https://www.baicxx.com/31084.html

常见问题
  • 1、自动:拍下后,点击(下载)链接即可下载;2、手动:拍下后,联系卖家发放即可或者联系官方找开发者发货。
查看详情
  • 1、源码默认交易周期:手动发货商品为1-3天,并且用户付款金额将会进入平台担保直到交易完成或者3-7天即可发放,如遇纠纷无限期延长收款金额直至纠纷解决或者退款!;
查看详情
  • 1、百创会对双方交易的过程及交易商品的快照进行永久存档,以确保交易的真实、有效、安全! 2、百创无法对如“永久包更新”、“永久技术支持”等类似交易之后的商家承诺做担保,请买家自行鉴别; 3、在源码同时有网站演示与图片演示,且站演与图演不一致时,默认按图演作为纠纷评判依据(特别声明或有商定除外); 4、在没有”无任何正当退款依据”的前提下,商品写有”一旦售出,概不支持退款”等类似的声明,视为无效声明; 5、在未拍下前,双方在QQ上所商定的交易内容,亦可成为纠纷评判依据(商定与描述冲突时,商定为准); 6、因聊天记录可作为纠纷评判依据,故双方联系时,只与对方在百创上所留的QQ、手机号沟通,以防对方不承认自我承诺。 7、虽然交易产生纠纷的几率很小,但一定要保留如聊天记录、手机短信等这样的重要信息,以防产生纠纷时便于百创介入快速处理。
查看详情
  • 1、百创作为第三方中介平台,依据交易合同(商品描述、交易前商定的内容)来保障交易的安全及买卖双方的权益; 2、非平台线上交易的项目,出现任何后果均与百创无关;无论卖家以何理由要求线下交易的,请联系管理举报。
查看详情
  • 免责声明 1、百创网作为第三方中介平台,依据交易合同(商品描述、交易前商定的内容)来保障交易的安全及买卖双方的权益; 2、非平台线上交易的项目,出现任何后果均与百创网无关;无论卖家以何理由要求线下交易的,请联系管理举报。 3. 百创网网站的资源均由店家上传出售,本站无法判断和识别资源的版权等合法性属性。如果您对本网站上传的信息资源的版权存有异议,请您及时联系 我们。如果需要删除链接,请下载下面的附件,正确填写信息后并发给我们,本站核实信息真实性后,在24小时内对商品进行删除处理。 联系邮箱:baicxx@baicxx.com (相关事务请发函至该邮箱)
查看详情

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务

  • 0 +

    访问总数

  • 0 +

    会员总数

  • 0 +

    文章总数

  • 0 +

    今日发布

  • 0 +

    本周发布

  • 0 +

    运行天数

你的前景,远超我们想象