7
$\begingroup$

TL;DR: For a given point in a face/polygon how can I compute the distance between that point and all the vertices on the given face with shader nodes?

Overall intent: I want to visualize complex physical data on complex geometries. For this, I want to color each pixel based on a scalar value data from each vertex. Each vertex will be assigned a value from 0 to 1 based on a physical quantity (temperature, velocity, ...) and then mapped to an RGB value based on some colormap (viridis, parula, ...) for scientific visualization.

Current approach: I can currently assign any scalar value I want to a vertex through vertex colors within face loops using a python script (using the approach from the following threads: 1, 2, 3, 4, 5). I am using the colors as containers of 4 scalar values; each of of the 4 scalar values can then be separated and assigned specific meaning (Red can be the actual physical scalar value, Green can be the roughness, and so on.); the mapping between scalar and color is done through a color ramp which I modify to be some perceptually uniform colormap.

Mapping between scalar value and color - Vertex color

Problem I am trying to avoid: Because of the way blender interpolates the colors of the underlying triangles of the geometry, the colors distribution within a single face/polygon is not uniform (i.e. it does not reflect the actual distribution of the scalar field within the face) - something I learned from a previous question.

Non-uniform color interpolation based on vertex colors (viridis) Non-uniform color interpolation based on vertex colors (red to black)

Envisioned solution: Coloring each individual pixel/point based on its coordinates and distance to the vertices of that specific face. I want to interpolate the scalar value extracted from the vertex color based on that distance and map the resulting scalar into a color. A simple test I did to showcase this was to assume one of the vertices of the cube ((x,y,z)generated = (0,0,1)) has a scalar value of 1 and all other vertices have a scalar value of 0. I computed the distance to the vertex of interest and used that as the new input to the color ramp nodes.

Mapping between scalar value and color - Distance to vertex

This yields exactly what I am looking for in terms of a color distribution that represents the scalar field.

Uniform color interpolation based on distance to vertex (viridis) Uniform color interpolation based on distance to vertex (red to black)

Current problem / question: This solution works well enough for this simple situation where the distance to a single vertex is needed - I am assuming all other vertices have a scalar of 0 thus they can be ignored in the interpolation. However, the goal is to visualize complex geometries and data which means that I would need to compute the distance to all adjacent vertices in order to correctly interpolate the scalar value. So my questions essentially boil down to the TL;DR on top and also:

  • Is what I am looking for feasible with this approach? Is some other approach preferable?
  • In more practical terms, can I act on a face-by-face basis through the shader nodes and access the vertex data of the nodes on that face for a given pixel/point?

I am happy with answers and suggestions that steer to completely different approaches than I what I am currently trying to do, I briefly mentioned my overall intent for this very reason.

Full node setup to replicate this on the default cube:

node setup

$\endgroup$
4
  • 1
    $\begingroup$ I don't think shaders are a good approach for this precisely because of the complexity reason you mention. I would suggest investigating geometry nodes, but I hesitate because they're being significantly rewritten for 3.0, so unless you can wait until January, or possibly later, they might not be usable. Have you considered generating meshes that match the fields you're trying to visualize? $\endgroup$ Oct 23 '21 at 17:02
  • $\begingroup$ Thanks for the suggestion to use geometry nodes, I am aware of them and will be looking into it. This is not a time sensitive project so if geometry nodes is the way to go I would definitely wait. I am not sure I fully understood what you mean by generating meshes that match the fields I am trying to visualize, could you expand on that a bit? The immediate use I have for something like this is to import simulation results that have a 3D mesh and some field data. $\endgroup$
    – Rui Costa
    Oct 23 '21 at 17:28
  • 1
    $\begingroup$ I'm not sure I can, because I'm not sure what you're trying to visualize, but consider how elevation data is converted into topographic maps by algorithms like marching cubes. If you were, for instance, simulating fluid and wanted to visualize density gradients, creating a series of surfaces of equal density would be much easier to color that trying to extrapolate from a flow mesh. Might not be at all relevant to what you want to visualize, though. $\endgroup$ Oct 23 '21 at 17:34
  • $\begingroup$ I get what you mean, that is definitely an option for some aspects of the data. Thank you $\endgroup$
    – Rui Costa
    Oct 23 '21 at 17:54
5
$\begingroup$

In general, you cannot access data from some other sample than the one you're evaluating. Not in Blender. (This is someplace where I wish we had partial derivatives to work with in shaders....) So you can see the vertex color or location of a sample, but not of two samples, not of some sample and some particular vertex, etc.

One reason vertex color doesn't interpolate the way you want is because shaders are run after meshes have been implicitly triangulated by Blender. What that means is that even if you could get the distance from a sample to a vertex, you'd be getting the distance to three vertices, not four.

Measuring distance from vertex is possible with geometry/parametric coordinates, if you were working with triangles, but even then, the problem is that the shader doesn't hand you these locations in understandable terms-- it doesn't know which vertices are associated with which vertex color.

The simplest thing to do here is to simply add more geometry. As you increase the vertex count, interpolation of vertex data like color improves:

enter image description here

That seems like a simple enough solution to the problem you're describing (at least, with the geometry that you're using to describe it.) Just throw on a subdivision modifier, bam, done.

If you don't mind changing the geometry destructively, using a poke faces operation on all faces will achieve the same thing, with less performance overhead.

Another option, which isn't as simple but can solve more problems, is to encode data as UV coordinates:

enter image description here

I should be dividing by sqrt(2), not sqrt(2)/2, but I already closed the file, and scalars are kind of incidental anyways.

Here, I've unwrapped the cube with a simple unwrap/reset operation to make every quad face a square that occupies the entire 0,1 UV space. Then, I can simply check the distance of my sample to the four corners of that space to see how far along the sample is along the face-- and since I can orient the faces' corners however I want in the UV map, I can even specify which vertex's distance I'm interested in measuring.

$\endgroup$
2
  • $\begingroup$ Thanks for the answer! This is great!! I like the first solution for the simplicity I have to test the overhead in the bigger models. But I think I prefer the second solution just for the fact that it should allow the coloring to be accurate. I understand that the data would have to be encoded in 0,1 2D space for each face, which seems fairly easy to accomplish, and you already outlined a quick interpolation procedure, the only concern I have is how to access the right face's data on this solution? I would need some index or face identifier to access the correct data, or am I mistaken? $\endgroup$
    – Rui Costa
    Oct 23 '21 at 18:24
  • $\begingroup$ @RuiCosta I don't know the details of your problem, so the second is really just a general answer of, "get distance to corner". You can edit the UV map for each face to specify which corner. In general, if you need to get data into a shader, you do it via a texture (which does not have to be image data, it can be any kind of data), at which point these techniques would no longer be very relevant. $\endgroup$
    – Nathan
    Oct 23 '21 at 18:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.