/// <summary>
        /// <para>Reads in a 2D texture in RGBM format, and extracts the cube faces.</para>
        /// <para>It also generates a Spherical Harmonic from the input, to approximate indirect lighting</para>
        /// </summary>
        public RgbmCubeMap(Texture2D sourceRgbmImage2D, Texture2D sourceRgbmAlphaImage2D, float maxRange)
        {
            //the two textures represent the RGB and M values of an RGBM texture. They have
            //been separated to make it easier to view their colour channels.
            //This is somewhat wasteful however. Normally it would be wise to combine them into one texture.

            if (sourceRgbmImage2D == null || sourceRgbmAlphaImage2D == null)
                throw new ArgumentNullException();
            if (sourceRgbmImage2D.Width != sourceRgbmImage2D.Height * 6 ||
                sourceRgbmAlphaImage2D.Width != sourceRgbmImage2D.Width ||
                sourceRgbmAlphaImage2D.Height != sourceRgbmImage2D.Height)
                throw new ArgumentException("Invalid size");	//expected to have 6 cube faces
            if (sourceRgbmImage2D.Format != SurfaceFormat.Color ||
                sourceRgbmAlphaImage2D.Format != SurfaceFormat.Color)
                throw new ArgumentException("Unexpected image format");

            if (maxRange <= 0.0f)
                throw new ArgumentException("MaxRange");

            this.MaxRange = maxRange;

            //extract the RGB pixels
            Color[] pixelDataRGB = new Color[sourceRgbmImage2D.Width * sourceRgbmImage2D.Height];
            sourceRgbmImage2D.GetData(pixelDataRGB);

            //extract the M pixels
            Color[] pixelDataM = new Color[sourceRgbmAlphaImage2D.Width * sourceRgbmAlphaImage2D.Height];
            sourceRgbmAlphaImage2D.GetData(pixelDataM);

            //at this point, the source textures are no longer needed

            //extract the faces from the cubemap,
            //and generate a spherical harmonic based off it's colours

            //width/height of each face of the cubemap
            int cubeFaceSize = sourceRgbmImage2D.Height;

            this.SphericalHarmonic = new Xen.Ex.SphericalHarmonicL2RGB();
            this.cubeMap = new TextureCube(sourceRgbmImage2D.GraphicsDevice, cubeFaceSize, false, SurfaceFormat.Color);

            int textureLineStride = sourceRgbmImage2D.Width;

            //storage for the decoded cubemap faces
            Vector3[][] cubeFaces = new Vector3[6][];

            //temporary storage of a single face of the cube
            Color[] singleFaceRGBM = new Color[cubeFaceSize * cubeFaceSize];

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

                //cube face data
                Vector3[] singleFaceRGB = new Vector3[cubeFaceSize * cubeFaceSize];
                cubeFaces[face] = singleFaceRGB;

                //each face is stored next to each other in the 2D texture
                int startPixel = cubeFaceSize * face;

                //copy the face from the 2D texture data into singleFace

                int writeIndex = 0;
                int readIndex = startPixel;

                for (int y = 0; y < cubeFaceSize; y++) // each hoizontal line
                {
                    for (int x = 0; x < cubeFaceSize; x++) // each pixel in the line
                    {
                        Color encodedRgb = pixelDataRGB[readIndex + x];
                        Color encodedM = pixelDataM[readIndex + x];

                        //combine to get the RGBM value
                        Color rgbm = new Color(encodedRgb.R, encodedRgb.G, encodedRgb.B, encodedM.R);

                        //decode the pixel
                        Vector3 rgb = RgbmTools.DecodeRGBM(rgbm, maxRange);

                        //convert to linear
                        rgb = rgb * rgb;

                        //store
                        singleFaceRGB[writeIndex + x] = rgb;
                        singleFaceRGBM[writeIndex + x] = rgbm;
                    }

                    //jump to the next line
                    readIndex += textureLineStride;
                    writeIndex += cubeFaceSize;
                }

                //write the pixels into the cubemap
                cubeMap.SetData(faceId, singleFaceRGBM);
            }

            //Generate the SH from the cubemap faces
            this.SphericalHarmonic = Xen.Ex.SphericalHarmonicL2RGB.GenerateSphericalHarmonicFromCubeMap(cubeFaces);
        }
		private static Xen.Ex.SphericalHarmonicL2RGB ExtractSphericalHarmonicForCubeFace(Matrix faceTransform, Vector3[] colourDataRGB, int faceSize)
		{
			Xen.Ex.SphericalHarmonicL2RGB sh = new Xen.Ex.SphericalHarmonicL2RGB();

			//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.

			//See the remarks in SphericalHarmonicL2RGB for more detals.

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

			float dirY = 1.0f;
			for (int y = 0; y < faceSize; y++)
			{
				Xen.Ex.SphericalHarmonicL2RGB lineSh = new Xen.Ex.SphericalHarmonicL2RGB();
				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;

					//decode the RGBM colour
					Vector3 rgb = colourDataRGB[pixelIndex++];

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

					dirX += directionStep;
				}

				//average the SH
				if (lineSh.Weighting > 0)
					lineSh /= 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 /= sh.Weighting;

			return sh;
		}
        private static Xen.Ex.SphericalHarmonicL2RGB ExtractSphericalHarmonicForCubeFace(Matrix faceTransform, Vector3[] colourDataRGB, int faceSize)
        {
            Xen.Ex.SphericalHarmonicL2RGB sh = new Xen.Ex.SphericalHarmonicL2RGB();

            //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.

            //See the remarks in SphericalHarmonicL2RGB for more detals.

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

            float dirY = 1.0f;

            for (int y = 0; y < faceSize; y++)
            {
                Xen.Ex.SphericalHarmonicL2RGB lineSh = new Xen.Ex.SphericalHarmonicL2RGB();
                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;

                    //decode the RGBM colour
                    Vector3 rgb = colourDataRGB[pixelIndex++];

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

                    dirX += directionStep;
                }

                //average the SH
                if (lineSh.Weighting > 0)
                {
                    lineSh /= 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 /= sh.Weighting;
            }

            return(sh);
        }
Exemple #4
0
        /// <summary>
        /// <para>Reads in a 2D texture in RGBM format, and extracts the cube faces.</para>
        /// <para>It also generates a Spherical Harmonic from the input, to approximate indirect lighting</para>
        /// </summary>
        public RgbmCubeMap(Texture2D sourceRgbmImage2D, Texture2D sourceRgbmAlphaImage2D, float maxRange)
        {
            //the two textures represent the RGB and M values of an RGBM texture. They have
            //been separated to make it easier to view their colour channels.
            //This is somewhat wasteful however. Normally it would be wise to combine them into one texture.

            if (sourceRgbmImage2D == null || sourceRgbmAlphaImage2D == null)
            {
                throw new ArgumentNullException();
            }
            if (sourceRgbmImage2D.Width != sourceRgbmImage2D.Height * 6 ||
                sourceRgbmAlphaImage2D.Width != sourceRgbmImage2D.Width ||
                sourceRgbmAlphaImage2D.Height != sourceRgbmImage2D.Height)
            {
                throw new ArgumentException("Invalid size");                    //expected to have 6 cube faces
            }
            if (sourceRgbmImage2D.Format != SurfaceFormat.Color ||
                sourceRgbmAlphaImage2D.Format != SurfaceFormat.Color)
            {
                throw new ArgumentException("Unexpected image format");
            }

            if (maxRange <= 0.0f)
            {
                throw new ArgumentException("MaxRange");
            }

            this.MaxRange = maxRange;

            //extract the RGB pixels
            Color[] pixelDataRGB = new Color[sourceRgbmImage2D.Width * sourceRgbmImage2D.Height];
            sourceRgbmImage2D.GetData(pixelDataRGB);

            //extract the M pixels
            Color[] pixelDataM = new Color[sourceRgbmAlphaImage2D.Width * sourceRgbmAlphaImage2D.Height];
            sourceRgbmAlphaImage2D.GetData(pixelDataM);

            //at this point, the source textures are no longer needed


            //extract the faces from the cubemap,
            //and generate a spherical harmonic based off it's colours

            //width/height of each face of the cubemap
            int cubeFaceSize = sourceRgbmImage2D.Height;

            this.SphericalHarmonic = new Xen.Ex.SphericalHarmonicL2RGB();
            this.cubeMap           = new TextureCube(sourceRgbmImage2D.GraphicsDevice, cubeFaceSize, 1, TextureUsage.None, SurfaceFormat.Color);

            int textureLineStride = sourceRgbmImage2D.Width;

            //storage for the decoded cubemap faces
            Vector3[][] cubeFaces = new Vector3[6][];

            //temporary storage of a single face of the cube
            Color[] singleFaceRGBM = new Color[cubeFaceSize * cubeFaceSize];

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

                //cube face data
                Vector3[] singleFaceRGB = new Vector3[cubeFaceSize * cubeFaceSize];
                cubeFaces[face] = singleFaceRGB;

                //each face is stored next to each other in the 2D texture
                int startPixel = cubeFaceSize * face;

                //copy the face from the 2D texture data into singleFace

                int writeIndex = 0;
                int readIndex  = startPixel;

                for (int y = 0; y < cubeFaceSize; y++)                 // each hoizontal line
                {
                    for (int x = 0; x < cubeFaceSize; x++)             // each pixel in the line
                    {
                        Color encodedRgb = pixelDataRGB[readIndex + x];
                        Color encodedM   = pixelDataM[readIndex + x];

                        //combine to get the RGBM value
                        Color rgbm = new Color(encodedRgb.R, encodedRgb.G, encodedRgb.B, encodedM.R);

                        //decode the pixel
                        Vector3 rgb = RgbmTools.DecodeRGBM(rgbm, maxRange);

                        //convert to linear
                        rgb = rgb * rgb;

                        //store
                        singleFaceRGB[writeIndex + x]  = rgb;
                        singleFaceRGBM[writeIndex + x] = rgbm;
                    }

                    //jump to the next line
                    readIndex  += textureLineStride;
                    writeIndex += cubeFaceSize;
                }

                //write the pixels into the cubemap
                cubeMap.SetData(faceId, singleFaceRGBM);
            }

            //Generate the SH from the cubemap faces
            this.SphericalHarmonic = Xen.Ex.SphericalHarmonicL2RGB.GenerateSphericalHarmonicFromCubeMap(cubeFaces);
        }