Draw colored lines with node material

Hi. My goal is to display several thousand meshes and their wireframes (created with WireframeGeometry) efficiently. Each has a color and an alpha, but otherwise the same material. To do this I’ve taken the route of merging all the objects and, separately, all the wires, together with BufferGeometryUtils.mergeBufferGeometries. To control the appearance of each object within the merged geometry I created a node material that sets the color and alpha properties of PhongNodeMaterial based on which object a given vertex originated in. This works great! Vast speed improvements.

However, when I use the same material on the LineSegments wireframe object, I get only black lines. How might I use the same approach for the wireframes? Here’s a simple version demonstrating that the color input doesn’t affect lines: three-js-nodes-wires-colors - CodeSandbox. Do I need to create a node material version of LineBasicMaterial? I got lost trying to compare PhongNodeMaterial with meshphong.glsl.js, it looks like there’s more difference than just a straight translation?

Here’s some code from the working node material for the meshes, if it’s helpful:

import { Color, DataTexture, RGBAFormat } from "three";
import * as Nodes from "three/examples/jsm/nodes/Nodes";
import { PhongNodeMaterial } from "three/examples/jsm/nodes/Nodes";

export class MergedMaterial extends PhongNodeMaterial
{
  private colorData: DataTexture;

  constructor(colors: { color: Color; alpha?: boolean }[]) {
    super();

    // Geometry has a buffer attribute matching vertices to object id.
    const objectId = new Nodes.AttributeNode("object", "float");

    const pixelCount = colors.length;

    const size = Math.ceil(Math.sqrt(pixelCount));

    this.colorData = new DataTexture(
      new Uint8Array(4 * size * size),
      size,
      size,
      RGBAFormat
    );

    this.updateColors(colors);

    const objectColors = new Nodes.TextureNode(this.colorData);

    const objectColorFunction = new Nodes.FunctionNode(
      `vec3 objectColor( float objectId, sampler2D colors ) {
        int index = int(objectId);
        int x = index % ${size};
        int y = index / ${size};
        return texelFetch(colors, ivec2(x,y), 0).rgb;
      }`
    );

    const objectAlphaFunction = new Nodes.FunctionNode(
      `float objectAlpha( float objectId, sampler2D colors ) {
        int index = int(objectId);
        int x = index % ${size};
        int y = index / ${size};
        return texelFetch(colors, ivec2(x,y), 0).a;
      }`
    );

    this.color = new Nodes.FunctionCallNode(objectColorFunction, [
      objectId,
      objectColors,
    ]);

    this.alpha = new Nodes.FunctionCallNode(objectAlphaFunction, [
      objectId,
      objectColors,
    ]);
  }

  public updateColors(colors: { color: Color; alpha?: boolean }[]): void {
    const colorArray = this.colorData.image.data;

    for (let i = 0; i < colors.length; i++) {
      const color = colors[i];
      const stride = i * 4;

      colorArray[stride] = Math.floor(color.color.r * 255);
      colorArray[stride + 1] = Math.floor(color.color.g * 255);
      colorArray[stride + 2] = Math.floor(color.color.b * 255);
      colorArray[stride + 3] = Math.floor(color.alpha * 255);
    }

    this.colorData.needsUpdate = true;
  }
}

After staring at the three.js source some more I made a version of BasicNodeMaterial that works for my needs: three-js-nodes-wires-colors (forked) - CodeSandbox.

1 Like