学习threejs,使用TSL计算粒子鼠标特效

👨‍⚕️ 主页: gis分享者

👨‍⚕️ 感谢各位大佬 点赞👍 收藏⭐ 留言📝 加关注✅!

👨‍⚕️ 收录于专栏:threejs gis工程师


文章目录

  • 一、🍀前言
    • [1.1 ☘️Three.js Shading Language (TSL)](#1.1 ☘️Three.js Shading Language (TSL))
      • [1.1.1 ☘️背景](#1.1.1 ☘️背景)
      • [1.1.2 ☘️着色语言的转变](#1.1.2 ☘️着色语言的转变)
      • [1.1.3 ☘️特点](#1.1.3 ☘️特点)
      • [1.1.4 ☘️优势](#1.1.4 ☘️优势)
  • 二、🍀使用TSL计算粒子鼠标特效
    • [1. ☘️实现思路](#1. ☘️实现思路)
    • [2. ☘️代码样例](#2. ☘️代码样例)

一、🍀前言

本文详细介绍如何基于threejs在三维场景中使用TSL计算粒子鼠标特效,亲测可用。希望能帮助到您。一起学习,加油!加油!

1.1 ☘️Three.js Shading Language (TSL)

Three.js宣布引入了一种新的着色语言,能够生成GLSL和WGSL代码。

  • TSL 抹平了 GLSL 和 WGSL 着色器语言的差异。
  • 通过 GLSLNodeBuilder 编译成适用于 WebGL 2 的 GLSL
  • 通过 WGSLNodeBuilder 编译成适用于 WebGPU 的 WGSL
  • GLSLNodeBuilder 和 WGSLNodeBuilder 都继承于 NodeBuilder,你甚至可以基于
    NodeBuilder 扩展到任何着色器编程语言。

1.1.1 ☘️背景

3D图形在Web上正经历一场革命,从WebGL过渡到更强大的WebGPU。

WebGPU利用最新的GPU技术,提供更好的性能。

1.1.2 ☘️着色语言的转变

WebGL使用GLSL编写着色器,而WebGPU需要使用WGSL。

两种语言相似,静态类型,与C语言紧密相关,专注于3D图形的复杂向量计算。

1.1.3 ☘️特点

TSL采用了基于节点的方法,类似于Unreal Engine的Blueprints、Blender和Unity的Shader Graph。

这种方法通过将着色器分解为一系列节点来促进着色器开发,每个节点应用特定效果,可以组合生成最终着色器。

1.1.4 ☘️优势

TSL的节点本质上是函数,可以被使用、组合和链接以生成最终着色器。

TSL自动处理适应不同API的适配,无论是WebGL的GLSL还是WebGPU的WGSL。

二、🍀使用TSL计算粒子鼠标特效

1. ☘️实现思路

通过THREE.Raycaster射线拾取,以及TSL计算粒子,实现鼠标移动粒子跳动特效。具体代码参考代码样例。可以直接运行。

2. ☘️代码样例

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>tsl计算</title>
</head>
<style>
    body {
        background-color: black;
        margin: 0;
    }
</style>
<body>

<script type="importmap">
 {
    "imports": {
      "three": "https://cdn.jsdelivr.net/npm/three@0.176.0/build/three.webgpu.js",
      "three/webgpu": 		"https://cdn.jsdelivr.net/npm/three@0.176.0/build/three.webgpu.js",
      "three/tsl": "https://cdn.jsdelivr.net/npm/three@0.176.0/build/three.tsl.js",
      "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.176.0/examples/jsm/"
    }
  }
</script>
<script type="module">
  import * as THREE from 'three';
  import { Fn, If, uniform, float, uv, vec2, vec3, hash, instancedArray, instanceIndex, viewportSize } from 'three/tsl';

  import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
  import Stats from 'three/addons/libs/stats.module.js';

  const particleCount = 500_000;

  const gravity = uniform( - .00098 );
  const bounce = uniform( .8 );
  const friction = uniform( .99 );
  const size = uniform( .12 );

  const clickPosition = uniform( new THREE.Vector3() );

  let camera, scene, renderer;
  let controls, stats;
  let computeParticles;

  let isOrbitControlsActive;

  init();

  function init() {

    const { innerWidth, innerHeight } = window;

    camera = new THREE.PerspectiveCamera( 50, innerWidth / innerHeight, .1, 1000 );
    camera.position.set( 0, 10, 30 );

    scene = new THREE.Scene();

    //

    const positions = instancedArray( particleCount, 'vec3' );
    const velocities = instancedArray( particleCount, 'vec3' );
    const colors = instancedArray( particleCount, 'vec3' );

    // compute

    const separation = 0.2;
    const amount = Math.sqrt( particleCount );
    const offset = float( amount / 2 );

    const computeInit = Fn( () => {

      const position = positions.element( instanceIndex );
      const color = colors.element( instanceIndex );

      const x = instanceIndex.mod( amount );
      const z = instanceIndex.div( amount );

      position.x = offset.sub( x ).mul( separation );
      position.z = offset.sub( z ).mul( separation );

      const randX = hash( instanceIndex );
      const randY = hash( instanceIndex.add( 2 ) );
      const randZ = hash( instanceIndex.add( 3 ) );

      color.assign( vec3( randX, randY.mul( 0.5 ), randZ ) );

    } )().compute( particleCount );

    //

    const computeUpdate = Fn( () => {

      const position = positions.element( instanceIndex );
      const velocity = velocities.element( instanceIndex );

      velocity.addAssign( vec3( 0.00, gravity, 0.00 ) );
      position.addAssign( velocity );

      velocity.mulAssign( friction );

      // floor

      If( position.y.lessThan( 0 ), () => {

        position.y = 0;
        velocity.y = velocity.y.negate().mul( bounce );

        // floor friction

        velocity.x = velocity.x.mul( .9 );
        velocity.z = velocity.z.mul( .9 );

      } );

    } );

    computeParticles = computeUpdate().compute( particleCount );

    // create particles

    const material = new THREE.SpriteNodeMaterial();
    material.colorNode = uv().mul( colors.element( instanceIndex ) );
    material.positionNode = positions.toAttribute();
    material.scaleNode = size;
    material.alphaTestNode = uv().mul( 2 ).distance( vec2( 1 ) );
    material.alphaToCoverage = true;
    material.transparent = false;

    const particles = new THREE.Sprite( material );
    particles.count = particleCount;
    particles.frustumCulled = false;
    scene.add( particles );

    //

    const helper = new THREE.GridHelper( 142, 71, 0x303030, 0x303030 );
    scene.add( helper );

    const geometry = new THREE.PlaneGeometry( 1000, 1000 );
    geometry.rotateX( - Math.PI / 2 );

    const plane = new THREE.Mesh( geometry, new THREE.MeshBasicMaterial( { visible: false } ) );
    scene.add( plane );

    const raycaster = new THREE.Raycaster();
    const pointer = new THREE.Vector2();

    //

    renderer = new THREE.WebGPURenderer( { antialias: false } );
    renderer.setPixelRatio( window.devicePixelRatio );
    renderer.setSize( window.innerWidth, window.innerHeight );
    renderer.setAnimationLoop( animate );
    document.body.appendChild( renderer.domElement );

    stats = new Stats();
    document.body.appendChild( stats.dom );

    //

    renderer.computeAsync( computeInit );

    // click event

    const computeHit = Fn( () => {

      const position = positions.element( instanceIndex );
      const velocity = velocities.element( instanceIndex );

      const dist = position.distance( clickPosition );
      const direction = position.sub( clickPosition ).normalize();
      const distArea = float( 3 ).sub( dist ).max( 0 );

      const power = distArea.mul( .01 );
      const relativePower = power.mul( hash( instanceIndex ).mul( 1.5 ).add( .5 ) );

      velocity.assign( velocity.add( direction.mul( relativePower ) ) );

    } )().compute( particleCount );

    //

    function onMove( event ) {

      if ( isOrbitControlsActive ) return;

      pointer.set( ( event.clientX / window.innerWidth ) * 2 - 1, - ( event.clientY / window.innerHeight ) * 2 + 1 );

      raycaster.setFromCamera( pointer, camera );

      const intersects = raycaster.intersectObjects( [ plane ], false );

      if ( intersects.length > 0 ) {

        const { point } = intersects[ 0 ];

        // move to uniform

        clickPosition.value.copy( point );
        clickPosition.value.y = - 1;

        // compute

        renderer.computeAsync( computeHit );

      }

    }

    // events

    renderer.domElement.addEventListener( 'pointermove', onMove );

    //

    controls = new OrbitControls( camera, renderer.domElement );
    controls.enableDamping = true;
    controls.minDistance = 5;
    controls.maxDistance = 200;
    controls.target.set( 0, -8, 0 );
    controls.update();

    controls.addEventListener( 'start', function () {

      isOrbitControlsActive = true;

    } );

    controls.addEventListener( 'end', function () {

      isOrbitControlsActive = false;

    } );

    controls.touches = {
      ONE: null,
      TWO: THREE.TOUCH.DOLLY_PAN
    };

    //

    window.addEventListener( 'resize', onWindowResize );

  }

  function onWindowResize() {

    const { innerWidth, innerHeight } = window;

    camera.aspect = innerWidth / innerHeight;
    camera.updateProjectionMatrix();

    renderer.setSize( innerWidth, innerHeight );

  }

  async function animate() {

    controls.update();

    await renderer.computeAsync( computeParticles );
    await renderer.renderAsync( scene, camera );

    stats.update();

  }
</script>
</body>
</html>

效果如下

参考:Three.js TSL 计算粒子

相关推荐
救救孩子把5 天前
Three.js 从零入门:构建你的第一个 Web 3D 世界
前端·javascript·3d·threejs
七月的冰红茶14 天前
【threejs】第一人称视角之八叉树碰撞检测
前端·threejs
花落已飘14 天前
HID协议
usb·鼠标·hid
三月的一天15 天前
React Three Fiber 实现 3D 模型点击高亮交互的核心技巧
3d·webgl·threejs·reactthreefiber
gis分享者17 天前
学习threejs,使用自定义GLSL 着色器,生成漂流的3D能量球
3d·threejs·着色器·glsl·shadermaterial·能量球
魂断蓝桥66619 天前
使用three.js,实现微信3D小游戏系列教程,框架篇(一)
webgl·threejs·微信小游戏·3d建筑·three.js路径规划、三维a*算法、javascript三维导航,·three.js小游戏
魂断蓝桥66624 天前
如何基于three.js(webgl)引擎架构,实现3D医院、3D园区导航,3D科室路径导航
webgl·数字孪生·threejs·3d定位、三维室内定位、3d建筑·three.js路径规划、三维a*算法、javascript三维导航,·3d医院·3d导航·园区导航
陶甜也1 个月前
threejs 实现720°全景图,;两种方式:环境贴图、CSS3DRenderer渲染
前端·vue.js·css3·threejs
AllBlue1 个月前
fbx导入blender结构错乱,但在threejs中加载正常
blender·threejs