Google
 
Showing posts with label Semantics. Show all posts
Showing posts with label Semantics. Show all posts

Tuesday, July 17, 2007

Shader Fog

Have found out how to do fog in the shader so this means that you can now have fog in your XBox 360 games as well as your Windows based games.

As usual it is a matter of adding the right semantic and setting the correct renderstate in the shader.

So, the code.

Shader Model 1.1 - 2
In your Shader's vertex return structure, add the following

float Fog : FOG;



In your vertex shader set the fog you require:

Output.Fog = 1.0f - ( length( Input.Position - EyePosition) / 50.0f);


Note: This is an example you can generate this how ever you need to.

Now all you have to do is set the render state for fog, so in the top of your pass add this

FOGENABLE = (3);
FOGCOLOR = (float4(.5,.5,.5,1));



Where 3 is Linear fog and float4(.5,.5,.5,1) would be the color gray.

Shader Model 3
To get fog working on SM 3 and/or the XBox 360 is a little different

  • Remove ALL state settings in the technique.
  • Swap the FOG simantic for a TEXCOORD.
  • As the FOGCOLOR state has been removed, you need to
       calulate this in the shader per vertex lerp between the
       calculated color and the fog color based on the FOG
       TEXCOORD

    For Example:

    Out.Color = lerp(g_FogColor, color * ((In.Diffuse * g_MaterialDiffuseColor) * diffuse + g_MaterialAmbientColor), In.Fog);



    Thanks to Leaf at XNA UK User Group and Jon Watte for your help with this I am sure this will help many others in there XNA development. Jon's shader also has the ability to specify the height of the fog and so giving a much richer fog effect, nice...

    If you have any interest in the discussions we had then take a look here or the comments on this post.

    Leaf has also put up an example of Jon's shader in action and you can find that here.
  • Tuesday, April 24, 2007

    Engine Design - RCModel

    Now this class is probably the biggest deviation from the tutorial. I have added a number of fields and properties for my shaders, from the basic AmbientLightColor to and environment cube map, bump maps and height maps. I don't see any point in documenting these as they are pretty much run of the mill and are required for the shaders you use. So I am going to document the fundimental differences here.

    New Fields

    public bool UseBasicRender;
    private float tick;
    private float animSpeed;

    UseBasicRender
    This field (should have a property) is used as with Mike's original render method shaders using reflective cube mapping did not draw correctly, I don't know why, but they just didn't work. So I had to use a more basic draw method. So if this field is set to true then this basic method is used:


    for (int msh = 0; msh < myModel.Meshes.Count; msh++)
    {
        ModelMesh mesh = myModel.Meshes[msh];
        for (int prt = 0; prt < mesh.MeshParts.Count; prt++)
            mesh.MeshParts[prt].Effect = shader.Effect;
        mesh.Draw();
    }



    tick & animSpeed
    This is how we decide how fast we want our shader animation to run.For examplt in the Microsoft HLSL exabmple it ripples the mesh at a given speed. The speed is set externaly in a Property called AnimationSpeed, this sets the animSpeed field. This is then passed to the shader.

    New Properties

    public BoundingSphere ObjectBoundingSphere
    I added this property so I could get a bounding sphere that was not always positioned at 0,0,0


    public BoundingSphere ObjectBoundingSphere
    {
        get
        {
            BoundingSphere bs = new BoundingSphere(myModel.Meshes[0].BoundingSphere.Center + myPosition, myModel.Meshes[0].BoundingSphere.Radius);
            return bs;
        }
    }



    New Methods

    protected override void DrawBounds(GraphicsDevice myDevice, Color col)
    This method draws the bounding box for the mesh


    protected override void DrawBounds(GraphicsDevice myDevice, Color col)
    {
        List<BoundingBox> bounds = (List<BoundingBox>)((object[])myModel.Tag)[0];
        for (int b = 0; b < bounds.Count; b++)
        {
            myBounds = bounds[b];
            BuildBoxCorners();

            myDevice.DrawUserIndexedPrimitives<VertexPositionColor>(PrimitiveType.LineList, points, 0, 8, index, 0, 12);
        }
    }



    public void UpdateAnimation()
    This was added recently to update the bones of an animated mesh. This method is called at the top of the RenderChildren method.


    public void UpdateAnimation()
    {
        if (animationData != null)
        {
            Matrix[] m_bonetrans = new Matrix[animationData.m_bones.Count];

            currentAnimationFrame++;
            if (currentAnimationFrame >= animationData.m_timeframes.Count)
                currentAnimationFrame = 0;

            int i = 0;
            foreach (TimeFrame tf in animationData.m_timeframes.Values)
            {
                if (i == currentAnimationFrame)
                {
                    for (int j = 0; j < tf.m_transforms.Count; j++)
                    {
                        m_bonetrans[tf.m_transforms[j].m_boneindex] = tf.m_transforms[j].m_transform;
                    }
                    break;
                }
                i++;
            }
            Effect effect = RCShaderManager.GetShader(myShader).Effect;
            if(effect.Parameters["mWorldMatrixArray"] != null)
                effect.Parameters["mWorldMatrixArray"].SetValue(m_bonetrans);

            if (effect.Parameters["Bones"] != null)
                effect.Parameters["Bones"].SetValue(m_bonetrans);
        }
    }



    And finaly the modified public void SetMaterialProperties() that now takes into account Semantics and Annotations.


    public void SetMaterialProperties()
    {
        Effect effect = RCShaderManager.GetShader(myShader).Effect;

        for (int parm = 0; parm < effect.Parameters.Count; parm++)
        {
            string paramSemantic = "";
            paramSemantic = effect.Parameters[parm].Semantic;

            if (effect.Parameters[parm].Semantic != null)
            {
                switch (effect.Parameters[parm].Semantic.ToLower())
                {
                    case "position": // Presume it's a light
                        for (int note = 0; note < effect.Parameters[parm].Annotations.Count; note++)
                        {
                            EffectAnnotation desc = effect.Parameters[parm].Annotations[note];

                            switch (desc.ParameterType)
                            {
                                case EffectParameterType.String:
                                switch (desc.ParameterClass)
                                {
                                    case EffectParameterClass.Object:
                                        switch (desc.GetValueString().ToLower())
                                        {
                                            case "lightfromsky":
                                                effect.Parameters[parm].SetValue(myLightFromSky);
                                                break;
                                            case "directionallight":
                                            case "pointlight":
                                                effect.Parameters[parm].SetValue(myLightPosition);
                                                break;
                                        }
                                        break;
                                }
                                break;
                            }
                        }
                        break;
                    case "ambient":
                        switch (effect.Parameters[parm].ParameterClass)
                        {
                            case EffectParameterClass.Vector: // Color
                                if (effect.Parameters[parm].Name.ToLower().IndexOf("light") != -1)
                                    effect.Parameters[parm].SetValue(myLightAmbientIntensity);
                                else
                                    effect.Parameters[parm].SetValue(myAmbientLightColor);
                                break;
                        }
                        break;
                    case "diffuse":
                        switch (effect.Parameters[parm].ParameterClass)
                        {
                            case EffectParameterClass.Vector: // Color
                                if (effect.Parameters[parm].Name.ToLower().IndexOf("light") != -1)
                                    effect.Parameters[parm].SetValue(myLightDiffuseColor);
                                else
                                {
                                    bool loaded = false;
                                    for (int an = 0; an < effect.Parameters[parm].Annotations.Count; an++)
                                    {
                                        if (effect.Parameters[parm].Annotations[an].Name == "UIName")
                                        {
                                            switch (effect.Parameters[parm].Annotations[an].GetValueString().ToLower())
                                            {
                                                case "groundcolor":
                                                    loaded = true;
                                                    effect.Parameters[parm].SetValue(myGroundColor);
                                                    break;
                                                case "skycolor":
                                                    loaded = true;
                                                    effect.Parameters[parm].SetValue(mySkyColor);
                                                    break;
                                            }
                                            break;
                                        }
                                    }
                                    if (!loaded)
                                        effect.Parameters[parm].SetValue(myDiffuseColor);
                                }
                                break;
                        }
                        break;
                    case "specular":
                        switch (effect.Parameters[parm].ParameterClass)
                        {
                            case EffectParameterClass.Vector: // Color
                                if (effect.Parameters[parm].Name.ToLower().IndexOf("light") != -1)
                                    effect.Parameters[parm].SetValue(myLightSpecularColor);
                                else
                                    effect.Parameters[parm].SetValue(mySpecularColor);
                                break;
                            case EffectParameterClass.Scalar: // Value
                                effect.Parameters[parm].SetValue(mySpecularPower);
                                break;
                            }
                            break;
                        case "environment":
                            if(myCube != null)
                                effect.Parameters[parm].SetValue(myCube);
                            else
                                if(myColorMap != null)
                                    effect.Parameters[parm].SetValue(myColorMap);
                                break;
                            case "rcmaterialparameter":
                                switch (effect.Parameters[parm].ParameterClass)
                                {
                                    case EffectParameterClass.Vector:
                                        for (int an = 0; an < effect.Parameters[parm].Annotations.Count; an++)
                                        {
                                            if (effect.Parameters[parm].Annotations[an].Name == "UIName")
                                            {
                                                switch (effect.Parameters[parm].Annotations[an].GetValueString().ToLower())
                                                {
                                                    case "etas":
                                                        effect.Parameters[parm].SetValue(new Vector3(0.80f, 0.82f, 0.84f));
                                                        break;
                                                }
                                                break;
                                            }
                                        }
                                        break;
                                    case EffectParameterClass.Scalar:
                                        for (int an = 0; an < effect.Parameters[parm].Annotations.Count; an++)
                                        {
                                            if (effect.Parameters[parm].Annotations[an].Name == "UIName")
                                            {
                                                switch (effect.Parameters[parm].Annotations[an].GetValueString().ToLower())
                                                {
                                                    case "reflectivestrength":
                                                        effect.Parameters[parm].SetValue(1.0f);
                                                        break;
                                                    case "refractstrength":
                                                        effect.Parameters[parm].SetValue(1.0f);
                                                        break;
                                                }
                                                break;
                                            }
                                        }
                                        break;
                                    case EffectParameterClass.Object:
                                        for (int an = 0; an < effect.Parameters[parm].Annotations.Count; an++)
                                        {
                                            if (effect.Parameters[parm].Annotations[an].Name == "UIName")
                                            {
                                                switch (effect.Parameters[parm].Annotations[an].GetValueString().ToLower())
                                                {
                                                    case "colormap":
                                                        effect.Parameters[parm].SetValue(myColorMap);
                                                        break;
                                                    case "bumpmap":
                                                        effect.Parameters[parm].SetValue(myBumpMap);
                                                        break;
                                                    case "heightmap":
                                                        effect.Parameters[parm].SetValue(myHeightMap);
                                                        break;
                                                }
                                                break;
                                            }
                                        }
                                        break;
                                }
                                break;
                            default:
                                break;
                }
            }
        }
    }

    Sunday, April 15, 2007

    Engine Design - Shader, Textured Quad and Camera Objects

    RCShader & RCShaderManager
    I will start with the Shader classes. The ShaderManager is as the tutorial has it but has the Hashtable replaced with a Dictionary.

    Basically I have redefined the myShaders container and added a variable to manage the index like this:

    private static Dictionary<string, RCShader> myShaders = new Dictionary<string,RCShader>();
    private static NameValueCollection keys = new NameValueCollection();



    Altered the AddShader method like this:

    public static void AddShader(RCShader newShader,string shaderLabel)
    {
        myShaders.Add(shaderLabel, newShader);
        keys.Add((myShaders.Count - 1).ToString(), shaderLabel);
    }



    And replaced the foreach loop with a for loop in the LoadGraphicsContent method lie this:

    public static void LoadGraphicsContent(GraphicsDevice myDevice,ContentManager myLoader)
    {
        for (int sh = 0; sh < myShaders.Count; sh++)
            myShaders[keys[sh.ToString()]].LoadGraphicsContent(
                        myDevice, myLoader);
    }



    The Shader class it's self however has been reworked to use Semantics. The class has stayed essentially the same, however the SetParameters method has been totally redone.


    public void SetParameters(RCObject myObject)
    {
        Matrix World = Matrix.Identity;
        Matrix View = Matrix.Identity;
        Matrix Projection = Matrix.Identity;
        Matrix WVP = Matrix.Identity;
        Matrix WorldView = Matrix.Identity;
        Matrix ViewProjection = Matrix.Identity;

        if (!myObject.AlwaysFacingCamera)
        {
            if (myObject.UseLeftHandedWorldCalc)
            {
                World = Matrix.CreateScale(myObject.Scaling) *
                    Matrix.CreateTranslation(myObject.Position) *
                    Matrix.CreateFromQuaternion(myObject.Rotation);
            }
            else
            {
                World = Matrix.CreateScale(myObject.Scaling) *
                    Matrix.CreateFromQuaternion(myObject.Rotation) *
                    Matrix.CreateTranslation(myObject.Position);
            }
        }
        else
        {
            World = Matrix.CreateScale(myObject.Scaling) *
                Matrix.CreateFromQuaternion(RCCameraManager.ActiveCamera.Rotation * -1) *
                Matrix.CreateTranslation(myObject.Position);
        }

        if (RCHelpers.RCHelper.UseRefelctionViewMatrix)
            View = RCHelpers.RCHelper.reflectionViewMatrix;
        else
            View = RCCameraManager.ActiveCamera.View;

        Projection = RCCameraManager.ActiveCamera.Projection;

        ViewProjection = View * Projection;
        WorldView = World * View;
        WVP = World * View * Projection;

        for (int parm = 0; parm < myEffect.Parameters.Count; parm++)
        {
            string paramSemantic = "";
            paramSemantic = myEffect.Parameters[parm].Semantic;
            if (paramSemantic != null)
            {
                switch (paramSemantic.ToLower())
                {
                    case "worldviewprojection":
                        myEffect.Parameters[parm].SetValue(WVP);
                        break;
                    case "world":
                        myEffect.Parameters[parm].SetValue(World);
                        break;
                    case "view":
                        myEffect.Parameters[parm].SetValue(View);
                        break;
                    case "projection":
                        myEffect.Parameters[parm].SetValue(Projection);
                        break;
                    case "cameraposition":
                        myEffect.Parameters[parm].SetValue(RCCameraManager.ActiveCamera.Position);
                        break;
                    case "worldinversetranspose":
                        myEffect.Parameters[parm].SetValue(Matrix.Transpose(Matrix.Invert(World)));
                        break;
                    case "worldinverse":
                        myEffect.Parameters[parm].SetValue(Matrix.Invert(World));
                    break;
                    case "worldview":
                        myEffect.Parameters[parm].SetValue(WorldView);
                        break;
                    case "viewprojection":
                        myEffect.Parameters[parm].SetValue(ViewProjection);
                        break;
                    case "viewinverse":
                        myEffect.Parameters[parm].SetValue(Matrix.Invert(View));
                        break;
                }
            }
        }
    }


    You will see above three ways of creating the World matrix, one is the one given in the tutorial, Scale * Rotation * Position, another is used when the UseLeftHandedWorldCalc property is set to true in myObject. I put this in as I was having some issues with a few shaders I found. The issue was that the models where not rotating but revolving when I called the rotate method on them, I thought this was due to the shaders having been written for a left handed system (XNA is right), being new to all this 3D stuff I could not alter the shader to behave properly so altered the world calculation for them so it would and the Scale * Position * Rotation method seemed to do the trick. If I ever get a better solution to this or find why it behaves like it does for the shaders I will post the fix here. The third method is only ever really used for my billboard class to keep the Textured Quad facing the camera.

    RCTexturedQuad & RC2SidedTexturdeQuad
    With the base Textured Quad code I have just added the ability to allow for alpha checking (image transparency). I have done this by adding a field and associated property called alphacheck. If set to true the Render method of the TQ sets the render state to use the alpha test.

    This code is called before the Vertex declaration:

    bool alphaTest = myDevice.RenderState.AlphaTestEnable;
    bool alphaBlend = myDevice.RenderState.AlphaBlendEnable;
    CompareFunction alphaFunc = myDevice.RenderState.AlphaFunction;

    if (alphaCheck)
    {
        if (myDevice.RenderState.AlphaTestEnable != true)
            myDevice.RenderState.AlphaTestEnable = true;
        if (myDevice.RenderState.AlphaBlendEnable != true)
            myDevice.RenderState.AlphaBlendEnable = true;
        if (myDevice.RenderState.AlphaFunction != CompareFunction.NotEqual)
            myDevice.RenderState.AlphaFunction = CompareFunction.NotEqual;
    }



    and this after to put the states back as they were if needed:

    if (alphaCheck)
    {
        if (myDevice.RenderState.AlphaTestEnable != alphaTest)
            myDevice.RenderState.AlphaTestEnable = alphaTest;
        if (myDevice.RenderState.AlphaBlendEnable != alphaBlend)
            myDevice.RenderState.AlphaBlendEnable = alphaBlend;
        if (myDevice.RenderState.AlphaFunction != alphaFunc)
            myDevice.RenderState.AlphaFunction = alphaFunc;
    }



    The bounding box for the Textured Quad is managed like this in the Render method:

    myBounds = new BoundingBox(myPosition - (myScaling / 2), myScaling / 2);



    I have also added a double sided textured quad, this is basically 2 textured quads back to back, displaying the same image.

    RCCamera & RCCameraManager
    Again there is very little deviation here, I have added an enumerator to manage the different camera types I want to use and added a field with an associated property of this type to the RCCamera class.


    public enum CameraViewType
    {
        Floating,
        ThirdPerson,
        POV
    }



    This tells me what kind of movement the camera should have and is managed in the calling assembly.