Skip to content

[p5.strands]: order of modifications changes between strands and outputted GLSL #7909

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
1 of 17 tasks
davepagurek opened this issue Jun 14, 2025 · 1 comment
Open
1 of 17 tasks

Comments

@davepagurek
Copy link
Contributor

Most appropriate sub-area of p5.js?

  • Accessibility
  • Color
  • Core/Environment/Rendering
  • Data
  • DOM
  • Events
  • Image
  • IO
  • Math
  • Typography
  • Utilities
  • WebGL
  • Build process
  • Unit testing
  • Internationalization
  • Friendly errors
  • Other (specify if possible)

p5.js version

2.0.3

Web browser and version

Firefox

Operating system

MacOS

Steps to reproduce this

I was writing a strands shader that modifies position and also texture coordinates, and uses the position to modify the texture coordinate:

textureMaterial = baseMaterialShader().modify(() => {
  const t = uniformFloat(() => millis());
  getWorldInputs((inputs) => {
    let size = [600, 400];
    inputs.texCoord = inputs.position.xy / size + 0.5;
    inputs.position.y += 50 * sin(t * 0.001 + inputs.position.x * 0.01);
    return inputs;
  });
});

Importantly, I modify the position after using it to modify the texture coordinate. I expect the texture to stretch a lot as because the texture coordinates stay the same while the positions they're attached to move. However, the texture remains largely stable in the end result: https://editor.p5js.org/davepagurek/sketches/jCo1EFc07

When I call .inspectHooks(), I can see that the GLSL looks like this:

Vertex getWorldInputs(Vertex inputs) {
  vec3 temp_0 = inputs.position;
  temp_0 = vec3(temp_0.x, temp_0.y + (50.0000 * sin((t * 0.0010) + (temp_0.x * 0.0100))), temp_0.z);
  vec2 temp_3 = vec2(temp_0.x, temp_0.y);
  vec2 temp_4 = vec2(600.0000, 400.0000);
  vec2 temp_2 = temp_3 / temp_4;
  vec2 temp_1 = temp_2 + 0.5000;

  Vertex finalReturnValue;
  finalReturnValue.position = temp_0;
  finalReturnValue.normal = inputs.normal;
  finalReturnValue.texCoord = temp_1;
  finalReturnValue.color = inputs.color;
  return finalReturnValue;
} 

Here we can see that it created the modified position (temp0) before using it as an input to the texture coordinates in temp_3.

If I don't assign to a single component of the vector, it works as expected:

textureMaterial = baseMaterialShader().modify(() => {
  const t = uniformFloat(() => millis());
  getWorldInputs((inputs) => {
    let size = [600, 400];
    inputs.texCoord = inputs.position.xy / size + 0.5;
    inputs.position = [
      inputs.position.x,
      inputs.position.y + 50 * sin(t * 0.001 + inputs.position.x * 0.01),
      inputs.position.z
    ];
    return inputs;
  });
});
Vertex getWorldInputs(Vertex inputs) {
  vec2 temp_2 = vec2(inputs.position.x, inputs.position.y);
  vec2 temp_3 = vec2(600.0000, 400.0000);
  vec2 temp_1 = temp_2 / temp_3;
  vec2 temp_0 = temp_1 + 0.5000;

  Vertex finalReturnValue;
  finalReturnValue.position = vec3(inputs.position.x, inputs.position.y + (50.0000 * sin((t * 0.0010) + (inputs.position.x * 0.0100))), inputs.position.z);
  finalReturnValue.normal = inputs.normal;
  finalReturnValue.texCoord = temp_0;
  finalReturnValue.color = inputs.color;
  return finalReturnValue;
}

I think the issue with assigning to a component is that it currently modifies the original node that ComponentNodes reference:

set(newValue) {
this.componentsChanged = true;
if (isUnaryExpressionNode(this)) {
this.node.value = newValue;
} else {
value = newValue;
}
}
})

Instead, we might need to make setting a component replace the whole original node so that references to components from before the modification stay unchanged, and new references after the modification have access to a different, changed value.

@davepagurek
Copy link
Contributor Author

Some more thoughts: If we replace the original node, then we'd likely have to rewrite statements like this:

// Before
inputs.position.y += sin(t);
// After
inputs.position = inputs.position.set('y', inputs.position.y + sin(t)); // set() returns a new node

Another approach could be to make it so that component nodes own their own value, so that earlier references to inputs.position.y aren't affected by later assignments, as it would replace the y component with a new, different node object.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant