TheBeach54 / SnowSimulation

116 stars 33 forks source link

Scalability Issue #1

Open razzraziel opened 5 years ago

razzraziel commented 5 years ago

Hey, first of all thank you for this clever solution for deforming terrains. Also you have another extra effect with particles, thats really impressive.

I used this and works great, well until it gets bigger. It works great when on max 10x10 sized planes/terrains but there is a scalebility issue. So a bigger plane/terrain than 10x scale (or a model with 100x100 gameworld size) camera area gets so small that it cant track smaller objects, like feet of characters. And even after 200x we cant detect them on camera render but only bigger objects.

I tried 2 solutions for this myself.

First I rearranged the cam position so it stays closer to player and follows the player everyframe underneath (This is also deform render area). But since it doesnt cover the whole area, camera script needs to render this image with a smaller one in comparison to tessellation image (zoomed area/whole area rate) and shaders also needs to follow player position and combine them instead of swapping.

I'm pretty new to shader coding so I couldn't manage to do this.

Second solution, i tried to swap to smaller terrains in relation with player position. Instead of one big 100x scaled terrain, 10x sized terrains to cover same area with same zoomed render camera. So when player moves away 50 from render cam, it moves to other new terrains' center and renders for its tessellation map. It worked but it is a lot of work since there will be tons of little terrains and we need to use their own maps etc.

So first solution is easier and optimal but as i said i couldn't manage to do. Is there any way or any ideas for this? Really need your help here because this project is great and i want to use.

TheBeach54 commented 5 years ago

Hey, glad you liked it ! There is multiple way you can achieve that but I implemented one for water simulation that seems to be a good way to scale virtually infinitely the simulation. The idea is to, as you said, follow the character around. Make sure to move the camera step by step on a grid and not precisely every frame. After that you want to offset the sample in the shader itself based on the camera movement on the grid. The grid can be small but needs to be coherent in terms of pixel ratio of the rendering. What you end up with is the simulation capture always catching up on the player position and the rendering/iteration compensating for this movement by offsetting the stored frame to line up with the new freshly rendered frame. To finalize you can smoothly erase the snow interaction before it goes out of the simulation area, sort of a gradient on the edges. The upside is that you can always have interaction without increasing the cost on anything CPU/GPU wise but the downside is that you cannot go back to an old place and see your mark, everything gets erase when you walk out. It works well for water simulation cause it fades out anyway but for snow sand or whatever it can be underwhelming. You can justify it by always fade out the simulation when it snows or in a dust storm tho.

Another solution, way harder to implement and optimize is to slice your world into a virtual grid and associate a renderTexture to each cell which you store in memory or even disk if your streamer can do that. Then it is a matter of creating a clever streaming system that would stream in at high resolution the texture of the current and three closest agacent cells and lower the mip map at a distance. You then do the simulation only on the cells around you or the ones that need it. Like, only render the cell when an object is moving inside. With this system you can imagine creating textures at a fixed resolution for every cells and stores them in memory/disk and when you're out of memory just trigger a storm that would wipe everything. You can imagine a system that would stream in a distant cell to draw decals on that texture for example to draw foosteps of animals, that would still be there when you visit the area but would have been dynamicly drawn by the system outside of your range/view. and without actually needing to render the depth pass itself, which would required more GPU time.

Personnally I went for the first solution because it was easier but I kind of like the second solution better ( which you kinda thought about on your own ). It is elegant and persistent while allowing you to actually tie gameplay into it. It is also more satisfying to be able to see your mark when you come back to a place. Anyways I hope it helps. Good luck on implementing that !

razzraziel commented 5 years ago

Hey mate, thanks for your answer. I'm gonna build something great and unique and this will help me a lot. So i got this before asking you previous post:

https://www.youtube.com/watch?v=khwhmGpOD4I

But i still have scaling problem. I tried the first method but no luck.

First lets look into code, each frame we send 3 textures to shaders right.

snowRenderMat.SetTexture("_DisplaceTex", _rT2);
snowReceiveMat.SetTexture("_SnowState", _rT2);
snowReceiveMat.SetTexture("_MainTex", _rT1);

One for sand terrain material, and two for Receive Terrain shader. So i made my grid/offset system with render cam. But these are sending the whole terrain texture so i needed to resize this to my grid size. I still cant figure it out in shader so to test it i made it in cs code with extra temporary Render Texture.

Graphics.CopyTexture(_rTtemp, 0, 0, 0, 0, _rTtemp.width, _rTtemp.height, _rT2 , 0, 0,
 _rT2.width / 2 - _rTtemp.width / 2 + offsetW * _rTtemp.width,
 _rT2.height / 2 - _rTtemp.height / 2 + offsetH * _rTtemp.height);

                //float ust = _rT2.width / 2 - _rTtemp.width / 2 + offsetW * _rTtemp.width;
                //float ust2 = _rT2.height / 2 - _rTtemp.height / 2 + offsetH * _rTtemp.height;

                //snowRenderMat.SetTextureOffset("_DisplaceTex", new Vector2(0, 0));
                //snowRenderMat.SetTextureScale("_DisplaceTex", new Vector2(1, 1));

                snowRenderMat.SetTexture("_DisplaceTex", _rT2);
                //snowRenderMat.SetTextureOffset("_DisplaceTex", new Vector2(0, 0));
                //snowReceiveMat.SetTextureOffset("_SnowState", new Vector2(0, 0));
                //snowReceiveMat.SetTextureScale("_SnowState", new Vector2(1, 1));
                snowReceiveMat.SetTexture("_SnowState", _rT2);

                //snowReceiveMat.SetTextureOffset("_MainTex", new Vector2(0, 0.1f));
                //snowReceiveMat.SetTextureScale("_MainTex", new Vector2(0.1f, 0.1f));
                snowReceiveMat.SetTexture("_MainTex", _rT1);

cam.targetTexture = _rTtemp;

_rTtemp here is smaller grid sized capture which render camera only uses. I'm doing this for each frame depends on rtFlag (to _rT1 and _rT2). But still i have this issue.

https://www.youtube.com/watch?v=fumOpatZnqY

For some reason in the shader it uses a mirrored texture i suppose. I think its Receiver.

float2 uv2 = float2(i.uv.x, 1-i.uv.y);

So it renders with right offset first frame but it renders mirrored offset next frame. So i guess i need to change shader code? How did you do that with your water example?

Also another question, there is also this issue. I guess this is because of render texture size. It is happening in your example scenes too. And it gets better when you increase the texture size (2048 here).

https://www.youtube.com/watch?v=oO63IfdPu4o

So these are my problems right now. I'm doing everything myself (solo dev) from modelling to coding. So if i cant find a solution, i will add these soft terrains in my scenes as partials not for whole terrain because of these issues. Maybe i'll fix these in the future but for now i cant.

Also your GPU particles are great, did i tell that? They consume less fps than regular rain effects but deforming terrain in a superb way, have a look at this snow melting rain example.

https://www.youtube.com/watch?v=bS1Osug2WCc

I have a lot of plans to use these (tornados, sand storms, snow effects etc). So if i can figure this issues i can make more dynamic world without borders. If i cant, i will only use partial terrain, which sucks but well... Thank you for reading this.

TheBeach54 commented 5 years ago

Hello, As I said there is no easy solution to this but i'm maybe going to upload the water shader too so you can have a look at how I worked there, i'm using a computer shader for the simulation and a renderTarget for the collisions. But I still don't think you want to do that for snow and sand.

"For some reason in the shader it uses a mirrored texture i suppose. I think its Receiver." I'm doing that because the capture from the bottom is effectively mirrored when you think about it.. Since i'm using the UV's of the terrain to project the texture but the camera captures from the bottom so I decided to inverse one. And it was easier to reverse the incoming new texture before entering the simulation. However I think the projection in the actual terrain shader shouldn't be base on UV's but world position so that you can free yourself of the terrain's UV's. Meaning you will always project where the camera is. Then it is up to you to move the camera and offset the whole Current Simulation texture of the same distance but in the other direction before comparing it with the newly captured frame, that would effectively move the simulation infinitely. The actual terrain will always render the heightmap in the right place because you use world pos defined by the camera position and size. I think it's the way to solve you mirrored problem and potentially the way to scale it infinitely.

With a system like so it should be easier to setup a terrain system with a grid of different simulations by splitting your terrain into cell that receives differents heightmap. Then you can turn them on and off and keep the texture in memory or disk for later use when you go far away.

I like the way you used my particles, good stuff, I guess you also use it for the trail in the sand on the first video ? Really cool. If I were you I would get rid of unused part of the Particle compute shader when you ship the game as it's expensive in terms of collisions computations and other effect that you might not use !

I hope it helped, cheers

TheBeach54 commented 5 years ago

Oh and https://youtu.be/fumOpatZnqY?t=45 I think you need to rerender the terrain backface render to compare with the right height ? Maybe

razzraziel commented 5 years ago

ok thanks for reply, i'll look into that. And yea your water shaders would also helps a lot.

what do you think about this? https://www.youtube.com/watch?v=oO63IfdPu4o

I think this is about antialiasing. it happens in your examples too but since here model is smaller and the terrain is bigger it effects more.

TheBeach54 commented 5 years ago

Yes its a problem i got and I've never been able to fix it

veerxyz commented 5 years ago

Hey there, is there any possible way i can use this for my android?

TheBeach54 commented 5 years ago

That's a very expensive GPU computation and probably can't be run properly on mobile hardware. So i'd say no, but it's still worth a try if you optimise it

veerxyz commented 5 years ago

Thank You. :D

razzraziel commented 5 years ago

Hi TheBeach54,

Your dedicated fan here. I'm still grateful for your efforts in these shaders. They're kinda costy, but i managed to improve performance. Now i can cover 4 square km with these without extra cost.

Here is the update:

Since 4 square km is big for these little planes (40.000 x 10unit sized planes), i scaled up them to 160 units. So i just use 156 planes to cover. They automatically instantiate and cover all terrain and snap to ground. Why 160 units? Because its perfect for 2k texture.

But instead of using 2k textures (156 x 2k :/ ) i'm using 1k textures with rgba channels. Each channel represent a quarter of 2k size. So i can cover same size of 2k texture with one 1k texture. That was one of main optimizations.

Other thing is i took advantage of my grid system and scaled down your ReceiveTerrain shader's texture. As i said, it needs 2k texture for 160x160 units plane for good deformation resolution (which means 4m pixel calculations per process per frame). And you know there is a lot of other calculations too (evaluation of slopes, particles etc). So since i use grids and another 1k texture for scaling down the main r channel (ReceiveTerrain output), i dont need bigger texture output for it anymore. So i zoomed the camera like 5x5 clip size with 128x128 texture for that area (Actually 64x64 was enough but for better resolution i doubled it). So all the calculations in ReceiveTerrain shader just done in 128x128, so i can keep all functions in them now. Result: 4K resolution deformation quality done in 128px.

https://i.ibb.co/j5JX2K9/image.png (Left upper debug is 128px, bottom one is 1k rgba)

Now i'll try to modify your slope function (H00, H01... one). I want something like in Red Dead Redemption 2, when character moves, snow or sand go up at closest point. Any idea would be great :)

https://i.ibb.co/sCYx9SF/image.png source vid: https://www.youtube.com/watch?v=mlFHT7EWeqk

MArk2122 commented 5 years ago

use a normal map

MArk2122 commented 5 years ago

Most likely the shader works with several materials, and uses it through a mask, and it seems to you that this is a mesh deformation.

razzraziel commented 5 years ago

no it is actual bump with real volume, you can see it with low angle. and since we're already doing slope calculations it would be easier to modify it without extra normal calc.

MArk2122 commented 5 years ago

https://drive.google.com/open?id=1qsy8D4UWCnp5mTCqPq4wzzEW1dlz5GW2

And what if you use the principle of displacement map In any case, you need to calculate the edge, the top point. Make offset to make extrusion to the top.

MArk2122 commented 5 years ago

An option with shaders can also be, one of the shaders can be done with the function displacements, which will calculate where the angle is, and in these places will do the same as RDR2

TheBeach54 commented 5 years ago

i'm not sure what you're asking but the h00 h01 etc is to calculate the slope as you said. To get an effect like RDR2 where the snow accumulate around the foot creating this hole with some kind of "edge" you need to change the way i'm rendering the character in the snow. Now i'm just taking the max of current value and depth map of the character... You need to go further than that. Maybe a combination of the precise depth pass and a sphere operation that adds snow around the rendering of the foot or something that only affects snow when the foot moves. I'm not sure i'm answering the right thing tho .. But anyway, don't use a normal map or generate it at runtime through a compute shader

veerxyz commented 5 years ago

This is so great! I saw @razzraziel's video on youtube and I liked it! Although any of you can also share some light and guide towards making it work for ES 3.0 or 2.0, as in more optimized to be stutter free on an android/iphone?