public OutputPanelHammersley(IContainer container) { container.Add(this); InitializeComponent(); WMath.Hammersley QRNG = new WMath.Hammersley(); double[,] Sequence = QRNG.BuildSequence(SAMPLES_COUNT, 2); for (int SampleIndex = 0; SampleIndex < SAMPLES_COUNT; SampleIndex++) { float Angle = 2.0f * (float)Math.PI * (float)Sequence[SampleIndex, 0]; float Radius = (float)Math.Sqrt(Sequence[SampleIndex, 1]); // Uniform // float Radius = (float) Sequence[SampleIndex,1]; m_Samples[SampleIndex] = new WMath.Vector2D(Radius * (float)Math.Cos(Angle), Radius * (float)Math.Sin(Angle)); } string Format = "const uint SHADOW_SAMPLES_COUNT = "+ SAMPLES_COUNT + ";\r\n" + "const float2 SamplesOffset[SHADOW_SAMPLES_COUNT] = {\r\n"; for (int SampleIndex = 0; SampleIndex < SAMPLES_COUNT; SampleIndex++) { Format += " float2( " + m_Samples[SampleIndex].x + ", " + m_Samples[SampleIndex].y + " ),\r\n"; } Format += "};\r\n\r\n"; OnSizeChanged(EventArgs.Empty); }
public OutputPanelHammersley( IContainer container ) { container.Add( this ); InitializeComponent(); WMath.Hammersley QRNG = new WMath.Hammersley(); double[,] Sequence = QRNG.BuildSequence( SAMPLES_COUNT, 2 ); for ( int SampleIndex=0; SampleIndex < SAMPLES_COUNT; SampleIndex++ ) { float Angle = 2.0f * (float) Math.PI * (float) Sequence[SampleIndex,0]; float Radius = (float) Math.Sqrt( Sequence[SampleIndex,1] ); // Uniform // float Radius = (float) Sequence[SampleIndex,1]; m_Samples[SampleIndex] = new WMath.Vector2D( Radius * (float) Math.Cos( Angle ), Radius * (float) Math.Sin( Angle ) ); } string Format = "const uint SHADOW_SAMPLES_COUNT = " + SAMPLES_COUNT + ";\r\n" + "const float2 SamplesOffset[SHADOW_SAMPLES_COUNT] = {\r\n"; for ( int SampleIndex=0; SampleIndex < SAMPLES_COUNT; SampleIndex++ ) { Format += " float2( " + m_Samples[SampleIndex].x + ", " + m_Samples[SampleIndex].y + " ),\r\n"; } Format += "};\r\n\r\n"; OnSizeChanged( EventArgs.Empty ); }
private void GenerateRays(int _RaysCount, float _MaxConeAngle, RendererManaged.StructuredBuffer <RendererManaged.float3> _Target) { _RaysCount = Math.Min(MAX_THREADS, _RaysCount); WMath.Hammersley hammersley = new WMath.Hammersley(); double[,] sequence = hammersley.BuildSequence(_RaysCount, 2); WMath.Vector[] rays = hammersley.MapSequenceToSphere(sequence, 0.5f * _MaxConeAngle); for (int RayIndex = 0; RayIndex < _RaysCount; RayIndex++) { WMath.Vector ray = rays[RayIndex]; // // Scale the ray so we ensure to always walk at least a texel in the texture // float SinTheta = (float) Math.Sqrt( 1.0 - ray.y * ray.y ); // float LengthFactor = 1.0f / SinTheta; // ray *= LengthFactor; _Target.m[RayIndex].Set(ray.x, -ray.z, ray.y); } _Target.Write(); }
/// <summary> /// Following the mathematica notebook found in "D:\Docs\Computer Graphics\Volumetric, Clouds, Participating Medium, Light Scattering, Translucency\2005 Sun, Ramamoorthi.nb" /// this routine generates the tables representing the integral of function Gn depending on 2 parameters. /// Several of these tables are generated for different exponents n, the goal is then to find an approximation to generated these tables from an analytical expression. /// </summary> void BuildSurfaceRadianceIntegrals() { BuildPreciseF(); // string ResultsPath = @"D:\Docs\Computer Graphics\Volumetric, Clouds, Participating Medium, Light Scattering, Translucency"; string ResultsPath = @".\"; const int TABLE_SIZE = 64; const int IMPORTANCE_SAMPLES_COUNT = 4096; double[,] QRNG = new WMath.Hammersley().BuildSequence(IMPORTANCE_SAMPLES_COUNT, 2); for (int TableIndex = 0; TableIndex < 1; TableIndex++) { float Exponent = 1.0f + TableIndex; double[,] Table = new double[TABLE_SIZE, TABLE_SIZE]; // Compute the integral for each parameter for (int Y = 0; Y < TABLE_SIZE; Y++) { double Theta_s = 0.5 * Math.PI * Y / (TABLE_SIZE - 1); // V in [0,PI/2] double CosTheta_s = Math.Sin(Theta_s); double SinTheta_s = Math.Cos(Theta_s); for (int X = 0; X < TABLE_SIZE; X++) { double Tsp = MAX_U * X / (TABLE_SIZE - 1); // U in [0,10] // Use importance sampling double Sum = 0.0; for (int SampleIndex = 0; SampleIndex < IMPORTANCE_SAMPLES_COUNT; SampleIndex++) { double X0 = QRNG[SampleIndex, 0]; double X1 = QRNG[SampleIndex, 1]; double Phi_i = 2.0 * Math.PI * X0; double Theta_i = Math.Asin(Math.Pow(X1, 1.0 / (1.0 + Exponent))); // Assuming we're integrating a cos^n(theta) double CosGamma = Math.Cos(Theta_i) * Math.Cos(Theta_s) + Math.Sin(Theta_i) * Math.Sin(Theta_s) * Math.Cos(Phi_i); // Angle between the incoming ray and the source double SinGamma = Math.Sqrt(1 - CosGamma * CosGamma); double Gamma = Math.Acos(CosGamma); // double x0 = Math.Sin(Theta_i) * Math.Sin( Phi_i ); // double y0 = Math.Cos(Theta_i); // double z0 = Math.Sin(Theta_i) * Math.Cos( Phi_i ); // double x1 = SinTheta_s; // double y1 = CosTheta_s; // Gamma = Math.Acos( x0*x1 + y0*y1 ); // // double CosGamma = Math.Cos( Gamma ); // double SinGamma = Math.Sin( Gamma ); double Extinction = Math.Exp(-Tsp * CosGamma); double DeltaF = F_Table(Tsp * SinGamma, HALF_PI) - F_Table(Tsp * SinGamma, 0.5 * Gamma); double Term = (Extinction / SinGamma) * DeltaF; if (double.IsNaN(Term)) { throw new Exception(); } Sum += Term; } Sum /= IMPORTANCE_SAMPLES_COUNT; /* // Use standard numerical integration * const double dPhi = 2.0 * Math.PI / 160; * const double dTheta = 0.5 * Math.PI / 40; * * double Sum = 0.0; * for ( int ThetaIndex=0; ThetaIndex < 40; ThetaIndex++ ) { * double Theta_i = 0.5 * Math.PI * (0.5+ThetaIndex) / 40; * double SolidAngle = Math.Sin( Theta_i ) * dPhi * dTheta; * * for ( int PhiIndex=0; PhiIndex < 160; PhiIndex++ ) { * double Phi_i = Math.PI * PhiIndex / 160.0; * * double CosGamma = Math.Cos( Theta_i )*CosTheta_s + Math.Sin( Theta_i )*SinTheta_s*Math.Cos( Phi_i ); // Angle between the incoming ray and the source * * // double x0 = Math.Sin(Theta_i)*Math.Cos(Phi_i); * // double y0 = Math.Cos(Theta_i); * // double z0 = Math.Sin(Theta_i)*Math.Sin(Phi_i); * // double x1 = SinTheta_s; * // double y1 = CosTheta_s; * // double CosGamma = x0*x1 + y0*y1; * * double SinGamma = Math.Sqrt( 1 - CosGamma*CosGamma ); * double Gamma = Math.Acos( CosGamma ); * * double Extinction = Math.Exp( -Tsp * CosGamma ); * double DeltaF = F_Table( Tsp * SinGamma, HALF_PI ) - F_Table( Tsp * SinGamma, 0.5 * Gamma ); * double Term = (Extinction / SinGamma) * DeltaF; * if ( double.IsNaN( Term ) ) * throw new Exception(); * * Sum += Term * Math.Pow( Math.Cos( Theta_i ), Exponent ) * SolidAngle; * } * } */ Table[X, Y] = Sum; } } // Write the result FileInfo TargetFile = new FileInfo(Path.Combine(ResultsPath, "TableG" + TableIndex + ".double")); using (FileStream S = TargetFile.Create()) using (BinaryWriter W = new BinaryWriter(S)) { for (int Y = 0; Y < TABLE_SIZE; Y++) { for (int X = 0; X < TABLE_SIZE; X++) { W.Write(Table[X, Y]); } } } } }
/// <summary> /// Performs a ray-tracing of the surface /// Outputs resulting directions into a texture then performs a histogram /// /// The surface is assigned a beam of photons, one for each texel of the heightfield texture /// At each iteration, the whole beam is offset a little using a Hammersley sequence that guarantees /// we end up ray-tracing the entire surface finely (hopefully, the full surface can be traced using enough terationsi) /// </summary> /// <param name="_roughness">Surface roughness</param> /// <param name="_albedo">Surface albedo for diffuse or F0 for dielectrics</param> /// <param name="_surfaceType">Type of surface we're simulating</param> /// <param name="_theta">Vertical angle of incidence</param> /// <param name="_phi">Azimuthal angle of incidence</param> /// <param name="_iterationsCount">Amount of iterations of beam tracing</param> public void RayTraceSurface( float _roughness, float _albedoF0, SURFACE_TYPE _surfaceType, float _theta, float _phi, int _iterationsCount ) { float sinTheta = (float) Math.Sin( _theta ); float cosTheta = (float) Math.Cos( _theta ); float sinPhi = (float) Math.Sin( _phi ); float cosPhi = (float) Math.Cos( _phi ); m_lastComputedDirection.Set( -sinTheta * cosPhi, -sinTheta * sinPhi, -cosTheta ); // Minus sign because we need the direction pointing TOWARD the surface (i.e. z < 0) m_lastComputedRoughness = _roughness; m_lastComputedAlbedo = _albedoF0; m_lastComputedIOR = Fresnel_IORFromF0( _albedoF0 ); m_lastComputedSurfaceType = _surfaceType; m_lastComputedHistogramIterationsCount = _iterationsCount; m_CB_RayTrace.m._Direction = m_lastComputedDirection; m_CB_RayTrace.m._Roughness = m_lastComputedRoughness; m_CB_RayTrace.m._Albedo = m_lastComputedAlbedo; m_CB_RayTrace.m._IOR = m_lastComputedIOR; m_Tex_OutgoingDirections_Reflected.RemoveFromLastAssignedSlots(); m_Tex_OutgoingDirections_Transmitted.RemoveFromLastAssignedSlots(); m_Tex_LobeHistogram_Reflected.RemoveFromLastAssignedSlots(); m_Tex_LobeHistogram_Transmitted.RemoveFromLastAssignedSlots(); m_Device.Clear( m_Tex_LobeHistogram_Reflected_Decimal, float4.Zero ); // Clear counters m_Device.Clear( m_Tex_LobeHistogram_Reflected_Integer, float4.Zero ); m_Device.Clear( m_Tex_LobeHistogram_Transmitted_Decimal, float4.Zero ); // Clear counters m_Device.Clear( m_Tex_LobeHistogram_Transmitted_Integer, float4.Zero ); WMath.Hammersley pRNG = new WMath.Hammersley(); double[,] sequence = pRNG.BuildSequence( _iterationsCount, 2 ); for ( int iterationIndex=0; iterationIndex < _iterationsCount; iterationIndex++ ) { // 1] Ray-trace surface switch ( m_lastComputedSurfaceType ) { case SURFACE_TYPE.CONDUCTOR: if ( m_Shader_RayTraceSurface_Conductor.Use() ) { // Update trace offset m_CB_RayTrace.m._Offset.Set( (float) sequence[iterationIndex,0], (float) sequence[iterationIndex,1] ); m_CB_RayTrace.UpdateData(); m_Device.Clear( m_Tex_OutgoingDirections_Reflected, float4.Zero ); // Clear target directions and weights m_Tex_Heightfield.SetCS( 0 ); m_Tex_Random.SetCS( 1 ); m_Tex_OutgoingDirections_Reflected.SetCSUAV( 0 ); // New target buffer where to accumulate m_Shader_RayTraceSurface_Conductor.Dispatch( HEIGHTFIELD_SIZE >> 4, HEIGHTFIELD_SIZE >> 4, 1 ); m_Tex_OutgoingDirections_Reflected.RemoveFromLastAssignedSlotUAV(); } // 2] Accumulate into target histogram if ( m_Shader_AccumulateOutgoingDirections.Use() ) { m_Tex_OutgoingDirections_Reflected.SetCS( 0 ); m_Tex_LobeHistogram_Reflected_Decimal.SetCSUAV( 0 ); m_Tex_LobeHistogram_Reflected_Integer.SetCSUAV( 1 ); m_Shader_AccumulateOutgoingDirections.Dispatch( HEIGHTFIELD_SIZE >> 4, HEIGHTFIELD_SIZE >> 4, MAX_SCATTERING_ORDER ); m_Tex_LobeHistogram_Reflected_Decimal.RemoveFromLastAssignedSlotUAV(); m_Tex_LobeHistogram_Reflected_Integer.RemoveFromLastAssignedSlotUAV(); m_Tex_OutgoingDirections_Reflected.RemoveFromLastAssignedSlots(); } break; case SURFACE_TYPE.DIELECTRIC: if ( m_Shader_RayTraceSurface_Dielectric.Use() ) { // Update trace offset m_CB_RayTrace.m._Offset.Set( (float) sequence[iterationIndex,0], (float) sequence[iterationIndex,1] ); m_CB_RayTrace.UpdateData(); m_Device.Clear( m_Tex_OutgoingDirections_Reflected, float4.Zero ); // Clear target directions and weights m_Device.Clear( m_Tex_OutgoingDirections_Transmitted, float4.Zero ); // Clear target directions and weights m_Tex_Heightfield.SetCS( 0 ); m_Tex_Random.SetCS( 1 ); m_Tex_OutgoingDirections_Reflected.SetCSUAV( 0 ); // New target buffer where to accumulate m_Tex_OutgoingDirections_Transmitted.SetCSUAV( 1 ); // New target buffer where to accumulate m_Shader_RayTraceSurface_Dielectric.Dispatch( HEIGHTFIELD_SIZE >> 4, HEIGHTFIELD_SIZE >> 4, 1 ); m_Tex_OutgoingDirections_Reflected.RemoveFromLastAssignedSlotUAV(); m_Tex_OutgoingDirections_Transmitted.RemoveFromLastAssignedSlotUAV(); } // 2] Accumulate into target histogram if ( m_Shader_AccumulateOutgoingDirections.Use() ) { // Accumulated reflections m_Tex_OutgoingDirections_Reflected.SetCS( 0 ); m_Tex_LobeHistogram_Reflected_Decimal.SetCSUAV( 0 ); m_Tex_LobeHistogram_Reflected_Integer.SetCSUAV( 1 ); m_Shader_AccumulateOutgoingDirections.Dispatch( HEIGHTFIELD_SIZE >> 4, HEIGHTFIELD_SIZE >> 4, MAX_SCATTERING_ORDER ); m_Tex_LobeHistogram_Reflected_Decimal.RemoveFromLastAssignedSlotUAV(); m_Tex_LobeHistogram_Reflected_Integer.RemoveFromLastAssignedSlotUAV(); m_Tex_OutgoingDirections_Reflected.RemoveFromLastAssignedSlots(); // Accumulated transmissions m_Tex_OutgoingDirections_Transmitted.SetCS( 0 ); m_Tex_LobeHistogram_Transmitted_Decimal.SetCSUAV( 0 ); m_Tex_LobeHistogram_Transmitted_Integer.SetCSUAV( 1 ); m_Shader_AccumulateOutgoingDirections.Dispatch( HEIGHTFIELD_SIZE >> 4, HEIGHTFIELD_SIZE >> 4, MAX_SCATTERING_ORDER ); m_Tex_LobeHistogram_Transmitted_Decimal.RemoveFromLastAssignedSlotUAV(); m_Tex_LobeHistogram_Transmitted_Integer.RemoveFromLastAssignedSlotUAV(); m_Tex_OutgoingDirections_Transmitted.RemoveFromLastAssignedSlots(); } break; case SURFACE_TYPE.DIFFUSE: if ( m_Shader_RayTraceSurface_Diffuse.Use() ) { // Update trace offset m_CB_RayTrace.m._Offset.Set( (float) sequence[iterationIndex,0], (float) sequence[iterationIndex,1] ); m_CB_RayTrace.UpdateData(); m_Device.Clear( m_Tex_OutgoingDirections_Reflected, float4.Zero ); // Clear target directions and weights m_Tex_Heightfield.SetCS( 0 ); m_Tex_Random.SetCS( 1 ); m_Tex_OutgoingDirections_Reflected.SetCSUAV( 0 ); // New target buffer where to accumulate m_Shader_RayTraceSurface_Diffuse.Dispatch( HEIGHTFIELD_SIZE >> 4, HEIGHTFIELD_SIZE >> 4, 1 ); m_Tex_OutgoingDirections_Reflected.RemoveFromLastAssignedSlotUAV(); } // 2] Accumulate into target histogram if ( m_Shader_AccumulateOutgoingDirections.Use() ) { m_Tex_OutgoingDirections_Reflected.SetCS( 0 ); m_Tex_LobeHistogram_Reflected_Decimal.SetCSUAV( 0 ); m_Tex_LobeHistogram_Reflected_Integer.SetCSUAV( 1 ); m_Shader_AccumulateOutgoingDirections.Dispatch( HEIGHTFIELD_SIZE >> 4, HEIGHTFIELD_SIZE >> 4, MAX_SCATTERING_ORDER ); m_Tex_LobeHistogram_Reflected_Decimal.RemoveFromLastAssignedSlotUAV(); m_Tex_LobeHistogram_Reflected_Integer.RemoveFromLastAssignedSlotUAV(); m_Tex_OutgoingDirections_Reflected.RemoveFromLastAssignedSlots(); } break; default: throw new Exception( "Not implemented!" ); } } // 3] Finalize if ( m_Shader_FinalizeOutgoingDirections.Use() ) { m_Tex_LobeHistogram_Reflected_Decimal.SetCSUAV( 0 ); m_Tex_LobeHistogram_Reflected_Integer.SetCSUAV( 1 ); m_Tex_LobeHistogram_Reflected.SetCSUAV( 2 ); m_CB_Finalize.m._IterationsCount = (uint) _iterationsCount; m_CB_Finalize.UpdateData(); m_Shader_FinalizeOutgoingDirections.Dispatch( (LOBES_COUNT_PHI + 15) >> 4, (LOBES_COUNT_THETA + 15) >> 4, MAX_SCATTERING_ORDER ); m_Tex_LobeHistogram_Reflected_Decimal.RemoveFromLastAssignedSlotUAV(); m_Tex_LobeHistogram_Reflected_Integer.RemoveFromLastAssignedSlotUAV(); m_Tex_LobeHistogram_Reflected.RemoveFromLastAssignedSlotUAV(); if ( m_lastComputedSurfaceType == SURFACE_TYPE.DIELECTRIC ) { // Finalize transmitted m_Tex_LobeHistogram_Transmitted_Decimal.SetCSUAV( 0 ); m_Tex_LobeHistogram_Transmitted_Integer.SetCSUAV( 1 ); m_Tex_LobeHistogram_Transmitted.SetCSUAV( 2 ); m_Shader_FinalizeOutgoingDirections.Dispatch( (LOBES_COUNT_PHI + 15) >> 4, (LOBES_COUNT_THETA + 15) >> 4, MAX_SCATTERING_ORDER ); m_Tex_LobeHistogram_Transmitted_Decimal.RemoveFromLastAssignedSlotUAV(); m_Tex_LobeHistogram_Transmitted_Integer.RemoveFromLastAssignedSlotUAV(); m_Tex_LobeHistogram_Transmitted.RemoveFromLastAssignedSlotUAV(); } else { m_Device.Clear( m_Tex_LobeHistogram_Transmitted, float4.Zero ); } } }
void ComputeBRDFIntegralImportanceSampling( System.IO.FileInfo _TableFileName0, System.IO.FileInfo _TableFileName1, int _TableSize ) { const int SAMPLES_COUNT = 1024; double[,] Table0 = new double[_TableSize,_TableSize]; double[,] Table1 = new double[_TableSize,_TableSize]; WMath.Hammersley QRNG = new WMath.Hammersley(); double[,] Sequence = QRNG.BuildSequence( SAMPLES_COUNT, 2 ); float3 View = new float3(); float3 Light = new float3(); float3 Half = new float3(); for ( int Y=0; Y < _TableSize; Y++ ) { double Roughness = Math.Max( 0.01f, (float) Y / (_TableSize-1) ); //Roughness = Math.Pow( 1.0 - Roughness, 4.0 ); double r2 = Roughness*Roughness; for ( int X=0; X < _TableSize; X++ ) { float CosThetaView = (float) (1+X) / _TableSize; float SinThetaView = (float) Math.Sqrt( 1.0f - CosThetaView*CosThetaView ); View.x = SinThetaView; View.y = CosThetaView; View.z = 0.0f; double SumA = 0.0; double SumB = 0.0; for ( int SampleIndex=0; SampleIndex < SAMPLES_COUNT; SampleIndex++ ) { double X0 = Sequence[SampleIndex,0]; double X1 = Sequence[SampleIndex,1]; double PhiH = 2.0 * Math.PI * X0; // WARD double ThetaH = Math.Atan( -r2 * Math.Log( 1.0 - X1 ) ); double CosThetaH = Math.Cos( ThetaH ); double SinThetaH = Math.Sin( ThetaH ); // // GGX // double a = r2; // double CosThetaH = Math.Sqrt( (1.0 - X1) / (1.0 + (a*a - 1.0) * X1 ) ); // double SinThetaH = Math.Sqrt( 1.0f - CosThetaH * CosThetaH ); double CosPhiH = Math.Cos( PhiH ); double SinPhiH = Math.Sin( PhiH ); Half.x = (float) (SinPhiH * SinThetaH); Half.y = (float) CosThetaH; Half.z = (float) (CosPhiH * SinThetaH); Light = 2.0f * View.Dot( Half ) * Half - View; // Light is on the other size of the Half vector... // Intuitively, we should have the same result if we sampled around the reflected view direction // float3 ReflectedView = 2.0f * View.Dot( float3.UnitY ) * float3.UnitY - View; // float3 OrthoY = ReflectedView.Cross( float3.UnitZ ).Normalized; // float3 OrthoX = float3.UnitZ; // Light = Half.x * OrthoX + Half.y * ReflectedView + Half.z * OrthoY; // // Half = (View + Light).Normalized; if ( Light.y <= 0 ) continue; double HoN = Half.y; double HoN2 = HoN*HoN; double HoV = Half.Dot( View ); // float HoV = Half.x * View.x + Half.y * View.y; // We know that Z=0 here... double HoL = Half.Dot( Light ); double NoL = Light.y; double NoV = View.y; // Apply sampling weight for correct distribution double SampleWeight = 2.0 / (1.0 + View.y / Light.y); double BRDF = SampleWeight; // Try with Unreal's GGX & Smith G term to see if we get the same thing // // // GGX NDF // // double alpha = r2; // // double alpha2 = alpha*alpha; // // double D = alpha2 / (Math.PI * Math.Pow( HoN2*(alpha2 - 1.0) + 1.0, 2.0 )); // // // Smith masking/shadowing // double k = (Roughness + 1)*(Roughness + 1) / 8.0; // double Gl = NoL / (NoL * (1-k) + k); // double Gv = NoV / (NoV * (1-k) + k); // double G = Gl * Gv; // // //double BRDF = G / (4.0 * View.y); // //double BRDF = G * HoV / (HoN * NoV); // double BRDF = NoL * GSmith( Roughness, NoV, NoL ) * 4.0f * HoV / HoN; // Compute Fresnel terms double Schlick = 1.0 - HoV; double Schlick5 = Schlick * Schlick; Schlick5 *= Schlick5 * Schlick; double FresnelA = 1.0f - Schlick5; double FresnelB = Schlick5; //FresnelA = FresnelB = 1.0; SumA += FresnelA * BRDF; SumB += FresnelB * BRDF; } // SumA *= 1.0 / (SAMPLES_COUNT * Math.PI * r2); // SumB *= 1.0 / (SAMPLES_COUNT * Math.PI * r2); SumA /= SAMPLES_COUNT; SumB /= SAMPLES_COUNT; // For few samples, the sum goes over 1 because we have poor solid angle sampling resolution... // SumA = Math.Min( 1.0, SumA ); // SumB = Math.Min( 1.0, SumB ); Table0[X,Y] = SumA; Table1[X,Y] = SumB; } } // Write table 0 using ( System.IO.FileStream S = _TableFileName0.Create() ) using ( System.IO.BinaryWriter W = new System.IO.BinaryWriter( S ) ) for ( int Y=0; Y < _TableSize; Y++ ) { for ( int X=0; X < _TableSize; X++ ) W.Write( Table0[X,Y] ); } // Write table 1 using ( System.IO.FileStream S = _TableFileName1.Create() ) using ( System.IO.BinaryWriter W = new System.IO.BinaryWriter( S ) ) for ( int Y=0; Y < _TableSize; Y++ ) { for ( int X=0; X < _TableSize; X++ ) W.Write( Table1[X,Y] ); } }
/// <summary> /// Following the mathematica notebook found in "D:\Docs\Computer Graphics\Volumetric, Clouds, Participating Medium, Light Scattering, Translucency\2005 Sun, Ramamoorthi.nb" /// this routine generates the tables representing the integral of function Gn depending on 2 parameters. /// Several of these tables are generated for different exponents n, the goal is then to find an approximation to generated these tables from an analytical expression. /// </summary> void BuildSurfaceRadianceIntegrals() { BuildPreciseF(); // string ResultsPath = @"D:\Docs\Computer Graphics\Volumetric, Clouds, Participating Medium, Light Scattering, Translucency"; string ResultsPath = @".\"; const int TABLE_SIZE = 64; const int IMPORTANCE_SAMPLES_COUNT = 4096; double[,] QRNG = new WMath.Hammersley().BuildSequence( IMPORTANCE_SAMPLES_COUNT, 2 ); for ( int TableIndex=0; TableIndex < 1; TableIndex++ ) { float Exponent = 1.0f + TableIndex; double[,] Table = new double[TABLE_SIZE,TABLE_SIZE]; // Compute the integral for each parameter for ( int Y=0; Y < TABLE_SIZE; Y++ ) { double Theta_s = 0.5 * Math.PI * Y / (TABLE_SIZE-1); // V in [0,PI/2] double CosTheta_s = Math.Sin(Theta_s); double SinTheta_s = Math.Cos(Theta_s); for ( int X=0; X < TABLE_SIZE; X++ ) { double Tsp = MAX_U * X / (TABLE_SIZE-1); // U in [0,10] // Use importance sampling double Sum = 0.0; for ( int SampleIndex=0; SampleIndex < IMPORTANCE_SAMPLES_COUNT; SampleIndex++ ) { double X0 = QRNG[SampleIndex,0]; double X1 = QRNG[SampleIndex,1]; double Phi_i = 2.0 * Math.PI * X0; double Theta_i = Math.Asin( Math.Pow( X1, 1.0 / (1.0 + Exponent) ) ); // Assuming we're integrating a cos^n(theta) double CosGamma = Math.Cos( Theta_i )*Math.Cos( Theta_s ) + Math.Sin( Theta_i )*Math.Sin( Theta_s )*Math.Cos( Phi_i ); // Angle between the incoming ray and the source double SinGamma = Math.Sqrt( 1 - CosGamma*CosGamma ); double Gamma = Math.Acos( CosGamma ); // double x0 = Math.Sin(Theta_i) * Math.Sin( Phi_i ); // double y0 = Math.Cos(Theta_i); // double z0 = Math.Sin(Theta_i) * Math.Cos( Phi_i ); // double x1 = SinTheta_s; // double y1 = CosTheta_s; // Gamma = Math.Acos( x0*x1 + y0*y1 ); // // double CosGamma = Math.Cos( Gamma ); // double SinGamma = Math.Sin( Gamma ); double Extinction = Math.Exp( -Tsp * CosGamma ); double DeltaF = F_Table( Tsp * SinGamma, HALF_PI ) - F_Table( Tsp * SinGamma, 0.5 * Gamma ); double Term = (Extinction / SinGamma) * DeltaF; if ( double.IsNaN( Term ) ) throw new Exception(); Sum += Term; } Sum /= IMPORTANCE_SAMPLES_COUNT; /* // Use standard numerical integration const double dPhi = 2.0 * Math.PI / 160; const double dTheta = 0.5 * Math.PI / 40; double Sum = 0.0; for ( int ThetaIndex=0; ThetaIndex < 40; ThetaIndex++ ) { double Theta_i = 0.5 * Math.PI * (0.5+ThetaIndex) / 40; double SolidAngle = Math.Sin( Theta_i ) * dPhi * dTheta; for ( int PhiIndex=0; PhiIndex < 160; PhiIndex++ ) { double Phi_i = Math.PI * PhiIndex / 160.0; double CosGamma = Math.Cos( Theta_i )*CosTheta_s + Math.Sin( Theta_i )*SinTheta_s*Math.Cos( Phi_i ); // Angle between the incoming ray and the source // double x0 = Math.Sin(Theta_i)*Math.Cos(Phi_i); // double y0 = Math.Cos(Theta_i); // double z0 = Math.Sin(Theta_i)*Math.Sin(Phi_i); // double x1 = SinTheta_s; // double y1 = CosTheta_s; // double CosGamma = x0*x1 + y0*y1; double SinGamma = Math.Sqrt( 1 - CosGamma*CosGamma ); double Gamma = Math.Acos( CosGamma ); double Extinction = Math.Exp( -Tsp * CosGamma ); double DeltaF = F_Table( Tsp * SinGamma, HALF_PI ) - F_Table( Tsp * SinGamma, 0.5 * Gamma ); double Term = (Extinction / SinGamma) * DeltaF; if ( double.IsNaN( Term ) ) throw new Exception(); Sum += Term * Math.Pow( Math.Cos( Theta_i ), Exponent ) * SolidAngle; } } */ Table[X,Y] = Sum; } } // Write the result FileInfo TargetFile = new FileInfo( Path.Combine( ResultsPath, "TableG"+TableIndex+".double" ) ); using ( FileStream S = TargetFile.Create() ) using ( BinaryWriter W = new BinaryWriter( S ) ) { for ( int Y=0; Y < TABLE_SIZE; Y++ ) for ( int X=0; X < TABLE_SIZE; X++ ) W.Write( Table[X,Y] ); } } }