Three.js 使用着色器设置每个点云的颜色 Material

Three.js setting each pointCloud's color with Shader Material

我想做的是创建多个 PointCloud,并为每个点分配不同的颜色。对于 PointCloudMaterial,它的颜色 属性 似乎可以解决问题,但由于我使用的是 ShaderMaterial,我似乎无法弄清楚如何实现类似的结果。

[编辑:添加图像以澄清]

What's happening

What I want to happen

我正在使用 ShaderMaterial,因为我想缩放一些自定义属性并更改每个顶点的不透明度。

创建粒子云的函数:

function addParticleCloud(width, height, colorIn, particleCount) {

  var geometry = new THREE.Geometry();
  
  // add randomized vertex positions for geometry
  for (var i = 0; i < particleCount; i++) {
    var vertex = new THREE.Vector3();
    vertex.x = Math.random() * width;
    vertex.y = Math.random() * height;
    vertex.z = z;

    geometry.vertices.push(vertex);
  }

  var attributeCount = attributes.alpha.value.length;
  var totalCount = attributeCount + geometry.vertices.length;
  
  // change attributes per particle/vertex
  for (var i = attributeCount; i < totalCount; i++) {
    // random alpha
    attributes.alpha.value[i] = Math.random();

    // random scale
    attributes.scale.value[i] = Math.random() * (250.0 - 100) + 100;
    
    // TRIED TO CHANGE COLORS HERE, but every cloud created afterwards has the
    // same color as the first one.
    attributes.colorVal.value[i] = new THREE.Color(colorIn);
    attributes.colorVal.needsUpdate = true;
    
    // update attributeCount
    attributeCount = attributes.alpha.value.length;
  }

  var material = new THREE.ShaderMaterial({
    uniforms: uniforms,
    attributes: attributes,
    vertexShader: document.getElementById('vertexshader').textContent,
    fragmentShader: document.getElementById('fragmentshader').textContent,
    transparent: true,
  });
  
  // This is the kind of thing I'd like to be able to do
  //material.color = new THREE.Color(colorIn);

  var particles = new THREE.PointCloud(geometry, material);
  scene.add(particles);
}

调用该函数的代码:

// [width, height, color]
var rectangles = [
  [400, 200, 0xA3422C],
  [40, 500, 0x0040f0],
  [200, 200, 0x2CA35E],
  [40, 500, 0x2C8DA3],
];

for (var i = 0; i < rectangles.length; i++) {
  var sqWidth = rectangles[i][0];
  var sqHeight = rectangles[i][3];
  var rectColor = rectangles[i][2];

  var particleCount = 100;

  // Note: I've removed some parameters for clarity (xyz positions, etc.)
  addParticleCloud(sqWidth, sqHeight, rectColor, particleCount);
}

顶点和片段着色器代码:

<script type="x-shader/x-vertex" id="vertexshader">

  attribute float alpha;
  attribute float scale;
  attribute vec3 colorVal;

  uniform float size;

  varying float vAlpha;
  varying vec3 vColor;

  void main() {
    vColor = colorVal;
    vAlpha = alpha;

    vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );

    gl_PointSize = size * ( scale / length( mvPosition.xyz ) );

    gl_Position = projectionMatrix * mvPosition;
  }

</script>

<script type="x-shader/x-fragment" id="fragmentshader">

  varying float vAlpha;
  varying vec3 vColor;

  void main() {
    gl_FragColor = vec4( vColor.rgb, vAlpha );
  }

</script>

属性和制服:

// attributes
attributes = {
  alpha: {type: 'f', value: []},
  shouldFade: {type: 'b', value: []},
  scale: {type: 'f', value: []},
  colorVal: {type: "c", value: []},
};

// uniforms
uniforms = {
  size: {type: "f", value: 100.0},
};

为每个 material 创建单独的制服和属性(这也将简化属性处理)

如果我没记错的话,你的属性和制服是全局变量,你正试图在 material 之间共享属性。

共享制服是可行的(当然除非您希望它们不同),但是属性与顶点具有一对一的相关性。每个顶点在添加顶点的索引处获取属性 - 在您的情况下,所有三个点云的顶点都引用属性第一部分中的值,因此具有相同的颜色。