Fur CG shader (based on zeroZshadow's work)

You are an experienced programmer and have a problem with the engine, shaders, or advanced effects? Here you'll get answers.
No questions about C++ programming or topics which are answered in the tutorials!
Post Reply
EvIl_DeViL
Posts: 89
Joined: Tue Nov 06, 2007 4:49 pm

Fur CG shader (based on zeroZshadow's work)

Post by EvIl_DeViL »

Hi there!
I'm trying to port the zeroZshadow's Fur shader (http://irrlicht.sourceforge.net/forum/v ... hp?t=31295 originally written in 2008 in GLSL, based itself on http://www.xbdev.net/directx3dx/specialX/Fur/index.php in HLSL) in CG that is now fully and officially supported by irrlicht.
BUT... here is the problem: I think there's some issues with EMT_ONETEXTURE_BLEND because I can't see the whole model...
As stated somewhere else on the forum and in the examples I already tryied to set the EMF_BLEND_OPERATION flag to the material.
here are the tests I've made using EMT_SOLID as material shader flag
  • you can see it produces "instanced copies" BEHIND the model and not AROUND as you would expect from fur
  • it turned out that passing matrices variable from irrlicht (wolrd, view, normals and other stuff) is better because otherwise you can't see anything (so using shaders global constants is wrong)
  • as stated in a topic on this forum, passing texture to shader was done before by casting the texture reference to float but now it has been fixed so I just pass the reference
so here is my code and my project:

http://www.mediafire.com/?nnfdbzo4vr4fjxt

main.cpp

Code: Select all

 
#include "global.h"
#include "shader.h"
 
bool InitIrrlicht();
 
int main()
{
    //Startup irrlicht
    if(!InitIrrlicht())
        return 0;
 
    //Setup Camera
    //smgr->addCameraSceneNodeFPS();
    ICameraSceneNode* cam = smgr->addCameraSceneNodeFPS(0, 100.0f, 0.5f);
    cam->setPosition(vector3df(0.0f, 100.0f, -100.0f));
 
    //Create CFurSceneNode
    IAnimatedMesh* meshFur = smgr->addHillPlaneMesh("plane", core::dimension2d<irr::f32>(10,10), core::dimension2d<irr::u32>(10,10));
    CFurSceneNode* nodeFur = new CFurSceneNode(meshFur, smgr->getRootSceneNode(), smgr, 666);
    if (nodeFur)
    {
        nodeFur->setName("nodeFur");
        nodeFur->setMaterialFlag(EMF_LIGHTING, false);
        nodeFur->setMaterialFlag(EMF_BLEND_OPERATION,true);
        nodeFur->setMaterialType((E_MATERIAL_TYPE)EMT_FUR);
        nodeFur->setPosition( vector3df( 0, -50, 100) );
        nodeFur->setLayers(10);
        nodeFur->setFurLength(0.3f);
 
        nodeFur->setMaterialTexture( 1, driver->getTexture("../res/FurTexColor.jpg") );       //FurTexture
        nodeFur->setMaterialTexture( 0, driver->getTexture("../res/Fur.tga") ); //FurAlpha
    }
 
    while(device->run())if (device->isWindowActive())
    {
        driver->beginScene(true, true, SColor(255, 255, 128, 64));
        smgr->drawAll();
        driver->endScene();
    }
 
    device->drop();
    return 0;
}
 
bool InitIrrlicht()
{
    //*************************starting irrlicht********************************
    device = createDevice(EDT_DIRECT3D9, dimension2d<u32>(800, 600), 32, false, false, false);
 
    if(!device)
        return 0;
 
    device->setWindowCaption(L"Fur Demo - by zeroZshadow");
    driver = device->getVideoDriver();
    smgr = device->getSceneManager();
    gpu = driver->getGPUProgrammingServices();
 
    driver->setTextureCreationFlag(ETCF_CREATE_MIP_MAPS,false);
    smgr->setAmbientLight(SColor(0,146,75,56));
 
    InitShaders();
 
    return 1;
}
 
shader.cpp

Code: Select all

 
#include "shader.h"
#include <irrlicht.h>
 
void MyShaderCallBack::OnSetConstants(video::IMaterialRendererServices* services, s32 userData)
{
    // set inverted world matrix
    // if we are using highlevel shaders (the user can select this when
    // starting the program), we must set the constants by name.
 
    float fUVScale = 1.0f;
    vector3df vGravity = vector3df(0.0, 0.0f, 0.0f);
 
    services->setVertexShaderConstant("fFurLength", &fFurLength, 1);
    services->setVertexShaderConstant("fMaxLength", &fMaxLength, 1);
    services->setVertexShaderConstant("fUVScale", &fUVScale, 1);
    services->setVertexShaderConstant("fLayer", &fLayer, 1);
    services->setVertexShaderConstant("vGravity", reinterpret_cast<f32*>(&vGravity), 3);
 
    //vLightDir
    vector3df vLightDir = vector3df(0.8f, 0.8f, 0.8f);
    services->setPixelShaderConstant("vLightDir", reinterpret_cast<f32*>(&vLightDir), 3);
 
    //vAmbient
    SColorf vAmbient = smgr->getAmbientLight().toSColor();
    services->setPixelShaderConstant("vAmbient", reinterpret_cast<f32*>(&vAmbient), 4);
 
    //Set Textures
    int texVar = 1;
    services->setPixelShaderConstant("FurTexture", &texVar, 1);
    texVar = 0;
    services->setPixelShaderConstant("ColourTexture", &texVar, 1);
 
    //mWorld
    matrix4 mWorld = driver->getTransform(video::ETS_WORLD);
    services->setVertexShaderConstant("mWorld", mWorld.pointer(), 16);
 
    //mWorldViewProj
    matrix4 mWorldViewProj;
    mWorldViewProj = driver->getTransform(video::ETS_PROJECTION);
    mWorldViewProj *= driver->getTransform(video::ETS_VIEW);
    mWorldViewProj *= driver->getTransform(video::ETS_WORLD);
    services->setVertexShaderConstant("mWorldViewProj", mWorldViewProj.pointer(), 16);
}
 
void InitShaders()
{
    ShaderCB = new MyShaderCallBack();
    blShader=true;
 
    if (!driver->queryFeature(video::EVDF_PIXEL_SHADER_1_3) &&
        !driver->queryFeature(video::EVDF_ARB_FRAGMENT_PROGRAM_1))
    {
        device->getLogger()->log("WARNING: Pixel shaders disabled "\
            "because of missing driver/hardware support.");
        blShader=false;
    }
 
    if (!driver->queryFeature(video::EVDF_VERTEX_SHADER_1_1) &&
        !driver->queryFeature(video::EVDF_ARB_VERTEX_PROGRAM_1))
    {
        device->getLogger()->log("WARNING: Vertex shaders disabled "\
            "because of missing driver/hardware support.");
        blShader=false;
    }
 
    EMT_FUR = 0;
    if(blShader)
    {
        /*EMT_FUR = gpu->addHighLevelShaderMaterialFromFiles(
            "../res/fur.vert", "main", video::EVST_VS_1_1,
            "../res/fur.frag", "main", video::EPST_PS_1_3,
            ShaderCB, video::EMT_ONETEXTURE_BLEND
        );*/
 
        const video::E_GPU_SHADING_LANGUAGE shadingLanguage = video::EGSL_CG;
 
        EMT_FUR = gpu->addHighLevelShaderMaterialFromFiles(
            "../res/fur.hlsl", "vertexMain", video::EVST_VS_1_1,
            "../res/fur.hlsl", "pixelMain", video::EPST_PS_1_3,
            ShaderCB, video::EMT_ONETEXTURE_BLEND, 0, shadingLanguage);
    }
    else EMT_FUR = EMT_SOLID;
}
 
CFurSceneNode::~CFurSceneNode()
{
 
}
 
void CFurSceneNode::OnRegisterSceneNode()
{
    if(blShader){
        //printf("shader\n");
        SceneManager->registerNodeForRendering(this, irr::scene::ESNRP_SOLID);
        SceneManager->registerNodeForRendering(this, irr::scene::ESNRP_SHADOW);
    }
    else{
        SceneManager->registerNodeForRendering(nodeSelf);
    }
}
 
void CFurSceneNode::render()
{
    f32 CurrentLength = 0.0f;
    f32 CurrentLayer = 0.0f;
    ShaderCB->fMaxLength = fFurLength + ( fFurLength * (1 / iLayers));
    if(smgr->getSceneNodeRenderPass() == ESNRP_SOLID)
    {
        ShaderCB->fLayer = 0.0f;
        ShaderCB->fFurLength = 0.0f;
        nodeSelf->render();
    }
    else
    {
        for(float i= 1.0f; i < iLayers; i++)
        {
            //calc value's
            CurrentLayer = i / iLayers;
            CurrentLength = fFurLength * CurrentLayer;
 
            //set value's
            ShaderCB->fLayer = CurrentLayer;
            ShaderCB->fFurLength = CurrentLength;
 
            //render child
            nodeSelf->render();
        }
    }
}
 
void CFurSceneNode::setMaterialFlag(video::E_MATERIAL_FLAG flag, bool newvalue)
{
    nodeSelf->setMaterialFlag(flag,newvalue);
    ISceneNode::setMaterialFlag(flag,newvalue);
}
 
 
SMaterial& CFurSceneNode::getMaterial(u32 i)
{
    return nodeSelf->getMaterial(i);
}
 
u32 CFurSceneNode::getMaterialCount() const
{
    return nodeSelf->getMaterialCount();
}
 
const aabbox3d<f32>& CFurSceneNode::getBoundingBox() const
{
    return nodeSelf->getBoundingBox();
}
 
void CFurSceneNode::setMaterialTexture(u32 textureLayer, video::ITexture* texture)
{
    nodeSelf->setMaterialTexture(textureLayer,texture);
}
 
void CFurSceneNode::setAnimationSpeed(f32 speed)
{
    nodeSelf->setAnimationSpeed(speed);
}
 
void CFurSceneNode::setFrameLoop(u32 first, u32 last)
{
    nodeSelf->setFrameLoop(first, last);
}
 
f32 CFurSceneNode::getFrameNr()
{
    return nodeSelf->getFrameNr();
}
 
void CFurSceneNode::setLayers(u32 Layer)
{
    if(Layer)
        iLayers = Layer;
}
 
void CFurSceneNode::setFurLength(f32 Length)
{
    fFurLength = Length;
}
 
//Custom Functions
void CFurSceneNode::Init()
{
    //create childnode
    nodeSelf = device->getSceneManager()->addAnimatedMeshSceneNode(meshSelf, this);
    getMaterial(0).MaterialTypeParam = pack_textureBlendFunc(EBF_SRC_ALPHA, EBF_ONE_MINUS_SRC_ALPHA, EMFN_MODULATE_2X );
    fFurLength = 0.0f;
    iLayers = 1;
}
 
IAnimatedMeshSceneNode* CFurSceneNode::getNode(){return nodeSelf;}
 
fur.hlsl

Code: Select all

 
float fFurLength = 0;
float fUVScale   = 1.0f;
float fLayer     = 0; // 0 to 1 for the level
float3 vGravity = float3(0,-2.0,0);
float4 vLightDir = float4(0.8,0.8,1,0);
//------------------------------------
sampler2D FurTexture;
sampler2D TextureSampler;
//------------------------------------ 
// transformations
float4x4 worldViewProj : WORLDVIEWPROJ;
float4x4 matWorld : WORLD;
 
float4x4 mWorldViewProj; // World * View * Projection transformation
float4x4 mWorld;      // Inverted world matrix
//------------------------------------
struct vertexInput {
    float3 position                 : POSITION;
    float3 normal                   : NORMAL;
    float2 texCoordDiffuse          : TEXCOORD0;
};
 
struct vertexOutput {
    float4 HPOS     : POSITION;   
    float2 T0       : TEXCOORD0; // fur alpha
    float3 normal   : TEXCOORD1;
};
 
vertexOutput vertexMain(vertexInput IN)
{
    vertexOutput OUT = (vertexOutput)0;
    //** MAIN LINE ** MAIN LINE ** MAIN LINE ** MAIN LINE ** MAIN LINE **//
    //This single line is responsible for creating the layers!  This is it! Nothing
    //more nothing less!
    float3 P = IN.position.xyz + (IN.normal * fFurLength);
 
    //Modify our normal so it faces the correct direction for lighting if we
    //want any lighting
    //float3 normal = normalize(mul(float4(IN.normal,1.0), matWorld));
    float3 normal = normalize(mul(float4(IN.normal,1.0), mWorld));
 
    // Couple of lines to give a swaying effect!
    // Additional Gravit/Force Code
    //vGravity = mul(float4(vGravity, 1.0), matWorld);
    float3 ovGravity = mul(float4(vGravity, 1.0), mWorld);
    float k =  pow(fLayer, 3.0);  // We use the pow function, so that only the tips of the hairs bend
                             // As layer goes from 0 to 1, so by using pow(..) function is still
                             // goes form 0 to 1, but it increases faster! exponentially
    P = P + ovGravity*k;
    // End Gravity Force Addit Code
    OUT.T0 = IN.texCoordDiffuse * fUVScale; // Pass long texture data
    // UVScale??  Well we scale the fur texture alpha coords so this effects the fur thickness
    // thinness, sort of stretches or shrinks the fur over the object!
    //OUT.HPOS = mul(float4(P, 1.0f), worldViewProj); // Output Vertice Position Data
    OUT.HPOS = mul(float4(P, 1.0f), mWorldViewProj); // Output Vertice Position Data
    OUT.normal = normal; // Output Normal
    return OUT;
}
 
//----------------------------------- ( ps 1.3 )
float4 pixelMain( vertexOutput IN): COLOR
{
    float4 FurColour = tex2D( FurTexture,  IN.T0 ); // Fur Texture - alpha is VERY IMPORTANT!
    float4 FinalColour = FurColour;
    //--------------------------
    //Basic Directional Lighting
    float4 ambient = {0.3, 0.3, 0.3, 0.0};
    ambient = ambient * FinalColour;
    float4 diffuse = FinalColour;
    FinalColour = ambient + diffuse * dot(vLightDir, IN.normal);
    //End Basic Lighting Code
    //--------------------------
    FinalColour.a = FurColour.a;
    //FinalColour.a = 1.0;
    return FinalColour;      // fur colour only!
    //return FinalColour;       // Use texture colour
    //return float4(0,0,0,0); // Use for totally invisible!  Can't see
}
 
many thanx!!!
netpipe
Posts: 670
Joined: Fri Jun 06, 2008 12:50 pm
Location: Edmonton, Alberta, Canada
Contact:

Re: Fur CG shader (based on zeroZshadow's work)

Post by netpipe »

did you make progress with this i have same issue
Live long and phosphor!
-- https://github.com/netpipe/Luna Game Engine Status 95%
EvIl_DeViL
Posts: 89
Joined: Tue Nov 06, 2007 4:49 pm

Re: Fur CG shader (based on zeroZshadow's work)

Post by EvIl_DeViL »

yes! almost successfull, but I think I would get no further with this because AFAIK the irrlicht 1.8 shader system has been massively retouched and many bugs were introducted...
anyway here is what I've got:
before this version the blend flag was automatically set.
now you have to

Code: Select all

nodeFur->setMaterialFlag(EMF_BLEND_OPERATION,true);
second thing:

Code: Select all

 
EMT_FUR = gpu->addHighLevelShaderMaterialFromFiles(
            "../res/fur.hlsl", "vertexMain", video::EVST_VS_1_1,
            "../res/fur.hlsl", "pixelMain", video::EPST_PS_1_3,
            ShaderCB, video::EMT_ONETEXTURE_BLEND, 0, shadingLanguage);
I checked also HLSL and GLSL, and part related to EMT_ONETEXTURE_BLEND seems to be seriuosly messed up because with this flag you can't see anything at all...
in fact if you use EMT_TRANSPARENT_ALPHA_CHANNEL or EMT_TRANSPARENT_ALPHA_CHANNEL_REF (I don't know with others type of material) you can see the (wrong) shader almost working!
I'm not good enough with shaders to understand if there are some bug or it's me doing something wrong...
anyway I'm going to post the full updated code in the weekend because I saw that this thread got a lot of view and few posts. (ok almost a half was mine but the other (shy) half deserve something to hope for :p)
I tried to mesh up the original shader on xbdev, fixing it with some parts of the zeroZshadows's shader but, as stated before, I'm not very good with shaders. I'm going to post my CG version soon! (got to check and revert a couple of test and clean it up a bit)
I would like to know how many persons are interested to this shader :D

p.s. there should be a "shader lab" section with requests and developing. shaders are so cool... :D
EvIl_DeViL
Posts: 89
Joined: Tue Nov 06, 2007 4:49 pm

Re: Fur CG shader (based on zeroZshadow's work)

Post by EvIl_DeViL »

ok... Here is the updated code with the changes I made (previous link removed): http://www.mediafire.com/download.php?91ottzkstsu3aqs
and here is an image of what I can see with video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF.
Image

in the donwloadable file there is also an executable version with video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF set as base material (like the image) but the code has the original EMT_ONETEXTURE_BLEND as material.
in the compressed file all the media were provided.

here is my final shader code

Code: Select all

float fFurLength;
float fMaxLength;
float fUVScale;
float fLayer; // 0 to 1 for the level
float3 vGravity;
float4 vLightDir;
float4 vAmbient;
//------------------------------------
sampler2D tFurTexture;
sampler2D tColourTexture;
//------------------------------------ 
// transformations
float4x4 worldViewProj : WORLDVIEWPROJ;
float4x4 matWorld : WORLD;
 
float4x4 mWorldViewProj; // World * View * Projection transformation
float4x4 mWorld;      // Inverted world matrix
//------------------------------------
struct vertexInput {
    float3 position                 : POSITION;
    float3 normal                   : NORMAL;
    float2 texCoordDiffuse          : TEXCOORD0;
};
 
struct vertexOutput {
    float4 HPOS     : POSITION;   
    float2 T0       : TEXCOORD0; // fur alpha
    float2 T1       : TEXCOORD1; // fur alpha
    float3 vNormal  : TEXCOORD2;
};
 
vertexOutput vertexMain(vertexInput IN)
{
    vertexOutput OUT = (vertexOutput)0;
    //** MAIN LINE ** MAIN LINE ** MAIN LINE ** MAIN LINE ** MAIN LINE **//
    //This single line is responsible for creating the layers!  This is it! Nothing
    //more nothing less!
    //float3 P = IN.position.xyz + (IN.normal * fFurLength);
    float3 P = IN.position.xyz + (IN.normal.xyz * ((fFurLength)*10.0f));
 
    //Modify our normal so it faces the correct direction for lighting if we
    //want any lighting
    //float3 normal = normalize(mul(float4(IN.normal,1.0), matWorld));
    float3 normal = normalize(mul(float4(IN.normal,1.0), mWorld));
 
    // Couple of lines to give a swaying effect!
    // Additional Gravit/Force Code
    float3 ovGravity = mul(float4(vGravity, 1.0), matWorld);
    //float3 ovGravity = mul(float4(vGravity, 1.0), mWorld);
    float k =  pow(fLayer, 3.0);  // We use the pow function, so that only the tips of the hairs bend
                             // As layer goes from 0 to 1, so by using pow(..) function is still
                             // goes form 0 to 1, but it increases faster! exponentially
    P = P + ovGravity*k;
    // End Gravity Force Addit Code
    OUT.T0 = IN.texCoordDiffuse * fUVScale; // Pass long texture data
    OUT.T1 = IN.texCoordDiffuse;
    // UVScale??  Well we scale the fur texture alpha coords so this effects the fur thickness
    // thinness, sort of stretches or shrinks the fur over the object!
    //OUT.HPOS = mul(float4(P, 1.0f), worldViewProj); // Output Vertice Position Data
    OUT.HPOS = mul(float4(P, 1.0f), mWorldViewProj); // Output Vertice Position Data
    OUT.vNormal = normal; // Output Normal
    return OUT;
}
 
//----------------------------------- ( ps 1.3 )
float4 pixelMain( vertexOutput IN): COLOR
{
    float4 diffuseTexture = tex2D( tFurTexture,  IN.T0 ); // Fur Texture - alpha is VERY IMPORTANT!
    float4 ColourTexture = tex2D( tColourTexture,  IN.T0 ); // Colour Texture for patterns
    float4 FinalColour = ColourTexture;
    //FinalColour = diffuseTexture;
    //--------------------------
    //Basic Directional Lighting
    float4 ambient = vAmbient * FinalColour;
    float4 diffuse = FinalColour;
    FinalColour = ambient + diffuse * dot(vLightDir, IN.vNormal);
    //FinalColour = ambient + diffuse;
    //End Basic Lighting Code
    //--------------------------
    if(fFurLength==0.0f)
    {
        FinalColour.a = 1;
    }
    else
        {
        //FinalColour.a  = diffuseTexture.a * ( (fMaxLength-fFurLength));
        FinalColour.a = 1-diffuseTexture.a; //fix 1-
        //FinalColour.a  = 1- diffuseTexture.a * ( (1.0f-fFurLength)); 
    }
    return FinalColour;      // fur colour only!
}
Post Reply