XNA Creators Club Online
Page 1 of 1 (3 items)
Sort Posts: Previous Next

Blender .fbx exports for XNA and normal maps: A different approach

Last post 5/9/2009 4:31 AM by moneras s. 2 replies.
  • 3/21/2009 2:28 AM

    Blender .fbx exports for XNA and normal maps: A different approach

    I have long been interested in getting normal maps to work with my .fbx exports from Blender as well as having multiple textures tied to one model.  Before I had only focused on trying to change settings within Blender to get the exports to work.  That approach has proved fruitless.  I have spent the past few hours messing around with the ASCII versions of my fbx exports and I stumbled on an easy way to get normal maps working with your Blender fbx exports.

    Now I'm no expert here, but it appears that the UV data for the diffuse map and normal map can be shared.  To get normal maps to work it only requires changing two lines in the fbx file.  This approach should also work as well for other types of maps.  However, note that you will need a custom content processor on the XNA end to support anything special you do to the fbx file.  So in this case I am using the Normal Mapping sample tutorial.  Also, if you have a text editor handy (like TextEdit) it will help a lot when it comes to finding the right lines to modify.

    So here goes:

    1. Create your model in Blender and unwrap the model.

    2. Export the model from Blender as an .fbx ASCII file.

    3. Open the fbx file and look for

    Model: "Model::TheModelName", "Mesh"

    4.  Under the properties for that model add the following property:

    Property: "NormalMap", "Enum", "A+U",0, "normal_map.tga"

    (It appears that the order the properties are in does not matter, just make sure this line is in the proper section of the file.  The file name of the normal map should match the name of the file within the content folder).

     5.  A few lines down you should see this line:

                Shading: Y

    Change that line to:      

    Shading: T

    6. Now add the model into the Normal Mapping tutorial using “Content”->"Add Existing Item”. 

    7.  Next you will need to build the NormalMappingContentPipeline and add the resulting NormalMappingEffectPipeline.dll (in the debug folder) as a reference.  This will give you access to the Normal Mapping Model Processor.

    8.  Now click on the model you added and change the content processor to “Normal Mapping Model Processor”.

    9.  Next add the normal map you want to use in the content folder of the project (don’t add it to the project, just put it in the content folder).

    10.  Now all you need to do in the C# code is under LoadContent() change the name of the asset that is loaded:

    model = Content.Load<Model>("themodelname");

    11.  Debug... and prepare to be amazed!


    screenshot

  • 5/8/2009 2:28 PM In reply to

    Re: Blender .fbx exports for XNA and normal maps: A different approach

    That is so helpful, works like a charm now. Thanks a bunch!
  • 5/9/2009 4:31 AM In reply to

    Re: Blender .fbx exports for XNA and normal maps: A different approach

    restorp:
    That is so helpful, works like a charm now. Thanks a bunch!


    Glad it worked for you.  I spent some time expanding on the NormalMapping.fx file used in that tutorial.  The code below is my expanded shader that will do normal maps, reflect maps, linear fog, and also has 3 specular lights.  I'll warn you that I have no clue how effiecient this shader is (only been working with shaders for a few weeks), but I can say it has been working fine on my tests with my 8800GTS and 360.  I wanted it to have similar functionality to BasicEffects, while adding normal maps and reflect maps.  Since it works off the same content processor you should be able to just drop it in.  Beware, I could only get it to work with PS3.0 and VS3.0, so you will need a newer gfx card to run it on a pc (again, this is my first crack at this so it probably is poorly written but it does work and some of it may be useful to you).

    float4x4 World; 
    float4x4 View; 
    float4x4 Projection; 
    //float4x4 WVP : WORLDVIEWPROJ; 
    float random = 1; 
    float fogNear : FOGNEAR; 
    float fogFar : FOGFAR; 
    float4 fogColor : FOGCOLOR; 
    float3 cameraPos : CAMERAPOS; 
    float3 worldPos; 
    // ***** Light properties ***** 
    float3 LightPosition; 
    float4 LightColor; 
    float4 AmbientLightColor; 
     
    float3 LightPosition2; 
    float4 LightColor2 = float4(.5,0,0,1); 
    float4 AmbientLightColor2 = float4(0,0,0,0); 
     
    float3 LightPosition3= float3(-10000,10000,-1000); 
    float4 LightColor3 = float4(0,.5,0,1); 
    float4 AmbientLightColor3 = float4(0,0,0,0); 
    // **************************** 
    float EnvMod = 1; 
    bool EnvEnabled = false; 
     
    // ***** material properties ***** 
     
    // output from phong specular will be scaled by this amount 
    float Shininess; 
    float Shininess2=.4; 
    float Shininess3=.3; 
    // specular exponent from phong lighting model.  controls the "tightness" of 
    // specular highlights. 
    float SpecularPower; 
    float SpecularPower2=2; 
    float SpecularPower3=1; 
    // ******************************* 
     
    texture2D NormalMap; 
    sampler2D NormalMapSampler = sampler_state 
        Texture = <NormalMap>; 
        MinFilter = linear; 
        MagFilter = linear; 
        MipFilter = linear; 
    }; 
     
    texture2D FogMap; 
    sampler2D FogMapSampler = sampler_state 
        Texture = <FogMap>; 
        MinFilter = linear; 
        MagFilter = linear; 
        MipFilter = linear; 
    }; 
     
    texture2D Texture; 
    sampler2D DiffuseTextureSampler = sampler_state 
        Texture = <Texture>; 
        MinFilter = linear; 
        MagFilter = linear; 
        MipFilter = linear; 
    }; 
     
    texture EnvMap; 
    sampler EnvSampler = sampler_state 
        Texture = <EnvMap>; 
        MinFilter = linear; 
        MagFilter = linear; 
        MipFilter = linear; 
    }; 
     
    texture EnvMapFade; 
    sampler EnvFadeSampler = sampler_state 
        Texture = <EnvMapFade>; 
        MinFilter = linear; 
        MagFilter = linear; 
        MipFilter = linear; 
    }; 
     
    struct VS_INPUT 
        float4 position            : POSITION0; 
        float2 texCoord            : TEXCOORD0; 
        float3 normal            : NORMAL0;     
        float3 binormal            : BINORMAL0; 
        float3 tangent            : TANGENT0; 
    }; 
     
    // output from the vertex shader, and input to the pixel shader. 
    // lightDirection and viewDirection are in world space. 
    // NOTE: even though the tangentToWorld matrix is only marked  
    // with TEXCOORD3, it will actually take TEXCOORD3, 4, and 5. 
    struct VS_OUTPUT 
        float fog        : TEXCOORD6;     
        float4 position            : POSITION0; 
        float2 texCoord            : TEXCOORD0; 
        float3 lightDirection    : TEXCOORD1; 
        float3 lightDirection2    : TEXCOORD7; 
        float3 lightDirection3  : TEXCOORD8;     
        float3 viewDirection    : TEXCOORD2; 
        float3x3 tangentToWorld    : TEXCOORD3; 
    }; 
     
    VS_OUTPUT VertexShader( VS_INPUT input ) 
        VS_OUTPUT output; 
         
        // transform the position into projection space 
        float4 worldSpacePos = mul(input.position, World); 
        output.position = mul(worldSpacePos, View); 
        output.position = mul(output.position, Projection); 
         
        // calculate the light direction ( from the surface to the light ), which is not 
        // normalized and is in world space 
        output.lightDirection = LightPosition - worldSpacePos; 
        output.lightDirection2 = LightPosition2 - worldSpacePos; 
        output.lightDirection3 = LightPosition3 - worldSpacePos;     
        // similarly, calculate the view direction, from the eye to the surface.  not 
        // normalized, in world space. 
        float3 eyePosition = mul(-View._m30_m31_m32, transpose(View));     
        output.viewDirection = worldSpacePos - eyePosition;     
         
        // calculate tangent space to world space matrix using the world space tangent, 
        // binormal, and normal as basis vectors.  the pixel shader will normalize these 
        // in case the world matrix has scaling. 
        output.tangentToWorld[0] = mul(input.tangent,    World); 
        output.tangentToWorld[1] = mul(input.binormal,    World); 
        output.tangentToWorld[2] = mul(input.normal,    World); 
        output.fog = length(cameraPos - worldSpacePos); 
        // pass the texture coordinate through without additional processing 
        output.texCoord = input.texCoord; 
         
        return output; 
    //float4 calculateFog(float4 Color, float3 Position) 
    //{ 
        //if(!FogEnable) 
        //return Color; 
        // else 
        //{ 
        //float d = length(cameraPos - Position); 
        //float l = saturate((d - FogNear) / (FogFar - FogNear) / clamp(Position.y / FogAltitudeScale + 1, 1, FogThinning)); 
        //float l = saturate((d - fogNear) / (fogFar - fogNear)); 
         
        //return lerp(Color, fogColor, l); 
        //} 
    //} 
    float4 PixelShader( VS_OUTPUT input ) : COLOR0 
        // look up the normal from the normal map, and transform from tangent space 
        // into world space using the matrix created above.  normalize the result 
        // in case the matrix contains scaling. 
        float3 normalFromMap = tex2D(NormalMapSampler, input.texCoord); 
        normalFromMap = mul(normalFromMap, input.tangentToWorld); 
        normalFromMap = normalize(normalFromMap); 
        // clean up our inputs a bit 
        input.viewDirection = normalize(input.viewDirection); 
        input.lightDirection = normalize(input.lightDirection);     
           input.lightDirection2 = normalize(input.lightDirection2);   
          input.lightDirection3 = normalize(input.lightDirection3);  
        // use the normal we looked up to do phong diffuse style lighting.     
        float nDotL = max(dot(normalFromMap, input.lightDirection), 0); 
      float nDotL2 = max(dot(normalFromMap, input.lightDirection2), 0); 
      float nDotL3 = max(dot(normalFromMap, input.lightDirection3), 0); 
        float4 diffuse = LightColor * nDotL * nDotL2 * nDotL3; 
         
        // use phong to calculate specular highlights: reflect the incoming light 
        // vector off the normal, and use a dot product to see how "similar" 
        // the reflected vector is to the view vector.     
        float3 reflectedLight = reflect(input.lightDirection, normalFromMap); 
    float3 reflectedLight2 = reflect(input.lightDirection2, normalFromMap); 
    float3 reflectedLight3 = reflect(input.lightDirection3, normalFromMap); 
        float rDotV = max(dot(reflectedLight, input.viewDirection), 0); 
     float rDotV2 = max(dot(reflectedLight2, input.viewDirection), 0); 
     float rDotV3 = max(dot(reflectedLight3, input.viewDirection), 0); 
        float4 specular = Shininess * LightColor * pow(rDotV, SpecularPower); 
          float4 specular2 = Shininess2 * LightColor2 * pow(rDotV2, SpecularPower2); 
         float4 specular3 = Shininess3 * LightColor3 * pow(rDotV3, SpecularPower3); 
        float4 diffuseTexture = tex2D(DiffuseTextureSampler, input.texCoord); 
        float4 fogTexture = tex2D(FogMapSampler, input.texCoord); 
        // return the combined result. 
        //return (diffuse + AmbientLightColor) * diffuseTexture + specular - calcFog; 
        float3 ref = reflect(normalize(mul(input.tangentToWorld,input.viewDirection)), normalFromMap); 
        float3 ref2 = reflect(normalize(mul(input.tangentToWorld,input.viewDirection)), float3(1,0,1)); 
        float4 envColorF = texCUBE(EnvSampler, ref2); 
        float4 envColor = texCUBE(EnvFadeSampler, ref); 
        float4 beforeFog = (diffuse + AmbientLightColor + AmbientLightColor2 + AmbientLightColor3) * diffuseTexture + specular + specular2+ specular3;// + //float4(envColor.xyz/EnvMod,-1); 
       if(EnvEnabled) 
       {beforeFog = beforeFog + float4(envColor.xyz/EnvMod,1); 
        } 
        //float4 calcFog = calculateFog(beforeFog, cameraPos); 
        //float4 calcFog = calculateFog(beforeFog, worldPos); 
        //worldPos = input.position.xyz; 
       // float4 calcFog = calculateFog(beforeFog, input); 
        float d = input.fog; 
        float l = saturate((d - fogNear) / (fogFar - fogNear)); 
        //float4 calcFog = lerp(beforeFog, fogColor, l); 
        //float4 calcFog = lerp(beforeFog, float4(envColorF.xyz/1,1) + fogTexture/10, l); 
        float4 calcFog = lerp(beforeFog, float4(fogColor.xyz/(1+random),1), l); 
        return calcFog; 
     
    Technique NormalMapping 
        Pass Go 
        { 
            VertexShader = compile vs_3_0 VertexShader(); 
            PixelShader = compile ps_3_0 PixelShader(); 
        } 


Page 1 of 1 (3 items) Previous Next
var gDomain='m.webtrends.com'; var gDcsId='dcschd84w10000w4lw9hcqmsz_8n3x'; var gTrackEvents=1; var gFpc='WT_FPC'; /*<\/scr"+"ipt>");} /*]]>*/
DCSIMG