#region File Description //----------------------------------------------------------------------------- // EffectHelpers.cs // // Microsoft XNA Community Game Platform // Copyright (C) Microsoft Corporation. All rights reserved. //----------------------------------------------------------------------------- #endregion #region Using Statements using System; #endregion namespace Microsoft.Xna.Framework.Graphics { /// /// Track which effect parameters need to be recomputed during the next OnApply. /// [Flags] internal enum EffectDirtyFlags { WorldViewProj = 1, World = 2, EyePosition = 4, MaterialColor = 8, Fog = 16, FogEnable = 32, AlphaTest = 64, ShaderIndex = 128, All = -1 } /// /// Helper code shared between the various built-in effects. /// internal static class EffectHelpers { /// /// Sets up the standard key/fill/back lighting rig. /// internal static Vector3 EnableDefaultLighting(DirectionalLight light0, DirectionalLight light1, DirectionalLight light2) { // Key light. light0.Direction = new Vector3(-0.5265408f, -0.5735765f, -0.6275069f); light0.DiffuseColor = new Vector3(1, 0.9607844f, 0.8078432f); light0.SpecularColor = new Vector3(1, 0.9607844f, 0.8078432f); light0.Enabled = true; // Fill light. light1.Direction = new Vector3(0.7198464f, 0.3420201f, 0.6040227f); light1.DiffuseColor = new Vector3(0.9647059f, 0.7607844f, 0.4078432f); light1.SpecularColor = Vector3.Zero; light1.Enabled = true; // Back light. light2.Direction = new Vector3(0.4545195f, -0.7660444f, 0.4545195f); light2.DiffuseColor = new Vector3(0.3231373f, 0.3607844f, 0.3937255f); light2.SpecularColor = new Vector3(0.3231373f, 0.3607844f, 0.3937255f); light2.Enabled = true; // Ambient light. return new Vector3(0.05333332f, 0.09882354f, 0.1819608f); } /// /// Lazily recomputes the world+view+projection matrix and /// fog vector based on the current effect parameter settings. /// internal static EffectDirtyFlags SetWorldViewProjAndFog(EffectDirtyFlags dirtyFlags, ref Matrix world, ref Matrix view, ref Matrix projection, ref Matrix worldView, bool fogEnabled, float fogStart, float fogEnd, EffectParameter worldViewProjParam, EffectParameter fogVectorParam) { // Recompute the world+view+projection matrix? if ((dirtyFlags & EffectDirtyFlags.WorldViewProj) != 0) { Matrix worldViewProj; Matrix.Multiply(ref world, ref view, out worldView); Matrix.Multiply(ref worldView, ref projection, out worldViewProj); worldViewProjParam.SetValue(worldViewProj); dirtyFlags &= ~EffectDirtyFlags.WorldViewProj; } if (fogEnabled) { // Recompute the fog vector? if ((dirtyFlags & (EffectDirtyFlags.Fog | EffectDirtyFlags.FogEnable)) != 0) { SetFogVector(ref worldView, fogStart, fogEnd, fogVectorParam); dirtyFlags &= ~(EffectDirtyFlags.Fog | EffectDirtyFlags.FogEnable); } } else { // When fog is disabled, make sure the fog vector is reset to zero. if ((dirtyFlags & EffectDirtyFlags.FogEnable) != 0) { fogVectorParam.SetValue(Vector4.Zero); dirtyFlags &= ~EffectDirtyFlags.FogEnable; } } return dirtyFlags; } /// /// Sets a vector which can be dotted with the object space vertex position to compute fog amount. /// static void SetFogVector(ref Matrix worldView, float fogStart, float fogEnd, EffectParameter fogVectorParam) { if (fogStart == fogEnd) { // Degenerate case: force everything to 100% fogged if start and end are the same. fogVectorParam.SetValue(new Vector4(0, 0, 0, 1)); } else { // We want to transform vertex positions into view space, take the resulting // Z value, then scale and offset according to the fog start/end distances. // Because we only care about the Z component, the shader can do all this // with a single dot product, using only the Z row of the world+view matrix. float scale = 1f / (fogStart - fogEnd); Vector4 fogVector = new Vector4(); fogVector.X = worldView.M13 * scale; fogVector.Y = worldView.M23 * scale; fogVector.Z = worldView.M33 * scale; fogVector.W = (worldView.M43 + fogStart) * scale; fogVectorParam.SetValue(fogVector); } } /// /// Lazily recomputes the world inverse transpose matrix and /// eye position based on the current effect parameter settings. /// internal static EffectDirtyFlags SetLightingMatrices(EffectDirtyFlags dirtyFlags, ref Matrix world, ref Matrix view, EffectParameter worldParam, EffectParameter worldInverseTransposeParam, EffectParameter eyePositionParam) { // Set the world and world inverse transpose matrices. if ((dirtyFlags & EffectDirtyFlags.World) != 0) { Matrix worldTranspose; Matrix worldInverseTranspose; Matrix.Invert(ref world, out worldTranspose); Matrix.Transpose(ref worldTranspose, out worldInverseTranspose); worldParam.SetValue(world); worldInverseTransposeParam.SetValue(worldInverseTranspose); dirtyFlags &= ~EffectDirtyFlags.World; } // Set the eye position. if ((dirtyFlags & EffectDirtyFlags.EyePosition) != 0) { Matrix viewInverse; Matrix.Invert(ref view, out viewInverse); eyePositionParam.SetValue(viewInverse.Translation); dirtyFlags &= ~EffectDirtyFlags.EyePosition; } return dirtyFlags; } /// /// Sets the diffuse/emissive/alpha material color parameters. /// internal static void SetMaterialColor(bool lightingEnabled, float alpha, ref Vector3 diffuseColor, ref Vector3 emissiveColor, ref Vector3 ambientLightColor, EffectParameter diffuseColorParam, EffectParameter emissiveColorParam) { // Desired lighting model: // // ((AmbientLightColor + sum(diffuse directional light)) * DiffuseColor) + EmissiveColor // // When lighting is disabled, ambient and directional lights are ignored, leaving: // // DiffuseColor + EmissiveColor // // For the lighting disabled case, we can save one shader instruction by precomputing // diffuse+emissive on the CPU, after which the shader can use DiffuseColor directly, // ignoring its emissive parameter. // // When lighting is enabled, we can merge the ambient and emissive settings. If we // set our emissive parameter to emissive+(ambient*diffuse), the shader no longer // needs to bother adding the ambient contribution, simplifying its computation to: // // (sum(diffuse directional light) * DiffuseColor) + EmissiveColor // // For futher optimization goodness, we merge material alpha with the diffuse // color parameter, and premultiply all color values by this alpha. if (lightingEnabled) { Vector4 diffuse = new Vector4(); Vector3 emissive = new Vector3(); diffuse.X = diffuseColor.X * alpha; diffuse.Y = diffuseColor.Y * alpha; diffuse.Z = diffuseColor.Z * alpha; diffuse.W = alpha; emissive.X = (emissiveColor.X + ambientLightColor.X * diffuseColor.X) * alpha; emissive.Y = (emissiveColor.Y + ambientLightColor.Y * diffuseColor.Y) * alpha; emissive.Z = (emissiveColor.Z + ambientLightColor.Z * diffuseColor.Z) * alpha; diffuseColorParam.SetValue(diffuse); emissiveColorParam.SetValue(emissive); } else { Vector4 diffuse = new Vector4(); diffuse.X = (diffuseColor.X + emissiveColor.X) * alpha; diffuse.Y = (diffuseColor.Y + emissiveColor.Y) * alpha; diffuse.Z = (diffuseColor.Z + emissiveColor.Z) * alpha; diffuse.W = alpha; diffuseColorParam.SetValue(diffuse); } } } }