- Home /
Using the GPU instead of multiple threads
I have made tiles of water, each of them made of a mesh whose vertices and normals are updated on each frame to generate waves.
This is using a lot of CPU power. So far I managed to make this multi-threaded by performing some calculations in separate threads while the main thread awaits, after what I assign the vertices/normals arrays back into the mesh in the main thread once the other threads have finished updating them.
This is how it works using pseudo code :
Awake() begin.
start threads and let them wait
Awake() end.
Update() begin
let the threads compute new vertices and normals for each water tile
wait for the threads to finish their work
assign the vertices and normals back into the mesh (here, on the main thread)
Update() end
... more Update() calls ...
OnDestroy() begin.
stop threads
OnDestroy() end.
This multithreading optimisation allowed for a huge performance gain on a quad core CPU. However, it is still very greedy in terms of power, and I have the feeling that I would be able to compute even more stuff in an even faster way if I could use the GPU.
My question is : How can I use the GPU to perform calculations that can be performed in parallel ? I.e the calculations that I currently do in separate threads and only use Vector3, Mathf and other parts of the Unity API which are allowed to be used in another thread than the main one ?
Could I create some kind of "dummy shader" that would not be used to display stuff as its supposed to be, provide it with the arrays of vertices and normals I want to update, and then retreive the resulting values in C# ?
Just a bit of detail about what I actually compute in threads, if it matters : Basically I have, for each water tile, a two dimensional array of "base" points that represent the water when there is no wave at all and a second two dimensional array of directions wich represent the normal of the water at each of these base points. Then, for each "base point", I'm moving the base point along the direction using a Simplex noise function. Something like : vertices[xzIn1D] = basePoints[x,z] + directions[x, z] * GetNoise(something)
Thank you :D
Answer by Pangamini · Feb 08, 2018 at 12:31 PM
Well, for your specific thing, waves on the water mesh, you just have to write a vertex shader, assign it to your material and watch it wave. No need to actually modify and update the mesh, CPU does not have to be involved at all. Vertex shader will do exactly what you want, without transferring the data between CPU and GPU (which is super slow). The vertex shader recalculates each displayed vertex in every frame, that's it's purpose. To place the vertex on the screen. Simply write your own vertex shader that will apply some distortion (the waves) to your vertex position, based on time and some other value (world position, wave offset texture, etc)(you might want to recalculate normals as well, but you will live without it) You can find tutorials on how to write shaders on official unity docsHow can I use the GPU to perform calculations that can be performed in parallel ?
Thanks for your answer. However I don't want my waves to be only visual. Currently, the shape of the water affects how objects like a boat behave on it. I have other components that compute buoyancy and drag physics which both require access to water level at a given point. I could probably use this approach of yours to get even better graphics, but I want my waves to be physical, not just visual, which is why I want to update the mesh. But maybe that I can provide water levels that take waves into account while having flat meshes and use a vertex shader to let the GPU deform the mesh as you suggest, so that I could have both the physical behaviour and visual apperance of waves. I don't know. Anyway, I'd still like to know how I could use the GPU for that kind of thing, even if, for this particular case your solution might be better, so that I can apply this to other things that could be done in parallel. I just read something about "compute shaders" that seems related to what I'm looking for, so I'll dig further into that.
Well, do you really need the mesh modified to apply the physics? If it's some sine wave, if you have the function for deformation in your program, can't you just evaluate the water level in location where needed, without using the actual mesh?
But O$$anonymous$$, for using GPU on such calculations, there are compute shaders but I never used those. Ins$$anonymous$$d, I like to write my input data into texture(s), then use Graphics.Blit to run it through shaders to do my calculations and then download the texture back to the CPU (which is always the slowest operation).
Yes I can return the right "wavy" water levels even with a flat mesh because i call the same code for both updating the mesh and getting water level. This is what I currently do so I could indeed go for a flat mesh. That Graphics.Blit looks like an interesting way to pass data in my case. I'll have a look into that. Are you talking about the OnRenderObject() unity event ?