Best way to update material/color triangles groups in a procedurally generated mesh?

Hi,

I want to find the best way to change the material color of part of the mesh, in my case, changing the color of the mesh’s square where the mouse is hovering.

Here’s and example of what I am doing:
8480492--1127447--grid_selection.gif

Right now the way I am doing it is I am raycasting the mouse cursor over the procedural mesh, then calculating which squares of the mesh are within the range of the mouse pointer and then recalculating the mesh data and creating two sub-meshes each with a different material.

This works fine as seen in the gif, but it seems to be quite expensive performance wise, recalculating the mesh data that often, I have added a cooldown timer for how often the mouse pointer raycast is done so that the mesh data isn’t re-calculated too often but it’s still pretty expensive.

So I am wondering if there is a better way to do the same thing with better peformance, perhaps with a shader? I have searched quite a bit but couldn’t find anything about assigning a specific color or material to a group of a triangles through a shader.

yes, you want to identify the triangle quickly, which you can do via Raycast’s hitinfo
and you probably want to affect the color channel on these vertices (you can use Hidden/Internal-Colored for testing, if I’m not mistaking the name, but you ought to create a custom shader to fully utilize vertex colors), instead of swapping materials, that’s just too crazy. I mean crazy for your particular use case.

edit:
if you don’t want to show these colors as plainly, that’s also fine, you can make a shader that utilizes vertex colors only to render something else based on this information. but it’s the easiest way to affect the mesh in a meaningful and persistent way. the only caveat is that you have zero control over fragments or texels, because the colors are vertex based. so if you want to really draw like a texture, you may try using SetPixels, which can lead you to even faster results for your particular use case, because you don’t even need raycasting for that. (though SetPixels isn’t cheap, don’t get me wrong.)
/edit

raycasting shouldn’t be too expensive in and of itself.
the slow part has to be your greedy algorithm which reinvents the mesh (or multiples of them) and then uploads the whole thing to GPU each frame, while also probably allocating crazy amounts of memory which then need to be constantly garbage collected.

there are ways and ways to optimally generate procedural content in Unity. what you’re doing is good enough only for a rough prototype, i.e. far from any production code.

now I’m not here to discourage you, having a working prototype is one step towards glory. you should learn your lessons from this, and redo the whole thing if you want this to shine. frame-by-frame procedural generation has to be handled much more delicately.

this is what you want at a minimum:

  • to minimize frequency and bandwidth of communication between CPU and GPU
  • to minimize garbage (to zero if possible)
  • to reuse the already allocated structures in memory (aka pooling)
  • to streamline marshalling (by organizing data in streams or native buffers)

there are also various exposed things and frameworks in Unity itself to help you achieve many of these goals, i.e. all kinds of flags, dynamic meshes, poolers, direct draws, native memory access etc.

Thanks for the exhaustive reply.

So I should use vertex colors and that’s what the shader will use to recognize the different colored squares of the grid?
That’s the part I don’t know, how do you “select” a group of triangles and how do you pass it to the shader? so it gives each triangle group a different color and different shading properties(handled by the shader itself instead of using two materials).

Thanks

Well, first of all, make sure your mesh isn’t all in one piece. That’s one way to do it.

The other way would be to open buffer streams so that you can “live edit” the mesh, although I’m sure there is someone more knowledgeable when it comes to vertex buffers, who can explains the process to you. I’ve never tried it myself, but that’s the more robust, industrial way to do it.

For the first way, though, you manage your mesh through “chunks”, pieces of it that are of reasonable size. And you never actually change the mesh itself, you create it once, then clone it by assigning to mesh property of mesh filters.

During raycasting, you get back which mesh was hit at which triangle and which coordinate. You write this information down into the color array that was already at your disposal, like a ready canvas. You should probably have these ready for all user-accessible meshes. (Don’t forget that your code lives on the CPU side, and you should be the owner of your data, once you send something to the GPU, don’t lose a local reference to it, if your aim is to recreate the thing with an update.) With this method, you wrap up the same preallocated mesh but upload this modified color vertex array along with it. GPU side can access the color information just like any other, it’s just mostly unused with the regular Unity shaders. However it’s a perfectly valid data stream, and many specialized shaders readily use it because it has a full 32-bit range and is natively supported.

With the other method I mentioned before, you can do even more because you get to stream only the updates to GPU, but as I said, let’s stick with what I’m certain how to implement. Because you don’t allocate nothing on the CPU side, there is no time wasted on heap access, and more importantly, there is no garbage. You also do not make a new mesh every time from scratch. You just wrap things up, assign them as they are, literally only references flying around.

Leave enough room in the codebase so that you can adjust the “chunk” size and thus find a perfect trade-off between update frequency and performance, at a later stage.

You should make the process of invalidating the chunk automated, this is called a ‘dirty flag’ or dirtying, where you basically “paint” by instructing some controller/manager that the “brush” has been detected there and there with such and such operation. The “painter” then queries which mesh chunk it was, and which vertices are affected, the information is stored accordingly, and the affected chunks are marked as ‘dirty’.

Another part of the system then sweeps through ‘dirty’ stuff, and repackages them in an optimal way, making sure to do a minimum of work, or even to distribute some work over multiple frames (or threads) as needed, until everything becomes ‘clean’ again. In the meantime, the renderers are always fresh and feel responsive to user input, because the process itself is super-localized and all load is distributed.

In your specific case, you can also mathematically construct a relationship between the “brush” coordinates and the chunk/vertices. Or you can even prebuild a so-called “map”, a one-to-one correspondence where you either analyze (or scan) the setup as it was during initialization, or you actually build everything from ground up, fully in charge of how you’re going to address this space later in the code. So you can very easy completely bypass the need for raycasting, although raycasting would be the easiest way to set this up, if you’re new to system design and would like to have one functional thing that you can rely upon because it simply works out of the box.

One useful way to make a mathematical relationship is by knowing that certain binary tricks can be used to your advantage. For example, if you make your chunk sizes follow the powers of two, you can quickly transform the coordinates by shifting the values to the right. Next, if your level is a finite 2D plane like you’ve shown, there is a very simple numeration scheme to quickly identify the index of a chunk. In a setup where WxH chunks cover the rectangular area, each (X,Y) chunk can be enumerated as N=Y*W+X, letting you to linearize the array and stop thinking in 2D any more (to get X,Y back, you would do X=N%W, Y=N/W, the latter being integer division, but you mostly don’t need the inverse, at least that’s the case in my practice, because the data is already spatially arranged). And you can follow a similar scheme for the quad cells as well.

And so on, this is too much text already. Hopefully some of it makes sense to you.

I forgot to address your questions specifically. You pass the color array to the GPU, a shader will sample it like it would sample a texture normally. And indeed, as I said in my first post, you can also make a texture painter by using a similar technique.

No, you just affect the color, because anything else would lead you back to your original solution, where you were splitting up and recreating your meshes needlessly. With this design you don’t switch between materials ever. However, there are ways to introduce submeshes if you really want to have a multi-material set up, and this is definitely a recommended approach to really flesh it out and do whatever you want. However, that’s much more work, and you really need to get to the point where you’re confident with the basics.

I’m sure there are many other tutorials on this, but here’s one of them I know to be good

Or try this one, it’s closer to what you’re doing, it has painting and splits the geometry into chunks at some point

Thanks a lot, I am currently trying to do submeshes while using the advanced mesh API to see if I get better performance.

But I will anyways most likely resort to using vertex colors that get passed to a shader, I don’t mind have to re-code everything from scratch, I like testing every solution to see what works best, thank you very much for all the detailed explanations and links, much appreciated.

I lost a good “chunk” of my life trying to sort all of this out in my head as well. I’ve had a lot of practice with this approach in the last 5-6 years.

Currently I’m doing something similar in 2D, but it’s a bit crazy in some other aspects, namely I also triangulate the meshes on the fly, and I’m so happy with the engine I wrote so far, that I believe it might become a genuine title on the market, because unlike my other concepts and products this one is kind of tech-first, and so it pretty much stands out from its genre even at a glance.

In the meantime I try to lend a hand to others, because I have a firm belief we should actually behave opposite to selfish capitalism, as the good we do revolves and comes back at us. Interestingly enough, some people were kind enough to repay me in their own ways, and I’m pretty confident in that my theory works. So you’re welcome.