Search Unity

Question Advanced Fur Shader - I'm dumb and trying to do complicated things

Discussion in 'General Graphics' started by JTemple, Oct 7, 2021.

  1. JTemple

    JTemple

    Joined:
    Jan 11, 2015
    Posts:
    11
    Hello, everyone! Not sure if this is the right place for the discussion, so sorry for the inconvenience if it isn't.

    I recently got into reading about and coding shaders, specifically geometry shaders. I wanted to do some complex fur for a project I wanted to do, and I actually managed to make something work for once! But, I'm pretty bad at shader code, so I'm getting stuck a lot.

    What I achieved so far, is some strands with a square base and pyramid tip, with controllable width and length with some randomness thrown in. But, everything is kinna hard coded right now, and I would like to be able to control things like bending and curling of the strands, probably through a texture input at the end.
    Here are some images for what I got right now and my process so far:



    Hardcoded smooth:



    GIF with subdivisions and length/width working:
    https://imgur.com/DUbpMlv

    I tried adding some color to it to make it easier to look at, but I messed something up in the UV assignment and only the tip works properly:



    Linking the code file with the post, its a bit too big to be pasting it in wholesale.

    And yes, I know IF statements in a shader are very bad, but right now, I just wanna see it working.

    What I'm basically asking for is any tips, ideas for better or more streamlined solutions, or recommendations for good tutorials/articles that go beyond rendering some grass that could help me move along with the project.
    Thank you very much for your time!
     

    Attached Files:

  2. burningmime

    burningmime

    Joined:
    Jan 25, 2014
    Posts:
    845
    If statements in shaders aren't bad per se, just that it does 32-64 threads at once, and if any one of those threads takes a branch, every single one will take that branch (meaning it often evaluates both sides of an if). But branching on constants like you're doing is not a problem at all, and if most of the verts will take only one path, then the branch is good.

    For your use case, I think* you probably should look into using tesselation and not geometry shaders, since it does 80% of the work for you at the hardware level (generating the subdivisions/verts; you can it can base subdivision level on how far the input vertex is from the camera instead of the whole object, etc). Also supposably geometry shaders are poorly implemented by many GPUs and not supported on some APIs (Metal). Best tutorial on tessellation I found: https://catlikecoding.com/unity/tutorials/advanced-rendering/tessellation/ . In the hull and patch shaders, you do the subdivision into verts, and once you get to the domain shader, you make the verts actually look furry. Compute shaders are another option.

    But fur effects (at least for reasonably short fur) are usually done by rendering multiple "shells" each slightly bigger than the previous with a custom fragment shader. Doing individual hairs is going to be much slower and potentially overkill. If you want to get really fancy, there's also raymarching-based approaches: https://www.shadertoy.com/view/XsfGWN or https://www.shadertoy.com/view/XsfSR8

    EDIT: although really if you just want to get 'er done... https://assetstore.unity.com/packages/vfx/shaders/imperial-fur-pbr-32522 or https://assetstore.unity.com/packag...ts/pidi-xfur-studio-2-ultimate-edition-145885

    * (but don't trust me on this, I'm a database engineer not a graphics programmer by day)
     
    Last edited: Oct 8, 2021
    Torbach78 and JTemple like this.
  3. JTemple

    JTemple

    Joined:
    Jan 11, 2015
    Posts:
    11
    Oooh, interesting, I didn't know tesselation could be used that way! Definitely something worth reading into, tho I should have probably mentioned, I really want the fur to have some thickness on it and be more "clumpy", kinna stylized.
    I don't mind that geometry shaders have problems on some GPUs, this isn't a commercial product that needs to run bug free on 1000s of pcs x3
    I will have to look up stuff on compute shaders, my attempts at implementing them in the past have not been very successful, but I would really like to learn!

    And yeah, I know there is assets out there that do similar stuff xD
    But, I enjoy the challenge, and I wanna know what goes on in there and be able to modify it. Modifying ready made stuff usually just breaks it for me
     
  4. JTemple

    JTemple

    Joined:
    Jan 11, 2015
    Posts:
    11
    Woke up with some new ideas today, parametrized the strand creation to be based on even points on a circle:


    So now the code is cleaner and it can do any shape from a flat line, to a triangle, to a hexagon or whatever one wants.




    Still need to find a way to handle the thickness falloff well, but though I would update the progress x3
     
  5. JTemple

    JTemple

    Joined:
    Jan 11, 2015
    Posts:
    11
    Solved some of my problems with a couple of lerps and adding more data to the quad creating part. Still not sure whats going on with the UVs, this felt straight forward to me, stretch every quad from left to right, and across the proportional height of the current segment, but for some reason, it only works like that on one subdivision.

    Screen Shot 10-08-21 at 10.15 PM 001.PNG Screen Shot 10-08-21 at 10.15 PM.PNG
    Screen Shot 10-08-21 at 10.16 PM.PNG Screen Shot 10-08-21 at 10.24 PM 001.PNG

    now i just gotta figure out how to make them vary in shape and stuff, thats gonna be the hard part...
     

    Attached Files:

    joshuacwilde likes this.
  6. JTemple

    JTemple

    Joined:
    Jan 11, 2015
    Posts:
    11
    Hello again!
    I took a little break from writing shaders to work on some gameplay, but I'm back at it today!
    Trying to learn some compute shaders after reading about what they can do, did a couple of tutorials, which went alright, but now I'm trying to write something from scratch and running into all sorts of problems.

    All I'm trying to do right now is for it to generate one triangle per given point, and everything generates fine, all indices and vertices are there, but for some reason, all vertices have the same value? What am I doing wrong?
    Screen Shot 10-15-21 at 11.28 PM.PNG Screen Shot 10-15-21 at 11.28 PM 001.PNG
    Screen Shot 10-15-21 at 11.27 PM.PNG
     
  7. burningmime

    burningmime

    Joined:
    Jan 25, 2014
    Posts:
    845
    It's hard to tell from what you put here. Could OUT_VERT_STRIDE be zero?
     
  8. JTemple

    JTemple

    Joined:
    Jan 11, 2015
    Posts:
    11
    I think I'm calculating it like you are supposed to, taking each float value of the struct, its just one vector for now, so I don't see how that could be wrong...
    Screen Shot 10-16-21 at 07.41 PM.PNG

    I fixed the threading too, since I was definitely doing that wrong, and that solved some problems, like vertices past index 4 having 0 values, but still, no mater how many triangles I try to do, they all have the same vertex value...
    Screen Shot 10-16-21 at 07.38 PM.PNG Screen Shot 10-16-21 at 07.43 PM.PNG

    I'm uploading the whole code, if you wanna have a closer look
     

    Attached Files:

  9. JTemple

    JTemple

    Joined:
    Jan 11, 2015
    Posts:
    11
    also, just in case there are doubts, i rechecked the function i use to generate the corners of a triangle in c#, and it works perfectly there, as it did when I was doing it in Geometry shader

    Screen Shot 10-16-21 at 07.56 PM.PNG
    Screen Shot 10-16-21 at 07.55 PM.PNG

    This is where the corners should be when I'm generating them so I dunno...
    Maybe it would be easier to just use a quad as a base with some variable offsets on width and thickness, but its really annoying that it just refuses to work xD
     
  10. burningmime

    burningmime

    Joined:
    Jan 25, 2014
    Posts:
    845
    I'll take a look this weekend; I need a reason to procrastinate on raking leaves anyway.

    Only things that jump out at me is first this looks like these 2 lines are in the wrong order:

    Code (CSharp):
    1.         furGenerator.Dispatch(kernelID, dispatchSize, 1, 1);
    2.         inputPointBuffer.SetData(inputPoints);
    I think it is the default, but you may want to add "uniform" to all the global variables in the compute shader like _NumOfPoints
     
  11. JTemple

    JTemple

    Joined:
    Jan 11, 2015
    Posts:
    11
    Yeah, I reordered this particular bit a bunch of times to no effect...
    This is actually how it was in the tutorials I was reading, which seemed odd to me, so I put it in front at first too.

    Tried the uniform thing, nothing happen either...
     
  12. JTemple

    JTemple

    Joined:
    Jan 11, 2015
    Posts:
    11
    SO, I went over the whole code with a friend, stripped down everything to check what was working and we finally found the culprit...

    its this guy here:
    Screen Shot 10-16-21 at 09.28 PM.PNG

    For some reason, everything done with this little guy returned 1, no matter what. We were both stunned, but the moment I replaced it with this:
    Screen Shot 10-16-21 at 09.28 PM 001.PNG

    Everything worked perfectly! (the triangles are upside down but thats easy to fix)
    Screen Shot 10-16-21 at 09.30 PM.PNG

    So yeah, hope that helps anyone else having troubles with PI in their code, I have no idea what the difference is between the two, but just use #define going forward xD
     
  13. burningmime

    burningmime

    Joined:
    Jan 25, 2014
    Posts:
    845
    Awesome; glad you figured it out.

    By the way, you shouldn't need to generate triangles indices at all if you use https://docs.unity3d.com/ScriptReference/Rendering.CommandBuffer.DrawProcedural.html . In your vertex shader you do something like:

    Code (CSharp):
    1. uniform StructuredBuffer<float4> _verticesIn;
    2. float4 shadowMesh_VS(uint vertexId : SV_VertexID) : SV_Position
    3. {
    4.     float3 positionWS = _verticesIn[vertexId].xyz;
    5.     // ...
    6. }
    The magic part is the SV_VertexID which gives you the index into your StructuredBuffer. This way, you never have to send any data back to the CPU; you just generate it and access it all on the GPU.
     
  14. JTemple

    JTemple

    Joined:
    Jan 11, 2015
    Posts:
    11
    Yeah, I'm currently reading up and watching tutorials on how to do just that, the mesh part in c# was just for testing
     
  15. JTemple

    JTemple

    Joined:
    Jan 11, 2015
    Posts:
    11
    Btw, a lot of people are using Universal RP in their tutorials, does it make difference if I'm doing this on built-in? Should I make a switch to URP?
     
  16. burningmime

    burningmime

    Joined:
    Jan 25, 2014
    Posts:
    845
    Well, SRPs (URP/HDRP) are the "future" of Unity so are getting new features, while builtin hasn't had any new features since like 2016. Also, they are starting to be more performant, although it depends on what you're trying to do. They are more complex, though.

    For what you're doing, HDRP might be a good fit. HDRP also has a ton more features than URP. URP is more for targeting low-mid end hardware, eg phones and Switch
     
    JTemple likes this.