Example #1
0
        } // Lerp

        #endregion

        #region Generate Spherical Harmonic from CubeMap

        /// <summary>
        /// Generate a spherical harmonic from the faces of a cubemap, treating each pixel as a light source and averaging the result.
        /// </summary>
        public static SphericalHarmonicL2 GenerateSphericalHarmonicFromCubeMap(TextureCube cubeMap)
        {
            SphericalHarmonicL2 sh = new SphericalHarmonicL2();

            // Extract the 6 faces of the cubemap.
            for (int face = 0; face < 6; face++)
            {
                CubeMapFace faceId = (CubeMapFace)face;

                // Get the transformation for this face,
                Matrix cubeFaceMatrix;
                switch (faceId)
                {
                case CubeMapFace.PositiveX:
                    cubeFaceMatrix = Matrix.CreateLookAt(Vector3.Zero, new Vector3(1, 0, 0), new Vector3(0, 1, 0));
                    break;

                case CubeMapFace.NegativeX:
                    cubeFaceMatrix = Matrix.CreateLookAt(Vector3.Zero, new Vector3(-1, 0, 0), new Vector3(0, 1, 0));
                    break;

                case CubeMapFace.PositiveY:
                    cubeFaceMatrix = Matrix.CreateLookAt(Vector3.Zero, new Vector3(0, 1, 0), new Vector3(0, 0, 1));
                    break;

                case CubeMapFace.NegativeY:
                    cubeFaceMatrix = Matrix.CreateLookAt(Vector3.Zero, new Vector3(0, -1, 0), new Vector3(0, 0, -1));
                    break;

                case CubeMapFace.PositiveZ:
                    cubeFaceMatrix = Matrix.CreateLookAt(Vector3.Zero, new Vector3(0, 0, -1), new Vector3(0, 1, 0));
                    break;

                case CubeMapFace.NegativeZ:
                    cubeFaceMatrix = Matrix.CreateLookAt(Vector3.Zero, new Vector3(0, 0, 1), new Vector3(0, 1, 0));
                    break;

                default:
                    throw new ArgumentOutOfRangeException();
                }
                Color[] colorArray = new Color[cubeMap.Size * cubeMap.Size];
                cubeMap.GetData(faceId, colorArray);

                // Extract the spherical harmonic for this face and accumulate it.
                sh += ExtractSphericalHarmonicForCubeFace(cubeFaceMatrix, colorArray, cubeMap.Size);
            }

            //average out over the sphere
            return(sh.GetWeightedAverageLightInputFromSphere());
        } // GenerateSphericalHarmonicFromCubeMap
Example #2
0
        } // SampleDirection

        #endregion

        #region Add

        /// <summary>
        /// Add two spherical harmonics together.
        /// </summary>
        public static SphericalHarmonicL2 Add(SphericalHarmonicL2 x, SphericalHarmonicL2 y)
        {
            return(new SphericalHarmonicL2
            {
                weighting = x.weighting + y.weighting,
                c0 = { X = x.c0.X + y.c0.X, Y = x.c0.Y + y.c0.Y, Z = x.c0.Z + y.c0.Z },
                c1 = { X = x.c1.X + y.c1.X, Y = x.c1.Y + y.c1.Y, Z = x.c1.Z + y.c1.Z },
                c2 = { X = x.c2.X + y.c2.X, Y = x.c2.Y + y.c2.Y, Z = x.c2.Z + y.c2.Z },
                c3 = { X = x.c3.X + y.c3.X, Y = x.c3.Y + y.c3.Y, Z = x.c3.Z + y.c3.Z },
                c4 = { X = x.c4.X + y.c4.X, Y = x.c4.Y + y.c4.Y, Z = x.c4.Z + y.c4.Z },
                c5 = { X = x.c5.X + y.c5.X, Y = x.c5.Y + y.c5.Y, Z = x.c5.Z + y.c5.Z },
                c6 = { X = x.c6.X + y.c6.X, Y = x.c6.Y + y.c6.Y, Z = x.c6.Z + y.c6.Z },
                c7 = { X = x.c7.X + y.c7.X, Y = x.c7.Y + y.c7.Y, Z = x.c7.Z + y.c7.Z },
                c8 = { X = x.c8.X + y.c8.X, Y = x.c8.Y + y.c8.Y, Z = x.c8.Z + y.c8.Z }
            });
        } // Add
Example #3
0
        } // Add

        #endregion

        #region Multiply by a scalar

        /// <summary>
        /// Multiply a spherical harmonic by a constant scale factor
        /// </summary>
        public static SphericalHarmonicL2 Multiply(SphericalHarmonicL2 x, float scale)
        {
            return(new SphericalHarmonicL2
            {
                weighting = x.weighting * scale,
                c0 = { X = x.c0.X * scale, Y = x.c0.Y * scale, Z = x.c0.Z * scale },
                c1 = { X = x.c1.X * scale, Y = x.c1.Y * scale, Z = x.c1.Z * scale },
                c2 = { X = x.c2.X * scale, Y = x.c2.Y * scale, Z = x.c2.Z * scale },
                c3 = { X = x.c3.X * scale, Y = x.c3.Y * scale, Z = x.c3.Z * scale },
                c4 = { X = x.c4.X * scale, Y = x.c4.Y * scale, Z = x.c4.Z * scale },
                c5 = { X = x.c5.X * scale, Y = x.c5.Y * scale, Z = x.c5.Z * scale },
                c6 = { X = x.c6.X * scale, Y = x.c6.Y * scale, Z = x.c6.Z * scale },
                c7 = { X = x.c7.X * scale, Y = x.c7.Y * scale, Z = x.c7.Z * scale },
                c8 = { X = x.c8.X * scale, Y = x.c8.Y * scale, Z = x.c8.Z * scale }
            });
        } // Multiply
Example #4
0
        } // Multiply

        #endregion

        #region Lerp

        /// <summary>
        /// Linear interpolate (Lerp) between two spherical harmonics based on a interpolation factor.
        /// </summary>
        /// <param name="factor">Determines the interpolation point. When factor is 1.0, the output will be x, when factor is 0.0, the output will be y</param>
        public static SphericalHarmonicL2 Lerp(ref SphericalHarmonicL2 x, ref SphericalHarmonicL2 y, float factor)
        {
            float xs = factor;
            float ys = 1.0f - factor;

            return(new SphericalHarmonicL2
            {
                weighting = x.weighting * xs + y.weighting * ys,
                c0 = { X = xs * x.c0.X + ys * y.c0.X, Y = xs * x.c0.Y + ys * y.c0.Y, Z = xs * x.c0.Z + ys * y.c0.Z },
                c1 = { X = xs * x.c1.X + ys * y.c1.X, Y = xs * x.c1.Y + ys * y.c1.Y, Z = xs * x.c1.Z + ys * y.c1.Z },
                c2 = { X = xs * x.c2.X + ys * y.c2.X, Y = xs * x.c2.Y + ys * y.c2.Y, Z = xs * x.c2.Z + ys * y.c2.Z },
                c3 = { X = xs * x.c3.X + ys * y.c3.X, Y = xs * x.c3.Y + ys * y.c3.Y, Z = xs * x.c3.Z + ys * y.c3.Z },
                c4 = { X = xs * x.c4.X + ys * y.c4.X, Y = xs * x.c4.Y + ys * y.c4.Y, Z = xs * x.c4.Z + ys * y.c4.Z },
                c5 = { X = xs * x.c5.X + ys * y.c5.X, Y = xs * x.c5.Y + ys * y.c5.Y, Z = xs * x.c5.Z + ys * y.c5.Z },
                c6 = { X = xs * x.c6.X + ys * y.c6.X, Y = xs * x.c6.Y + ys * y.c6.Y, Z = xs * x.c6.Z + ys * y.c6.Z },
                c7 = { X = xs * x.c7.X + ys * y.c7.X, Y = xs * x.c7.Y + ys * y.c7.Y, Z = xs * x.c7.Z + ys * y.c7.Z },
                c8 = { X = xs * x.c8.X + ys * y.c8.X, Y = xs * x.c8.Y + ys * y.c8.Y, Z = xs * x.c8.Z + ys * y.c8.Z }
            });
        } // Lerp
Example #5
0
        } // GetWeightedAverageLightInputFromSphere

        private static SphericalHarmonicL2 ExtractSphericalHarmonicForCubeFace(Matrix faceTransform, Color[] colorDataRgb, int faceSize)
        {
            SphericalHarmonicL2 sh = new SphericalHarmonicL2();

            // For each pixel in the face, generate it's SH contribution.
            // Treat each pixel in the cube as a light source, which gets added to the SH.
            // This is used to generate an indirect lighting SH for the scene.

            float directionStep = 2.0f / (faceSize - 1.0f);
            int   pixelIndex    = 0;

            float dirY = 1.0f;

            for (int y = 0; y < faceSize; y++)
            {
                SphericalHarmonicL2 lineSh = new SphericalHarmonicL2();
                float dirX = -1.0f;

                for (int x = 0; x < faceSize; x++)
                {
                    //the direction to the pixel in the cube
                    Vector3 direction = new Vector3(dirX, dirY, 1);
                    Vector3.TransformNormal(ref direction, ref faceTransform, out direction);

                    //length of the direction vector
                    float length = direction.Length();
                    //approximate area of the pixel (pixels close to the cube edges appear smaller when projected)
                    float weight = 1.0f / length;

                    //normalise:
                    direction.X *= weight;
                    direction.Y *= weight;
                    direction.Z *= weight;

                    Vector3 rgbFloat;

                    Color rgbm = colorDataRgb[pixelIndex++];
                    rgbFloat = rgbm.ToVector3();

                    //Add it to the SH
                    lineSh.AddLight(rgbFloat, direction, weight);

                    dirX += directionStep;
                }

                //average the SH
                if (lineSh.weighting > 0)
                {
                    lineSh *= 1 / lineSh.weighting;
                }

                // Add the line to the full SH
                // (SH is generated line by line to ease problems with floating point accuracy loss)
                sh += lineSh;

                dirY -= directionStep;
            }

            if (sh.weighting > 0)
            {
                sh *= 1 / sh.weighting;
            }

            return(sh);
        } // ExtractSphericalHarmonicForCubeFace
        } // GetWeightedAverageLightInputFromSphere

        private static SphericalHarmonicL2 ExtractSphericalHarmonicForCubeFace(Matrix faceTransform, Color[] colorDataRgb, int faceSize)
        {
            SphericalHarmonicL2 sh = new SphericalHarmonicL2();

            // For each pixel in the face, generate it's SH contribution.
            // Treat each pixel in the cube as a light source, which gets added to the SH.
            // This is used to generate an indirect lighting SH for the scene.

            float directionStep = 2.0f / (faceSize - 1.0f);
            int pixelIndex = 0;

            float dirY = 1.0f;
            for (int y = 0; y < faceSize; y++)
            {
                SphericalHarmonicL2 lineSh = new SphericalHarmonicL2();
                float dirX = -1.0f;

                for (int x = 0; x < faceSize; x++)
                {
                    //the direction to the pixel in the cube
                    Vector3 direction = new Vector3(dirX, dirY, 1);
                    Vector3.TransformNormal(ref direction, ref faceTransform, out direction);

                    //length of the direction vector
                    float length = direction.Length();
                    //approximate area of the pixel (pixels close to the cube edges appear smaller when projected)
                    float weight = 1.0f / length;

                    //normalise:
                    direction.X *= weight;
                    direction.Y *= weight;
                    direction.Z *= weight;

                    Vector3 rgbFloat;
                    
                    Color rgbm = colorDataRgb[pixelIndex++];
                    rgbFloat = rgbm.ToVector3();
                    
                    //Add it to the SH
                    lineSh.AddLight(rgbFloat, direction, weight);

                    dirX += directionStep;
                }

                //average the SH
                if (lineSh.weighting > 0)
                    lineSh *= 1 / lineSh.weighting;

                // Add the line to the full SH
                // (SH is generated line by line to ease problems with floating point accuracy loss)
                sh += lineSh;

                dirY -= directionStep;
            }

            if (sh.weighting > 0)
                sh *= 1 / sh.weighting;

            return sh;
        } // ExtractSphericalHarmonicForCubeFace
        } // Lerp

        #endregion

        #region Generate Spherical Harmonic from CubeMap

        /// <summary>
        /// Generate a spherical harmonic from the faces of a cubemap, treating each pixel as a light source and averaging the result.
        /// </summary>
        public static SphericalHarmonicL2 GenerateSphericalHarmonicFromCubeMap(TextureCube cubeMap)
        {
            SphericalHarmonicL2 sh = new SphericalHarmonicL2();

            // Extract the 6 faces of the cubemap.
            for (int face = 0; face < 6; face++)
            {
                CubeMapFace faceId = (CubeMapFace)face;

                // Get the transformation for this face,
                Matrix cubeFaceMatrix;
                switch (faceId)
                {
                    case CubeMapFace.PositiveX:
                        cubeFaceMatrix = Matrix.CreateLookAt(Vector3.Zero, new Vector3(1, 0, 0), new Vector3(0, 1, 0));
                        break;
                    case CubeMapFace.NegativeX:
                        cubeFaceMatrix = Matrix.CreateLookAt(Vector3.Zero, new Vector3(-1, 0, 0), new Vector3(0, 1, 0));
                        break;
                    case CubeMapFace.PositiveY:
                        cubeFaceMatrix = Matrix.CreateLookAt(Vector3.Zero, new Vector3(0, 1, 0), new Vector3(0, 0, 1));
                        break;
                    case CubeMapFace.NegativeY:
                        cubeFaceMatrix = Matrix.CreateLookAt(Vector3.Zero, new Vector3(0, -1, 0), new Vector3(0, 0, -1));
                        break;
                    case CubeMapFace.PositiveZ:
                        cubeFaceMatrix = Matrix.CreateLookAt(Vector3.Zero, new Vector3(0, 0, -1), new Vector3(0, 1, 0));
                        break;
                    case CubeMapFace.NegativeZ:
                        cubeFaceMatrix = Matrix.CreateLookAt(Vector3.Zero, new Vector3(0, 0, 1), new Vector3(0, 1, 0));
                        break;
                    default:
                        throw new ArgumentOutOfRangeException();
                }
                Color[] colorArray = new Color[cubeMap.Size * cubeMap.Size];
                cubeMap.GetData(faceId, colorArray);

                // Extract the spherical harmonic for this face and accumulate it.
                sh += ExtractSphericalHarmonicForCubeFace(cubeFaceMatrix, colorArray, cubeMap.Size);
            }

            //average out over the sphere
            return sh.GetWeightedAverageLightInputFromSphere();
        } // GenerateSphericalHarmonicFromCubeMap
        } // Multiply

        #endregion

        #region Lerp

        /// <summary>
        /// Linear interpolate (Lerp) between two spherical harmonics based on a interpolation factor.
        /// </summary>
        /// <param name="factor">Determines the interpolation point. When factor is 1.0, the output will be x, when factor is 0.0, the output will be y</param>
        public static SphericalHarmonicL2 Lerp(ref SphericalHarmonicL2 x, ref SphericalHarmonicL2 y, float factor)
        {
            float xs = factor;
            float ys = 1.0f - factor;
            return new SphericalHarmonicL2
            {
                weighting = x.weighting * xs + y.weighting * ys,
                c0 = { X = xs * x.c0.X + ys * y.c0.X, Y = xs * x.c0.Y + ys * y.c0.Y, Z = xs * x.c0.Z + ys * y.c0.Z },
                c1 = { X = xs * x.c1.X + ys * y.c1.X, Y = xs * x.c1.Y + ys * y.c1.Y, Z = xs * x.c1.Z + ys * y.c1.Z },
                c2 = { X = xs * x.c2.X + ys * y.c2.X, Y = xs * x.c2.Y + ys * y.c2.Y, Z = xs * x.c2.Z + ys * y.c2.Z },
                c3 = { X = xs * x.c3.X + ys * y.c3.X, Y = xs * x.c3.Y + ys * y.c3.Y, Z = xs * x.c3.Z + ys * y.c3.Z },
                c4 = { X = xs * x.c4.X + ys * y.c4.X, Y = xs * x.c4.Y + ys * y.c4.Y, Z = xs * x.c4.Z + ys * y.c4.Z },
                c5 = { X = xs * x.c5.X + ys * y.c5.X, Y = xs * x.c5.Y + ys * y.c5.Y, Z = xs * x.c5.Z + ys * y.c5.Z },
                c6 = { X = xs * x.c6.X + ys * y.c6.X, Y = xs * x.c6.Y + ys * y.c6.Y, Z = xs * x.c6.Z + ys * y.c6.Z },
                c7 = { X = xs * x.c7.X + ys * y.c7.X, Y = xs * x.c7.Y + ys * y.c7.Y, Z = xs * x.c7.Z + ys * y.c7.Z },
                c8 = { X = xs * x.c8.X + ys * y.c8.X, Y = xs * x.c8.Y + ys * y.c8.Y, Z = xs * x.c8.Z + ys * y.c8.Z }
            };
        } // Lerp
        } // Add

        #endregion

        #region Multiply by a scalar

        /// <summary>
        /// Multiply a spherical harmonic by a constant scale factor
        /// </summary>
        public static SphericalHarmonicL2 Multiply(SphericalHarmonicL2 x, float scale)
        {
            return new SphericalHarmonicL2
            {
                weighting = x.weighting * scale,
                c0 = { X = x.c0.X * scale, Y = x.c0.Y * scale, Z = x.c0.Z * scale },
                c1 = { X = x.c1.X * scale, Y = x.c1.Y * scale, Z = x.c1.Z * scale },
                c2 = { X = x.c2.X * scale, Y = x.c2.Y * scale, Z = x.c2.Z * scale },
                c3 = { X = x.c3.X * scale, Y = x.c3.Y * scale, Z = x.c3.Z * scale },
                c4 = { X = x.c4.X * scale, Y = x.c4.Y * scale, Z = x.c4.Z * scale },
                c5 = { X = x.c5.X * scale, Y = x.c5.Y * scale, Z = x.c5.Z * scale },
                c6 = { X = x.c6.X * scale, Y = x.c6.Y * scale, Z = x.c6.Z * scale },
                c7 = { X = x.c7.X * scale, Y = x.c7.Y * scale, Z = x.c7.Z * scale },
                c8 = { X = x.c8.X * scale, Y = x.c8.Y * scale, Z = x.c8.Z * scale }
            };
        } // Multiply
        } // SampleDirection

        #endregion

        #region Add

        /// <summary>
        /// Add two spherical harmonics together.
        /// </summary>
        public static SphericalHarmonicL2 Add(SphericalHarmonicL2 x, SphericalHarmonicL2 y)
        {
            return new SphericalHarmonicL2
            {
                weighting = x.weighting + y.weighting,
                c0 = { X = x.c0.X + y.c0.X, Y = x.c0.Y + y.c0.Y, Z = x.c0.Z + y.c0.Z },
                c1 = { X = x.c1.X + y.c1.X, Y = x.c1.Y + y.c1.Y, Z = x.c1.Z + y.c1.Z },
                c2 = { X = x.c2.X + y.c2.X, Y = x.c2.Y + y.c2.Y, Z = x.c2.Z + y.c2.Z },
                c3 = { X = x.c3.X + y.c3.X, Y = x.c3.Y + y.c3.Y, Z = x.c3.Z + y.c3.Z },
                c4 = { X = x.c4.X + y.c4.X, Y = x.c4.Y + y.c4.Y, Z = x.c4.Z + y.c4.Z },
                c5 = { X = x.c5.X + y.c5.X, Y = x.c5.Y + y.c5.Y, Z = x.c5.Z + y.c5.Z },
                c6 = { X = x.c6.X + y.c6.X, Y = x.c6.Y + y.c6.Y, Z = x.c6.Z + y.c6.Z },
                c7 = { X = x.c7.X + y.c7.X, Y = x.c7.Y + y.c7.Y, Z = x.c7.Z + y.c7.Z },
                c8 = { X = x.c8.X + y.c8.X, Y = x.c8.Y + y.c8.Y, Z = x.c8.Z + y.c8.Z }
            };
        } // Add