Using Lumonix ShaderFX with Sunburn (noob tutorial)

This post has 13 Replies | 3 Followers

Top 100 Contributor
Posts 35
SunBurn_Community_Licensee
SunBurn_Indie_Licensee
SunBurn_Pro_Licensee
eoigames Posted: 10-27-2010 8:36 AM

ShaderFX is a plugin created by Lumonix that permit to make shaders visualy in 3dS Max. The plugin is completly free for individuals and companies smaller then 2 employees. It can be downloaded from: http://www.lumonix.net/shaderfx.html

In this tutorial (very simple, I know), we will do a shader that will move automaticly a model's texture using the UV coordenates. First, open 3DS Max and run ShaderFX (Rendering/shaderFX), and you will see a Windows like this:



Drop a Texture Map (in Maps tab) into the scene, and conect the Diffuse Color of the material with the RGB output of the Texture Map. Now, go to Math tab and create a Vector Construction node, and two Math Operators. The result of the first of them must to be conected with the Y input of the vector constructor, and result of the second math operator with the Input B of the first one:



Include a UV Coordinates node into the proyect (in the Inputs tab), and connect the U output with the vector constructor's X, and the V with the Input A of first math operator. Now, you can make an union between the Vector output for the vector constructor and the UV Coords for the texture.

Now, include into the proyect two Constants (in Maps tab), and join with the Input A and B of the second Math Operator. Name the first of both like "Amount", and the second as "Speed". The last thing we must to do is change the operator settings from Multiplication(A*B) to Subtraction(A-B) in the first math operator.



Export the shader selecting the "XNA (DXSAS)" format, and the "Y-Up" in Up Axis. Well, the shader is ready to use, and here's the code:

/*** Generated through Lumonix shaderFX  by: eoigames at: 27/10/2010 13:50:34  ***/

int texcoord0 : Texcoord
<
    int Texcoord = 0;
    int MapChannel = 1;
    string UIWidget = "None";
>;

float Amount
<
    string UIWidget = "Spinner";
    float UIMin = -999.0;
    float UIMax = 999.0;
    float UIStep = 0.1;
    string UIName = "Amount";
> = 20.0;
 
float Speed
<
    string UIWidget = "Spinner";
    float UIMin = -999.0;
    float UIMax = 999.0;
    float UIStep = 0.1;
    string UIName = "Speed";
> = 0.1;
 
texture TextureMap_293
<
    string ResourceName = "bark.dds";
    string UIName = "Texture Map";
    string ResourceType = "2D";
>;
 
sampler2D TextureMap_293Sampler = sampler_state
{
    Texture = <TextureMap_293>;
    MinFilter = LINEAR;
    MagFilter = LINEAR;
    MipFilter = LINEAR;
    AddressU = WRAP;
    AddressV = WRAP;
};
 

// this function does the different types of light attenuation
float attenuation_func(int lightattenType, float4 lightAttenuation, float3 lightVec)
{
    float att = 1.0;
    return att;
}
     
// this function does the different types of cone angle
float coneangle_func(int lightconeType, float lightHotspot, float lightFalloff, float3 lightVec, float3 lightDir)
{
    float cone = 1.0;
    return cone;
}

/************** light info **************/

float3 light1Dir : Direction
<
    string UIName = "Light 1 Direction";
    string Object = "TargetLight";
    string Space = "World";
        int refID = 1;
> = {100.0f, 100.0f, 100.0f};

float3 light1Pos : POSITION
<
    string UIName = "Light 1 Position";
    string Object = "PointLight";
    string Space = "World";
        int refID = 1;
> = {100.0f, 100.0f, 100.0f};

float4 light1Color : LIGHTCOLOR <int LightRef = 1; string UIWidget = "None"; > = { 1.0f, 1.0f, 1.0f, 1.0f};
float4 light1Attenuation : Attenuation <int LightRef = 1; string UIWidget = "None"; > = { 1.0f, 1.0f, 1.0f, 1.0f};
float light1Hotspot : HotSpot <int LightRef = 1; string UIWidget = "None"; > = { 43.0f };
float light1Falloff : FallOff <int LightRef = 1; string UIWidget = "None"; > = { 45.0f };

#define light1Type 1
#define light1attenType 0
#define light1coneType 0
#define light1CastShadows false

//----------------------------------

float4x4 wvp : WorldViewProjection < string UIWidget = "None"; >; 
float4x4 worldI : WorldInverse < string UIWidget = "None"; >; 
float4x4 worldIT : WorldInverseTranspose < string UIWidget = "None"; >; 
float4x4 viewInv : ViewInverse < string UIWidget = "None"; >; 
float4x4 world : World < string UIWidget = "None"; >; 
// create the light vector
float3 lightVec_func(float3 worldSpacePos, float3 lightVector, float3x3 objTangentXf, int lightType)
{
    float3 lightVec = mul(objTangentXf, (mul((lightVector - worldSpacePos), worldI).xyz));
    return lightVec;
}



// input from application
    struct a2v {
    float4 position        : POSITION;
    float4 tangent        : TANGENT;
    float4 binormal        : BINORMAL;
    float4 normal        : NORMAL;

    float2 texCoord        : TEXCOORD0;

};

// output to fragment program
struct v2f {
        float4 position            : POSITION;
        float3 lightVec            : TEXCOORD0;

    float2 texCoord            : TEXCOORD1;

};

//Diffuse and Specular Pass Vertex Shader
v2f v(a2v In, uniform float3 lightPos, uniform int lightType, uniform float3 lightDir)
{
    v2f Out = (v2f)0;
    float3x3 objTangentXf;                                //build object to tangent space transform matrix
        objTangentXf[0] = In.tangent.xyz;
    objTangentXf[1] = -In.binormal.xyz;
    objTangentXf[2] = In.normal.xyz;
    float3 worldSpacePos = mul(In.position, world).xyz;    //world space position
    Out.lightVec = lightVec_func(worldSpacePos, lightPos, objTangentXf, lightType);
    Out.position = mul(In.position, wvp);                //transform vert position to homogeneous clip space

    Out.texCoord = In.texCoord;                        //pass through texture coordinates from channel 1

    return Out;
}

//Diffuse and Specular Pass Pixel Shader
float4 f(v2f In, uniform float3 lightDir, uniform float4 lightColor, uniform float4 lightAttenuation, uniform float lightHotspot, uniform float lightFalloff, uniform int lightType, uniform int lightattenType, uniform int lightconeType, uniform bool lightCastShadows, uniform int shadowPassCount) : COLOR
{
    float3 ret = float3(0,0,0);
    float3 L = normalize(In.lightVec);        //creating the light vector 

    float2 INUV_141 = In.texCoord.xy;
    float2 MathVecConstuct_4064 = float2(INUV_141.x, (INUV_141.y - (Amount * Speed)));
    float4 TextureMap_293 = tex2D(TextureMap_293Sampler, MathVecConstuct_4064.xy);
    float3 input2 = TextureMap_293.rgb;

    float3 N = float3(0.0, 0.0, 1.0);        //the Normal socket was empty - using default value
    float3 diffuseColor = input2;            //using the Diffuse Color socket 
    float NdotL = dot(N, L);                //calculate the diffuse 
    float diffuse = saturate(NdotL);        //clamp to zero 
    diffuseColor *= diffuse;                //the resulting diffuse color 
    float3 specularColor = float3(0, 0, 0);    //Specular Color socket is empty so using black
    ret += specularColor + diffuseColor;    //add specular and diffuse color together
    ret *= lightColor;                        //multiply by the color of the light
    float attenuation = attenuation_func(lightattenType, lightAttenuation, In.lightVec);                     //calculate the light attenuation 
    float coneangle = coneangle_func(lightconeType, lightHotspot, lightFalloff, In.lightVec, lightDir);     //calculate the light's cone angle
    ret *= attenuation * coneangle;            //multiply by the light decay 
    float4 done = float4(ret, 1);            //create the final ouput value
    return done;
}

technique Complete 

    pass light1 
    {         
        VertexShader = compile vs_2_0 v(light1Pos,  light1Type, light1Dir);
        ZEnable = true;
        CullMode = ccw;
        ShadeMode = Gouraud;
        ZWriteEnable = true;
        AlphaBlendEnable = false;
        SrcBlend = SrcAlpha;
        DestBlend = One;
        AlphaTestEnable = FALSE;
        PixelShader = compile ps_2_0 f(light1Dir, light1Color, light1Attenuation, light1Hotspot, light1Falloff, light1Type, light1attenType, light1coneType, light1CastShadows, 1);
    } 
}   


Easy, isn't it? If you want to move the texture, you only have to modify the value "Amount" or "Speed" in your code, with something like "effect.Parameters["Scroll"].SetValue(0.1f);".

One last thing... Maybe we don't want set the value of the movement in every loop, so... Can the shader do it? Open the shader with a text editor and change the Amout values for:
"float Amount
<
    string UIWidget = "Spinner";
    float UIMin = -999.0;
    float UIMax = 999.0;
    float UIStep = 0.1;
    string UIName = "Amount";
> = 20.0;"

to:
"float Amount
<
    string SasBindAddress = "Sas.Time.Now";
> = 20.0;"

Change the Speed value if you want to do the movement more slower or faster.

Done! I hope this tutorial helps you to do incredible shaders.

P.S.: You know, my english is very very bad, if you don't understand something is my fault :-)

Top 10 Contributor
Posts 1,128
SunBurn_Community_Licensee
SunBurn_Contributor

Your English is brilliant and so is your tutorial.

Top 500 Contributor
Posts 10
SunBurn_Community_Licensee

Wow, thanks for the tutorial. I'll have to check this out at some point!

(and your English is great :-) )

Top 10 Contributor
Posts 338
SunBurn_Community_Licensee
SunBurn_Pro_Licensee

nice tut

but, remember, that the above shader will be called once for every light in the scene. so use wisely.

for such a shader to work efficiently, some clever scene management could be used so, to only use high detail FX shading when the camera (POV) is a close distance (bounds) from shaded objects, and another bounds from that object used to eliminate lights that are a given distance too far.

i used shaderFX too. it is a very good place to start learning FX, and the GUI is very intuitive. I have hundreds of .FX files made with it.

let me know if you need help

Top 100 Contributor
Posts 35
SunBurn_Community_Licensee
SunBurn_Indie_Licensee
SunBurn_Pro_Licensee

Thanks to all.

@Dug diamond: I'm a complete rookie with the shaders, and I never used ShaderFX before :-) Now I'm trying to include the Sunburn's lights and fog into the shader, do you know how to do it?

Top 10 Contributor
Posts 338
SunBurn_Community_Licensee
SunBurn_Pro_Licensee

it is done using SAS semantics.

take a look at the reflection example, and just fill in the blanks ;)

Top 75 Contributor
Posts 49
SunBurn_Community_Licensee

Hi and thanks for this tutorial.

I'm using shader FX with Sunburn and i don't find how to import fx files for custom effect tab. I'm starting with shaders so i don't have knowledges about process in sunburn. I create my shader like in your example and it's working perfectly in 3ds max. Then i export it, but i can't see it in Sunburn. I made my test on a simple cube exported in FBX format. I'm not able to import the fx file in Sunburn, so my question is: what i forgot to do?

Top 10 Contributor
Posts 5,368
Employee
SunBurn_Studio_Licensee

Hi PixelClover,

To add an fx file to your scene: make sure it's placed in your content folder (or any of the sub-folders), open the editor, select any of the existing materials, right-click, and select "Convert Effect...".  The menu will expand to show all fx files available in your content folder.

After converting the material to your fx file, you will be able to edit it just like the built-in effects.

Let me know if this helps!

Follow me on Twitter – development and personal tweets
Awesome XNA Videos – Lighting, Rendering, and game videos

Top 75 Contributor
Posts 49
SunBurn_Community_Licensee

Hi John and thank you for the fast answer ;)

Unfortunately, I'm able to load the .fx into SunBurn but it always appears pink instead of the normal shade.

Here are the steps I took for the whole thing:

1) Creating a standard material in Shader Fx (Fx Format = 3dsMax / Shader Model = 2,0). Note that I tried with different options here but it didn't seem to change anything.

2) Adding a texture and linking RGB to Diffuse Color. I can see it correctly in 3dsMax viewport.

3) Exporting in "Export Fx" to the format "XNA (DXSAS)" in "Y-Up (DirectX)" in the project folder.

4) Exporting the cube primitive with the texture in fbx format in the same folder.

5) Now that I have both, I open my project in SunBurn.

6) Importing the Model and dragging the fbx to Scene Objects.

7) In the "Materials" tab, I right click on the material and choose "Convert Effect to" and then select my shader (fx).

8) The shader appears but is bright pink.

9) In the "Custom Effect" tab only Drink_Technique and black color appear.

I don't understand why this is happening.

Note that I tried the same process with Fx Composer without going through Shader Fx and everything is working normally there! This leaves me rather confused...

Thank you for your time

 

Top 10 Contributor
Posts 5,368
Employee
SunBurn_Studio_Licensee

Hi PixelClover,

The effect is failing to build due to some error in the shader code (maybe we need to make it more clear when this happens - an error prompt in the editor?).

If you save the material, exit the game, then build / run the game from Visual Studio you should see an in-depth error message that explains any problems in the shader code.

Let me know if this helps!

Follow me on Twitter – development and personal tweets
Awesome XNA Videos – Lighting, Rendering, and game videos

Top 75 Contributor
Posts 49
SunBurn_Community_Licensee

Hi, finally i found causes of my error :

"The effect state 'ShadeMode' is obsolete and can no longer be used".

When i remove "ShadeMode = Gouraud" and "AlphaTestEnable = FALSE" in my shader, it works. ShadeMode and AlphaTestEnable are in Technique complete at the end of file. I guess this block comes from shaderFX but i don't know how to remove it before exporting. At least now i can use my shader in sunburn after modifying code.

eoigames have the same code and it works for him, so i don't understand what's the difference.

Top 75 Contributor
Posts 49
SunBurn_Community_Licensee

And many thanks for help, i will not forget to check error in visual studio.

Top 10 Contributor
Posts 5,368
Employee
SunBurn_Studio_Licensee

Hi PixelClover,

PixelClover:
eoigames have the same code and it works for him, so i don't understand what's the difference.

I believe he was using SunBurn 1.3 at the time, which runs on XNA 3.1.  It's possible the XNA 4.0 shader compiler no longer supports those state.

Please note that changing rendering states in the shader is not recommended when rendering it with the SunBurn renderers.  The renderers sets up specific states for both deferred and forward rendering, changing them could affect lighting, shadows, and more.

Let me know if this helps!

Follow me on Twitter – development and personal tweets
Awesome XNA Videos – Lighting, Rendering, and game videos

Top 75 Contributor
Posts 49
SunBurn_Community_Licensee

thanks for reply, that's what i thought about the version of XNA.

Page 1 of 1 (14 items) | RSS