Three.js TSL 计算粒子

2025-06-06 0 675

使用 Three.js 的 WebGL 小实验。计算粒子鼠标特效。

Three.js TSL 计算粒子
实现代码

HTML:

<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>
CSS:
body {  background-color: black;  margin0;}

JAVASCRIPT:

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 uniformnew THREE.Vector3() );
let camera, scene, renderer;let controls, stats;let computeParticles;
let isOrbitControlsActive;
init();
function init() {
  const { innerWidth, innerHeight } = window;
  camera = new THREE.PerspectiveCamera50, innerWidth / innerHeight, .11000 );  camera.position.set01030 );
  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 = instanceIndex.mod( amount );    const = 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.add2 ) );    const randZ hash( instanceIndex.add3 ) );
    color.assignvec3( randX, randY.mul0.5 ), randZ ) );
  } )().compute( particleCount );
  //
  const computeUpdate Fn( () => {
    const position = positions.element( instanceIndex );    const velocity = velocities.element( instanceIndex );
    velocity.addAssignvec30.00, gravity, 0.00 ) );    position.addAssign( velocity );
    velocity.mulAssign( friction );
    // floor
    If( position.y.lessThan0 ), () => {
      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().mul2 ).distancevec21 ) );  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.GridHelper142710x3030300x303030 );  scene.add( helper );
  const geometry new THREE.PlaneGeometry10001000 );  geometry.rotateX( - Math.PI / 2 );
  const plane new THREE.Mesh( geometry, new THREE.MeshBasicMaterial( { visiblefalse } ) );  scene.add( plane );
  const raycaster new THREE.Raycaster();  const pointer new THREE.Vector2();
  //
  renderer = new THREE.WebGPURenderer( { antialiasfalse } );  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 float3 ).sub( dist ).max0 );
    const power = distArea.mul.01 );    const relativePower = power.mulhash( instanceIndex ).mul1.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.set0, -80 );  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();
}

源码:

https://codepen.io/mrdoob_/pen/myyQLPL

体验:

https://codepen.io/mrdoob_/full/myyQLPL

 

收藏 (0) 打赏

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

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

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

百创网-源码交易平台_网站源码_商城源码_小程序源码 行业资讯 Three.js TSL 计算粒子 https://www.baicxx.com/31286.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 +

    运行天数

你的前景,远超我们想象