Fur Shader

Does anyone have a fur shader?

cg-I dont but if your any shadersmith you could try to mod the grassonmeshgenerator? I might know how to do it in about 3 years from now I estimate… :wink:

When I tried the grass thingy, I got hair but it floated in 3d space around my model, which is not the effect we’re after. I would say its do-able, I just dont know how…yet…

Hows your wicked nature scene going?
AC

I’ve considered modifying the grass on mesh generator to work with fur, but it would take a LOT of effort to make:

  1. the “grass” stay normalized to the skin’s normals, and also billboarded to the camera
  2. to keep its positions on the surface of an animated mesh

Good points.

I’d be very suprised if the freakin’ genius’s’ round here havent tried it because based on billboarding, It should work, and , it would look awesome (if it ran fast enough) and would be a cool advertisment for unity.

It just occured to me to try the mesh particle emmitter inna oneshot style sometime, of course it only emits from verts ( I may be wrong) and getting the rotation of each particle will be a trick. I cant try it yet, too much to do.
AC

Ive been using this Fur shader in another 3D engine - I’m not familiar enough with Unity/Shaders to know how to update it. Maybe one of our resident Shader gurus can help :wink:

float4x4 World      : WORLD;               	// Object World Matrix
float4x4 Wvp        : WORLDVIEWPROJECTION; 	// World x View x Projection Matrix

int PassCount 		: PASSCOUNT;           	// Shader's Pass Count
int PassIndex 		: PASSINDEX;           	// Current Rendering Pass Index

float4   mat_diffuse  : DIFFUSE; 	 		// material default diffuse color

float3	localLightPos	: NEARESTLIGHT <string Space="Local";>;
float4	LightColor		: NEARESTLIGHT <float4 Default={1,1,1,1};>;
float	FurLength		<float UIMin=0.5; float UIMax=3;> = 1.5;
float	FurThickness	<float UIMin=1.0; float UIMax=5;> = 3.0;

/******************************************************
Compute Diffuse color using a position, normal
and light position
*******************************************************/
float4 DiffuseLighting(float3 lPos,float3 lNorm)  
{
	float3 lightDir = normalize( localLightPos - lPos );
	
	//--- Compute resulting diffuse
	return clamp((mat_diffuse * LightColor * dot(lNorm, lightDir)),0,1) + mat_diffuse*0.5f;
}



//---------------------------------------------------------------------------------
//Output vertex format
struct vOUTPUT
{
	float4 Position     : POSITION; 
	float4 Color		: COLOR; 
	float2 TexCoord 	: TEXCOORD0;	
	float2 TexCoord1 	: TEXCOORD1;	
};

/**********************************************
Vertex shader is the same for all 20 passes:
Automatic parameters (PassIndex and PassCount) are used
to compute the correct position of the current shell
***********************************************/
vOUTPUT FurVertexShaderPass( float3 Position : POSITION,
						     float3 Normal   : NORMAL,
						     float2 Tex0     : TEXCOORD0 ) 
{
	vOUTPUT Out = (vOUTPUT)0;


	//--- Compute current pass ratio[0..1]
	float passRatio = (float)(PassIndex) / (float)PassCount;
	
	//--- Square it so that is not linear
	passRatio  *= passRatio;

	//--- Compute position for this shell 
    float3 p =  Position + (Normal * passRatio * FurLength);
	
	Out.Position = mul(float4(p,1),Wvp);                    
	Out.Color = DiffuseLighting(p,Normal);
	Out.Color.a = (1.0f - passRatio);

	Out.TexCoord = Tex0;
	Out.TexCoord1 = Tex0 * FurThickness;

	return Out;
}

texture BaseTexture : TEXTURE; 	// Current Material Texture
texture NoiseTexture;

//---------- Sampler
sampler BaseSampler = sampler_state {
	texture = <BaseTexture>;
	MipFilter = LINEAR;
	Minfilter = LINEAR;
	Magfilter = LINEAR;	
};
sampler NoiseSampler = sampler_state {
	texture = <NoiseTexture>;
	MipFilter = LINEAR;
	Minfilter = LINEAR;
	Magfilter = LINEAR;	
};


/**********************************************
The pixel shader is the same for all 20 passes:
It multiplies the color of the base texture with iterated color
and the fur texture color.
***********************************************/
float4 FurPixelShader(vOUTPUT In) : COLOR
{
	float4 bCol = tex2D( BaseSampler, In.TexCoord );
	float4 nCol = tex2D( NoiseSampler, In.TexCoord1 );
	float4 alpha = float4(1,1,1,nCol.a);
	return (bCol+bCol.a*nCol.a*0.35) * alpha * In.Color;
}

/************************************
This technique consists in rendering 20 shells 
of the same object using a decreasing alpha
**************************************/
technique Fur
{
	pass p0
	{
		AlphaTestEnable = TRUE;	
		AlphaFunc 		= GREATER;
		AlphaRef 		= 3;		

		AlphaBlendEnable= TRUE;	

		DestBlend 		= INVSRCALPHA;
		SrcBlend 		= SRCALPHA;
	
		VertexShader = compile vs_1_1 FurVertexShaderPass();
		PixelShader = compile ps_1_1 FurPixelShader();
	}
	pass p1 {
		VertexShader = compile vs_1_1 FurVertexShaderPass();
		PixelShader = compile ps_1_1 FurPixelShader();	
	}
	pass p2 {
		VertexShader = compile vs_1_1 FurVertexShaderPass();
		PixelShader = compile ps_1_1 FurPixelShader();	
	}
	pass p3
	{
		VertexShader = compile vs_1_1 FurVertexShaderPass();
		PixelShader = compile ps_1_1 FurPixelShader();	
	}
	pass p4
	{
		VertexShader = compile vs_1_1 FurVertexShaderPass();
		PixelShader = compile ps_1_1 FurPixelShader();	
	}
	pass p5
	{
		VertexShader = compile vs_1_1 FurVertexShaderPass();
		PixelShader = compile ps_1_1 FurPixelShader();	
	}
	pass p6
	{
		VertexShader = compile vs_1_1 FurVertexShaderPass();
		PixelShader = compile ps_1_1 FurPixelShader();	
	}
	pass p7
	{
		VertexShader = compile vs_1_1 FurVertexShaderPass();
		PixelShader = compile ps_1_1 FurPixelShader();	
	}
	pass p8
	{
		VertexShader = compile vs_1_1 FurVertexShaderPass();
		PixelShader = compile ps_1_1 FurPixelShader();	
	}
	pass p9
	{
		VertexShader = compile vs_1_1 FurVertexShaderPass();
		PixelShader = compile ps_1_1 FurPixelShader();	
	}
	pass p10
	{
		VertexShader = compile vs_1_1 FurVertexShaderPass();
		PixelShader = compile ps_1_1 FurPixelShader();	
	}
	pass p11
	{
		VertexShader = compile vs_1_1 FurVertexShaderPass();
		PixelShader = compile ps_1_1 FurPixelShader();	
	}
	pass p12
	{
		VertexShader = compile vs_1_1 FurVertexShaderPass();
		PixelShader = compile ps_1_1 FurPixelShader();	
	}
	pass p13
	{
		VertexShader = compile vs_1_1 FurVertexShaderPass();
		PixelShader = compile ps_1_1 FurPixelShader();	
	}
	pass p14
	{
		VertexShader = compile vs_1_1 FurVertexShaderPass();
		PixelShader = compile ps_1_1 FurPixelShader();	
	}
	pass p15
	{
		VertexShader = compile vs_1_1 FurVertexShaderPass();
		PixelShader = compile ps_1_1 FurPixelShader();	
	}
	pass p16
	{
		VertexShader = compile vs_1_1 FurVertexShaderPass();
		PixelShader = compile ps_1_1 FurPixelShader();	
	}
	pass p17
	{
		VertexShader = compile vs_1_1 FurVertexShaderPass();
		PixelShader = compile ps_1_1 FurPixelShader();	
	}				
	pass p18
	{
		VertexShader = compile vs_1_1 FurVertexShaderPass();
		PixelShader = compile ps_1_1 FurPixelShader();	
	}
	pass p19
	{
		VertexShader = compile vs_1_1 FurVertexShaderPass();
		PixelShader = compile ps_1_1 FurPixelShader();	
	}
	pass p20
	{
		VertexShader = compile vs_1_1 FurVertexShaderPass();
		PixelShader = compile ps_1_1 FurPixelShader();	
	}			

}

That technique would translate to Unity easily. I’m not sure vertex programs are required. Seems you coule just pregenerate the shell mesh. Maybe not without a Bone interface in Unity. But 20 passes seems a bit crazy. Is there a better technique out there than the shells/fins method? … besides a GeForce 8800 and a geometry shader.

-Jon

I thought 20 passes seemed a lot too, but the performance seems to be very good. Ive also tried with only 10 passes and it still looks good for closely “cut” hair - maybe it should be called a fuzz shader rather than fur. Either way it softens the objects outline nicely and looks best at mid-distance to camera.

What other techniques are there for doing fur? Would there be a more optimal way than the shell technique?

OMG look at that…Id… to see this in unity… :shock:
AC

Wow sweetness.

I wonder if there is a way to drive the fur iterations based on alpha from a texture map?

Usually you don’t drive the iterations; instead you drive “fur length” from the texture map. The card still draws all the layers (=iterations), you just kill the pixels in higher layers based on the texture.