Rust Shader
Home Up Research Ultimate List

I implemented a rust shader based on Perlin lattice gradient noise using Cg and the NV30 emulator.  The shader calculates the 3D noise function on the surface of an object and uses a threshold value to determine whether a point on the surface is rusty or reflective.  If the point is reflective, a look up is made to a cubemap.  Otherwise it is rendered as rust.  Because of the way the threshold was implemented, the noise is higher valued near the edges of rust spots and lower towards the center.  By multiplying a constant rust color value by the noise function the color ramps from orange on the edges of the rust spots to black towards the center.  The rust is then lit using per pixel standard lighting.  The computed rust color is set to be the diffuse term and the specular component is 1/3 the diffuse term.

In the original implementation the spots were too smooth at the borders and so was the color change within the spots.  To correct this turbulent fractal noise was used by choosing 3 frequencies of  noise and adding them together.

Originally I tried to implement my own noise function, but it was using too many instructions to run as a fragment program.  However, I did expose two compiler bugs! :)  I used a Perlin noise shader function implemented by Yury Uralsky.  I wanted an input parameter to control the amount of rust in the image.  Thus the threshold between metal and rust is based on an input term.  Unfortunately it seemed that the values computed by the noise function were mostly near 1, so the threshold is based on the 16th root of the input value to make the parameter seem more or less linear in the amount of rust it produces..  This involves 4 square roots, so this would probably be implemented as a lookup table if speed were an issue.

Here is the shader code that uses his noise function:

fragout rust(vertout indata,
uniform samplerRECT permute,
uniform samplerRECT gradient,
uniform samplerCUBE cubeMap,
uniform float rustLevel)
{
    fragout outval;
    const float3 rustColor = {100.0 / 255.0, 50.0 / 255.0, 128.0 / 128.0};
    rustLevel = sqrt(sqrt(sqrt(sqrt(rustLevel))));
    float n = abs((1 - noise3(4 * indata.tcoords, permute, gradient, 1)));
    n += abs(0.25 * (1 - noise3(16 * indata.tcoords, permute, gradient, 1)));
    n += abs(0.25 * (1 - noise3(32 * indata.tcoords, permute, gradient, 1)));
    n *= 1 / 1.5;
    float3 ambient, diffuse, specular;
    float3 zero3 = {0, 0, 0};
    outval.col.rgb = zero3;
    if (n < rustLevel)
    {
        diffuse = rustColor * n * n;|
        ambient = zero3;
        specular = diffuse / 3;
        n = 0;
    }
    else
    {
        n = 1;
        ambient.r = 0.3;
        ambient.g = 0.3;
        ambient.b = 0.3;
   
        diffuse.r = 0.4;
        diffuse.g = 0.4;
        diffuse.b = 0.4;
       
        specular.r = 0.9;
        specular.g = 0.9;
        specular.b = 0.9;
    }
    outval.col.rgb = calc_lighting(ambient, diffuse, specular, 50, indata.normal, indata.vlight, indata.vhalf);
    if (n == 1)
    {
        outval.col.rgb += 0.8 * f3texCUBE(cubeMap, indata.normal);
    }
    return outval;
}

And finally, a resulting image: