Three.js устанавливает цвет каждого облака точек с помощью материала шейдера

Я пытаюсь создать несколько PointCloud и назначить каждому из них свой цвет. С PointCloudMaterial у него было свойство цвета, которое, казалось, делало свое дело, но, поскольку я использую ShaderMaterial, я не могу понять, как добиться аналогичных результатов.

[Изменить: добавлены изображения для уточнения]

Что происходит

Что я хочу сделать

Я использую 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},
};


person Dylan Gallardo    schedule 12.03.2015    source источник
comment
Этот вопрос основан на устаревшем API. Атрибуты теперь вообще не устанавливаются в ShaderMaterial. Предпочтительным способом является использование BufferGeometry.   -  person Elias Hasle    schedule 29.07.2019


Ответы (1)


Создайте отдельные униформы и атрибуты для каждого материала (что также упростит обработку атрибутов).

Если я не ошибаюсь, ваши атрибуты и униформы являются глобальными переменными, и вы пытаетесь разделить атрибуты между материалами.

Совместное использование униформ работает (если вы, конечно, не хотите, чтобы они отличались), но атрибуты имеют пошаговую корреляцию с вершинами. Каждая вершина получает атрибут по индексу, по которому вершина была добавлена ​​— в вашем случае вершины всех трех облаков точек ссылаются на значения в первой части атрибутов и, следовательно, имеют одинаковый цвет.

person user3391859    schedule 20.05.2015
comment
Прошло некоторое время с тех пор, как я задал этот вопрос, и с тех пор я почти не касался three.js, но спасибо, что нашли время, чтобы опубликовать ответ. Ваш ответ кажется разумным решением того, с чем у меня возникли проблемы, поэтому я принимаю его как ответ (хотя в данный момент я не могу его проверить). - person Dylan Gallardo; 22.05.2015