当前位置: 首页 > article >正文

使用 Three.js 转换 GLSL 粒子效果着色器

大家好!我是 [数擎AI],一位热爱探索新技术的前端开发者,在这里分享前端和 Web3D、AI 技术的干货与实战经验。如果你对技术有热情,欢迎关注我的文章,我们一起成长、进步!
开发领域:前端开发 | AI 应用 | Web3D | 元宇宙
技术栈:JavaScript、React、ThreeJs、WebGL、Go
经验经验:6 年+ 前端开发经验,专注于图形渲染和 AI 技术
开源项目:AI简历、元宇宙、数字孪生

在这篇博客中,我们将探讨如何将一个经典的 GLSL 粒子效果着色器转换成 Three.js 可用的自定义着色器,并通过交互控制其行为。这将是一个逐步教程,帮助你理解如何在 Three.js 中应用自定义 GLSL 代码并通过鼠标交互调整效果。

目标

我们将使用 Three.js 渲染一个基于粒子的视觉效果,类似于一个动态生成的渐变色圆环,随着时间的推移,颜色和形状会发生变化。用户还可以通过鼠标移动来控制缩放和动画持续时间。

步骤一:准备工作

首先,你需要确保你的项目中包含了 Three.js。如果你还没有安装 Three.js,可以通过 npm 安装:

npm install three

如果你使用的是纯 HTML 文件,也可以直接在 HTML 中引入 Three.js:

<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>

步骤二:GLSL 粒子效果着色器代码解析

原始的 GLSL 代码创建了一个基于时间变化的粒子系统,颜色从绿色到蓝色渐变,并且粒子的大小和分布随时间变化。核心的功能包括:

  • 粒子数量(n):控制生成的粒子数量。
  • 起始和结束颜色(startColor 和 endColor):粒子颜色的渐变。
  • 粒子半径变化:随着粒子距离中心的变化,粒子的半径逐渐增大。
  • 交互性:鼠标控制缩放和动画的持续时间。

步骤三:在 Three.js 中实现 GLSL 着色器

接下来,我们将创建一个简单的 Three.js 场景,并将这个 GLSL 着色器嵌入其中。

完整代码实现


import * as THREE from 'three';

const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

// Shader code
const vertexShader = `
    void main() {
        gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
    }
`;

const fragmentShader = `
    uniform float iTime;
    uniform vec3 iResolution;
    uniform vec3 iMouse;

    void mainImage( out vec4 fragColor, in vec2 fragCoord ) {
        float t = iTime+5.0;
        float z = 6.0;
        const int n = 100; // particle count

        vec3 startColor = vec3(0.0, 0.64, 0.2);
        vec3 endColor = vec3(0.06, 0.35, 0.85);

        float startRadius = 0.84;
        float endRadius = 1.6;

        float power = 0.51;
        float duration = 4.0;

        vec2 s = iResolution.xy;
        vec2 v = z * (2.0 * fragCoord.xy - s) / s.y;

        // Mouse axis y => zoom
        if(iMouse.z > 0.0) v *= iMouse.y / s.y * 20.0;

        // Mouse axis x => duration
        if(iMouse.z > 0.0) duration = iMouse.x / s.x * 10.0;

        vec3 col = vec3(0.0);
        vec2 pm = v.yx * 2.8;
        float dMax = duration;

        float evo = (sin(iTime * 0.01 + 400.0) * 0.5 + 0.5) * 99.0 + 1.0;

        float mb = 0.0;
        float mbRadius = 0.0;
        float sum = 0.0;
        for(int i = 0; i < n; i++) {
            float d = fract(t * power + 48934.4238 * sin(float(i / int(evo)) * 692.7398));
            float tt = 0.0;
            float a = 6.28 * float(i) / float(n);

            float x = d * cos(a) * duration;
            float y = d * sin(a) * duration;

            float distRatio = d / dMax;
            mbRadius = mix(startRadius, endRadius, distRatio);

            vec2 p = v - vec2(x, y);
            mb = mbRadius / dot(p, p);
            sum += mb;

            col = mix(col, mix(startColor, endColor, distRatio), mb / sum);
        }

        sum /= float(n);
        col = normalize(col) * sum;

        sum = clamp(sum, 0.0, 0.4);

        vec3 tex = vec3(1.0);
        col *= smoothstep(tex, vec3(0.0), vec3(sum));

        fragColor.rgb = col;
    }

    void main() {
        vec2 fragCoord = gl_FragCoord.xy;
        mainImage(gl_FragColor, fragCoord);
    }
`;

const uniforms = {
    iTime: { value: 0.0 },
    iResolution: { value: new THREE.Vector3(window.innerWidth, window.innerHeight, 1) },
    iMouse: { value: new THREE.Vector3(0, 0, 0) }
};

const material = new THREE.ShaderMaterial({
    vertexShader: vertexShader,
    fragmentShader: fragmentShader,
    uniforms: uniforms
});

const geometry = new THREE.PlaneGeometry(2, 2);
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);

// Set camera position
camera.position.z = 5;

function animate() {
    requestAnimationFrame(animate);
    
    uniforms.iTime.value += 0.05; 

    window.addEventListener('mousemove', (event) => {
        uniforms.iMouse.value.x = event.clientX;
        uniforms.iMouse.value.y = window.innerHeight - event.clientY; 
        uniforms.iMouse.value.z = 1; 
    });

    renderer.render(scene, camera);
}

animate();

步骤四:代码解析

  • ShaderMaterial 创建:我们通过 THREE.ShaderMaterial 来定义自定义的着色器,并将 GLSL 代码传入其中。通过 uniforms 对象传递外部变量(如时间、分辨率、鼠标位置)到着色器中。
  • 顶点着色器:这是一个简单的顶点着色器,它将每个顶点的坐标从模型空间转换到裁剪空间。
  • 片段着色器:在片段着色器中,我们实现了粒子效果的核心逻辑,包括动态计算粒子颜色、大小以及它们的分布。我们使用 sin 函数和一些数学操作来实现粒子效果的变化。
  • 鼠标交互:通过 mousemove 事件监听器,我们捕捉到鼠标的 X 和 Y 位置,并传递给着色器。鼠标的 Y 轴控制图形的缩放,而 X 轴控制动画的持续时间。
  • 动画循环:使用 requestAnimationFrame 来实现平滑的动画效果,定期更新时间和鼠标状态,并重新渲染场景。

步骤五:优化与扩展

在实际开发中,你可以根据需要对该代码进行优化和扩展。以下是一些常见的改进方式:

  • 性能优化:增加粒子数量时可能会导致性能下降,可以通过减少 n 或优化粒子计算逻辑来改善性能。
  • 扩展交互性:除了鼠标交互,你还可以加入键盘控制、触摸事件等方式,使用户体验更加丰富。
  • 增加更多粒子效果:可以通过修改着色器中的数学公式,增加更多动态的粒子效果,如旋转、形状变化等。

总结

通过本教程,你学会了如何将一个 GLSL 粒子效果着色器转化为 Three.js 中的自定义着色器,并通过鼠标交互来控制图形的缩放和动画时间。这种方法让你可以在 Web 上实现复杂的视觉效果,具有高度的自定义性和灵活性。


http://www.kler.cn/a/560608.html

相关文章:

  • 【C++设计模式】观察者模式(1/2):从基础到优化实现
  • Mesh自组网技术及应用
  • 网络运维学习笔记(DeepSeek优化版)002网工初级(HCIA-Datacom与CCNA-EI)子网划分与协议解析
  • 七.智慧城市数据治理平台架构
  • 【LeetCode 热题100】48. 旋转图像以及旋转任意角度的算法思路及python代码
  • LabVIEW Browser.vi 库说明
  • H5--开发适配
  • Web Developer 1靶场渗透测试
  • 深度学习基础--ResNet网络的讲解,ResNet50的复现(pytorch)以及用复现的ResNet50做鸟类图像分类
  • 基于Matlab实现报童问题仿真
  • CI/CD的定义
  • 23种设计模式之《代理模式(Proxy)》在c#中的应用及理解
  • ddd 文章总结分享,ddd实战代码分享, 领域驱动设计java实战源码大全,我看过的ddd java源码
  • 3D Web轻量化引擎HOOPS Communicator如何赋能航空航天制造?
  • Linux编辑器
  • hackmyvm-buster
  • C语言学习,希尔排序
  • Websock Demo(一)前端代码
  • pytest运行用例的常见方式及参数
  • ChatGPT免费背后的技术暗战 国产数字孪生如何打造“虚实共生”新生态?