1
votes

I'm writing a rather complex webgl application in three.js and in order to have more control on my mesh materials I'm defining them through shaders.

However, I'm facing a serious problem for which I could not find any truly satisfying answer. Let's assume the following strong pre-requisites:

  1. my geometry is indexed, i.e. the vertices are NOT duplicated among faces and I don't want to change this
  2. the geometry is stored in a THREE.BufferGeometry object, again I don't want to switch to THREE.Geometry
  3. I don't want to duplicate faces and/or create new geometries
  4. I don't want any two-ways rendering that would drop my framerate

Given the above, let's say I click on a triangle (i.e., shoot a ray and get the closest face intersecting the ray) of my geometry and I want such triangle to be highlighted with a different color.

I'm already performing this in a quite efficient way by assigning a custom vertex attribute, say 1.0, to each vertex belonging to a face to be highlighted.

In the vertex shader I define a varying with such vertex attribute and within the fragment shader I just execute the following (pseudocode):

vec4 frag_color=mix(default_color,highlight_color,(vert_attribute>1-eps));

where eps is a small value (say 1.0e-4).

The idea is that each fragment interpolates its corresponding vertex attributes, say Vatt_1, Vatt_2 and Vatt_3, and if all the three attributes get the value 1.0 thus their interpolation is still nearly 1.0 (not precisely because of some roundoff error, that's why I use a small tolerance eps) and the test is true.

If the test is true I have frag_color=highlight_color.

On the other hand, if at least one Vatt_i (i=1,2,3) is not 1.0 but the default 0.0, the interpolation at the current fragment is <1-eps and the test is false (giving frag_color=default_color).

This seems to work perfectly, but now I have the following problem that looks truly challenging (given the constraints 1 and 2 above):

I don't have any simple way in the fragment shader to know whether or not the current fragment belongs to a specific face (or stays within three specific vertices, which is the same). So, if I select two triangles T1 and T2, and by chance a third triangle T3 has one (or two) vertices shared with T1 and two (or one) vertices shared with T2 I get that T3 gets highlighted too because its three vertices get the attribute 1.0.

Of course, seen from a human perspective this shouldn't happen because the three "highlighted" vertices of T3 should "logically" refer to two different highlighted faces but for obvious reasons the fragment shader highlights also T3.

This is a quite general problem I guess, I read a lot of forums without finding any satisfying possible solution. I understand that "this is how it works" and that the fragment shader does not have any knowledge of the background triangle nor its vertices, but here I'm looking for some clever idea or trick.

Does anybody have any suggestion to face this issue? Sorry for bothering but just to prevent some possible arguments: I consider the four points 1-4 above as strong requirements because otherwise I'd have other problems related to the overall performance and I don't want to pay this price.

Thanks in advance

1
Just a quick Idea, could you maybe assign different integer-numbers per selected face (all others set to 0.5 for instance) and then use fract(vert_attribute) > 1e-5 for the test? EDIT: well, forget about that. I missed the obvious point that you'd need to have different values for the same vertex :/Martin Schuhfuß
also: thanks a lot for the insight, I always thought something like that simply isn't at all possible when using indexed geometries.Martin Schuhfuß
@MartinSchuhfuß, thanks for your suggestion but I think this won't work. The interpolation made by the fragment shader would completely mess up any logic stored within one single float attribute assigned to each vertex. Moreover, what to do in case you want to select two faces sharing a vertex? What value should you assign to that vertex? Some combination of the face ids? And with more faces? That's nearly unfeasible I guess. I tried a different approach which should work but looks too complex to me, like shooting a fly with a cannon.Luca Detomi
let's assume we pass a texture as uniform to the vertex shader, where we store all the vertices and their vertex faces, then we could encode the vertex faces within an array of rgba values (setting 1.0 if a face shares the vertex, 0.0 if not), if the face ids have been smartly set a "reasonable" triangular mesh should require a rather small vector, I'd say 8 vec4 (i.e. 32 floats), maybe less, which is normally allowed by MAX_VARYING_VECTORS. So the frag shader would interpolate 3 vectors of, say, 32 floats that are 0.0 or 1.0, and only one face would have all 1.0.Luca Detomi
May be you can turn the face to point the other way. That is, swap 2 of the vertices in the face definition, this will make the face normal point in the opposite direction and you can render the face differentlyvals

1 Answers

1
votes

You can know if a point is inside a triangle using barycentric coordinates. This can be done at vertex or fragment shader level depending on your needs.

Just get the coordinates of the vertices of the triangle that you want to test and convert the coordinates of the current vertex or fragment to barycentric coordinates. After that a simple test of the value of barycentric coordinates will tell you if the point is inside the triangle or not.