three.js着色器以保持点云顶点在球体内移动

huangapple go评论65阅读模式
英文:

three.js shader to keep moving vertices of points cloud inside sphere

问题

I am trying to achieve the effect of having a points cloud with moving points while also keeping them confined within a sphere of radius X.

我正在尝试实现一个点云效果,其中点在移动,同时保持在半径为X的球内。

I have managed to do it without using shaders but I want to try and do it with shaders as well.

我已经成功地做到了,而且没有使用着色器,但我也想尝试使用着色器来实现。

What I have so far is this

我目前的代码如下:

const uniforms = {
  u_time: { value: 0 },
  u_radius: { value: 1500 },
};

const vShader = `
uniform float u_time;
uniform int u_radius;

attribute vec3 velocity;

varying vec3 v_position;

void main() {
  v_position = position;
  vec3 vel = velocity * u_time;
  
  if(length(position) > float(u_radius)) {
    vel = vel * -1.0;
  }
  
  gl_Position = projectionMatrix * modelViewMatrix * vec4(position + vel, 1.0);
  gl_PointSize = 10.0;
}`;
const fShader = `
varying vec3 v_position;

void main() {
  vec3 color = vec3(1.0);
  gl_FragColor = vec4(color, 0.7);
}`;

The logic is that for each vertex, I am comparing its length based on its current position to the sphere radius and if it falls outside of it its velocity vector should be negated, thus keeping it within the sphere.

逻辑是,对于每个顶点,我根据其当前位置的长度与球体半径进行比较,如果超出了球体半径,它的速度向量应该被否定,从而使其保持在球体内部。

Sadly what I have above does not seem to work, as the particles spread out to infinity over time.

遗憾的是,我上面的代码似乎不起作用,因为粒子随着时间的推移会无限扩散。

I am new to three.js and GLSL, so I must be missing something obvious and doing this wrong.

我是three.js和GLSL的新手,所以我肯定漏掉了一些明显的东西,做错了什么。

EDIT:

Shown in the gif: The effect as I have currently managed to implement it using Points, and an array of positions and an array of velocities (these are seeded once during initialization with random values)

在GIF图中显示的是我目前使用Points以及位置数组和速度数组来实现的效果(这些在初始化时使用随机值进行一次初始化)。

(it may be hard to see in the gif due to compression, but each particle is moving randomly but is always contained within the sphere)

(由于压缩,可能在GIF图中很难看到,但每个粒子都在球体内部随机移动)

On every tick I iterate over the array of particles and add their velocity vector to their position vector. If their position vector length is > than the sphere radius I negate the velocity vector and update the velocities array to the indices that correspond to that particle. I then mark the Points geometry position attribute as needing an update and that's it.

在每个时间步上,我遍历粒子数组,并将它们的速度向量添加到它们的位置向量中。如果它们的位置向量长度大于球体半径,我会否定速度向量,并更新与该粒子对应的速度数组的索引。然后,我将Points几何体的位置属性标记为需要更新,就这样。

英文:

I am trying to achieve the effect of having a points cloud with moving points while also keeping them confined within a sphere of radius X.

I have managed to do it without using shaders but I want to try and do it with shaders as well.

What I have so far is this

const uniforms = {
  u_time: { value: 0 },
  u_radius: { value: 1500 },
};

const vShader = `
uniform float u_time;
uniform int u_radius;

attribute vec3 velocity;

varying vec3 v_position;

void main() {
  v_position = position;
  vec3 vel = velocity * u_time;
  
  if(length(position) > float(u_radius)) {
    vel = vel * -1.0;
  }
  
  gl_Position = projectionMatrix * modelViewMatrix * vec4(position + vel, 1.0);
  gl_PointSize = 10.0;
}`;

const fShader = `
varying vec3 v_position;

void main() {
  vec3 color = vec3(1.0);
  gl_FragColor = vec4(color, 0.7);
}`;

The logic is that for each vertex, I am comparing its length based on its current position to the sphere radius and if it falls outside of it its velocity vector should be negated ,thus keeping it within the sphere.

Sadly what I have above does not seem to work, as the particles spread out to infinity over time.

I am new to three.js and GLSL so I must be missing something obvious and doing this wrong.

EDIT:

Shown in the gif: The effect as I have currently managed to implement it using Points, and an array of positions and an array of velocities (these are seeded once during initialisation with random values)

(it may be hard to see in the gif due to compression, but each particle is moving randomly but is always contained within the sphere)

On every tick I iterate over the array of particles and add their velocity vector to their position vector. If their position vector length is > than the sphere radius I negate the velocity vector and update the velocities array to the indices that correspond to that particle. I then mark the Points geometry position attribute as needing an update and that's it.

three.js着色器以保持点云顶点在球体内移动

答案1

得分: 1

这是一个关于 pingpong 动画的示例代码,使用了修改后的 PointsMaterial 材质。如果有任何需要,请随时告诉我。

英文:

If I got you correctly, you want kind of pingpong animation.

Here is an example with modified PointsMaterial:

<!-- begin snippet: js hide: false console: false babel: false -->

<!-- language: lang-css -->

body{
  overflow: hidden;
  margin: 0;
}

<!-- language: lang-html -->

&lt;script async src=&quot;https://ga.jspm.io/npm:es-module-shims@1.6.3/dist/es-module-shims.js&quot; crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;importmap&quot;&gt;
  {
    &quot;imports&quot;: {
      &quot;three&quot;: &quot;https://unpkg.com/three@0.153.0/build/three.module.js&quot;,
      &quot;three/addons/&quot;: &quot;https://unpkg.com/three@0.153.0/examples/jsm/&quot;
    }
  }
&lt;/script&gt;
&lt;script type=&quot;module&quot;&gt;
import * as THREE from &quot;three&quot;;
import { OrbitControls } from &quot;three/addons/controls/OrbitControls.js&quot;;
import { GUI } from &quot;three/addons/libs/lil-gui.module.min.js&quot;;
console.clear();

let scene = new THREE.Scene();
scene.background = new THREE.Color(0x202020);
let camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 1000);
camera.position.set(0, 0, 10);
camera.lookAt(scene.position);
let renderer = new THREE.WebGLRenderer({
  antialias: true
});
renderer.setSize(innerWidth, innerHeight);
//renderer.setClearColor(0x404040);
document.body.appendChild(renderer.domElement);
window.addEventListener(&quot;resize&quot;, (event) =&gt; {
  camera.aspect = innerWidth / innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(innerWidth, innerHeight);
});

let controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;

let light = new THREE.DirectionalLight(0xffffff, 0.8);
light.position.setScalar(1);
scene.add(light, new THREE.AmbientLight(0xff8888, 0.2));

//scene.add(new THREE.GridHelper());

let gu = {
  time: {value: 0}
}

let amount = 10000;
let inits = new Array(amount).fill().map(() =&gt; {
  let v =  new THREE.Vector3().randomDirection();
  return [v.x, v.y, v.z, Math.random() * 2 - 1]
}).flat();

let g = new THREE.BufferGeometry().setFromPoints(new Array(amount).fill().map(() =&gt; {return new THREE.Vector3}));
g.setAttribute(&quot;inits&quot;, new THREE.Float32BufferAttribute(inits, 4));
let u = {
  radius: {value: 5},
  speed: {value: 0.25}
}
let m = new THREE.PointsMaterial({
  color: 0xff8800,
  size: 0.1,
  onBeforeCompile: shader =&gt; {
    shader.uniforms.time = gu.time;
    shader.uniforms.radius = u.radius;
    shader.uniforms.speed = u.speed;
    shader.vertexShader = `
      uniform float time;
      uniform float radius;
      uniform float speed;
      attribute vec4 inits;
      float euclideanModulo( float n, float m ) {
        return mod( mod( n, m ) + m , m);
      }
      float pingpong(float x, float l){
        return l - abs( euclideanModulo( x, l * 2. ) - l );
      }
      ${shader.vertexShader}
    `.replace(
      `#include &lt;begin_vertex&gt;`,
      `#include &lt;begin_vertex&gt;
        float t = time * speed;
        float startRadius = inits.w * radius;
        float currentDist = -radius + (startRadius + t + radius);
        float ppVal = pingpong(currentDist, radius * 2.);
        transformed = (-radius + ppVal) * inits.xyz;
      
      `
    );
    console.log(shader.vertexShader);
  }
});
let p = new THREE.Points(g, m);
scene.add(p)

let gui = new GUI();
gui.add(u.radius, &quot;value&quot;, 2, 5).name(&quot;radius&quot;);
gui.add(u.speed, &quot;value&quot;, 0.1, 1).name(&quot;speed&quot;);

let clock = new THREE.Clock();

renderer.setAnimationLoop((_) =&gt; {
  let t = clock.getElapsedTime();
  gu.time.value = t;
  controls.update();
  renderer.render(scene, camera);
});
&lt;/script&gt;

<!-- end snippet -->

huangapple
  • 本文由 发表于 2023年6月12日 15:57:40
  • 转载请务必保留本文链接:https://go.coder-hub.com/76454600.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定