I have finally succeeded in writing my first shader. I would say it's a pretty good fur shader, and does work. Since the file is over 900 lines long, I load it as a separate .fx file:


matrix matWorldViewProj;

vector mtlSkill1; // Fur color, alpha. pixel_from_vec()
float fur_depth = 0.200000;
// Doesn't translate well from mtlSkill.

texture mtlSkin1;

technique default_technique {

pass p0 {

//--------------------------------------------------------------//
// ASM Fur
//--------------------------------------------------------------//

//--------------------------------------------------------------//
// Pass 1
//--------------------------------------------------------------//
//float pass = 0.000000;
//texture base_map = <mtlSkin1>;
//sampler base_map = sampler_state
//{
// Texture = (base_map);
// MAGFILTER = LINEAR;
// MINFILTER = LINEAR;
// MIPFILTER = LINEAR;
//};

Texture[0] = <mtlSkin1>;

PixelShaderConstant[0] = <mtlSkill1>;
VertexShaderConstant[4] = <fur_depth>;
VertexShaderConstant[5] = 0.000000;
VertexShaderConstant[6] = <matWorldViewProj>;

zWriteEnable = false; // Delay writing to the z-buffer.

AlphaBlendEnable = true;

//--------------------------------------------------------------//
// Vertex Shader
//--------------------------------------------------------------//
VertexShader =
decl {
stream 0;
float v0[3];
float v3[3];
float v7[2];
}

asm {
vs.1.1

; get model space vertex
mov r0, v0

; get shell distance
mov r1, c4

; offset shell in direction of normal
mul r1.x, r1.x, c5.x
mul r1.xyz, r1.xxx, v3.xyz
add r0.xyz, r0.xyz, r1.xyz

; output transformed vertex
m4x4 oPos, r0, c6

; output texture coordinates
mov oT0, v7
};

//--------------------------------------------------------------//
// Pixel Shader
//--------------------------------------------------------------//
PixelShader = asm
{
ps.1.4

texld r0, t0 ; base map

mul r0, r0, c0
};

} pass p1 {

Texture[0] = <mtlSkin1>;
PixelShaderConstant[0] = <mtlSkill1>;
VertexShaderConstant[4] = <fur_depth>;
VertexShaderConstant[5] = 1.000000;
VertexShaderConstant[6] = <matWorldViewProj>;

//zWriteEnable = true; // Enable writing to the z-buffer.

AlphaBlendEnable = true;

//--------------------------------------------------------------//
// Pass 2
//--------------------------------------------------------------//
//float pass = 1.000000;
//--------------------------------------------------------------//
// Vertex Shader
//--------------------------------------------------------------//
VertexShader =

decl {
stream 0;
float v0[3];
float v3[3];
float v7[2];
}

asm {
vs.1.1

; get model space vertex
mov r0, v0

; get shell distance
mov r1, c4

; offset shell in direction of normal
mul r1.x, r1.x, c5.x
mul r1.xyz, r1.xxx, v3.xyz
add r0.xyz, r0.xyz, r1.xyz

; output transformed vertex
m4x4 oPos, r0, c6

; output texture coordinates
mov oT0, v7
};

PixelShader = asm
{
ps.1.4

texld r0, t0 ; base map

mul r0, r0, c0
};

} pass p2 {

Texture[0] = <mtlSkin1>;
PixelShaderConstant[0] = <mtlSkill1>;
VertexShaderConstant[4] = <fur_depth>;
VertexShaderConstant[5] = 2.000000;
VertexShaderConstant[6] = <matWorldViewProj>;

//zWriteEnable = true; // Enable writing to the z-buffer.

AlphaBlendEnable = true;

//--------------------------------------------------------------//
// Pass 3
//--------------------------------------------------------------//
//float pass = 2.000000;
//--------------------------------------------------------------//
// Vertex Shader
//--------------------------------------------------------------//
VertexShader =

decl {
stream 0;
float v0[3];
float v3[3];
float v7[2];
}

asm {
vs.1.1

; get model space vertex
mov r0, v0

; get shell distance
mov r1, c4

; offset shell in direction of normal
mul r1.x, r1.x, c5.x
mul r1.xyz, r1.xxx, v3.xyz
add r0.xyz, r0.xyz, r1.xyz

; output transformed vertex
m4x4 oPos, r0, c6

; output texture coordinates
mov oT0, v7
};

PixelShader = asm
{
ps.1.4

texld r0, t0 ; base map

mul r0, r0, c0
};

} pass p3 {

Texture[0] = <mtlSkin1>;
PixelShaderConstant[0] = <mtlSkill1>;
VertexShaderConstant[4] = <fur_depth>;
VertexShaderConstant[5] = 3.000000;
VertexShaderConstant[6] = <matWorldViewProj>;

//zWriteEnable = true; // Enable writing to the z-buffer.

AlphaBlendEnable = true;

//--------------------------------------------------------------//
// Pass 4
//--------------------------------------------------------------//
//float pass = 3.000000;
//--------------------------------------------------------------//
// Vertex Shader
//--------------------------------------------------------------//
VertexShader =

decl {
stream 0;
float v0[3];
float v3[3];
float v7[2];
}

asm {
vs.1.1

; get model space vertex
mov r0, v0

; get shell distance
mov r1, c4

; offset shell in direction of normal
mul r1.x, r1.x, c5.x
mul r1.xyz, r1.xxx, v3.xyz
add r0.xyz, r0.xyz, r1.xyz

; output transformed vertex
m4x4 oPos, r0, c6

; output texture coordinates
mov oT0, v7
};

PixelShader = asm
{
ps.1.4

texld r0, t0 ; base map

mul r0, r0, c0
};

}

(...)

My code adds another 10 passes, and as long as alpha-blending is enabled, they produce realistic fur. They produce more of a rug in 3D. I did it. I have added 2 fallback techniques as well, for graphics cards without ps.1.4 , and for graphics cards which can't even perform 6 passes.

During the last pass, I set zWriteEnable = on again.

One improvement I made, took notice of the fact that the above shader left small holes in the entity. Therefore, I now use the following pixel shader IN THE FIRST PASS ONLY:


PixelShader = asm
{
ps.1.4

texld r0, t0 ; base map

// mul r0, r0, c0
mov r0, c0 ; so can't be seen through
};


This one shader deposits a layer of solid colour, so that the entity appears to have skin under that fur, after which the remaining 13 passes deposit fur.

Dirk


Last edited by dirkmittler; 07/25/04 01:02.