// Helpers // Intersection between a plane and a ray public bool Intersect( Ray _Ray, ref float3 _intersection ) { float fGradient = n.Dot( _Ray.Aim ); if ( (float) System.Math.Abs( fGradient ) < float.Epsilon ) return false; // It cannot hit! (or very far away at least!) _Ray.Length = (-d - _Ray.Pos.Dot(n)) / fGradient; _intersection = _Ray.GetHitPos(); return true; }
public AngleAxis( Quat _q ) { Angle = (float) System.Math.Acos( _q.qs ); float fSine = (float) System.Math.Sin( Angle ); Angle *= 2.0f; Quat Temp = new Quat( _q ); Temp.Normalize(); if ( System.Math.Abs( fSine ) > float.Epsilon ) Axis = Temp.qv / fSine; else Axis.Set( 0, 0, 0 ); }
// Here, we choose the convention that the vertical axis defining THETA is the Y axis // and the axes defining PHI are X and Z where PHI = 0 when the vector is aligned to the positive Z axis // // NOTE ==> The '_Direction' vector must be normalized!! // public static double ComputeZH( int l, float3 _Direction ) { // Convert from cartesian to polar coords double θ = 0.0; double ϕ = 0.0f; CartesianToSpherical( _Direction, out θ, out ϕ ); return ComputeZH( l, θ ); }
/// <summary> /// Encodes a function evaluated by the provided delegate into a series of ZH coefficients /// </summary> /// <param name="_Coefficients">The coefficients supposed to best fit the provided function (the length of the vector must be _Order²)</param> /// <param name="_SamplesCount">The amount of samples to use to fit the function</param> /// <param name="_RandomSeed">The random seed to initialize the RNG with when drawing random sample position</param> /// <param name="_Order">The order of the SH vector to encode</param> /// <param name="_Delegate">The delegate that will be called to evaluate the function to encode</param> public static void EncodeIntoSH( float3[] _Coefficients, int _SamplesCountTheta, int _SamplesCountPhi, int _RandomSeed, int _Order, EvaluateFunctionSHVector3 _Delegate ) { Random RNG = new Random( _RandomSeed ); // Reset coefficients for ( int CoefficientIndex=0; CoefficientIndex < _Coefficients.Length; CoefficientIndex++ ) { _Coefficients[CoefficientIndex] = float3.Zero; } float3 Value = new float3(); for ( int PhiIndex=0; PhiIndex < _SamplesCountPhi; PhiIndex++ ) for ( int ThetaIndex=0; ThetaIndex < _SamplesCountTheta; ThetaIndex++ ) { // Draw uniformly sampled θ and ϕ angles double θ = 2.0 * Math.Acos( Math.Sqrt( 1.0 - (ThetaIndex + RNG.NextDouble()) / _SamplesCountTheta ) ); double ϕ = 2.0 * Math.PI * (PhiIndex + RNG.NextDouble()) / _SamplesCountPhi; // Accumulate coefficients _Delegate( θ, ϕ, Value ); for ( int l=0; l < _Order; l++ ) for ( int m=-l; m <= +l; m++ ) _Coefficients[l*(l+1)+m] += (float) Ylm( l, m, θ, ϕ ) * Value; } // Final normalizing float Normalizer = 4.0f * (float) Math.PI / (_SamplesCountTheta * _SamplesCountPhi); for ( int CoefficientIndex=0; CoefficientIndex < _Order*_Order; CoefficientIndex++ ) _Coefficients[CoefficientIndex] *= Normalizer; }
// Modulate all coefficients of degree l by scalar a. private static void Filter( float3[] _SH, int l, float a ) { for ( int m=-l; m <= l; m++ ) _SH[l*(l+1)+m] *= a; }
/// <summary> /// Converts a cartesian UNIT vector into spherical coordinates (θ,ϕ) /// </summary> /// <param name="_Direction">The cartesian unit vector to convert</param> /// <param name="_θ">The polar elevation</param> /// <param name="_ϕ">The azimuth</param> public static void CartesianToSpherical( float3 _Direction, out double _θ, out double _ϕ ) { _θ = Math.Acos( Math.Max( -1.0f, Math.Min( +1.0f, _Direction.z ) ) ); _ϕ = Math.Atan2( _Direction.y, _Direction.x ); }
/// <summary> /// Applies a simple fast rotation about the Y axis /// </summary> /// <param name="_Vector">The vector to rotate</param> /// <param name="_ϕ">The rotation angle</param> /// <param name="_RotatedVector">The rotated vector</param> /// <param name="_Order">The order of the vectors (i.e. vectors must have a length of Order²)</param> public static void RotateY( float3[] _Vector, double _ϕ, float3[] _RotatedVector, int _Order ) { for ( int l=0; l < _Order; l++ ) for ( int m=-l; m <= l; m++ ) if ( m != 0 ) { float fCos = (float) Math.Cos( Math.Abs( m ) * _ϕ ); float fSin = (float) Math.Sin( Math.Abs( m ) * _ϕ ); int CoeffIndex0 = l*(l+1) + m; int CoeffIndex1 = l*(l+1) - m; _RotatedVector[CoeffIndex0] = _Vector[CoeffIndex0] * fCos - Math.Sign( m ) * _Vector[CoeffIndex1] * fSin; } else _RotatedVector[l*(l+1)] = _Vector[l*(l+1)]; // <= Don't rotate zonal harmonics }
/// <summary> /// Converts spherical coordinates (θ,ϕ) into a cartesian UNIT vector /// </summary> /// <param name="_θ">The polar elevation</param> /// <param name="_ϕ">The azimuth</param> /// <param name="_Direction">The unit vector in cartesian coordinates</param> public static void SphericalToCartesian( double _θ, double _ϕ, float3 _Direction ) { _Direction.x = (float) (Math.Sin( _θ ) * Math.Cos( _ϕ )); _Direction.y = (float) (Math.Sin( _θ ) * Math.Sin( _ϕ )); _Direction.z = (float) Math.Cos( _θ ); }
/// <summary> /// Initializes the provided vector with the SH coefficients corresponding to the specified direction /// </summary> /// <param name="_Order">The order of the SH vector</param> /// <param name="_Direction">The direction of the SH sampling</param> /// <param name="_Coefficients">The array of coefficients to fill up</param> public static void InitializeSHCoefficients( int _Order, float3 _Direction, float3[] _Coefficients ) { int Index = 0; for ( int l=0; l < _Order; l++ ) for ( int m=-l; m <= +l; m++ ) { float fCoeff = (float) Ylm( l, m, _Direction ); _Coefficients[Index++].Set( fCoeff, fCoeff, fCoeff ); } }
/// <summary> /// Applies a simple mirroring on the Y axis /// </summary> /// <param name="_Vector">The vector to mirror</param> /// <param name="_MirroredVector">The mirrored vector</param> /// <param name="_Order">The order of the vectors (i.e. vectors must have a length of Order²)</param> public static void MirrorY( float3[] _Vector, float3[] _MirroredVector, int _Order ) { for ( int l=0; l < _Order; l++ ) for ( int m=-l; m <= l; m++ ) { int k = l*(l+1) + m; _MirroredVector[k] = (((l+m) & 1) == 0 ? +1 : -1) * _Vector[k]; } }
// Assignment operators public void Set ( Ray _r ) { m_Pos = _r.m_Pos; m_Aim = _r.m_Aim; m_Length = _r.m_Length; m_MarchedLength = _r.m_MarchedLength; m_Datum = _r.m_Datum; }
// Apply a Gaussian window of width w (usually the SH order). public static void FilterGaussian( float3[] _SH, int w ) { for ( int l = 0; l < 3; l++ ) Filter( _SH, l, (float) Math.Exp( -(Math.PI * l / w) * (Math.PI * l / w) / 2.0 ) ); }
public Ray Bend ( float3 _Axis, float _fBendFactor ) { m_Aim += _fBendFactor * m_Aim.Dot(_Axis) * _Axis; m_Aim.Normalize(); return this; }
// Helpers public Ray March ( float _fDelta ) { m_Pos += _fDelta * m_Aim; m_Length -= _fDelta; m_MarchedLength += _fDelta; return this; }
public Ray ( float3 _Pos, float3 _Aim, float _fLength, float _fMarchedLength ) : this( _Pos, _Aim, _fLength ) { m_MarchedLength = _fMarchedLength; }
public Ray ( float3 _Pos, float3 _Aim, float _fLength ): this( _Pos, _Aim ) { m_Length = _fLength; }
public Ray ( float3 _Pos, float3 _Aim ) { m_Pos = _Pos; m_Aim = _Aim; }
// Apply a Lanczos window of width w (usually the SH order). public static void FilterLanczos( float3[] _SH, int w ) { for (int l = 0; l < 3; l++) if ( l == 0 ) Filter( _SH, l, 1 ); else Filter( _SH, l, (float) (Math.Sin(Math.PI * l / w) / (Math.PI * l / w)) ); }
// Constructors/Destructor public BoundingSphere() { m_Center = new float3(); m_Radius = 0.0f; }
public void Set(float3 _Source) { x = _Source.x; y = _Source.y; }
public BoundingSphere(float _X, float _Y, float _Z, float _Radius) { m_Center = new float3(_X, _Y, _Z); m_Radius = _Radius; }
/// <summary> /// Applies rotation to the specified 3-vector using the specified rotation matrix returned by the "BuildRotationMatrix()" method /// </summary> /// <param name="_Vector">The 3-vector to rotate</param> /// <param name="_RotationMatrix">The SH rotation matrix</param> /// <param name="_RotatedVector">The rotated vector</param> /// <param name="_Order">The order of the vectors (i.e. vectors must have a length of Order²)</param> public static void Rotate( float3[] _Vector, double[,] _RotationMatrix, float3[] _RotatedVector, int _Order ) { int MatrixSize = 1; int SourceCoefficientIndex = 0; int DestCoefficientIndex = 0; for ( int l=0; l < _Order; l++, MatrixSize+=2 ) { int BandOffset = l * l; for ( int n=0; n < MatrixSize; n++ ) { _RotatedVector[DestCoefficientIndex] = new float3( 0.0f, 0.0f, 0.0f ); for ( int m=0; m < MatrixSize; m++ ) _RotatedVector[DestCoefficientIndex] += _Vector[SourceCoefficientIndex+m] * (float) _RotationMatrix[BandOffset + m,BandOffset + n]; DestCoefficientIndex++; } SourceCoefficientIndex += MatrixSize; } }
public BoundingSphere(float3 _Center, float _Radius) { m_Center = _Center; m_Radius = _Radius; }
/// <summary> /// Converts spherical coordinates (θ,ϕ) into a cartesian UNIT vector /// </summary> /// <param name="_θ">The polar elevation</param> /// <param name="_ϕ">The azimuth</param> /// <returns>The unit vector in cartesian coordinates</returns> public static float3 SphericalToCartesian( double _θ, double _ϕ ) { float3 Result = new float3(); SphericalToCartesian( _θ, _ϕ, Result ); return Result; }
public float Distance( float3 _p ) { return _p.Dot(n) + d; }
// Here, we choose the convention that the vertical axis defining THETA is the Y axis // and the axes defining PHI are X and Z where PHI = 0 when the vector is aligned to the positive Z axis // // NOTE ==> The '_Direction' vector must be normalized!! // public static double Ylm( int l, int m, float3 _Direction ) { // Convert from cartesian to polar coords double θ = 0.0; double ϕ = 0.0f; CartesianToSpherical( _Direction, out θ, out ϕ ); return Ylm( l, m, θ, ϕ ); }
// Intersection between 2 planes public bool Intersect( Plane _p, Ray _ray ) { // Check if both planes are coplanar if ( (float) System.Math.Abs( 1.0f - _p.n.Dot(n) ) < float.Epsilon ) return false; // Let's have fun! float3 I = new float3(); _ray.Pos.Set( 0, 0, 0 ); _ray.Aim = n; if ( !_p.Intersect( _ray, ref I ) ) return false; _ray.Aim = _p.n; if ( !_p.Intersect( _ray, ref I ) ) return false; _ray.Pos = I; _ray.Aim = I - _ray.Pos; if ( !Intersect( _ray, ref I ) ) return false; _ray.Pos = I; // We have at least one point belonging to both planes! _ray.Aim = n.Cross(_p.n).Normalized; return true; }
/// <summary> /// Computes the 9 Ylm coefficients for order 2 SH evaluated in the requested direction /// </summary> /// <param name="_direction"></param> /// <param name="_SH"></param> static void Ylm( float3 _direction, double[] _SH ) { const double c0 = 0.28209479177387814347403972578039; // 1/2 sqrt(1/pi) const double c1 = 0.48860251190291992158638462283835; // 1/2 sqrt(3/pi) const double c2 = 1.09254843059207907054338570580270; // 1/2 sqrt(15/pi) const double c3 = 0.31539156525252000603089369029571; // 1/4 sqrt(5/pi) float x = _direction.x; float y = _direction.y; float z = _direction.z; _SH[0] = c0; _SH[1] = c1*y; _SH[2] = c1*z; _SH[3] = c1*x; _SH[4] = c2*x*y; _SH[5] = c2*y*z; _SH[6] = c3*(3.0*z*z - 1.0); _SH[7] = c2*x*z; _SH[8] = 0.5*c2*(x*x - y*y); }
/// <summary> /// Computes the ambient occlusion of the specified coordinate by shooting N rays in a lobe oriented in the specified light direction /// </summary> /// <param name="_Light"></param> /// <param name="_X"></param> /// <param name="_Y"></param> /// <param name="_Z2HeightScale">Scale factor to apply to world Z coordinate to be remapped into the heights' [0,1] range</param> /// <param name="_LobeExponent">1 is a simple cosine lobe</param> /// <returns></returns> /// private float ComputeAO( int _LightIndex, uint _X, uint _Y, float _Z2HeightScale, float3[,] _rays ) { int RaysCount = _rays.GetLength( 1 ); int maxStepsCount = integerTrackbarControlMaxStepsCount.Value; float Z0 = m_imageSourceHeightMap[_X,_Y].y; double AO = 0.0f; int samplesCount = 0; for ( int rayIndex=0; rayIndex < RaysCount; rayIndex++ ) { float3 wsRay = _rays[_LightIndex,rayIndex]; if ( wsRay.z < 0.0f ) { // AO += 1.0; // SamplesCount++; continue; // Pointing to the ground so don't account for it... } // Make sure the ray has a unit step so we always travel at least one pixel // m_RayWorld.z *= _Z2HeightScale; // float Normalizer = 1.0f / Math.Max( Math.Abs( m_RayWorld.x ), Math.Abs( m_RayWorld.y ) ); // float Normalizer = 1.0f; // Normalizer = Math.Max( Normalizer, (1.0f - Z) / (128.0f * m_RayWorld.z) ); // This makes sure we can't use more than 128 steps to escape the heightfield // // float Normalizer = (1.0f - Z) / (128.0f * m_RayWorld.z); // // m_RayWorld.x *= Normalizer; // m_RayWorld.y *= Normalizer; // m_RayWorld.z *= Normalizer; // Start from the provided coordinates float X = _X; float Y = _Y; float Z = Z0; // Compute intersection with the height field int stepIndex = 0; while ( stepIndex < maxStepsCount && Z < 1.0f && X > 0.0f && Y > 0.0f && X < W && Y < H ) { X += wsRay.x; Y += wsRay.y; Z += wsRay.z; float height = SampleHeightField( X, Y ); if ( height > Z ) { // Hit! AO += 1.0; break; } stepIndex++; } samplesCount++; } AO /= samplesCount; return (float) (1.0 - AO); }
/// <summary> /// Computes the SH coefficients of originaly Y-aligned Zonal Harmonics coefficients rotated to match the provided axis /// </summary> /// <param name="_ZHCoefficients">The ZH coefficients to rotate</param> /// <param name="_TargetAxis">The target axis to match (the original axis being positive Y)</param> /// <param name="_RotatedSHCoefficients">The rotated SH coefficients</param> /// <remarks>Be careful the returned coefficients are SH coefficients, hence the _RotatedSHCoefficients vector should be N² if rotating an order N Zonal Harmonics vector</remarks> public static void ComputeRotatedZHCoefficients( double[] _ZHCoefficients, float3 _TargetAxis, double[] _RotatedSHCoefficients ) { // Compute the convolution coefficients from input ZH coeffs double[] ConvolutionCoefficients = new double[_ZHCoefficients.Length]; for ( int ConvolutionCoefficientIndex=0; ConvolutionCoefficientIndex < _ZHCoefficients.Length; ConvolutionCoefficientIndex++ ) ConvolutionCoefficients[ConvolutionCoefficientIndex] = Math.Sqrt( 4.0 * Math.PI / (2 * ConvolutionCoefficientIndex + 1) ) * _ZHCoefficients[ConvolutionCoefficientIndex]; // Perform rotation for ( int l=0; l < _ZHCoefficients.Length; l++ ) for ( int m=-l; m <= +l; m++ ) _RotatedSHCoefficients[l*(l+1)+m] = Ylm( l, m, _TargetAxis ) * ConvolutionCoefficients[l]; }
private void GenerateRays( int _raysCount, StructuredBuffer<float3> _target ) { _raysCount = Math.Min( MAX_THREADS, _raysCount ); // Half-Life 2 basis float3[] HL2Basis = new float3[] { new float3( (float) Math.Sqrt( 2.0 / 3.0 ), 0.0f, (float) Math.Sqrt( 1.0 / 3.0 ) ), new float3( -(float) Math.Sqrt( 1.0 / 6.0 ), (float) Math.Sqrt( 1.0 / 2.0 ), (float) Math.Sqrt( 1.0 / 3.0 ) ), new float3( -(float) Math.Sqrt( 1.0 / 6.0 ), -(float) Math.Sqrt( 1.0 / 2.0 ), (float) Math.Sqrt( 1.0 / 3.0 ) ) }; float centerTheta = (float) Math.Acos( HL2Basis[0].z ); float[] centerPhi = new float[] { (float) Math.Atan2( HL2Basis[0].y, HL2Basis[0].x ), (float) Math.Atan2( HL2Basis[1].y, HL2Basis[1].x ), (float) Math.Atan2( HL2Basis[2].y, HL2Basis[2].x ), }; for ( int rayIndex=0; rayIndex < _raysCount; rayIndex++ ) { double phi = (Math.PI / 3.0) * (2.0 * SimpleRNG.GetUniform() - 1.0); // Stratified version double theta = (Math.Acos( Math.Sqrt( (rayIndex + SimpleRNG.GetUniform()) / _raysCount ) )); // // Don't give a shit version (a.k.a. melonhead version) // // double Theta = Math.Acos( Math.Sqrt(WMath.SimpleRNG.GetUniform() ) ); // double Theta = 0.5 * Math.PI * WMath.SimpleRNG.GetUniform(); theta = Math.Min( 0.499f * Math.PI, theta ); double cosTheta = Math.Cos( theta ); double sinTheta = Math.Sin( theta ); double lengthFactor = 1.0 / sinTheta; // The ray is scaled so we ensure we always walk at least a texel in the texture cosTheta *= lengthFactor; sinTheta *= lengthFactor; // Yeah, yields 1... :) _target.m[0*MAX_THREADS+rayIndex].Set( (float) (Math.Cos( centerPhi[0] + phi ) * sinTheta), (float) (Math.Sin( centerPhi[0] + phi ) * sinTheta), (float) cosTheta ); _target.m[1*MAX_THREADS+rayIndex].Set( (float) (Math.Cos( centerPhi[1] + phi ) * sinTheta), (float) (Math.Sin( centerPhi[1] + phi ) * sinTheta), (float) cosTheta ); _target.m[2*MAX_THREADS+rayIndex].Set( (float) (Math.Cos( centerPhi[2] + phi ) * sinTheta), (float) (Math.Sin( centerPhi[2] + phi ) * sinTheta), (float) cosTheta ); } _target.Write(); }
/// <summary> /// Computes the convolution of 2 3-vectors of SH coefficients of the specified order using Clebsch-Gordan coefficients /// </summary> /// <param name="_Vector0">First 3-vector</param> /// <param name="_Vector1">Second 3-vector</param> /// <param name="_Order">The order of the vectors (i.e. vectors must have a length of Order²)</param> /// <returns>The convolution of the 2 vectors</returns> /// <remarks>This method is quite time-consuming as the convolution is computed using 5 loops but, as an optimisation, we can notice that most Clebsh-Gordan are 0 /// and a vector of non-null coefficients could be precomputed as only the vectors' coefficients change</remarks> public static float3[] Convolve( float3[] _Vector0, float3[] _Vector1, int _Order ) { if ( _Vector0 == null || _Vector1 == null ) throw new Exception( "Invalid coefficients!" ); if ( _Vector0.Length != _Vector1.Length ) throw new Exception( "Coefficient vectors length mismatch!" ); if ( _Order * _Order != _Vector0.Length ) throw new Exception( "Coefficient vectors are not of the specified order!" ); float3[] ConvolvedCoeffs = new float3[_Vector0.Length]; // Compute convolution int TotalCoeffIndex = 0; for ( int l=0; l < _Order; l++ ) for ( int m=-l; m <= +l; m++, TotalCoeffIndex++ ) { ConvolvedCoeffs[TotalCoeffIndex] = new float3( 0.0f, 0.0f, 0.0f ); int InnerTotalCoeffIndex = 0; for ( int l1=0; l1 < _Order; l1++ ) for ( int m1=-l1; m1 <= +l1; m1++, InnerTotalCoeffIndex++ ) for ( int l2=0; l2 < _Order; l2++ ) { int Bl2m1mIndex = l2*(l2+1) + m1 - m; if ( Bl2m1mIndex < 0 || Bl2m1mIndex >= _Order * _Order ) continue; double Sign = ((m1 - m) & 1) == 0 ? +1 : -1; double Sqrt = Math.Sqrt( (2.0 * l1 + 1) * (2.0 * l2 + 1) / (4.0 * Math.PI * (2.0 * l + 1)) ); double CGC0 = ComputeClebschGordan( l1, l2, 0, 0, l, 0 ); if ( Math.Abs( CGC0 ) < 1e-4 ) continue; double CGC1 = ComputeClebschGordan( l1, l2, m1, m - m1, l, m ); if ( Math.Abs( CGC1 ) < 1e-4 ) continue; float3 A = _Vector0[InnerTotalCoeffIndex]; float3 B = _Vector1[Bl2m1mIndex]; double FinalCoeff = Sqrt * CGC0 * CGC1; ConvolvedCoeffs[TotalCoeffIndex] += (float) (Sign * FinalCoeff) * A * B; } } return ConvolvedCoeffs; }
private void Generate_CPU( int _RaysCount ) { try { tabControlGenerators.Enabled = false; // Half-life basis (Z points outside of the surface, as in normal maps) float3[] basis = new float3[] { new float3( (float) Math.Sqrt( 2.0 / 3.0 ), 0.0f, (float) Math.Sqrt( 1.0 / 3.0 ) ), new float3( (float) -Math.Sqrt( 1.0 / 6.0 ), (float) Math.Sqrt( 1.0 / 2.0 ), (float) Math.Sqrt( 1.0 / 3.0 ) ), new float3( (float) -Math.Sqrt( 1.0 / 6.0 ), (float) -Math.Sqrt( 1.0 / 2.0 ), (float) Math.Sqrt( 1.0 / 3.0 ) ), }; // // 1] Compute normal map // float3 dX = new float3(); // float3 dY = new float3(); // float3 N; // float ddX = floatTrackbarControlPixelSize.Value; // float ddH = floatTrackbarControlHeight.Value; // for ( int Y=0; Y < H; Y++ ) // { // int Y0 = Math.Max( 0, Y-1 ); // int Y1 = Math.Min( H-1, Y+1 ); // for ( int X=0; X < W; X++ ) // { // int X0 = Math.Max( 0, X-1 ); // int X1 = Math.Min( W-1, X+1 ); // // float Hx0 = m_BitmapSource.ContentXYZ[X0,Y].y; // float Hx1 = m_BitmapSource.ContentXYZ[X1,Y].y; // float Hy0 = m_BitmapSource.ContentXYZ[X,Y0].y; // float Hy1 = m_BitmapSource.ContentXYZ[X,Y1].y; // // dX.Set( 2.0f * ddX, 0.0f, ddH * (Hx1 - Hx0) ); // dY.Set( 0.0f, 2.0f * ddX, ddH * (Hy1 - Hy0) ); // // N = dX.Cross( dY ).Normalized; // // m_Normal[X,Y] = new float3( // N.Dot( Basis[0] ), // N.Dot( Basis[1] ), // N.Dot( Basis[2] ) ); // } // // // Update and show progress // UpdateProgress( m_Normal, Y, true ); // } // UpdateProgress( m_Normal, H, true ); float LobeExponent = 4.0f;//floatTrackbarControlLobeExponent.Value; float PixelSize_mm = 1000.0f / floatTrackbarControlPixelDensity.Value; float scale = 0.1f * PixelSize_mm / floatTrackbarControlHeight.Value; // Scale factor to apply to pixel distances so they're renormalized in [0,1], our "heights space"... // Scale *= floatTrackbarControlZFactor.Value; // Cheat Z velocity so AO is amplified! // 2] Build local rays only once int raysCount = integerTrackbarControlRaysCount.Value; float3[,] rays = new float3[3,raysCount]; // Create orthonormal bases to orient the lobe float3 Xr = basis[0].Cross( float3.UnitZ ).Normalized; // We can safely use (0,0,1) as the "up" direction since the HL2 basis doesn't have any vertical direction float3 Yr = Xr.Cross( basis[0] ); float3 Xg = basis[1].Cross( float3.UnitZ ).Normalized; // We can safely use (0,0,1) as the "up" direction since the HL2 basis doesn't have any vertical direction float3 Yg = Xg.Cross( basis[1] ); float3 Xb = basis[2].Cross( float3.UnitZ ).Normalized; // We can safely use (0,0,1) as the "up" direction since the HL2 basis doesn't have any vertical direction float3 Yb = Xb.Cross( basis[2] ); double Exponent = 1.0 / (1.0 + LobeExponent); for ( int RayIndex=0; RayIndex < raysCount; RayIndex++ ) { // if ( false ) { // double Phi = 2.0 * Math.PI * WMath.SimpleRNG.GetUniform(); // // double Theta = Math.Acos( Math.Pow( WMath.SimpleRNG.GetUniform(), Exponent ) ); // double Theta = Math.PI / 3.0 * WMath.SimpleRNG.GetUniform(); // // float3 RayLocal = new float3( // (float) (Math.Cos( Phi ) * Math.Sin( Theta )), // (float) (Math.Sin( Phi ) * Math.Sin( Theta )), // (float) Math.Cos( Theta ) ); // // Rays[0,RayIndex] = RayLocal.x * Xr + RayLocal.y * Yr + RayLocal.z * Basis[0]; // Rays[1,RayIndex] = RayLocal.x * Xg + RayLocal.y * Yg + RayLocal.z * Basis[1]; // Rays[2,RayIndex] = RayLocal.x * Xb + RayLocal.y * Yb + RayLocal.z * Basis[2]; // } else { double Phi = Math.PI / 3.0 * (2.0 * SimpleRNG.GetUniform() - 1.0); double Theta = 0.49 * Math.PI * SimpleRNG.GetUniform(); rays[0,RayIndex] = new float3( (float) (Math.Cos( Phi ) * Math.Sin( Theta )), (float) (Math.Sin( Phi ) * Math.Sin( Theta )), (float) Math.Cos( Theta ) ); Phi = Math.PI / 3.0 * (2.0 * SimpleRNG.GetUniform() - 1.0 + 2.0); Theta = 0.49 * Math.PI * SimpleRNG.GetUniform(); rays[1,RayIndex] = new float3( (float) (Math.Cos( Phi ) * Math.Sin( Theta )), (float) (Math.Sin( Phi ) * Math.Sin( Theta )), (float) Math.Cos( Theta ) ); Phi = Math.PI / 3.0 * (2.0 * SimpleRNG.GetUniform() - 1.0 + 4.0); Theta = 0.49 * Math.PI * SimpleRNG.GetUniform(); rays[2,RayIndex] = new float3( (float) (Math.Cos( Phi ) * Math.Sin( Theta )), (float) (Math.Sin( Phi ) * Math.Sin( Theta )), (float) Math.Cos( Theta ) ); } rays[0,RayIndex].z *= scale; rays[1,RayIndex].z *= scale; rays[2,RayIndex].z *= scale; } // 3] Compute directional occlusion float4[] scanline = new float4[W]; float4 gammaRGB = float4.Zero; for ( uint Y=0; Y < H; Y++ ) { for ( uint X=0; X < W; X++ ) { gammaRGB.x = ComputeAO( 0, X, Y, scale, rays ); gammaRGB.y = ComputeAO( 1, X, Y, scale, rays ); gammaRGB.z = ComputeAO( 2, X, Y, scale, rays ); gammaRGB.w = (gammaRGB.x+gammaRGB.y+gammaRGB.z) / 3.0f; m_sRGBProfile.GammaRGB2LinearRGB( gammaRGB, ref scanline[X] ); } m_imageResult.WriteScanline( Y, scanline ); // Update and show progress UpdateProgress( m_imageResult, Y, true ); } UpdateProgress( m_imageResult, H, true ); // m_BitmapResult.Save( "eye_generic_01_disp_hl2.png", ImageFormat.Png ); } catch ( Exception _e ) { MessageBox( "An error occurred during generation:\r\n" + _e.Message, MessageBoxButtons.OK, MessageBoxIcon.Error ); } finally { tabControlGenerators.Enabled = true; } }
/// <summary> /// Evaluates the amplitude encoded by the Y-aligned ZH coefficients in the provided direction /// </summary> /// <param name="_Coefficients">The SH coefficients encoding an amplitude in various directions</param> /// <param name="_Direction">The direction where to evalute the ZH</param> /// <returns>The ZH evaluation in the provided direction</returns> public static double EvaluateZH( double[] _Coefficients, float3 _Direction ) { // Convert from cartesian to polar coords double θ = 0.0; double ϕ = 0.0f; CartesianToSpherical( _Direction, out θ, out ϕ ); return EvaluateZH( _Coefficients, θ ); }
float4x4 ComputeCameraProjection() { float t = (float) (DateTime.Now - m_startTime).TotalSeconds; // Make a camera orbiting around the cube float T = t; float3 cameraPosition = 3.0f * new float3( (float) Math.Cos( T ), (float) Math.Cos( 3 * T ), (float) Math.Sin( T ) ); float3 cameraTarget = new float3( 0, 0, 0 ); float4x4 camera2World = new float4x4(); camera2World.BuildRotLeftHanded( cameraPosition, cameraTarget, float3.UnitY ); // Build the perspective projection matrix float4x4 camera2Proj = new float4x4(); camera2Proj.BuildProjectionPerspective( 80.0f * (float) Math.PI / 180.0f, (float) Width / Height, 0.01f, 100.0f ); // Compose the 2 matrices together to obtain the final matrix that transforms world coordinates into projected 2D coordinates return camera2World.Inverse * camera2Proj; }
// Filtering stolen from http://csc.lsu.edu/~kooima/sht/sh.hpp // Apply a Hanning window of width w (usually the SH order). public static void FilterHanning( float3[] _SH, int w ) { for (int l = 0; l < 3; l++) if ( l > w ) Filter( _SH, l, 0 ); else Filter( _SH, l, (float) ((Math.Cos(Math.PI * l / w) + 1.0) * 0.5) ); }
// void timer1_Tick(object sender, System.EventArgs e) { // throw new System.NotImplementedException(); // } ////////////////////////////////////////////////////////////////////////// // Test methods compile static void TestFloat3x3() { if ( System.Runtime.InteropServices.Marshal.SizeOf(typeof(float3x3)) != 36 ) throw new Exception( "Not the appropriate size!" ); float3x3 test1 = new float3x3( new float[9] { 9, 8, 7, 6, 5, 4, 3, 2, 1 } ); float3x3 test2 = new float3x3( new float3( 1, 0, 0 ), new float3( 0, 1, 0 ), new float3( 0, 0, 1 ) ); float3x3 test3 = new float3x3( 0, 1, 2, 3, 4, 5, 6, 7, 8 ); float3x3 test0; test0 = test3; test2.Scale( new float3( 2, 2, 2 ) ); float3x3 mul0 = test1 * test2; float3x3 mul1 = 3.0f * test2; float3 mul2 = new float3( 1, 1, 1 ) * test3; float3 access0 = test3[2]; test3[1] = new float3( 12, 13, 14 ); float access1 = test3[1,2]; test3[1,2] = 18; // float coFactor = test3.CoFactor( 0, 2 ); float det = test3.Determinant; float3x3 inv = test2.Inverse; float3x3 id = float3x3.Identity; test3.BuildRotationX( 0.5f ); test3.BuildRotationY( 0.5f ); test3.BuildRotationZ( 0.5f ); test3.BuildFromAngleAxis( 0.5f, float3.UnitY ); }
/// <summary> /// Initializes the provided vector with the SH coefficients corresponding to the specified direction /// </summary> /// <param name="_Order">The order of the SH vector</param> /// <param name="_Direction">The direction of the SH sampling</param> /// <param name="_Coefficients">The array of coefficients to fill up</param> public static void InitializeSHCoefficients( int _Order, float3 _Direction, double[] _Coefficients ) { int Index = 0; for ( int l=0; l < _Order; l++ ) for ( int m=-l; m <= +l; m++ ) _Coefficients[Index++] = Ylm( l, m, _Direction ); }
protected override void OnLoad(EventArgs e) { base.OnLoad(e); // Initialize the device m_device.Init( panelOutput1.Handle, false, true ); // Build cube primitive { float3 colorXp = new float3( 1, 0, 0 ); float3 colorXn = new float3( 1, 1, 0 ); float3 colorYp = new float3( 0, 1, 0 ); float3 colorYn = new float3( 0, 1, 1 ); float3 colorZp = new float3( 0, 0, 1 ); float3 colorZn = new float3( 1, 0, 1 ); VertexP3N3G3T2[] vertices = new VertexP3N3G3T2[6*4] { // +X new VertexP3N3G3T2() { P = new float3( 1, 1, 1 ), N = new float3( 1, 0, 0 ), T = colorXp, UV = new float2( 0, 0 ) }, new VertexP3N3G3T2() { P = new float3( 1, -1, 1 ), N = new float3( 1, 0, 0 ), T = colorXp, UV = new float2( 0, 1 ) }, new VertexP3N3G3T2() { P = new float3( 1, -1, -1 ), N = new float3( 1, 0, 0 ), T = colorXp, UV = new float2( 1, 1 ) }, new VertexP3N3G3T2() { P = new float3( 1, 1, -1 ), N = new float3( 1, 0, 0 ), T = colorXp, UV = new float2( 1, 0 ) }, // -X new VertexP3N3G3T2() { P = new float3( -1, 1, -1 ), N = new float3( -1, 0, 0 ), T = colorXn, UV = new float2( 0, 0 ) }, new VertexP3N3G3T2() { P = new float3( -1, -1, -1 ), N = new float3( -1, 0, 0 ), T = colorXn, UV = new float2( 0, 1 ) }, new VertexP3N3G3T2() { P = new float3( -1, -1, 1 ), N = new float3( -1, 0, 0 ), T = colorXn, UV = new float2( 1, 1 ) }, new VertexP3N3G3T2() { P = new float3( -1, 1, 1 ), N = new float3( -1, 0, 0 ), T = colorXn, UV = new float2( 1, 0 ) }, // +Y new VertexP3N3G3T2() { P = new float3( -1, 1, -1 ), N = new float3( 0, 1, 0 ), T = colorYp, UV = new float2( 0, 0 ) }, new VertexP3N3G3T2() { P = new float3( -1, 1, 1 ), N = new float3( 0, 1, 0 ), T = colorYp, UV = new float2( 0, 1 ) }, new VertexP3N3G3T2() { P = new float3( 1, 1, 1 ), N = new float3( 0, 1, 0 ), T = colorYp, UV = new float2( 1, 1 ) }, new VertexP3N3G3T2() { P = new float3( 1, 1, -1 ), N = new float3( 0, 1, 0 ), T = colorYp, UV = new float2( 1, 0 ) }, // -Y new VertexP3N3G3T2() { P = new float3( -1, -1, 1 ), N = new float3( 0, -1, 0 ), T = colorYn, UV = new float2( 0, 0 ) }, new VertexP3N3G3T2() { P = new float3( -1, -1, -1 ), N = new float3( 0, -1, 0 ), T = colorYn, UV = new float2( 0, 1 ) }, new VertexP3N3G3T2() { P = new float3( 1, -1, -1 ), N = new float3( 0, -1, 0 ), T = colorYn, UV = new float2( 1, 1 ) }, new VertexP3N3G3T2() { P = new float3( 1, -1, 1 ), N = new float3( 0, -1, 0 ), T = colorYn, UV = new float2( 1, 0 ) }, // +Z new VertexP3N3G3T2() { P = new float3( -1, 1, 1 ), N = new float3( 0, 0, 1 ), T = colorZp, UV = new float2( 0, 0 ) }, new VertexP3N3G3T2() { P = new float3( -1, -1, 1 ), N = new float3( 0, 0, 1 ), T = colorZp, UV = new float2( 0, 1 ) }, new VertexP3N3G3T2() { P = new float3( 1, -1, 1 ), N = new float3( 0, 0, 1 ), T = colorZp, UV = new float2( 1, 1 ) }, new VertexP3N3G3T2() { P = new float3( 1, 1, 1 ), N = new float3( 0, 0, 1 ), T = colorZp, UV = new float2( 1, 0 ) }, // -Z new VertexP3N3G3T2() { P = new float3( 1, 1, -1 ), N = new float3( 0, 0, -1 ), T = colorZn, UV = new float2( 0, 0 ) }, new VertexP3N3G3T2() { P = new float3( 1, -1, -1 ), N = new float3( 0, 0, -1 ), T = colorZn, UV = new float2( 0, 1 ) }, new VertexP3N3G3T2() { P = new float3( -1, -1, -1 ), N = new float3( 0, 0, -1 ), T = colorZn, UV = new float2( 1, 1 ) }, new VertexP3N3G3T2() { P = new float3( -1, 1, -1 ), N = new float3( 0, 0, -1 ), T = colorZn, UV = new float2( 1, 0 ) }, }; uint[] indices = new uint[3*2*6] { 4*0+0, 4*0+1, 4*0+2, 4*0+0, 4*0+2, 4*0+3, 4*1+0, 4*1+1, 4*1+2, 4*1+0, 4*1+2, 4*1+3, 4*2+0, 4*2+1, 4*2+2, 4*2+0, 4*2+2, 4*2+3, 4*3+0, 4*3+1, 4*3+2, 4*3+0, 4*3+2, 4*3+3, 4*4+0, 4*4+1, 4*4+2, 4*4+0, 4*4+2, 4*4+3, 4*5+0, 4*5+1, 4*5+2, 4*5+0, 4*5+2, 4*5+3, }; m_prim_cube = new Primitive( m_device, 6*4, VertexP3N3G3T2.FromArray( vertices ), indices, Primitive.TOPOLOGY.TRIANGLE_LIST, VERTEX_FORMAT.P3N3G3T2 ); } // Build the shader to render the cube m_shader_renderCube = new Shader( m_device, new ShaderFile( new System.IO.FileInfo( @".\Shaders\RenderCube.hlsl" ) ), VERTEX_FORMAT.P3N3G3T2, "VS", null, "PS", null ); // Build constant buffer to provide camera transform m_CB_Camera = new ConstantBuffer<CBCamera>( m_device, 0 ); Application.Idle += Application_Idle; }
public float3[] MapSequenceToSphere( double[,] _Sequence, double _MaxTheta ) { if ( _Sequence.GetLength( 1 ) != 2 ) throw new Exception( "Expecting a 2-dimensional sequence !" ); double cosMaxTheta = Math.Cos( _MaxTheta ); float3[] Result = new float3[_Sequence.GetLength( 0 )]; for ( int i=0; i < Result.Length; i++ ) { // Pick a uniformly random VDOT, which must be between -1 and 1. // This represents the dot product of the random vector with the Y unit vector. // // Note: this works because the surface area of the sphere between // Y and Y + dY is independent of Y. So choosing Y uniformly chooses // a patch of area uniformly. // double vdot = cosMaxTheta + (1.0 - cosMaxTheta) * _Sequence[i,0]; double theta = Math.Acos( vdot ); // Pick a uniformly random rotation between 0 and 2 Pi around the axis of the Y vector. // double phi = 2.0 * Math.PI * _Sequence[i,1]; Result[i] = new float3() { x = (float) (Math.Sin( phi ) * Math.Sin( theta )), y = (float) Math.Cos( theta ), z = (float) (Math.Cos( phi ) * Math.Sin( theta )) }; } return Result; }
public Point2D(float3 _Source) { Set(_Source); }