Fur Shader

I made a 20 pass two directional light + ambient fur shader for a game I’m working on. I’m sharing it in hopes that someone will help optimize it, since this is my first Cg program. :slight_smile:

It should run on anything that supports vertex programs. It runs really slowly on a GMA 950 as it is vertex bound, but taking out a light and making it 10 passes can make it acceptable for that card. Not much else to do about it. :frowning:

The crummy thing is that it doesn’t use Unity’s lighting system, so I have a script that passes two directional lights of your choice to the shader that you stick on the furry object. I don’t know how to make it use Unity’s lighting system.

It uses shells and not fins, but you can fade out the shells along the edges with one of the parameters if you want to add some fins with another material / triangle list.

Cheers,
-Jon

33957–1236–$fur_110.unitypackage (4.08 MB)
33957--1237--$furshader_153.jpg

2 Likes

If only all the soldiers in Global Conflicts Palestine looked like this. :smile:

-Jon

33961--1238--$picture_11_202.jpg

Your Cg is quite good in fact! Two things I changed:

I changed the way you do extrusion. I just add normal to the object space position, and then transform that by regular MVP matrix. This produces different result than extrusion you did (which was some sort of extrusion in post-perspective space), but it saves a couple of instructions; and possibly some shader constants because less parameters are used.

Then I removed support for texture scale&offset. So that’s a loss of flexibility, but a save of matrix multiplication in the vertex shader.

So now the vertex shader is 28 instructions, down from 34. That could make some difference on GMA950; hard to guess how much exactly.

I’ve also moved some more code into the helper .cginc file, so the shader file itself is now shorter.

Other interesting things to try (no for GMA950 though… I think): do a proper anisotropic lighting on the fur. Now it treats fur normals the same as surface normals; whereas in real fur the normals point to all directions orthogonal to surface normal.

33964–1239–$fur_aras_100.unitypackage (4.08 MB)

Yeah, I’m not sure what was going on with that. I tried something like you’ve done first but couldn’t get it working for some reason. Thanks!

Yeah, don’t need this for a real character.

It did! In a test scene it went from 17 fps to 25 fps. Now with a 10 pass 1 light version it is even more acceptable. Of course it still runs hundreds of FPS even on a lowly Radeon 9600…

Ahh, clever. Thanks

Yeah, there seems to be a ton more to be done with this, but I think it may be good/slow enough for our purposes for now. Nicholas pointed me to these cool slides with way better fur. He also helped idea wise with the shader, so thanks Nicholas. :slight_smile:
http://ati.amd.com/developer/SIGGRAPH02/CustomizableFur_Isidoro.pdf

-Jon

Jon-Aras,
You guys are fantastic-seriously. Is there anything you cant do?

This takes things to a new level. I cant believe how quickly you can whip stuff up like this. Mega cred 2 u guys.
AC

The soldier looks nice. :O)

One question: Do the shells result into real polys in the end like if you just would clone a modell, enlarge it and reduce the alpha value of the texture step by step?

Old skool, shaderless… http://spielwiese.marune.de/_dir/_tmp/fur.html

Yes. In essence this draws the model 20 times, extruded more and more each time. So it’s almost the same as creating 20 shells manually (the vertex shader also does some fading out at the edges).

1 Like

Taumel: That would be rather cool as a sun with a wreath of flame.

@Aras

Is a shader version theoretically any faster because for instance it spares this or that mathematical transformation compared to a grouped object where i just rotate the parent?

The sw3d example is overall under 50 lines of code and also runs on really old shaderless hardware. A shaderversion for sure is more flexible and the better way to go if you make it more complex.

By the way is there a good tutorial around (a crash course) for working with shaders in unity? I only did four really simple things and i guess there’s much more to explore…

@Marble
I’m not sure, i guess there are better ways to fake a sun. I think i would more go for a particel solution then…

Does the shaderless version simply scale the object up? That only works for very simple geometry as you want to extrude each vertex along the normal. For a sphere that’s fine, but not for anthing real.

You can use the mesh interface to do pretty much the same in Unity at loadtime. Performance should be about the same.

The shader one is faster once you begin skinning the objects.

Yep, it only clones (same ressource) and scales a sphere but it would be enough for a marble game or even a fur-pacman… ;O)

I managed to shave off two more instructions from the vertex shader. Replace FurVertexPass in my previous package’s Fur/Support/FurHelpers.cginc with this:

void FurVertexPass(float multiplier, appdata_base v, float furLength,
    out float4 pos, out float fog, out float4 color, out float4 uv)
{
    // extrude position
    float4 wpos = v.vertex;
    wpos.xyz += v.normal * furLength * multiplier;
    pos = mul(glstate.matrix.mvp, wpos);    

    // fog
    fog = pos.w;
    
    // UV (pass through to save instr - loses tiling/offset though)
    uv = v.texcoord;
    
    // edge fade out
    float alpha = 1 - (multiplier * multiplier);
    // transform normal into eye space and use Z component
    // to control the fade
    float3 eyeNormal = mul((float3x3)glstate.matrix.modelview[0],
        v.normal);
    alpha += saturate(eyeNormal.z) - _EdgeFade;
    
    // lighting
    float3 normalWorld = mul ((float3x3)_Object2World, v.normal);
    
    float light0 = clamp(dot(normalWorld, _LightDirection0.xyz),
        _MyLightColor0.w, 1);
    float light1 = clamp(dot(normalWorld, _LightDirection1.xyz),
        _MyLightColor1.w, 1);

    color = float4((light0 * _MyLightColor0).xyz +
        (light1 * _MyLightColor1).xyz,alpha);
}

I changed the part that calculates the alpha - instead of calculating view direction (which involves a costly normalize), I just transform normal into view space and use it’s Z component to do the fade.

Works…

Any idea on a shader crash course?

Here’s how I learnt it:

I started out by playing with texture combiners - like trying to do nice glass with fresnel falloff (which can be made to run on anything).

Next I would make them run on more modern combiners. IIRC, you have a Mac mini with a radeon9000 GFX card. That means moving the shaders to the ATIFS1 assembly language (which is simpler than the name implies). There are a bunch of examples at the bottom of the ATI_TEXT_FRAGMENT_SHADER spec.

ATI has a lot of Radeon85000-based articles online from ShaderX1 - they are made for DirectX, so the format is bit different from the OpenGL one used in Unity. Moving these over gets you going - basically you get in the habit of “thinking like a pixel”.

Ignore lighting at first - it’s quite complex, a major hassle not very fun. Just focus on doing fun effects (gems, ligtning, water, etc).

@Nicholas
Okay, thanks

@Aras
Could you alter your one so that the tiling is back?

Just insert matrix multiplication where UV is computed in the vertex shader; just like it was in the first Jon’s package.

Which then try to overwrite yours in unity…couldn’t you cut&paste the relevant lines here and say where to insert?

ditto! cool as… fur!