Пример #1
0
        void    DrawLine(float3 _start, float3 _end, float3 _ortho, float _thickness, float4 _color)
        {
            if (_thickness <= 0.0f)
            {
                // Compute the world size of a pixel for the farthest extremity of the line
                float3 wsCameraPos = (float3)m_Camera.World2Camera.GetRow(3);
                float3 wsDir       = _end - _start;
                float  t           = -wsDir.Dot(_start - wsCameraPos) / wsDir.Dot(wsDir);
                t = Math.Max(0.0f, Math.Min(1.0f, t));
                float3 wsNearest = _start + t * wsDir;
                float4 csNearest = new float4(wsNearest, 1) * m_Camera.World2Camera;
                float  worldSize = 2.0f * (float)Math.Tan(0.5f * m_Camera.PerspectiveFOV) * csNearest.z;
                _thickness *= -worldSize / panelOutput.Height;
            }

            m_CB_Line.m._wsPosition0 = _start;
            m_CB_Line.m._thickness   = _thickness;
            m_CB_Line.m._wsPosition1 = _end;
            m_CB_Line.m._wsOrtho     = _ortho;
            m_CB_Line.m._color       = _color;
            m_CB_Line.UpdateData();

            m_Shader_Line.Use();
            m_Prim_Quad.Render(m_Shader_Line);
        }
Пример #2
0
        float   DistBox(float3 _wsPosition, float3 _wsBoxCenter, float3 _wsBoxSize, float _rotationAngle)
        {
            float  s        = (float)Math.Sin(_rotationAngle);
            float  c        = (float)Math.Cos(_rotationAngle);
            float3 rotatedX = new float3(c, 0.0f, s);
            float3 rotatedZ = new float3(-s, 0.0f, c);

            _wsPosition -= _wsBoxCenter;
            _wsPosition  = new float3(_wsPosition.Dot(rotatedX), _wsPosition.y, _wsPosition.Dot(rotatedZ));
            return(DistBox(_wsPosition, float3.Zero, _wsBoxSize));
        }
Пример #3
0
        public void             InitializeCamera(float3 _Position, float3 _Target, float3 _Up)
        {
            // Build the camera matrix
            float3 At = _Target - _Position;

            if (At.Dot(At) > 1e-2f)
            {                   // Normal case
                m_CameraTargetDistance = At.Length;
                At /= m_CameraTargetDistance;
            }
            else
            {                   // Special bad case
                m_CameraTargetDistance = 0.01f;
                At = new float3(0.0f, 0.0f, -1.0f);
            }

            float3 Ortho = _Up.Cross(At).Normalized;

            float4x4 CameraMat = float4x4.Identity;

            CameraMat.r0 = new float4(Ortho, 0.0f);
            CameraMat.r1 = new float4(At.Cross(Ortho), 0.0f);
            CameraMat.r2 = new float4(At, 0.0f);
            CameraMat.r3 = new float4(_Position, 1.0f);

            CameraTransform = CameraMat;

            // Setup the normalized target distance
            m_NormalizedTargetDistance = NormalizeTargetDistance(m_CameraTargetDistance);
        }
Пример #4
0
        const int SAMPLES_COUNT = 50;                                   // Number of samples used to compute the average terms

        // compute the average direction of the BRDF
        public void     ComputeAverageTerms(IBRDF _BRDF, ref float3 _tsView, float _alpha)
        {
            magnitude = 0.0;
            fresnel   = 0.0;
            Z         = float3.Zero;
            error     = 0.0;

            double weight, pdf, eval;
            float3 tsLight = float3.Zero;
            float3 H       = float3.Zero;

            for (int j = 0; j < SAMPLES_COUNT; ++j)
            {
                for (int i = 0; i < SAMPLES_COUNT; ++i)
                {
                    float U1 = (i + 0.5f) / SAMPLES_COUNT;
                    float U2 = (j + 0.5f) / SAMPLES_COUNT;

                    // sample
                    _BRDF.GetSamplingDirection(ref _tsView, _alpha, U1, U2, ref tsLight);

                    // eval
                    eval = _BRDF.Eval(ref _tsView, ref tsLight, _alpha, out pdf);
                    if (pdf == 0.0f)
                    {
                        continue;
                    }

                    H = (_tsView + tsLight).Normalized;

                    // accumulate
                    weight = eval / pdf;
                    if (double.IsNaN(weight))
                    {
                        throw new Exception("NaN!");
                    }

                    magnitude += weight;
                    fresnel   += weight * Math.Pow(1 - Math.Max(0.0f, _tsView.Dot(H)), 5.0);
                    Z         += (float)weight * tsLight;
                }
            }
            magnitude /= SAMPLES_COUNT * SAMPLES_COUNT;
            fresnel   /= SAMPLES_COUNT * SAMPLES_COUNT;

            // Finish building the average TBN orthogonal basis
            Z.y = 0.0f;                         // clear y component, which should be zero with isotropic BRDFs
            float length = Z.Length;

            if (length > 0.0f)
            {
                Z /= length;
            }
            else
            {
                Z = float3.UnitZ;
            }
            X.Set(Z.z, 0, -Z.x);
            Y = float3.UnitY;
        }
Пример #5
0
        public double   Eval(ref float3 _tsView, ref float3 _tsLight, float _alpha, out double _pdf)
        {
            if (_tsView.z <= 0)
            {
                _pdf = 0;
                return(0);
            }

            _alpha = Mathf.Max(0.002f, _alpha);

            float3 H     = (_tsView + _tsLight).Normalized;
            double NdotL = Math.Max(1e-8, _tsLight.z);
            double NdotV = Math.Max(1e-8, _tsView.z);
            double NdotH = H.z;
            double LdotH = Math.Max(1e-8, _tsLight.Dot(H));

            // D
            double cosb2 = NdotH * NdotH;
            double m2    = _alpha * _alpha;
            double D     = Math.Exp((cosb2 - 1.0) / (cosb2 * m2))               // exp( -tan(a)² / m² )
                           / (Math.PI * m2 * cosb2 * cosb2);                    // / (PI * m² * cos(a)^4)

            // masking/shadowing
            double G = Math.Min(1, 2.0 * NdotH * Math.Min(NdotV, NdotL) / LdotH);

            // fr = F(H) * G(V, L) * D(H) / (4 * (N.L) * (N.V))
            double res = D * G / (4.0 * NdotV);                         // Full specular mico-facet model is F * D * G / (4 * NdotL * NdotV) but since we're fitting with the NdotL included, it gets nicely canceled out!

            // pdf = D(H) * (N.H) / (4 * (L.H))
            _pdf = Math.Abs(D * NdotH / (4.0 * LdotH));

            return(res);
        }
Пример #6
0
        public void     GetSamplingDirection(ref float3 _tsView, float _alpha, float _U1, float _U2, ref float3 _direction)
        {
            float  phi = Mathf.TWOPI * _U1;
            float  r   = _alpha * Mathf.Sqrt(_U2 / (1.0f - _U2));
            float3 H   = new float3(r * Mathf.Cos(phi), r * Mathf.Sin(phi), 1.0f).Normalized;

            _direction = -_tsView + 2.0f * H * H.Dot(_tsView);
        }
Пример #7
0
        public void     GetSamplingDirection(ref float3 _tsView, float _alpha, float _U1, float _U2, ref float3 _direction)
        {
            float  phi      = Mathf.TWOPI * _U1;
            float  cosTheta = 1.0f / Mathf.Sqrt(1 - _alpha * _alpha * Mathf.Log(Mathf.Max(1e-6f, _U2)));
            float  sinTheta = Mathf.Sqrt(1 - cosTheta * cosTheta);
            float3 H        = new float3(sinTheta * Mathf.Cos(phi), sinTheta * Mathf.Sin(phi), cosTheta);

            _direction = 2.0f * H.Dot(_tsView) * H - _tsView;                   // Mirror view direction
        }
Пример #8
0
        public void     GetSamplingDirection(ref float3 _tsView, float _alpha, float _U1, float _U2, ref float3 _direction)
        {
            // Ward NDF sampling (eqs. 6 & 7 from above paper)
            float tanTheta = _alpha * Mathf.Sqrt(-Mathf.Log(Mathf.Max(1e-6f, _U1)));
            float phi      = _U2 * Mathf.TWOPI;

            float  cosTheta = 1.0f / Mathf.Sqrt(1 + tanTheta * tanTheta);
            float  sinTheta = Mathf.Sqrt(1 - cosTheta * cosTheta);
            float3 H        = new float3(sinTheta * Mathf.Cos(phi), sinTheta * Mathf.Sin(phi), cosTheta);

            _direction = 2.0f * H.Dot(_tsView) * H - _tsView;                   // Mirror view direction
        }
Пример #9
0
        private void timer1_Tick(object sender, EventArgs e)
        {
            if (!m_bHasRendered)
            {
                return;
            }
            m_bHasRendered = false;

            // Perform simulation
            int NeighborsCount = m_NeighborPositions.Length;

            // Compute pressure forces
            float F = 0.01f * floatTrackbarControlForce.Value;

            float3[] Forces = new float3[NeighborsCount];
            for (int i = 0; i < NeighborsCount - 1; i++)
            {
                float3 D0 = m_NeighborPositions[i].Normalized;
                for (int j = i + 1; j < NeighborsCount; j++)
                {
                    float3 D1 = m_NeighborPositions[j].Normalized;

                    float3 Dir = (D1 - D0).Normalized;

                    float Dot   = D0.Dot(D1) - 1.0f;                            // in [0,-2]
                    float Force = F * (float)Math.Exp(Dot);
                    Forces[i] = Forces[i] - Force * Dir;                        // Pushes 0 away from 1
                    Forces[j] = Forces[j] + Force * Dir;                        // Pushes 1 away from 0
                }
            }

            // Apply force
            for (int i = 0; i < NeighborsCount; i++)
            {
                float3 NewPosition = (m_NeighborPositions[i] + Forces[i]).Normalized;
                m_NeighborPositions[i]         = NewPosition;
                m_SB_Neighbors.m[i].m_Position = NewPosition;
            }

            // Update
            m_SB_Neighbors.Write();

            if (checkBoxRenderCell.Checked)
            {
                buttonBuildCell_Click(this, EventArgs.Empty);
                Application.DoEvents();
            }
        }
Пример #10
0
        public double   Eval(ref float3 _tsView, ref float3 _tsLight, float _alpha, out double _pdf)
        {
            if (_tsView.z <= 0)
            {
                _pdf = 0;
                return(0);
            }

            // masking
            double lambdaV = Lambda(_tsView.z, _alpha);

            // shadowing
            double G2 = 0;

            if (_tsLight.z > 0.0f)
            {
                double lambdaL = Lambda(_tsLight.z, _alpha);
                G2 = 1.0 / (1.0 + lambdaV + lambdaL);
            }

            // D
            float3 H       = _tsView + _tsLight;
            float  lengthH = H.Length;

            if (lengthH > 1e-8f)
            {
                H = H / lengthH;
            }
            else
            {
                H = float3.UnitZ;
            }

            double slopex = H.x / H.z;
            double slopey = H.y / H.z;
            double D      = 1.0 / (1.0 + (slopex * slopex + slopey * slopey) / _alpha / _alpha);

            D = D * D;
            D = D / (Math.PI * _alpha * _alpha * H.z * H.z * H.z * H.z);

            double res = D * G2 / 4.0 / _tsView.z;                      // Full specular mico-facet model is F * D * G / (4 * NdotL * NdotV) but since we're fitting with the NdotL included, it gets nicely canceled out!

            // pdf = D(H) * (N.H) / (4 * (L.H))
            _pdf = Math.Abs(D * H.z / 4.0 / _tsView.Dot(H));

            return(res);
        }
Пример #11
0
        public double   Eval(ref float3 _tsView, ref float3 _tsLight, float _alpha, out double _pdf)
        {
            if (_tsView.z <= 0)
            {
                _pdf = 0;
                return(0);
            }

            _alpha = Mathf.Max(0.002f, _alpha);

            double NdotL = Math.Max(0, _tsLight.z);
            double NdotV = Math.Max(0, _tsView.z);
            double LdotV = Math.Max(0, _tsLight.Dot(_tsView));

            double perceptualRoughness = Math.Sqrt(_alpha);

            // (2 * LdotH * LdotH) = 1 + LdotV
            // real fd90 = 0.5 + 2 * LdotH * LdotH * perceptualRoughness;
            double fd90 = 0.5 + (perceptualRoughness + perceptualRoughness * LdotV);

            // Two schlick fresnel term
            double lightScatter = F_Schlick(1.0, fd90, NdotL);
            double viewScatter  = F_Schlick(1.0, fd90, NdotV);

            // Normalize the BRDF for polar view angles of up to (Pi/4).
            // We use the worst case of (roughness = albedo = 1), and, for each view angle,
            // integrate (brdf * cos(theta_light)) over all light directions.
            // The resulting value is for (theta_view = 0), which is actually a little bit larger
            // than the value of the integral for (theta_view = Pi/4).
            // Hopefully, the compiler folds the constant together with (1/Pi).
            double res = lightScatter * viewScatter / Math.PI;

            res /= 1.03571;

            // Remember we must include the N.L term!
            res *= NdotL;


//res = NdotL / Math.PI;	// Lambert test


            // Cosine-weighted hemisphere sampling
            _pdf = NdotL / Math.PI;

            return(res);
        }
Пример #12
0
        public double   Eval(ref float3 _tsView, ref float3 _tsLight, float _alpha, out double _pdf)
        {
            if (_tsView.z <= 0)
            {
                _pdf = 0;
                return(0);
            }

//			_alpha = Mathf.Max( 0.002f, _alpha );

            // masking
            double lambdaV = Lambda(_tsView.z, _alpha);
//			double	G1 = 1.0 / (1.0 + lambdaV);

            // shadowing
            double G2 = 0;

            if (_tsLight.z > 0.0f)
            {
                double lambdaL = Lambda(_tsLight.z, _alpha);
                G2 = 1.0 / (1.0 + lambdaV + lambdaL);
            }

            // D
            float3 H = (_tsView + _tsLight).Normalized;
//			H.z = Mathf.Max( 1e-8f, H.z );
//          if ( Mathf.Almost( H.z, 0, 1e-8f ) )
//              H.z = 1e-8f;

            double slopex = H.x / H.z;
            double slopey = H.y / H.z;
            double D      = 1.0 / (1.0 + (slopex * slopex + slopey * slopey) / _alpha / _alpha);

            D = D * D;
            D = D / (Math.PI * _alpha * _alpha * H.z * H.z * H.z * H.z);

            double res = D * G2 / 4.0 / _tsView.z;                      // Full specular mico-facet model is F * D * G / (4 * NdotL * NdotV) but since we're fitting with the NdotL included, it gets nicely canceled out!

            // pdf = D(H) * (N.H) / (4 * (L.H))
            _pdf = Math.Abs(D * H.z / 4.0 / _tsView.Dot(H));

            return(res);
        }
Пример #13
0
            // Cut polygon with a new plane, yielding a new polygon
            public void     Cut(float3 _P, float3 _N)
            {
                List <float3> NewVertices = new List <float3>();

                for (int EdgeIndex = 0; EdgeIndex < m_Vertices.Length; EdgeIndex++)
                {
                    float3 P0       = m_Vertices[EdgeIndex + 0];
                    float3 P1       = m_Vertices[(EdgeIndex + 1) % m_Vertices.Length];
                    float  Dot0     = (P0 - _P).Dot(_N);
                    float  Dot1     = (P1 - _P).Dot(_N);
                    bool   InFront0 = Dot0 >= 0.0f;
                    bool   InFront1 = Dot1 >= 0.0f;
                    if (!InFront0 && !InFront1)
                    {
                        continue;                               // This edge is completely behind the cutting plane, skip it entirely
                    }
                    if (InFront0 && InFront1)
                    {
                        // This edge is completely in front of the cutting plane, add P1
                        NewVertices.Add(P1);
                    }
                    else
                    {
                        // The edge intersects the plane
                        float3 D = P1 - P0;
                        float  t = -Dot0 / D.Dot(_N);
                        float3 I = P0 + t * D;
                        NewVertices.Add(I);                                     // Add intersection no matter what
                        if (InFront1)
                        {
                            NewVertices.Add(P1);                                // Since the edge is entering the plane, also add end point
                        }
                    }
                }

                m_Vertices = NewVertices.ToArray();
            }
Пример #14
0
        public double   Eval(ref float3 _tsView, ref float3 _tsLight, float _alpha, out double _pdf)
        {
            if (_tsView.z <= 0)
            {
                _pdf = 0;
                return(0);
            }

            _alpha = Mathf.Max(0.002f, _alpha);

            float3 H     = (_tsView + _tsLight).Normalized;
            double NdotL = Math.Max(1e-8, _tsLight.z);
            double NdotV = _tsView.z;
            double NdotH = Math.Max(1e-8, H.z);
            double LdotH = Math.Max(1e-8, _tsLight.Dot(H));

            // D (basically a Beckmann distribution + an additional divider for albedo bounding)
            double m2    = _alpha * _alpha;
            double cosb2 = NdotH * NdotH;
            double D     = Math.Exp(-(1 - cosb2) / (m2 * cosb2))                // exp( -tan(a)² / m² )
                           / (Math.PI * m2 * cosb2 * cosb2);                    // / (PI * m² * cos(a)^4)

            D /= 4.0 * LdotH * LdotH;                                           // Moroder

            // fr = F(H) * D(H)
            double res = D;

            // Remember we must include the N.L term!
            res *= NdotL;

            // From Walter, eq. 24 we know that pdf(H) = D(H) * (N.H)
            _pdf = Math.Abs(D * NdotH);
//          double	weight = 2 * NdotL / (NdotL + NdotV);	// Eq. 21 from Moroder paper
//          _pdf = D / weight;

            return(res);
        }
Пример #15
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]);
                            }
                        }
                    }
                }
        }
Пример #16
0
        // Samples the irradiance reflected from a screen-space position located around the center pixel position
        //	_ssPosition, the screen-space position to sample
        //	_H0, the center height
        //	_radius_m, the radius (in meters) from the center position
        //	_integralFactors, some pre-computed factors to feed the integral
        //	_maxCos, the floating maximum cos(theta) that indicates the angle of the perceived horizon
        //	_optionnal_centerRho, the reflectance of the center pixel (fallback only necessary if no albedo map is available and if it's only irradiance that is stored in the source irradiance map instead of radiance, in which case albedo is already pre-mutliplied)
        //
        float3  SampleIrradiance(float2 _csPosition, float4x4 _localCamera2World, float _Z0, float2 _mipLevel, float2 _integralFactors, ref float3 _previousRadiance, ref float _maxCos)
        {
            // Transform camera-space position into screen space
            float3 wsNeighborPosition = _localCamera2World[3].xyz + _csPosition.x * _localCamera2World[0].xyz + _csPosition.y * _localCamera2World[1].xyz;
//          float3	deltaPos = wsNeighborPosition = _Camera2World[3].xyz;
//          float2	gcsPosition = float3( dot( deltaPos, _Camera2World[0].xyz ), dot( deltaPos, _Camera2World[1].xyz ), dot( deltaPos, _Camera2World[2].xyz ) );


// #TODO: Optimize!
            float4 projPosition = new float4(wsNeighborPosition, 1.0f) * m_world2Proj;

            projPosition /= projPosition.w;
            float2 UV         = new float2(0.5f * (1.0f + projPosition.x), 0.5f * (1.0f - projPosition.y));
            float2 ssPosition = UV * m_resolution;


            // Sample new depth and rebuild final world-space position
            float neighborZ = FetchDepth(ssPosition, _mipLevel.x);

            float3 wsView = wsNeighborPosition - m_camera2World[3].xyz;                 // Neighbor world-space position (not projected), relative to camera

            wsView /= wsView.Dot(m_camera2World[2].xyz);                                // Scaled so its length against the camera's Z axis is 1
            wsView *= neighborZ;                                                        // Scaled again so its length agains the camera's Z axis equals our sampled Z

            wsNeighborPosition = m_camera2World[3].xyz + wsView;                        // Final reprojected world-space position

            // Update horizon angle following eq. (3) from the paper
            wsNeighborPosition -= _localCamera2World[3].xyz;                                            // Neighbor position, relative to central position
            float3 csNeighborPosition = new float3(wsNeighborPosition.Dot(_localCamera2World[0].xyz), wsNeighborPosition.Dot(_localCamera2World[1].xyz), wsNeighborPosition.Dot(_localCamera2World[2].xyz));
            float  radius             = csNeighborPosition.xy.Length;
            float  d = csNeighborPosition.z;

//float	bilateralWeight = BilateralFilterDepth( _Z0, neighborZ, radius );	// Attenuate
//d *= bilateralWeight;

            float cosHorizon = d / Mathf.Sqrt(radius * radius + d * d);                 // Cosine to horizon angle

            if (cosHorizon <= _maxCos)
            {
                return(float3.Zero);                    // Below the horizon... No visible contribution.
            }
                        #if SAMPLE_NEIGHBOR_RADIANCE
// NOW USELESS I THINK...
//				// Sample neighbor's incoming radiance value, only if difference in depth is not too large
//				float	bilateralWeight = BilateralFilterRadiance( _H0, neighborH, _radius );
//				if ( bilateralWeight > 0.0 )
//					_previousRadiance = lerp( _previousRadiance, FetchRadiance( _ssPosition ), bilateralWeight );	// Accept new height and its radiance value

            // Sample always (actually, it's okay now we accepted the height through the first bilateral filter earlier)
            _previousRadiance = FetchRadiance(ssPosition, _mipLevel.y);
                        #endif

            // Integrate over horizon difference (always from smallest to largest angle otherwise we get negative results!)
            float3 incomingRadiance = _previousRadiance * IntegrateSolidAngle(_integralFactors, cosHorizon, _maxCos);

// #TODO: Integrate with linear interpolation of irradiance as well??
// #TODO: Integrate with Fresnel F0!

            _maxCos = cosHorizon;                       // Register a new positive horizon

            return(incomingRadiance);
        }
Пример #17
0
        /// <summary>
        /// Computes the solid angle covered by the light as seen by provided position
        /// </summary>
        /// <param name="_wsPosition"></param>
        void    ComputeResult(float3 _wsPosition)
        {
            float3 lsLightPos = m_CB_Light.m._wsLight2World[3].xyz - _wsPosition;
            float3 lsLightX   = m_CB_Light.m._wsLight2World[0].w * m_CB_Light.m._wsLight2World[0].xyz;
            float3 lsLightY   = m_CB_Light.m._wsLight2World[1].w * m_CB_Light.m._wsLight2World[1].xyz;

            float3 n = float3.UnitY;                    // Plane normal is straight up

            float dA = Mathf.TWOPI / 1024.0f;

            float c0, s0, c1, s1;
            float FdotN, dot;

            double dTheta;
            float3 v0, v1, u0, u1, cross, dF;

            double sum0  = 0;
            double sum1  = 0;
            float3 sumF0 = float3.Zero;
            float3 sumF1 = float3.Zero;

            for (int i = 0; i < 1024; i++)
            {
                c0 = Mathf.Cos(i * dA);
                s0 = Mathf.Sin(i * dA);
                c1 = Mathf.Cos(i * dA + dA);
                s1 = Mathf.Sin(i * dA + dA);

                v0 = lsLightPos + c0 * lsLightX + s0 * lsLightY;
                v1 = lsLightPos + c1 * lsLightX + s1 * lsLightY;

                u0     = v0.NormalizedSafe;
                u1     = v1.NormalizedSafe;
                dot    = u0.Dot(u1);
                dTheta = Math.Acos(Mathf.Clamp(dot, -1, 1));

                //////////////////////////////////////////////////////////////////////////
                // Regular way
                cross = v0.Cross(v1);
                cross.NormalizeSafe();

                // Directly accumulate F
                dF     = (float)dTheta * cross;
                sumF0 += dF;

                // Accumulate F.n each step of the way
//				FdotN = dF.Dot( n );
                FdotN = dF.y;
                sum0 += FdotN;


                //////////////////////////////////////////////////////////////////////////
                // Bent way
//				FdotN = Mathf.Sqrt( 1 - Mathf.Pow2( u0.Dot( n ) ) );	// sin( alpha )
                FdotN  = Mathf.Sqrt(1 - Mathf.Pow2(u0.y));                                      // sin( alpha )
                dF     = (float)dTheta * FdotN * u0;
                sumF1 += dF;
                sum1  += (float)dTheta * FdotN;
            }

            textBoxResults.Text = "Target = " + _wsPosition + "\r\n"
                                  + "\r\n"
                                  + "Regular F = " + sumF0 + "\r\n"
                                  + "Regular sum = " + sum0 + "\r\n"
                                  + "Regular F.N = " + sumF0.Dot(n) + "\r\n"
                                  + "\r\n"
                                  + "Ortho F = " + sumF1 + "\r\n"
                                  + "Ortho sum = " + sum1 + "\r\n"
                                  + "Ortho |F| = " + sumF1.Length + "  <== \r\n";
        }
Пример #18
0
        /// <summary>
        /// This code tests the determinant of the transform matrices that rectify a
        /// </summary>
        void    CheckMatrix()
        {
            const int COUNT_RADIUS = 10;
            const int COUNT_X      = 10;
            const int COUNT_Y      = 10;
            const int COUNT_Z      = 10;

            const float Xmax = 10.0f;
            const float Ymax = 10.0f;
            const float Zmax = 4.0f;
            const float Rb   = 2.0f;

            float3 P0 = float3.Zero;
            float3 T  = new float3(1, 0, 0);
            float3 B  = new float3(0, 1, 0);
            float3 N  = new float3(0, 0, 1);

//			float3	Nt, Nb, Tn, Bn, Dtn, Dbn;

//			float4		P  = float4.UnitW, Pt;
//			float4x4	M = new float4x4();

            float3   P, Pt;
            float3x3 M     = new float3x3();
            float3x3 M2    = new float3x3();
            float3x3 invM  = new float3x3();
            float3x3 invM2 = new float3x3();

            float det, illuminance;
//			float	depth;

            List <float[, , ]> determinantss = new List <float[, , ]>(COUNT_RADIUS);

//			for ( int radiusIndex=0; radiusIndex < COUNT_RADIUS; radiusIndex++ )
            int radiusIndex = 10;
            {
                float[,,]       determinants = new float[COUNT_Z, 1 + 2 * COUNT_Y, 1 + 2 * COUNT_X];
                determinantss.Add(determinants);

                float radiusFactor = Mathf.Max(0.01f, 2.0f * radiusIndex / 100);
                float Rt           = radiusFactor * Rb;

                T.Set(Rt, 0, 0);
                B.Set(0, Rb, 0);

                for (int Z = COUNT_Z; Z > 0; Z--)
                {
                    P0.z = Zmax * Z / COUNT_Z;
                    for (int Y = -COUNT_Y; Y <= COUNT_Y; Y++)
                    {
                        P0.y = Ymax * Y / COUNT_Y;
                        for (int X = -COUNT_X; X <= COUNT_X; X++)
                        {
                            P0.x = Xmax * X / COUNT_X;

#if true
                            // Matrix is a simple slanted parallelogram
                            M.r0 = (T - (P0.Dot(T) / P0.Dot(N)) * N) / (Rt * Rt);
                            M.r1 = (B - (P0.Dot(B) / P0.Dot(N)) * N) / (Rb * Rb);
                            M.r2 = N / P0.Dot(N);

                            invM = M.Inverse;

                            det = M.Determinant;

                            // Construct inverse directly
                            invM2.r0.Set(T.x, B.x, P0.x);
                            invM2.r1.Set(T.y, B.y, P0.y);
                            invM2.r2.Set(T.z, B.z, P0.z);

                            M2 = invM2.Inverse;

                            // Test matrix is working
                            P  = P0;
                            Pt = M * P;
                            P  = T;
                            Pt = M * P;
                            P  = -T;
                            Pt = M * P;
                            P  = B;
                            Pt = M * P;
                            P  = -B;
                            Pt = M * P;
                            P  = float3.Lerp(0.5f * T - 0.25f * B, 0.5f * T - 0.25f * B + P0, 0.666f);
                            Pt = M * P;

//                          invM *= M;
//                          invM2 *= M2;

                            // Test lighting computation

                            // Build rectangular area light corners in local space
                            float3   lsAreaLightPosition = P0;
                            float3[] lsLightCorners      = new float3[4];
                            lsLightCorners[0] = lsAreaLightPosition + T + B;
                            lsLightCorners[1] = lsAreaLightPosition + T - B;
                            lsLightCorners[2] = lsAreaLightPosition - T - B;
                            lsLightCorners[3] = lsAreaLightPosition - T + B;

                            float3x3 world2TangentSpace = float3x3.Identity;                                    // Assume we're already in tangent space

                            // Transform them into tangent-space
                            float3[] tsLightCorners = new float3[4];
                            tsLightCorners[0] = lsLightCorners[0] * world2TangentSpace;
                            tsLightCorners[1] = lsLightCorners[1] * world2TangentSpace;
                            tsLightCorners[2] = lsLightCorners[2] * world2TangentSpace;
                            tsLightCorners[3] = lsLightCorners[3] * world2TangentSpace;

                            // Compute diffuse disk
                            illuminance = DiskIrradiance(tsLightCorners);
#else
// Stupid version where I still thought we needed a perspective projection matrix!
                            depth = P0.Dot(N);                                  // Altitude from plane
                            Nt    = P0.Dot(T) * N;
                            Nb    = P0.Dot(B) * N;
                            Tn    = P0.Dot(N) * T;                              // = depth * T
                            Bn    = P0.Dot(N) * B;                              // = depth * B

#if true
                            M.r0.Set((Tn - Nt) / Rt, 0);
                            M.r1.Set((Bn - Nb) / Rb, 0);
                            M.r2.Set(0, 0, 1, 0);
                            M.r3.Set(-N, depth);
#else
                            Dtn = (Nt - Tn) / Rt;
                            Dbn = (Nb - Bn) / Rb;

                            M.r0.Set(Dtn, -P0.Dot(Dtn));
                            M.r1.Set(Dbn, -P0.Dot(Dbn));
                            M.r2.Set(N / depth, 0.0f);                                          // Will normalize Z to 1 if P is at same altitude as P0
                            M.r3.Set(N, -P0.Dot(N));                                            // Here, use "depth"
#endif
                            det = M.Determinant;

                            determinants[Z - 1, COUNT_Y + Y, COUNT_X + X] = det;

                            // Test matrix is working
                            P.Set(P0, 1);
                            Pt = M * P;
//							Pt /= Pt.w;	// This will NaN because W=0 in this particular case
                            P.Set(Rt * T, 1);
                            Pt  = M * P;
                            Pt /= Pt.w;
                            P.Set(-Rt * T, 1);
                            Pt  = M * P;
                            Pt /= Pt.w;
                            P.Set(Rb * B, 1);
                            Pt  = M * P;
                            Pt /= Pt.w;
                            P.Set(-Rb * B, 1);
                            Pt  = M * P;
                            Pt /= Pt.w;
                            P.Set(float3.Lerp(0.5f * Rt * T - 0.25f * Rb * B, P0, 0.666f), 1);
                            Pt  = M * P;
                            Pt /= Pt.w;
#endif
                        }
                    }
                }
            }
        }
Пример #19
0
        private void timer1_Tick(object sender, EventArgs e)
        {
            if (!m_bHasRendered)
            {
                return;
            }
            m_bHasRendered = false;

            // Perform simulation
            int   neighborsCount = m_neighborPositions.Length;
            bool  hemi           = checkBoxHemisphere.Checked;
            float solidAngle     = (float)(2.0 * Math.PI / neighborsCount);             // Hemisphere mode only covers 2PI
            float cosHalfAngle   = 1.0f - solidAngle / (float)Math.PI;
            float halfAngle      = (float)Math.Acos(cosHalfAngle);                      // Not used, just to check

            // Compute pressure forces
            float F = 0.01f * floatTrackbarControlForce.Value;

            float3[] forces = new float3[neighborsCount];
            for (int i = 0; i < neighborsCount - 1; i++)
            {
                float3 D0 = m_neighborPositions[i];
                for (int j = i + 1; j < neighborsCount; j++)
                {
                    float3 D1 = m_neighborPositions[j];

                    float3 Dir = (D1 - D0).Normalized;

                    float Dot   = D0.Dot(D1) - 1.0f;                            // in [0,-2]
                    float Force = F * (float)Math.Exp(Dot);
                    forces[i] = forces[i] - Force * Dir;                        // Pushes 0 away from 1
                    forces[j] = forces[j] + Force * Dir;                        // Pushes 1 away from 0
                }

                if (hemi)
                {
//					forces[i] += Math.Max( 0.0f, cosHalfAngle / Math.Max( 1e-1f, D0.z ) - 1.0f ) * float3.UnitZ;
                    forces[i] += forces[i].Length * F * Math.Max(0.0f, 1.0f - D0.y / cosHalfAngle) * float3.UnitY;
                }
            }

            if (hemi)
            {
                forces[neighborsCount - 1] += forces[neighborsCount - 1].Length * F * Math.Max(0.0f, 1.0f - forces[neighborsCount - 1].y / cosHalfAngle) * float3.UnitY;
            }

            if (checkBoxForceOneSample.Checked)
            {
                // Force first sample up
                m_neighborPositions[0] = float3.UnitY;
                forces[0] = float3.Zero;
            }

            // Apply force
            for (int i = 0; i < neighborsCount; i++)
            {
                float3 newPosition = (m_neighborPositions[i] + forces[i]).Normalized;
                m_neighborPositions[i]         = newPosition;
                m_SB_Neighbors.m[i].m_Position = newPosition;
            }

            // Update
            m_SB_Neighbors.Write();

            if (checkBoxRenderCell.Checked)
            {
                buttonBuildCell_Click(this, EventArgs.Empty);
                Application.DoEvents();
            }
        }
Пример #20
0
        private void    PerformExpectationMaximization(float3[] _Directions, FitLobe[] _Lobes)
        {
            int    n    = _Directions.Length;
            double invN = 1.0 / n;
            int    k    = _Lobes.Length;

            double[,]       probabilities = new double[n, k];

            // 1] Initialize lobes
            for (int h = 0; h < k; h++)
            {
                _Lobes[h].Direction     = _Directions[(int)((h + 0.5f) * n / k)];
                _Lobes[h].Concentration = 0.5;
                _Lobes[h].Alpha         = 1.0 / k;
            }

            // 2] Iterate
            int iterationsCount = 0;

            while (++iterationsCount < 1000)
            {
                // 2.1] Compute Expectation (the E step of the algorithm)
                for (int i = 0; i < n; i++)
                {
                    float3 dir = _Directions[i];

                    // 2.1.1) Compute weighted probability for each direction to belong to each lobe
                    double weightedSumProbabilities = 0.0;
                    for (int h = 0; h < k; h++)
                    {
                        FitLobe Lobe = _Lobes[h];

                        double kappa = Lobe.Concentration;
                        double dot   = dir.Dot(Lobe.Direction);
                        double f     = kappa / (2.0 * Math.PI * (Math.Exp(kappa) - Math.Exp(-kappa))) * Math.Exp(kappa * dot);
                        f *= Lobe.Alpha;
                        probabilities[i, h]       = f;
                        weightedSumProbabilities += f;
                    }
                    // 2.1.2) Normalize
                    double normalizer = weightedSumProbabilities > 1e-12 ? 1.0 / weightedSumProbabilities : 0.0;
                    for (int h = 0; h < k; h++)
                    {
                        probabilities[i, h] *= normalizer;
                    }
                }

                // 2.2] Compute Maximization (the M step of the algorithm)
                double sqConvergenceRate = 0.0;
                for (int h = 0; h < k; h++)
                {
                    FitLobe Lobe = _Lobes[h];

                    // Accumulate new alpha and average direction
                    Lobe.Alpha = 0.0;
                    double mu_x = 0.0;
                    double mu_y = 0.0;
                    double mu_z = 0.0;
                    for (int i = 0; i < n; i++)
                    {
                        float3 dir      = _Directions[i];
                        double p        = probabilities[i, h];
                        double p_over_N = invN * p;

                        Lobe.Alpha += p_over_N;

                        mu_x += p_over_N * dir.x;
                        mu_y += p_over_N * dir.y;
                        mu_z += p_over_N * dir.z;
                    }

                    // Compute new direction
                    double mu_length = Math.Sqrt(mu_x * mu_x + mu_y * mu_y + mu_z * mu_z);
                    double r         = Lobe.Alpha > 1e-12 ? mu_length / Lobe.Alpha : 0.0;

                    // Normalize direction
                    mu_length        = mu_length > 1e-12 ? 1.0 / mu_length : 0.0;
                    Lobe.Direction.x = (float)(mu_length * mu_x);
                    Lobe.Direction.y = (float)(mu_length * mu_y);
                    Lobe.Direction.z = (float)(mu_length * mu_z);

                    // Compute new concentration
                    double oldConcentration = Lobe.Concentration;
                    double newConcentration = (3.0 * r - r * r * r) / (1.0 - r * r);
                    Lobe.Concentration = newConcentration;

                    sqConvergenceRate += (newConcentration - oldConcentration) * (newConcentration - oldConcentration);
                }
                sqConvergenceRate /= k * k;

                if (sqConvergenceRate < 1e-6)
                {
                    break;
                }
            }
        }
Пример #21
0
        //
        float   DiskIrradiance(float3[] _tsQuadVertices)
        {
            // 1) Extract center position, tangent and bi-tangent axes
            float3 T  = 0.5f * (_tsQuadVertices[1] - _tsQuadVertices[2]);
            float3 B  = -0.5f * (_tsQuadVertices[3] - _tsQuadVertices[2]);
            float3 P0 = 0.5f * (_tsQuadVertices[0] + _tsQuadVertices[2]);                       // Half-way through the diagonal

            float sqRt = T.Dot(T);
            float sqRb = B.Dot(B);

            float3 N = T.Cross(B).Normalized;                   // @TODO: Optimize! Do we need to normalize anyway?

            // 2) Build frustum matrices
            // M transform P' = M * P into canonical frustum space
            float3x3 M;
            float    invDepth = 1.0f / P0.Dot(N);

            M.r0 = (T - P0.Dot(T) * invDepth * N) / sqRt;
            M.r1 = (B - P0.Dot(B) * invDepth * N) / sqRb;
            M.r2 = N * invDepth;

            // M^-1 transforms P = M^-1 * P' back into original space
            float3x3 invM = new float3x3();

            invM.r0.Set(T.x, B.x, P0.x);
            invM.r1.Set(T.y, B.y, P0.y);
            invM.r2.Set(T.z, B.z, P0.z);

            // Compute the determinant of M^-1 that will help us scale the resulting vector
            float det = invM.Determinant;

            det = Mathf.Sqrt(sqRt * sqRb) * P0.Dot(N);

            // 3) Compute the exact integral in the canonical space
            // We know the expression of the orthogonal vector at any point on the unit circle as:
            //
            //							| cos(theta)
            //	Tau(theta) = 1/sqrt(2)  | sin(theta)
            //							| 1
            //
            // We move the orientation so we always compute 2 symetrical integrals on a half circle
            //	then we only need to compute the X component integral as the Y components have opposite
            //	sign and simply cancel each other
            //
            // The integral we need to compute is thus:
            //
            //	X = Integral[-maxTheta,maxTheta]{ cos(theta) dtheta }
            //	X = 2 * sin(maxTheta)
            //
            // The Z component is straightforward Z = Integral[-maxTheta,maxTheta]{ dtheta } = 2 maxTheta
            //
            float cosMaxTheta = -1.0f;                                       // No clipping at the moment
            float maxTheta    = Mathf.Acos(Mathf.Clamp(cosMaxTheta, -1, 1)); // @TODO: Optimize! Use fast acos or something...

            float3 F = new float3(2 * Mathf.Sqrt(1 - cosMaxTheta * cosMaxTheta), 0, 2 * maxTheta) / (Mathf.Sqrt(2) * Mathf.TWOPI);

            float3 F2 = F / -det;

            return(F2.Length);

//			// 4) Transform back into LTC space using M^-1
//			float3	F2 = invM * F;	// @TODO: Optimize => simply return length(F2) = determinant of M^-1
//
//			// 5) Estimate scalar irradiance
////			return -1.0f / det;
////			return Mathf.Abs(F2.z);
//			return F2.Length;
        }
        /// <summary>
        /// Computes the 2 radii of the surface tangent to a vertex given a set of neighbor vertices
        /// </summary>
        /// <param name="_P"></param>
        /// <param name="_N"></param>
        /// <param name="_neighbors"></param>
        /// <returns></returns>
        float2  ComputeTangentCurvatureRadii(float3 _P, float3 _N, float3 _T, float3[] _neighbors, bool _debugGraph)
        {
            // Compute the square distance between the local neighbors and the double curvature surface of equation f(x,y) = Rt * x² + Rb * y²
            Func <double, double, float3[], double> SquareDistance = (double _Rt, double _Rb, float3[] _Pns) => {
                double result = 0.0;
                foreach (float3 Pn in _Pns)
                {
                    double Fxy = _Rt * Pn.x * Pn.x + _Rb * Pn.y * Pn.y;
                    double Dz  = Fxy - Pn.z;
                    result += Dz * Dz;
//					result += Dz;
                }
                return(result);
            };

            // Transform neighbors into local space
            float3 B = _T.Cross(_N).Normalized;

            float3[] neighbors = new float3[_neighbors.Length];
            for (int neighborIndex = 0; neighborIndex < _neighbors.Length; neighborIndex++)
            {
                float3 D = _neighbors[neighborIndex] - _P;
                neighbors[neighborIndex].Set(D.Dot(_T), D.Dot(B), D.Dot(_N));
            }

            float2 rangeX = new float2(-100, 100), rangeY = new float2(-1000, 1000);
            float4 pipo = new float4(1, 0, 0, 1);

            if (_debugGraph)
            {
                graph.WritePixels((uint X, uint Y, ref float4 _color) => {
                    float R0         = rangeX.x + X * (rangeX.y - rangeX.x) / graph.Width;
                    float R1         = rangeY.y + Y * (rangeY.x - rangeY.y) / graph.Height;
                    float sqDistance = (float)SquareDistance(R0, R1, _neighbors);
                    float V          = Math.Abs(sqDistance / 200.0f);
                    _color.Set(V, V, V, 1);
                });
                graph.DrawLine(new float4(0, 0, 1, 1), new float2(0, 0.5f * graph.Height), new float2(graph.Width, 0.5f * graph.Height));
                graph.DrawLine(new float4(0, 0, 1, 1), new float2(0.5f * graph.Width, 0), new float2(0.5f * graph.Width, graph.Height));
            }

            const float  eps = 0.01f;
            const double tol = 1e-3;

            // Compute gradient descent
            double previousRt = -double.MaxValue;
            double previousRb = -double.MaxValue;
            double Rt = 0.0, Rb = 0.0;
            int    iterationsCount     = 0;
            double previousSqDistance  = double.MaxValue;
            double bestSqDistance      = double.MaxValue;
            double bestRt              = 0.0;
            double bestRb              = 0.0;
            double stepSize            = 10.0;
            int    bestIterationsCount = 0;

            while (Math.Abs(Rt) < 10000.0f && Math.Abs(Rb) < 10000.0f && iterationsCount < 1000)
            {
                // Compute gradient
                double sqDistance = SquareDistance(Rt, Rb, neighbors);                          // Central value
                if (Math.Abs(sqDistance - previousSqDistance) / sqDistance < tol)
                {
                    break;                      // Relative error is low enough
                }

                double sqDistance_dT = SquareDistance(Rt + eps, Rb, neighbors);
                double sqDistance_dB = SquareDistance(Rt, Rb + eps, neighbors);
//				double	sqDistance_dTdB = SquareDistance( Rt + eps, Rb + eps, neighbors );
                double grad_dT = (sqDistance_dT - sqDistance) / eps;
                double grad_dB = (sqDistance_dB - sqDistance) / eps;
//              double	grad_dTdB = (sqDistance_dTdB - sqDistance) / eps;
//                      grad_dT = 0.5 * (grad_dT + grad_dTdB);
//                      grad_dB = 0.5 * (grad_dB + grad_dTdB);

                // Compute intersection with secant Y=0
//              double	t_T = -sqDistance * (Math.Abs( grad_dT ) > 1e-6 ? 1.0 / grad_dT : (Math.Sign( grad_dT ) * 1e6));
//              double	t_B = -sqDistance * (Math.Abs( grad_dB ) > 1e-6 ? 1.0 / grad_dB : (Math.Sign( grad_dB ) * 1e6));
                double t_T = -stepSize * eps * grad_dT;
                double t_B = -stepSize * eps * grad_dB;
                if (t_T * t_T + t_B * t_B < tol * tol)
                {
                    break;
                }

                previousRt = Rt;
                previousRb = Rb;
                Rt        += t_T;
                Rb        += t_B;
                iterationsCount++;

                if (_debugGraph)
                {
                    graph.DrawLine(pipo, new float2(((float)previousRt - rangeX.x) * graph.Width / (rangeX.y - rangeX.x), ((float)previousRb - rangeY.y) * graph.Height / (rangeY.x - rangeY.y))
                                   , new float2(((float)Rt - rangeX.x) * graph.Width / (rangeX.y - rangeX.x), ((float)Rb - rangeY.y) * graph.Height / (rangeY.x - rangeY.y)));
                }

                // Keep best results
                previousSqDistance = sqDistance;
                if (sqDistance < bestSqDistance)
                {
                    bestSqDistance      = sqDistance;
                    bestRt              = previousRt;
                    bestRb              = previousRb;
                    bestIterationsCount = iterationsCount;
                }
            }

            Rt = Math.Max(-10000.0, Math.Min(10000.0, Rt));
            Rb = Math.Max(-10000.0, Math.Min(10000.0, Rb));

            if (_debugGraph)
            {
                panelOutputGraph.m_bitmap = graph.AsBitmap;
                panelOutputGraph.Refresh();
                labelResult.Text = "R = " + Rt.ToString("G4") + ", " + Rb.ToString("G4") + " (" + previousSqDistance.ToString("G4") + ") in " + iterationsCount + " iterations...\r\nBest = " + bestRt.ToString("G4") + ", " + bestRb.ToString("G4") + " (" + bestSqDistance.ToString("G4") + ") in " + bestIterationsCount + " iterations...";
            }

            return(new float2((float)Rt, (float)Rb));
        }
        void    UpdateGraph()
        {
//          float3	P = new float3( 1, 1, 1 );
//          float3	N = P.Normalized;
//          float	R = ComputeTangentSphereRadius( P, N, new float3[] { new float3( 1, 1, -1 ), new float3( -1, 1, 1 ), new float3( 1, -1, 1 ) } );
// //			float	R = ComputeTangentSphereRadius( P, N, new float3[] { new float3( 1, 1, 0 ), new float3( 0, 1, 1 ), new float3( 1, 0, 1 ) } );
//
// //           float3	P = new float3( 1, 1, 0 );
// //           float3	N = P.Normalized;
// //           float	R = ComputeTangentSphereRadius( P, N, new float3[] { new float3( -1, 1, 0 ), new float3( 1, -1, 0 ) } );

            float3 V0 = float3.One;
            float3 N0 = V0.Normalized;
            float3 V1 = new float3(1, 1, -1);
            float3 V2 = new float3(-1, 1, 1);
            float3 V3 = new float3(1, -1, 1);
            float3 D0 = V1 - V0;
            float3 D1 = V2 - V0;
            float3 D2 = V3 - V0;
            float  L  = D0.Length;
            float  L_ = (V1 - D0.Dot(N0) * N0 - V0).Length;

            float3 P = float3.Zero;
            float3 N = float3.UnitZ;
            float  A = floatTrackbarControlA.Value;
            float  B = floatTrackbarControlB.Value;
            float  C = floatTrackbarControlC.Value;
            float  a = (float)(2 * Math.Sqrt(2.0 / 3));
            float  b = a * (float)Math.Sqrt(3) / 2.0f;
//          float	L1 = (new float3( 0, a, A ) - P).Length;
//          float	L2 = (new float3( -b, -0.5f * a, A ) - P).Length;
//          float	L3 = (new float3( b, -0.5f * a, A ) - P).Length;
//			float	R = ComputeTangentSphereRadius( P, N, new float3[] { new float3( 0, a, A ), new float3( -b, -0.5f * a, B ), new float3( b, -0.5f * a, C ) }, true );

            float D = floatTrackbarControlD.Value;

//          float2	curvature = ComputeTangentCurvatureRadii( P, N, float3.UnitX, new float3[] { new float3( -1, 0, A ), new float3( +1, 0, B ), new float3( 0, -1, C ), new float3( 0, +1, D ) }, true );

            // Torus inner vertex
            P = new float3(0.5f, 0.0f, 0.0f);
            N = new float3(-1.0f, 0.0f, 0.0f);
            float3 T = new float3(0.0f, 1.0f, 0.0f);

            float3[] neighbors = new float3[] {
                new float3(0.566346049f, -0.184016943f, 0.2938926f),
                new float3(0.595491469f, 0.0f, 0.2938926f),
                new float3(0.566346049f, 0.184016988f, 0.2938926f),
                new float3(0.47552827f, -0.154508471f, 0.0f),
                new float3(0.47552827f, 0.1545085f, 0.0f),
                new float3(0.5663461f, -0.184016973f, -0.293892682f),
                new float3(0.5954915f, 0.0f, -0.293892682f),
                new float3(0.5663461f, 0.184017f, -0.293892682f)
            };
            float2 curvature2 = ComputeTangentCurvatureRadii(P, N, T, neighbors, true);

            P         = new float3(1.5f, 0.0f, 0.0f);
            N         = new float3(1.0f, 0.0f, 0.0f);
            T         = new float3(0.0f, 1.0f, 0.0f);
            neighbors = new float3[] {
                new float3(1.335767f, -0.4340169f, -0.293892652f),
                new float3(1.40450847f, 0.0f, -0.293892652f),
                new float3(1.335767f, 0.434017f, -0.293892652f),
                new float3(1.42658484f, -0.4635254f, 0.0f),
                new float3(1.42658484f, 0.4635255f, 0.0f),
                new float3(1.335767f, -0.4340169f, 0.293892622f),
                new float3(1.40450847f, 0.0f, 0.293892622f),
                new float3(1.335767f, 0.434017f, 0.293892622f),
            };
            float2 curvature3 = ComputeTangentCurvatureRadii(P, N, T, neighbors, true);
        }
Пример #24
0
        float3  GatherIrradiance(float2 _csDirection, float4x4 _localCamera2World, float3 _csNormal, float _stepSize_meters, uint _stepsCount, float _Z0, float3 _centralRadiance, out float3 _csBentNormal, out float2 _coneAngles, out float _AO, ref float4 _DEBUG)
        {
            // Pre-compute factors for the integrals
            float2 integralFactors_Front = ComputeIntegralFactors(_csDirection, _csNormal);
            float2 integralFactors_Back  = ComputeIntegralFactors(-_csDirection, _csNormal);

            // Compute initial cos(angle) for front & back horizons
            // We do that by projecting the screen-space direction ssDirection onto the tangent plane given by the normal
            //	then the cosine of the angle from the Z axis is simply given by the Pythagorean theorem:
            //                             P
            //			   N\  |Z		  -*-
            //				 \ |	  ---  ^
            //				  \|  ---      |
            //             --- *..........>+ ssDirection
            //        ---
            //
            float hitDistance_Front = -_csDirection.Dot(_csNormal.xy) / _csNormal.z;
            float maxCosTheta_Front = hitDistance_Front / Mathf.Sqrt(hitDistance_Front * hitDistance_Front + 1.0f);
            float maxCosTheta_Back  = -maxCosTheta_Front;               // Back cosine is simply the mirror value

            // Gather irradiance from front & back directions while updating the horizon angles at the same time
            float3 sumRadiance            = float3.Zero;
            float3 previousRadiance_Front = _centralRadiance;
            float3 previousRadianceBack   = _centralRadiance;
//*
//			float2	radius = float2.Zero;
            float2 csStep           = _stepSize_meters * _csDirection;
            float2 csPosition_Front = float2.Zero;
            float2 csPosition_Back  = float2.Zero;

            for (uint stepIndex = 0; stepIndex < _stepsCount; stepIndex++)
            {
//				radius += _stepSize_meters;
                csPosition_Front += csStep;
                csPosition_Back  -= csStep;

//				float2	mipLevel = ComputeMipLevel( radius, _radialStepSizes );
                float2 mipLevel = float2.Zero;

                sumRadiance += SampleIrradiance(csPosition_Front, _localCamera2World, _Z0, mipLevel, integralFactors_Front, ref previousRadiance_Front, ref maxCosTheta_Front);
                sumRadiance += SampleIrradiance(csPosition_Back, _localCamera2World, _Z0, mipLevel, integralFactors_Back, ref previousRadianceBack, ref maxCosTheta_Back);
            }
//*/
            // Accumulate bent normal direction by rebuilding and averaging the front & back horizon vectors
            float2 ssNormal = new float2(_csNormal.xy.Dot(_csDirection), _csNormal.z);

                        #if USE_NUMERICAL_INTEGRATION
            // Half brute force where we perform the integration numerically as a sum...
            //
            const uint STEPS = 256;

            float thetaFront = Mathf.Acos(maxCosTheta_Front);
            float thetaBack  = -Mathf.Acos(maxCosTheta_Back);

            float2 ssBentNormal = float2.Zero;
            for (uint i = 0; i < STEPS; i++)
            {
                float  theta = Mathf.Lerp(thetaBack, thetaFront, (i + 0.5f) / STEPS);
                float  sinTheta = Mathf.Sin(theta), cosTheta = Mathf.Cos(theta);
                float2 ssOmega = new float2(sinTheta, cosTheta);

                float cosAlpha = Mathf.Saturate(ssOmega.Dot(ssNormal));

                cosAlpha = 1.0f;

                float weight = cosAlpha * Mathf.Abs(sinTheta);                                          // cos(alpha) * sin(theta).dTheta  (be very careful to take abs(sin(theta)) because our theta crosses the pole and becomes negative here!)

                ssBentNormal += weight * ssOmega;
            }

            float dTheta = (thetaFront - thetaBack) / STEPS;
            ssBentNormal *= dTheta;

            _csBentNormal = new float3(ssBentNormal.x * _csDirection, ssBentNormal.y);
                        #else
            // Analytical solution
// Accounts for dot product with normal
//              float	cosTheta0 = maxCosTheta_Front;
//              float	cosTheta1 = maxCosTheta_Back;
//              float	sinTheta0 = Mathf.Sqrt( 1.0f - cosTheta0*cosTheta0 );
//              float	sinTheta1 = Mathf.Sqrt( 1.0f - cosTheta1*cosTheta1 );
//
//              float	cosTheta0_3 = cosTheta0*cosTheta0*cosTheta0;
//              float	cosTheta1_3 = cosTheta1*cosTheta1*cosTheta1;
//              float	sinTheta0_3 = sinTheta0*sinTheta0*sinTheta0;
//              float	sinTheta1_3 = sinTheta1*sinTheta1*sinTheta1;
//
//              float	averageX = ssNormal.x * (cosTheta0_3 + cosTheta1_3 - 3.0f * (cosTheta0 + cosTheta1) + 4.0f)
//                               + ssNormal.y * (sinTheta0_3 - sinTheta1_3);
//
//              float	averageY = ssNormal.x * (sinTheta0_3 - sinTheta1_3)
//                               + ssNormal.y * (2.0f - cosTheta0_3 - cosTheta1_3);
//

            // Raw integration, without dot product with normal
            float theta0 = -Mathf.Acos(maxCosTheta_Back);
            float theta1 = Mathf.Acos(maxCosTheta_Front);

            float averageX = theta1 + theta0 - Mathf.Sin(theta0) * Mathf.Cos(theta0) - Mathf.Sin(theta1) * Mathf.Cos(theta1);
            float averageY = 2.0f - Mathf.Cos(theta0) * Mathf.Cos(theta0) - Mathf.Cos(theta1) * Mathf.Cos(theta1);

            _csBentNormal = new float3(averageX * _csDirection, averageY);
                        #endif

            // DON'T NORMALIZE RESULT!!
//			_csBentNormal = _csBentNormal.Normalized;

            // Compute cone angles
            float3 csNormalizedBentNormal = _csBentNormal.Normalized;
            float3 csHorizon_Front        = new float3(Mathf.Sqrt(1.0f - maxCosTheta_Front * maxCosTheta_Front) * _csDirection, maxCosTheta_Front);
            float3 csHorizon_Back         = new float3(-Mathf.Sqrt(1.0f - maxCosTheta_Back * maxCosTheta_Back) * _csDirection, maxCosTheta_Back);

                        #if USE_FAST_ACOS
            _coneAngles.x = FastPosAcos(saturate(dot(csNormalizedBentNormal, csHorizon_Front)));
            _coneAngles.y = FastPosAcos(saturate(dot(csNormalizedBentNormal, csHorizon_Back)));
                        #else
            _coneAngles.x = Mathf.Acos(Mathf.Saturate(csNormalizedBentNormal.Dot(csHorizon_Front)));
            _coneAngles.y = Mathf.Acos(Mathf.Saturate(csNormalizedBentNormal.Dot(csHorizon_Back)));
                        #endif

            // Compute AO using equation 11 of the paper
            _AO = 2.0f - maxCosTheta_Back - maxCosTheta_Front;
//          _AO = 0.0f;
// //           float	theta0 = -Mathf.Acos( maxCosTheta_Back );
// //           float	theta1 = Mathf.Acos( maxCosTheta_Front );
//          for ( uint i=0; i < 256; i++ ) {
//              float	theta = Mathf.Lerp( theta0, theta1, (i+0.5f) / 256 );
//              _AO += Math.Abs( Mathf.Sin( theta ) );
//          }
//          _AO *= (theta1 - theta0) / 256.0f;

            return(sumRadiance);
        }
Пример #25
0
 // From Walter 2007 eq. 40
 // Expects _incoming pointing AWAY from the surface
 // eta = IOR_above / IOR_below
 //
 public static float3 Refract( float3 _incoming, float3 _normal, float _eta )
 {
     float	c = _incoming.Dot( _normal );
     float	k = (float) (_eta * c - Math.Sign(c) * Math.Sqrt( 1.0 + _eta * (c*c - 1.0) ));
     float3	R = k * _normal - _eta * _incoming;
     return R.Normalized;
 }
Пример #26
0
        void    NumericalIntegration()
        {
            // 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 = 100;

            float3 coneDirection = float3.Zero;

            float3[,]       integratedSHCoeffs = new float3[TABLE_SIZE, TABLE_SIZE];

            double avgDiffA0 = 0.0;
            double avgDiffA1 = 0.0;
            double avgDiffA2 = 0.0;

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

                for (int AOIndex = 0; AOIndex < TABLE_SIZE; AOIndex++)
                {
//					float	AO = 1.0f - (float) AOIndex / TABLE_SIZE;
//					float	coneHalfAngle = 0.5f * (float) Math.PI * AO;			// Cone half angle varies in [0,PI/2]
//					float	cosConeHalfAngle = (float) Math.Cos( coneHalfAngle );
                    float cosConeHalfAngle = (float)AOIndex / TABLE_SIZE;

                    double A0 = 0.0;
                    double A1 = 0.0;
                    double A2 = 0.0;
                    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)
                        float u2 = u * u;
                        float u3 = u * u2;
                        A0 += u;
//A0 += 1.0;
                        A1 += u2;
                        A2 += 0.5 * (3 * u3 - u);
                    }

                    A0 *= 2.0 * Math.PI / SAMPLES_COUNT;
                    A1 *= 2.0 * Math.PI / SAMPLES_COUNT;
                    A2 *= 2.0 * Math.PI / SAMPLES_COUNT;
                    A0 *= Math.Sqrt(1.0 / (4.0 * Math.PI));
                    A1 *= Math.Sqrt(3.0 / (4.0 * Math.PI));
                    A2 *= Math.Sqrt(5.0 / (4.0 * Math.PI));

// float3	verify = EstimateLambertReflectanceFactors( cosConeHalfAngle, 0.5f * (float) Math.PI * thetaIndex / TABLE_SIZE );
// avgDiffA0 += Math.Abs( A0 - verify.x );
// avgDiffA1 += Math.Abs( A1 - verify.y );
// avgDiffA2 += Math.Abs( A2 - verify.z );

                    integratedSHCoeffs[thetaIndex, AOIndex].Set((float)A0, (float)A1, (float)A2);
                }
            }

            avgDiffA0 /= TABLE_SIZE * TABLE_SIZE;
            avgDiffA1 /= TABLE_SIZE * TABLE_SIZE;
            avgDiffA2 /= TABLE_SIZE * TABLE_SIZE;

            using (System.IO.FileStream S = new System.IO.FileInfo(@"ConeTable_cosAO.float3").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++)
                        {
                            W.Write(integratedSHCoeffs[thetaIndex, AOIndex].x);
                            W.Write(integratedSHCoeffs[thetaIndex, AOIndex].y);
                            W.Write(integratedSHCoeffs[thetaIndex, AOIndex].z);
                        }
                    }
                }
        }
Пример #27
0
        public double Eval(double[] _newParameters)
        {
            double lobeTheta         = _newParameters[0];
            double lobeRoughness     = _newParameters[1];
            double lobeGlobalScale   = _newParameters[2];
            double lobeFlatten       = _newParameters[3];
            double maskingImportance = _newParameters[4];


            // Flattening is not linear when using the anisotropic lobe model!
            if (m_lobeType == LOBE_TYPE.MODIFIED_PHONG_ANISOTROPIC)
            {
                lobeFlatten = Math.Pow(2.0, 4.0 * (lobeFlatten - 1.0));                         // in [2e-4, 2e4], log space
            }
            double invLobeFlatten = 1.0 / lobeFlatten;

            // Compute constant masking term due to incoming direction
            double maskingIncoming = Masking(m_direction.z, lobeRoughness);                             // Masking( incoming )

            // Compute lobe's reflection vector and tangent space using new parameters
            double cosTheta = Math.Cos(lobeTheta);
            double sinTheta = Math.Sin(lobeTheta);

            float3 lobe_normal    = new float3((float)(sinTheta * m_incomingDirection_CosPhi), (float)(sinTheta * m_incomingDirection_SinPhi), (float)cosTheta);
            float3 lobe_tangent   = new float3((float)-m_incomingDirection_SinPhi, (float)m_incomingDirection_CosPhi, 0.0f);                    // Always lying in the X^Y plane
            float3 lobe_biTangent = lobe_normal.Cross(lobe_tangent);

            // Compute sum
            double phi, theta, cosPhi, sinPhi;
            double outgoingIntensity_Simulated, length;
            double outgoingIntensity_Analytical, lobeIntensity;
            double difference;
            float3 wsOutgoingDirection  = float3.Zero;
            float3 wsOutgoingDirection2 = float3.Zero;
            float3 lsOutgoingDirection  = float3.Zero;
            double maskingOutGoing      = 0.0;
            double maskingShadowing;

            double sum              = 0.0;
            double sum_Simulated    = 0.0;
            double sum_Analytical   = 0.0;
            double sqSum_Simulated  = 0.0;
            double sqSum_Analytical = 0.0;

            for (int Y = 0; Y < H; Y++)
            {
// Formerly used wrong stuff!
//              // Y = theta bin index = 2.0 * LOBES_COUNT_THETA * pow2( sin( 0.5 * theta ) )
//              // We need theta:
//				theta = 2.0 * Math.Asin( Math.Sqrt( 0.5 * Y / H ) );

                // Y = theta bin index = LOBES_COUNT_THETA * (1 - cos( theta ) )
//              // We need theta:
                theta    = Math.Acos(1.0 - (float)Y / H);
                cosTheta = Math.Cos(theta);
                sinTheta = Math.Sin(theta);

                for (int X = 0; X < W; X++)
                {
                    // X = phi bin index = LOBES_COUNT_PHI * X / (2PI)
                    // We need phi:
                    phi    = 2.0 * Math.PI * X / W;
                    cosPhi = Math.Cos(phi);
                    sinPhi = Math.Sin(phi);

                    // Build simulated microfacet reflection direction in macro-surface space
                    outgoingIntensity_Simulated = m_histogramData[X, Y];
                    wsOutgoingDirection.Set((float)(cosPhi * sinTheta), (float)(sinPhi * sinTheta), (float)cosTheta);

                    // Compute maksing term due to outgoing direction
                    maskingOutGoing = Masking(wsOutgoingDirection.z, lobeRoughness);                                    // Masking( outgoing )

                    // Compute projection of world space direction onto reflected direction
                    float Vx = wsOutgoingDirection.Dot(lobe_tangent);
                    float Vy = wsOutgoingDirection.Dot(lobe_biTangent);
                    float Vz = wsOutgoingDirection.Dot(lobe_normal);

//Vz = Math.Min( 0.99f, Vz );

                    float cosTheta_M = Math.Max(1e-6f, Vz);

                    // Compute the lobe intensity in local space
                    lobeIntensity = NDF(cosTheta_M, lobeRoughness);

                    maskingShadowing = 1.0 + maskingImportance * (maskingIncoming * maskingOutGoing - 1.0); // = 1 when importance = 0, = masking when importance = 1
                    lobeIntensity   *= maskingShadowing;                                                    // * Masking terms

                    lobeIntensity *= lobeGlobalScale;

                    // Apply additional lobe scaling/flattening
                    length = m_flatteningEval(Vx, Vy, Vz, lobeFlatten, invLobeFlatten);

                    outgoingIntensity_Analytical = lobeIntensity * length;                      // Lobe intensity was estimated in lobe space, account for scaling when converting back in world space

                    // Sum the difference between simulated intensity and lobe intensity
                    outgoingIntensity_Analytical *= m_oversizeFactor;                           // Apply tolerance factor so we're always a bit smaller than the simulated lobe


                    if (m_fitUsingCenterOfMass)
                    {
                        double difference0 = outgoingIntensity_Simulated - outgoingIntensity_Analytical;

                        float3 wsLobePosition_Simulated  = (float)outgoingIntensity_Simulated * wsOutgoingDirection;
                        float3 wsLobePosition_Analytical = (float)outgoingIntensity_Analytical * wsOutgoingDirection;
                        // Subtract center of mass
                        wsLobePosition_Simulated  -= m_centerOfMass;
                        wsLobePosition_Analytical -= m_centerOfMass;
                        // Compute new intensities, relative to center of mass
                        outgoingIntensity_Simulated  = wsLobePosition_Simulated.Length;
                        outgoingIntensity_Analytical = wsLobePosition_Analytical.Length;

                        double difference1 = outgoingIntensity_Simulated - outgoingIntensity_Analytical;

                        difference = 0.5 * difference0 + 0.5 * difference1;

//						difference *= (wsLobePosition_Simulated - wsLobePosition_Analytical).Length;
//						difference += (wsLobePosition_Simulated - wsLobePosition_Analytical).Length;	// We also add the distance between lobe positions so it goes to the best of the 2 minima!
                    }
                    else
                    {
                        difference = outgoingIntensity_Simulated - outgoingIntensity_Analytical;
//						difference = outgoingIntensity_Simulated / Math.Max( 1e-6, outgoingIntensity_Analytical ) - 1.0;
//						difference = outgoingIntensity_Analytical / Math.Max( 1e-6, outgoingIntensity_Simulated ) - 1.0;
                    }


                    sum += difference * difference;

                    sum_Simulated    += outgoingIntensity_Simulated;
                    sum_Analytical   += outgoingIntensity_Analytical;
                    sqSum_Simulated  += outgoingIntensity_Simulated * outgoingIntensity_Simulated;
                    sqSum_Analytical += outgoingIntensity_Analytical * outgoingIntensity_Analytical;
                }
            }
            sum /= W * H;               // Not very useful since BFGS won't care but I'm doing it anyway to have some sort of normalized sum, better for us humans

            return(sum);
        }
Пример #28
0
        public void             Compute(uint _X, uint _Y)
        {
            float2 __Position     = new float2(0.5f + _X, 0.5f + _Y);
            float2 UV             = __Position / m_resolution;
            uint   pixelPositionX = (uint)Mathf.Floor(__Position.x);
            uint   pixelPositionY = (uint)Mathf.Floor(__Position.y);
            float  noise          = 0.0f;    //_tex_blueNoise[pixelPosition & 0x3F];


//PerformIntegrationTest();


            // Setup camera ray
            float3 csView     = BuildCameraRay(UV);
            float  Z2Distance = csView.Length;

            csView /= Z2Distance;
            float3 wsView = (new float4(csView, 0.0f) * m_camera2World).xyz;

            // Read back depth, normal & central radiance value from last frame
            float Z = FetchDepth(__Position, 0.0f);

            Z -= 1e-2f;                 // Prevent acnea by offseting the central depth closer

//			float	distance = Z * Z2Distance;
            float3 wsNormal = m_arrayNormal[0][pixelPositionX, pixelPositionY].xyz.Normalized;

            // Read back last frame's radiance value that we always can use as a default for neighbor areas
            float3 centralRadiance = m_arrayIrradiance[0][pixelPositionX, pixelPositionY].xyz;

            // Compute local camera-space
            float3 wsPos   = m_camera2World[3].xyz + Z * Z2Distance * wsView;
            float3 wsRight = wsView.Cross(m_camera2World[1].xyz).Normalized;
            float3 wsUp    = wsRight.Cross(wsView);
            float3 wsAt    = -wsView;

            float4x4 localCamera2World = new float4x4(new float4(wsRight, 0), new float4(wsUp, 0), new float4(wsAt, 0), new float4(wsPos, 1));

            // Compute local camera-space normal
            float3 N = new float3(wsNormal.Dot(wsRight), wsNormal.Dot(wsUp), wsNormal.Dot(wsAt));

            N.z = Math.Max(1e-4f, N.z);                                 // Make sure it's never 0!

//			float3	T, B;
//			BuildOrthonormalBasis( N, T, B );

            // Compute screen radius of gather sphere
            float screenSize_m        = 2.0f * Z * TAN_HALF_FOV;        // Vertical size of the screen in meters when extended to distance Z
            float sphereRadius_pixels = m_resolution.y * m_gatherSphereMaxRadius_m / screenSize_m;

            sphereRadius_pixels = Mathf.Min(GATHER_SPHERE_MAX_RADIUS_P, sphereRadius_pixels);                                                           // Prevent it to grow larger than our fixed limit
            float radiusStepSize_pixels = Mathf.Max(1.0f, sphereRadius_pixels / MAX_SAMPLES);                                                           // This gives us our radial step size in pixels
            uint  samplesCount          = Mathf.Clamp((uint)Mathf.Ceiling(sphereRadius_pixels / radiusStepSize_pixels), 1, MAX_SAMPLES);                // Reduce samples count if possible
            float radiusStepSize_meters = sphereRadius_pixels * screenSize_m / (samplesCount * m_resolution.y);                                         // This gives us our radial step size in meters

            // Start gathering radiance and bent normal by subdividing the screen-space disk around our pixel into Z slices
            float4 GATHER_DEBUG        = float4.Zero;
            float3 sumIrradiance       = float3.Zero;
            float3 csAverageBentNormal = float3.Zero;
//          float	averageConeAngle = 0.0f;
//          float	varianceConeAngle = 0.0f;
            float sumAO = 0.0f;

            for (uint angleIndex = 0; angleIndex < MAX_ANGLES; angleIndex++)
            {
                float phi = (angleIndex + noise) * Mathf.PI / MAX_ANGLES;

//phi = 0.0f;

                float2 csDirection;
                csDirection.x = Mathf.Cos(phi);
                csDirection.y = Mathf.Sin(phi);

                // Gather irradiance and average cone direction for that slice
                float3 csBentNormal;
                float2 coneAngles;
                float  AO;
                sumIrradiance += GatherIrradiance(csDirection, localCamera2World, N, radiusStepSize_meters, samplesCount, Z, centralRadiance, out csBentNormal, out coneAngles, out AO, ref GATHER_DEBUG);

// if ( AO < -0.01f || AO > 1.01f )
//  throw new Exception( "MERDE!" );

                csAverageBentNormal += csBentNormal;
                sumAO += AO;

//              // We're using running variance computation from https://www.johndcook.com/blog/standard_deviation/
//              //	Avg(N) = Avg(N-1) + [V(N) - Avg(N-1)] / N
//              //	S(N) = S(N-1) + [V(N) - Avg(N-1)] * [V(N) - Avg(N)]
//              // And variance = S(finalN) / (finalN-1)
//              //
//              float	previousAverageConeAngle = averageConeAngle;
//              averageConeAngle += (coneAngles.x - averageConeAngle) / (2*angleIndex+1);
//              varianceConeAngle += (coneAngles.x - previousAverageConeAngle) * (coneAngles.x - averageConeAngle);
//
//              previousAverageConeAngle = averageConeAngle;
//              averageConeAngle += (coneAngles.y - averageConeAngle) / (2*angleIndex+2);
//              varianceConeAngle += (coneAngles.y - previousAverageConeAngle) * (coneAngles.y - averageConeAngle);
            }

            // Finalize bent cone & irradiance
            csAverageBentNormal = csAverageBentNormal.Normalized;

//csAverageBentNormal *= Mathf.PI / MAX_ANGLES;

            sumIrradiance *= Mathf.PI / MAX_ANGLES;

//          varianceConeAngle /= 2.0f*MAX_ANGLES - 1.0f;
//          float	stdDeviation = Mathf.Sqrt( varianceConeAngle );

            sumAO /= 2.0f * MAX_ANGLES;
//if ( sumAO < 0.0f || sumAO > 1.0f )
//	throw new Exception( "MERDE!" );

            float averageConeAngle  = Mathf.Acos(1.0f - sumAO);
            float varianceConeAngle = 0.0f;                     // Unfortunately, we don't have a proper value for the variance anymore... :'(
            float stdDeviation      = Mathf.Sqrt(varianceConeAngle);

            sumIrradiance.Max(float3.Zero);

            //////////////////////////////////////////////////////////////////////////
            // Finalize results
            m_sumIrradiance = new float4(sumIrradiance, 0);
            m_bentCone      = new float4(Mathf.Max(0.01f, Mathf.Cos(averageConeAngle)) * csAverageBentNormal, 1.0f - stdDeviation / (0.5f * Mathf.PI));


            float3 DEBUG_VALUE = new float3(1, 0, 1);

            DEBUG_VALUE = csAverageBentNormal;
            DEBUG_VALUE = csAverageBentNormal.x * wsRight - csAverageBentNormal.y * wsUp - csAverageBentNormal.z * wsAt; // World-space normal
//DEBUG_VALUE = cos( averageConeAngle );
//DEBUG_VALUE = dot( ssAverageBentNormal, N );
//DEBUG_VALUE = 0.01 * Z;
//DEBUG_VALUE = sphereRadius_pixels / GATHER_SPHERE_MAX_RADIUS_P;
//DEBUG_VALUE = 0.1 * (radiusStepSize_pixels-1);
//DEBUG_VALUE = 0.5 * float(samplesCount) / MAX_SAMPLES;
//DEBUG_VALUE = varianceConeAngle;
//DEBUG_VALUE = stdDeviation;
//DEBUG_VALUE = float3( GATHER_DEBUG.xy, 0 );
//DEBUG_VALUE = float3( GATHER_DEBUG.zw, 0 );
            DEBUG_VALUE = GATHER_DEBUG.xyz;
//DEBUG_VALUE = N;
//m_bentCone = float4( DEBUG_VALUE, 1 );

            //////////////////////////////////////////////////////////////////////////
            // Finalize bent code debug info
            m_wsConePosition  = wsPos;
            m_wsConeDirection = csAverageBentNormal.x * wsRight + csAverageBentNormal.y * wsUp + csAverageBentNormal.z * wsAt;
//m_wsConeDirection = wsNormal;
            m_averageConeAngle = averageConeAngle;
            m_stdDeviation     = stdDeviation;
        }