} // 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. /// This method only accepts RGBM and color textures. /// </summary> public static SphericalHarmonicL2 GenerateSphericalHarmonicFromCubeTexture(TextureCube cubeTexture) { if (cubeTexture.Resource.Format != SurfaceFormat.Color) { throw new InvalidOperationException("Spherical Harmonic: the texture has to have a color surface format. DXT and floating point formats are not supported for the moment."); } 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[cubeTexture.Size * cubeTexture.Size]; cubeTexture.Resource.GetData(faceId, colorArray); // Extract the spherical harmonic for this face and accumulate it. sh += ExtractSphericalHarmonicForCubeFace(cubeFaceMatrix, colorArray, cubeTexture.Size, cubeTexture.IsRgbm, cubeTexture.RgbmMaxRange); } // Average out over the sphere. return(sh.GetWeightedAverageLightInputFromSphere()); } // GenerateSphericalHarmonicFromCubeTexture
} // ExtractSphericalHarmonicForCubeFace #endregion #region Generate Spherical Harmonic from Lat-Long Texture /// <summary> /// Generate a spherical harmonic from the faces of a cubemap, treating each pixel as a light source and averaging the result. /// This method only accepts floating point textures. /// </summary> public static SphericalHarmonicL2 GenerateSphericalHarmonicFromLatLongTexture(Texture texture) { if (texture.Resource.Format != SurfaceFormat.Vector4) { throw new InvalidOperationException("Spherical Harmonic: the texture has to have a floating point surface format. DXT formats are not supported for the moment."); } SphericalHarmonicL2 sh = new SphericalHarmonicL2(); float[] colorArray = new float[texture.Width * texture.Height * 4]; texture.Resource.GetData(colorArray); int pixelIndex = 0; for (int y = 0; y < texture.Height; y++) { SphericalHarmonicL2 lineSh = new SphericalHarmonicL2(); for (int x = 0; x < texture.Width; x++) { // http://www.scratchapixel.com/lessons/3d-advanced-lessons/reflection-mapping/converting-latitute-longitude-maps-and-mirror-balls/ float theta = (x / (float)texture.Width) * (float)Math.PI; float phi = (1 - (y / (float)texture.Height)) * 2 * (float)Math.PI; Vector3 direction = -new Vector3((float)(Math.Sin(theta) * Math.Cos(phi)), (float)(Math.Sin(theta) * Math.Sin(phi)), (float)(Math.Cos(theta))); direction.Normalize(); Vector3 rgb = new Vector3(colorArray[pixelIndex * 4], colorArray[pixelIndex * 4 + 1], colorArray[pixelIndex * 4 + 2]); //Add it to the SH lineSh.AddLight(rgb, direction, 1); pixelIndex++; } //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; } //average out over the sphere return(sh.GetWeightedAverageLightInputFromSphere()); } // GenerateSphericalHarmonicFromLatLongHdrTexture