How to displace and color a hovered triangle and its neighbours of a mesh?

I have a Raytracer and interaction going with a mesh. Hovering over the mesh I get the three indices of the vertecies that form the triangle and the faceIndex.

But I am a bit at a loss on how I actually pass the information down to the shader level, so that I can actually do stuff based on that hovering info (like displacing the vertices or changing the color of the triangle, etc.)

I have this piece of code that sets the faceIndex array as an attribute to the geometry:

if (!geometry.attributes.faceIndex) {
  const vertexCount = geometry.index ? geometry.index.count : geometry.attributes.position.count;
  const faceIndices = new Float32Array(vertexCount);

  for (let i = 0; i < vertexCount; i += 3) {
    const faceIndex = Math.floor(i / 3);

    faceIndices[i] = faceIndex;
    faceIndices[i + 1] = faceIndex;
    faceIndices[i + 2] = faceIndex;
  }

  geometry.setAttribute('faceIndex', new BufferAttribute(faceIndices, 1));
}

And in my vertex shader I have this code:

attribute float faceIndex;

varying vec3 vWorldPosition;
varying vec2 vUv;
varying float vIsHovered;

uniform float uHoveredFaceIndex;

void main() {
    vWorldPosition = (modelMatrix * vec4(position, 1.0)).xyz;
    vUv = uv;

    vIsHovered = step(abs(faceIndex - uHoveredFaceIndex), 0.5);
}

And in my fragment shader I check vIsHovered and set the color to red if it is high. But what I get is this when hovering the lower triangle:

And this when hovering the upper triangle:

I think I am missing some fundamental concept here, because as far as I understand it the vertex shader is run three times for a triangle (for each vertex). So how can I determine if the current fragment is inside the hovered triangle?

What I would like to do ultimately is hover over a a triangle and displace it and the surrounding triangles (in a given radius) and lower the opacity. Is this the correct approach?

There are many to approach this…

One way is to create a unform vector3 containing the raycast result .point, then in the vertex shader, checking the worldspace vertex position distance to the raycast point.

If you’re really interested in triangles and not vertex distance to hit point, then you could store the triangle untransformed vertices in 3 uniforms, say, A, B, and C, and compare in the vs (position == A) || (position == B) etc.

There are more approaches but those are just 2 off the top of my head.

Are you trying to paint/deform heightmaps/terrains?

1 Like

What I envision is “opening up” a blocky tube capsule. Bascially it’s a repeated cylinder with 6 sides. There is a model inside that tube and the user can hover over the capsule and then the faces open up and get half transparent. I want to do that per face, but I just realized that triangles might look a bit “meh”, since as a user I would expect that rectangles get displaced and not triangles.

And now I am asking myself how I can ensure that. Can I do this with SDFs? Now I am thinking about creating a box SDF around the raycast point and check if the worldPosition in the fragment shader is inside that box. Sorry, I am still very new to all this and the amount of new concepts I am learning is quite overwhelming. Any tip on a good direction would be very helpful :slight_smile:

I’m not sure I understand what you actually want to do, but if the geometry is not very complex, there is no need to go down to a shader. A pure JS solution might be sufficient as performance, and much easier to work with. Here is a model with almost 10000 vertices modified in real-time without shader. Maybe your blocky tube capsule has much less vertices?

https://codepen.io/boytchev/full/eYxGyPG

image

2 Likes

Hmh, well yes the capsule has way less vertices. Maybe I’ll should try to reiterate what I want to achieve:

I have this mesh and I want the user to be able to hover the ring mesh (basically a torus). On hover the hovered rectangle and its neighbors (and their neighbors) should distinctively displace a defined distance along their normal vector, so that the mesh basically “explodes” at that point. That displacement distance gets reduced the farther away from the hovered point the rectangle is. But the faces should “break” off the mesh and basically float in space and once the user removes the hover it falls back into it’s original space in the mesh. Furthermore the opacity should get reduced to a value so that the user can look inside the mesh (which I can set on the material). Basically think of it as a “plating hull” that the user can move out of the way to see what’s inside.

It looks like there will be no any performance issues to do this with JS. So, unless there is some compelling reason to push things down to a shader, I’d suggest doing it in JS. At least this is what I’d do. For each face you could store its counterpart face, so all pairs (making a quadrilateral) could move together. The shape looks rather simple, so it might be possible to have each couple of triangles as a separate object. This will make things much simpler but unnoticeably less performant.

1 Like

Would I have to do this on the model side? Or can i break out faces on three js side?

To allow faces can be popped out individually.. you can use geometry = geometry.toNonIndexed() at init time… once you do that.. each triangle (not quad!) will be manipulable without distorting its neighbors. however, making them selectively transparent in only js might be a bit tricky I think.
I’m unsure on how well per vertex alpha is supported on the js geometry side. You could maybe shrink them toward their centroid or something though.. or blast them outward.

If it’s going to be an animated effect, you may want to have a copy of the geometry, and each frame, restore your working model from the copy one before applying the mouse cursor based distortion.

Here is a demo of a pure Three.js-only torus (without external model, without custom shaders). Each “face” of the torus can be moved along its normal vector and can has its transparency set independent on the other “faces”. I hope this may give you some idea.

https://codepen.io/boytchev/full/RNPyxoQ

image

4 Likes