Ejemplo n.º 1
0
        /// <summary>
        /// Initializes the collection of samples
        /// </summary>
        /// <param name="_Order">The order of the SH</param>
        /// <param name="_ThetaSamplesCount">The amount of samples on Theta (total samples count will be 2*N*N)</param>
        /// <param name="_Up">The vector to use as the Up direction (use [0,1,0] if not sure)</param>
        public void                     Initialize(int _Order, int _ThetaSamplesCount, WMath.Vector _Up)
        {
            m_Order             = _Order;
            m_ThetaSamplesCount = _ThetaSamplesCount;
            m_SamplesCount      = 2 * m_ThetaSamplesCount * m_ThetaSamplesCount;
            m_Random            = new Random(m_RandomSeed);

            // Compute the rotation matrix
            Matrix3x3 Rotation = new Matrix3x3();
            Vector    Ortho    = new Vector(0, 1, 0) ^ _Up;
            float     fNorm    = Ortho.SquareMagnitude();

            if (fNorm < 1e-6f)
            {
                Rotation.MakeIdentity();
            }
            else
            {
                Ortho /= (float)Math.Sqrt(fNorm);
                Rotation.SetRow0(Ortho);
                Rotation.SetRow1(_Up);
                Rotation.SetRow2(Ortho ^ _Up);
            }

            // Initialize the SH samples
            m_SHSamples = new SHSample[m_SamplesCount];

            // Build the samples using stratified sampling
            int SampleIndex = 0;

            for (int ThetaIndex = 0; ThetaIndex < m_ThetaSamplesCount; ThetaIndex++)
            {
                for (int PhiIndex = 0; PhiIndex < 2 * m_ThetaSamplesCount; PhiIndex++)
                {
                    double fTheta = 2.0 * System.Math.Acos(System.Math.Sqrt(1.0 - (ThetaIndex + m_Random.NextDouble()) / m_ThetaSamplesCount));
                    double fPhi   = System.Math.PI * (PhiIndex + m_Random.NextDouble()) / m_ThetaSamplesCount;

                    // Compute direction, rotate it then cast it back to sphercial coordinates
                    Vector Direction = SphericalHarmonics.SHFunctions.SphericalToCartesian(fTheta, fPhi);
                    Direction *= Rotation;

                    SphericalHarmonics.SHFunctions.CartesianToSpherical(Direction, out fTheta, out fPhi);

                    // Fill up the new sample
                    m_SHSamples[SampleIndex]             = new SHSample((float)fPhi, (float)fTheta);
                    m_SHSamples[SampleIndex].m_SHFactors = new double[m_Order * m_Order];

                    // Build the SH Factors
                    SHFunctions.InitializeSHCoefficients(m_Order, fTheta, fPhi, m_SHSamples[SampleIndex].m_SHFactors);

                    SampleIndex++;
                }
            }
        }
Ejemplo n.º 2
0
        void    EncodeSH_20Orders()
        {
            const int ORDERS = 20;

            uint   W      = m_HDRImage.Width;
            uint   H      = m_HDRImage.Height;
            double dPhi   = 2.0 * Math.PI / W;
            double dTheta = Math.PI / H;

            double[,]       coeffs = new double[ORDERS * ORDERS, 3];
            for (uint Y = 0; Y < H; Y++)
            {
                double theta    = (0.5 + Y) * dTheta;
                double sinTheta = Math.Sin(theta);
                double omega    = sinTheta * dPhi * dTheta;

                for (uint X = 0; X < W; X++)
                {
                    double phi = X * dPhi - Math.PI;

                    float4 HDRColor = m_HDRImage[X, Y];

                    // Accumulate weighted SH
                    for (int l = 0; l < ORDERS; l++)
                    {
                        for (int m = -l; m <= l; m++)
                        {
                            double Ylm = SHFunctions.Ylm(l, m, theta, phi);
                            int    i   = l * (l + 1) + m;
                            coeffs[i, 0] += HDRColor.x * omega * Ylm;
                            coeffs[i, 1] += HDRColor.y * omega * Ylm;
                            coeffs[i, 2] += HDRColor.z * omega * Ylm;
                        }
                    }
                }
            }

            // Save table
            using (System.IO.FileStream S = new System.IO.FileInfo(@"Ennis_order20.float3").Create())
                using (System.IO.BinaryWriter Wr = new System.IO.BinaryWriter(S)) {
                    for (int i = 0; i < ORDERS * ORDERS; i++)
                    {
                        Wr.Write((float)coeffs[i, 0]);
                        Wr.Write((float)coeffs[i, 1]);
                        Wr.Write((float)coeffs[i, 2]);
                    }
                }
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Encodes HDR radiance image into SH
        /// We're assuming the input image is encoded as panoramic format described here: http://gl.ict.usc.edu/Data/HighResProbes/
        /// </summary>
        /// <returns></returns>
        string  EncodeSH()
        {
            // Thus, if we consider the images to have a rectangular image domain of u=[0,2], v=[0,1], we have theta= pi*(u-1), phi=pi*v.
            // The unit vector pointing in the corresponding direction is obtained by (Dx,Dy,Dz) = (sin(phi)*sin(theta), cos(phi), -sin(phi)*cos(theta)).
            // For the reverse mapping from the direction vector in the world (Dx, Dy, Dz), the corresponding (u,v) coordinate in the light probe image is ( 1 + atan2(Dx,-Dz) / pi, arccos(Dy) / pi).
            //
            // This mapping is convenient but does not have equal area.
            // Thus to find the average pixel value one must first multiply by the vertical cosine falloff function cos(phi).
            //
            // NOTE: Apparently, they consider Y-up axis and we're using Z-up
            //
            uint   W      = m_HDRImage.Width;
            uint   H      = m_HDRImage.Height;
            double dPhi   = 2.0 * Math.PI / W;
            double dTheta = Math.PI / H;

            double[] SH0 = new double[9];
            double[] SH1 = new double[9];

            double[,]       coeffs = new double[9, 3];
            for (uint Y = 0; Y < H; Y++)
            {
                double theta = (0.5 + Y) * dTheta;
//				double	cosTheta = Math.Cos( theta );
                double sinTheta = Math.Sin(theta);

                for (uint X = 0; X < W; X++)
                {
                    double phi = X * dPhi - Math.PI;
//                  double	cosPhi = Math.Cos( phi );
//                  double	sinPhi = Math.Sin( phi );

                    float4 HDRColor = m_HDRImage[X, Y];
//HDRColor.Set( 1, 1, 1, 1 );

                    // Accumulate weighted SH
                    double omega = sinTheta * dPhi * dTheta;


// Compare our 2 ways of generating Ylm
// SHFunctions.Ylm( new float3( (float) (sinTheta * cosPhi), (float) (sinTheta * sinPhi), (float) cosTheta ), SH0 );
// for ( int l=0; l < 3; l++ ) {
//  for ( int m=-l; m <= l; m++ ) {
//      int		i = l*(l+1)+m;
//      SH1[i] = SHFunctions.Ylm( l, m, theta, phi );
//  }
// }
// for ( int i=0; i < 9; i++ )
//  if ( Math.Abs( SH0[i] - SH1[i] ) > 1e-4 )
//      throw new Exception( "ARGH!" );



                    for (int l = 0; l < 3; l++)
                    {
                        for (int m = -l; m <= l; m++)
                        {
                            double Ylm = SHFunctions.Ylm(l, m, theta, phi);
                            int    i   = l * (l + 1) + m;
                            coeffs[i, 0] += HDRColor.x * omega * Ylm;
                            coeffs[i, 1] += HDRColor.y * omega * Ylm;
                            coeffs[i, 2] += HDRColor.z * omega * Ylm;
                        }
                    }
                }
            }

            string SHText = "float3	SH[9] = {\r\n";

            for (int l = 0; l < 3; l++)
            {
                for (int m = -l; m <= l; m++)
                {
                    int i = l * (l + 1) + m;
                    SHText += "					float3( "+ coeffs[i, 0] + ", " + coeffs[i, 1] + ", " + coeffs[i, 2] + " ), \r\n";
                }
            }
            SHText += "};\r\n";
            return(SHText);
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Computes up to 20 orders of A coefficients for various AO and angle values
        /// </summary>
        void    NumericalIntegration_20Orders()
        {
            // Generate a bunch of rays with equal probability on the hemisphere
            const int    THETA_SAMPLES = 100;
            const int    SAMPLES_COUNT = 4 * THETA_SAMPLES * THETA_SAMPLES;
            const double dPhi          = 2.0 * Math.PI / (4 * THETA_SAMPLES);

            float3[] directions = new float3[SAMPLES_COUNT];
            for (int Y = 0; Y < THETA_SAMPLES; Y++)
            {
                for (int X = 0; X < 4 * THETA_SAMPLES; X++)
                {
                    double phi   = dPhi * (X + SimpleRNG.GetUniform());
                    double theta = 2.0 * Math.Acos(Math.Sqrt(1.0 - 0.5 * (Y + SimpleRNG.GetUniform()) / THETA_SAMPLES));                                // Uniform sampling on theta
                    directions[4 * THETA_SAMPLES * Y + X].Set((float)(Math.Sin(theta) * Math.Cos(phi)), (float)(Math.Sin(theta) * Math.Sin(phi)), (float)Math.Cos(theta));
                }
            }

            // Compute numerical integration for various sets of angles
            const int TABLE_SIZE = 64;
            const int ORDERS     = 20;

            float3 coneDirection = float3.Zero;

            float[,,]       integratedSHCoeffs = new float[TABLE_SIZE, TABLE_SIZE, ORDERS];

            double[] A = new double[ORDERS];
            for (int thetaIndex = 0; thetaIndex < TABLE_SIZE; thetaIndex++)
            {
                float V = (float)thetaIndex / TABLE_SIZE;
//				float	cosTheta = (float) Math.Cos( 0.5 * Math.PI * V );
                float cosTheta = V;
                coneDirection.x = (float)Math.Sqrt(1.0f - cosTheta * cosTheta);
                coneDirection.z = cosTheta;

                for (int AOIndex = 0; AOIndex < TABLE_SIZE; AOIndex++)
                {
                    float U = (float)AOIndex / TABLE_SIZE;
//					float	cosConeHalfAngle = U;
                    float cosConeHalfAngle = (float)Math.Cos(0.5 * Math.PI * U);

                    Array.Clear(A, 0, ORDERS);
                    for (int sampleIndex = 0; sampleIndex < SAMPLES_COUNT; sampleIndex++)
                    {
                        float3 direction = directions[sampleIndex];
                        if (direction.Dot(coneDirection) < cosConeHalfAngle)
                        {
                            continue;                                   // Sample is outside cone
                        }
                        float u = direction.z;                          // cos(theta_sample)
                        for (int order = 0; order < ORDERS; order++)
                        {
                            A[order] += u * SHFunctions.P0(order, u);
                        }
                    }

                    // Finalize integration
                    for (int order = 0; order < ORDERS; order++)
                    {
                        A[order] *= 2.0 * Math.PI / SAMPLES_COUNT;
                    }
                    for (int order = 0; order < ORDERS; order++)
                    {
                        integratedSHCoeffs[thetaIndex, AOIndex, order] = (float)A[order];
                    }
                }
            }

            // Save table
            using (System.IO.FileStream S = new System.IO.FileInfo(@"ConeTable_cosTheta_order20.float").Create())
                using (System.IO.BinaryWriter W = new System.IO.BinaryWriter(S)) {
                    for (int thetaIndex = 0; thetaIndex < TABLE_SIZE; thetaIndex++)
                    {
                        for (int AOIndex = 0; AOIndex < TABLE_SIZE; AOIndex++)
                        {
                            for (int order = 0; order < ORDERS; order++)
                            {
                                W.Write(integratedSHCoeffs[thetaIndex, AOIndex, order]);
                            }
                        }
                    }
                }
        }
Ejemplo n.º 5
0
 public SHSample(float _fPhi, float _fTheta)
 {
     m_Direction = SHFunctions.SphericalToCartesian(_fTheta, _fPhi);
     m_Phi       = _fPhi;
     m_Theta     = _fTheta;
 }