如何将粒子系统初始化为球形帽?

时间:2019-07-02 08:39:56

标签: javascript three.js

我的Three.js项目包括在雪球(sphereGeometry)(一种经典的Xmas雪球)内部造雪(带有一些粒子)。我使用THREE.BufferGeometry()创建了粒子,并对其进行了初始化,并为每个参数(x,y,z)赋予了initial_position。

有没有办法让它们仅在球体内可见?我解决了使球体外部的粒子具有与背景相同的颜色的问题,但是效果并不理想。

有没有办法使球外的粒子不可见?也许使它们透明。

否则,如何将粒子初始化为球形帽? 谢谢!

这就是我初始化粒子位置(作为平行六面体)的方式:

for(i=0; i<n; i++){
    init_pos_y[i] = 50 + (Math.random()-0.5)*20;
    init_pos_x[i] = (Math.random()-0.5)*100;
    init_pos_z[i] = (Math.random()-0.5)*100;
    acceleration[i] = Math.random()*1;

在“顶点着色器”中,这就是我如何使粒子掉落并赋予它们颜色(并根据其在球体内或外的位置改变其不透明度):

void main(){

    vec3 p = position; 
    p.x = initial_position_x;
    p.z = initial_position_z;

    if (initial_position_y - time * acceleration > -32.8 + min_level){  
        p.y = initial_position_y - time * acceleration;
    }
    else{
        p.y = -33.8 + min_level;
    }
    float opacity;

    if (p.x*p.x + p.y*p.y + p.z*p.z > 2490.0){ 
        opacity = 0.40;
        vColor = vec4( customColor, opacity );              
    }
    else{
        opacity = 1.0;
        vColor = vec4( customColor2, opacity );
    }

    gl_Position = projectionMatrix * modelViewMatrix * vec4(p, 1.0);            
    vUv = projectionMatrix * vec4(p, 1.0);
    gl_PointSize = 3.0*acceleration;
}

1 个答案:

答案 0 :(得分:1)

首先,您需要将点放在圆柱体的形状中(随机在圆内,高度随机)。为此,请参见函数setInCircle()

然后,您需要修改着色器的代码。我更喜欢使用.onBeforeCompile()进行操作,因此您可以修改必需品部件,同时保留材料的所有其他功能。

在着色器中,您通过添加距离(时间*速度)并除以10(mod()函数)来更改y值,因此将其置于一个循环中以在范围内从上到下移动5到-5。

最后一件事是检查给定的变换矢量的长度是否小于或等于给定的半径,将结果传递给片段着色器,在该片段着色器中,如果结果小于0.5,则丢弃像素。 / p>

var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.setScalar(10);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

var controls = new THREE.OrbitControls(camera, renderer.domElement);

scene.add(new THREE.GridHelper(10, 10));

var pts = [];
var radius = 5;
var pointsCount = 5000;

for (i = 0; i < pointsCount; i++) {
  let v2 = setInCircle().multiplyScalar(radius);
  let v3 = new THREE.Vector3(v2.x, THREE.Math.randFloat(-radius, radius), v2.y);
  pts.push(v3);
}

var geom = new THREE.BufferGeometry().setFromPoints(pts);
var uniforms = {
  speed: {
    value: 1
  },
  time: {
    value: 0
  },
  radius: {
    value: radius
  }
}
var mat = new THREE.PointsMaterial({
  color: "magenta",
  size: 0.05
});
mat.onBeforeCompile = shader => {
  shader.uniforms.speed = uniforms.speed;
  shader.uniforms.time = uniforms.time;
  shader.uniforms.radius = uniforms.radius;
  shader.vertexShader = `
    uniform float speed;
    uniform float time;
    uniform float radius;
    varying float vVisible;
  ` + shader.vertexShader;
  //console.log(shader.vertexShader);
  shader.vertexShader = shader.vertexShader.replace(
    `#include <begin_vertex>`,
    `#include <begin_vertex>
    transformed.y = mod((transformed.y - speed * time) - 5., 10.) - 5.;
    vVisible = length(transformed) <= radius ? 1.: 0.;
  `
  );
  shader.fragmentShader = `
    varying float vVisible;
  ` + shader.fragmentShader;
  console.log(shader.fragmentShader);
  shader.fragmentShader = shader.fragmentShader.replace(
    `void main() {`,
    `void main() {
      if (vVisible < 0.5) discard;
    `
  );

}
var points = new THREE.Points(geom, mat);
scene.add(points);

function setInCircle() {
  let v = new THREE.Vector2();
  v.set(
    THREE.Math.randFloat(-1, 1),
    THREE.Math.randFloat(-1, 1)
  )
  return v.length() <= 1 ? v : setInCircle();
}

var clock = new THREE.Clock();

renderer.setAnimationLoop(() => {
  uniforms.time.value = clock.getElapsedTime();
  renderer.render(scene, camera)
});
body {
  overflow: hidden;
  margin: 0;
}
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>