/// <summary> /// Returns a value in [0,_maxValue[ /// </summary> /// <param name="_maxValue"></param> /// <returns></returns> uint GetUniformInt(uint _maxValue) { ulong value = (ulong)_maxValue * (ulong)SimpleRNG.GetUint(); value >>= 32; return((uint)value); }
/// <summary> /// Randomly fills bottom states /// </summary> public void Init() { for (int X = 0; X < m_size; X++) { for (int Z = 0; Z < m_size; Z++) { m_grid[X, m_size - 1, Z] = (byte)(1 + (SimpleRNG.GetUint() & 1)); } } }
/// <summary> /// Generates blue noise distribution by randomly swapping pixels in the texture to reach lowest possible score and minimize a specific energy function /// </summary> /// <param name="_randomSeed"></param> /// <param name="_maxIterations">The maximum amount of iterations before exiting with the last best solution</param> /// <param name="_standardDeviationImage">Standard deviation for image space. If not sure, use 2.1</param> /// <param name="_standardDeviationValue">Standard deviation for value space. If not sure, use 1.0</param> /// <param name="_neighborsOnlyMutations">True to only authorize mutations of neighbor pixels, false to randomly mutate any pixel</param> /// <param name="_notifyProgressEveryNIterations">Will read back the GPU texture to the CPU and notify of progress every N iterations</param> /// <param name="_progress"></param> public void Generate(uint _randomSeed, uint _maxIterations, float _standardDeviationImage, float _standardDeviationValue, bool _neighborsOnlyMutations, uint _notifyProgressEveryNIterations, ProgressDelegate _progress) { m_CB_Main.m._texturePOT = (uint)m_texturePOT; m_CB_Main.m._textureSize = m_textureSize; m_CB_Main.m._textureMask = m_textureSizeMask; m_CB_Main.m._kernelFactorSpatial = -1.0f / (_standardDeviationImage * _standardDeviationImage); m_CB_Main.m._kernelFactorValue = -1.0f / (_standardDeviationValue * _standardDeviationValue); m_CB_Main.UpdateData(); ////////////////////////////////////////////////////////////////////////// // Generate initial white noise { SimpleRNG.SetSeed(_randomSeed, 362436069U); switch (m_vectorDimension) { case 1: { // Build ordered initial values float[,] initialValues = new float[m_textureSize, m_textureSize]; for (uint Y = 0; Y < m_textureSize; Y++) { for (uint X = 0; X < m_textureSize; X++) { initialValues[X, Y] = (float)(m_textureSize * Y + X) / m_textureTotalSize; } } // Displace them randomly for (uint i = 0; i < m_textureTotalSize; i++) { uint startX = GetUniformInt(m_textureSize); uint startY = GetUniformInt(m_textureSize); uint endX = GetUniformInt(m_textureSize); uint endY = GetUniformInt(m_textureSize); float temp = initialValues[startX, startY]; initialValues[startX, startY] = initialValues[endX, endY]; initialValues[endX, endY] = temp; } m_texNoiseCPU.WritePixels(0, 0, (uint _X, uint _Y, System.IO.BinaryWriter _W) => { _W.Write(initialValues[_X, _Y]); }); break; } case 2: { // Build ordered initial values float2[,] initialValues = new float2[m_textureSize, m_textureSize]; for (uint Y = 0; Y < m_textureSize; Y++) { for (uint X = 0; X < m_textureSize; X++) { initialValues[X, Y].Set((float)(m_textureSize * Y + X) / m_textureTotalSize, (float)(m_textureSize * Y + X) / m_textureTotalSize); } } // Displace them randomly for (uint i = 0; i < m_textureTotalSize; i++) { uint startX = GetUniformInt(m_textureSize); uint startY = GetUniformInt(m_textureSize); uint endX = GetUniformInt(m_textureSize); uint endY = GetUniformInt(m_textureSize); float temp = initialValues[startX, startY].x; initialValues[startX, startY].x = initialValues[endX, endY].x; initialValues[endX, endY].x = temp; startX = GetUniformInt(m_textureSize); startY = GetUniformInt(m_textureSize); endX = GetUniformInt(m_textureSize); endY = GetUniformInt(m_textureSize); temp = initialValues[startX, startY].y; initialValues[startX, startY].y = initialValues[endX, endY].y; initialValues[endX, endY].y = temp; } m_texNoiseCPU.WritePixels(0, 0, (uint _X, uint _Y, System.IO.BinaryWriter _W) => { _W.Write(initialValues[_X, _Y].x); _W.Write(initialValues[_X, _Y].y); }); break; } } m_texNoise0.CopyFrom(m_texNoiseCPU); } ////////////////////////////////////////////////////////////////////////// // Perform iterations float bestScore = ComputeScore(m_texNoise0); float score = bestScore; uint iterationIndex = 0; uint mutationsRate = MAX_MUTATIONS_RATE; int iterationsCountWithoutImprovement = 0; #if !CAILLOU float maxIterationsCountWithoutImprovementBeforeDecreasingMutationsCount = 0.1f * m_textureTotalSize; // Arbitrary: 10% of the texture size #else float maxIterationsCountWithoutImprovementBeforeDecreasingMutationsCount = 0.01f * m_textureTotalSize; // Arbitrary: 1% of the texture size #endif // float maxIterationsCountWithoutImprovementBeforeDecreasingMutationsCount = 0.002f * m_textureTotalSize; // Arbitrary: 0.2% of the texture size float averageIterationsCountWithoutImprovement = 0.0f; float alpha = 0.001f; uint[] neighborOffsetX = new uint[8] { 0, 1, 2, 2, 2, 1, 0, 0 }; uint[] neighborOffsetY = new uint[8] { 0, 0, 0, 1, 2, 2, 2, 1 }; List <float> statistics = new List <float>(); //ReadBackScoreTexture( m_texNoiseScore2, textureCPU ); while (iterationIndex < _maxIterations) { ////////////////////////////////////////////////////////////////////////// // Copy if (m_CS_Copy.Use()) { m_texNoise0.SetCS(0); m_texNoise1.SetCSUAV(0); uint groupsCount = m_textureSize >> 4; m_CS_Copy.Dispatch(groupsCount, groupsCount, 1); } ////////////////////////////////////////////////////////////////////////// // Mutate current solution by swapping up to N pixels randomly if (m_CS_Mutate.Use()) { // Fill up mutations buffer if (_neighborsOnlyMutations) { // Swap neighbor pixels only for (int mutationIndex = 0; mutationIndex < mutationsRate; mutationIndex++) { uint sourceIndex = GetUniformInt(m_textureTotalSize); uint X, Y; ComputeXYFromSingleIndex(sourceIndex, out X, out Y); // Randomly pick one of the 8 neighbors uint neighborIndex = SimpleRNG.GetUint() & 0x7; uint Xn = (X + m_textureSizeMask + neighborOffsetX[neighborIndex]) & m_textureSizeMask; uint Yn = (Y + m_textureSizeMask + neighborOffsetY[neighborIndex]) & m_textureSizeMask; m_SB_Mutations.m[mutationIndex]._pixelSourceX = X; m_SB_Mutations.m[mutationIndex]._pixelSourceY = Y; m_SB_Mutations.m[mutationIndex]._pixelTargetX = Xn; m_SB_Mutations.m[mutationIndex]._pixelTargetY = Yn; if (m_vectorDimension > 1) { m_SB_Mutations.m[mutationIndex]._pixelTargetY |= SimpleRNG.GetUniform() > 0.5 ? 0x80000000U : 0x40000000U; } } } else { // Swap pixels randomly for (int mutationIndex = 0; mutationIndex < mutationsRate; mutationIndex++) { uint sourceIndex = GetUniformInt(m_textureTotalSize); uint targetIndex = GetUniformInt(m_textureTotalSize); ComputeXYFromSingleIndex(sourceIndex, out m_SB_Mutations.m[mutationIndex]._pixelSourceX, out m_SB_Mutations.m[mutationIndex]._pixelSourceY); ComputeXYFromSingleIndex(targetIndex, out m_SB_Mutations.m[mutationIndex]._pixelTargetX, out m_SB_Mutations.m[mutationIndex]._pixelTargetY); if (m_vectorDimension > 1) { m_SB_Mutations.m[mutationIndex]._pixelTargetY |= SimpleRNG.GetUniform() > 0.5 ? 0x80000000U : 0x40000000U; } } } m_SB_Mutations.Write(mutationsRate); m_SB_Mutations.SetInput(1); m_CS_Mutate.Dispatch(mutationsRate, 1, 1); m_texNoise0.RemoveFromLastAssignedSlots(); m_texNoise1.RemoveFromLastAssignedSlotUAV(); } ////////////////////////////////////////////////////////////////////////// // Compute new score float previousScore = score; score = ComputeScore(m_texNoise1); if (score < bestScore) { // New best score! Swap textures so we accept the new state... bestScore = score; Texture2D temp = m_texNoise0; m_texNoise0 = m_texNoise1; m_texNoise1 = temp; iterationsCountWithoutImprovement = 0; } else { iterationsCountWithoutImprovement++; } averageIterationsCountWithoutImprovement *= 1.0f - alpha; averageIterationsCountWithoutImprovement += alpha * iterationsCountWithoutImprovement; if (averageIterationsCountWithoutImprovement > maxIterationsCountWithoutImprovementBeforeDecreasingMutationsCount) { averageIterationsCountWithoutImprovement = 0.0f; // Start over... mutationsRate >>= 1; // Halve mutations count if (mutationsRate == 0) { break; // Clearly we've reached a steady state here... } } //statistics.Add( averageIterationsCountWithoutImprovement ); ////////////////////////////////////////////////////////////////////////// // Notify iterationIndex++; if (_progress == null || (iterationIndex % _notifyProgressEveryNIterations) != 1) { continue; } // _progress( iterationIndex, mutationsCount, bestScore, ReadBackScoreTexture( m_texNoiseScore ), statistics ); // Notify! switch (m_vectorDimension) { case 1: _progress(iterationIndex, mutationsRate, bestScore, ReadBackTexture1D(m_texNoise0), statistics); break; // Notify! case 2: _progress(iterationIndex, mutationsRate, bestScore, ReadBackTexture2D(m_texNoise0), statistics); break; // Notify! } } // One final call with our best final result switch (m_vectorDimension) { case 1: ReadBackTexture1D(m_texNoise0); break; case 2: ReadBackTexture2D(m_texNoise0); break; } if (_progress != null) { // _progress( iterationIndex, mutationsCount, bestScore, ReadBackScoreTexture( m_texNoiseScore ), statistics ); // Notify! switch (m_vectorDimension) { case 1: _progress(iterationIndex, mutationsRate, bestScore, ReadBackTexture1D(m_texNoise0), statistics); break; // Notify! case 2: _progress(iterationIndex, mutationsRate, bestScore, ReadBackTexture2D(m_texNoise0), statistics); break; // Notify! } } }
protected override void OnLoad(EventArgs e) { base.OnLoad(e); try { m_device.Init(panelOutput.Handle, false, true); } catch (Exception _e) { m_device = null; MessageBox.Show("Failed to initialize DX device!\n\n" + _e.Message, "MSBRDF Test", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } try { // m_shader_Render = new Shader( m_device, new System.IO.FileInfo( "Shaders/Render.hlsl" ), VERTEX_FORMAT.Pt4, "VS", null, "PS", null ); // OBSOLETE MSBRDF CODE! For historical purpose only... m_shader_Finalize = new Shader(m_device, new System.IO.FileInfo("Shaders/RenderComplete.hlsl"), VERTEX_FORMAT.Pt4, "VS", null, "PS_Finalize", null); #if TEST_SH_ENVIRONMENT m_shader_Accumulate = new Shader(m_device, new System.IO.FileInfo("Shaders/RenderCompareSH.hlsl"), VERTEX_FORMAT.Pt4, "VS", null, "PS", null); // Use this to show a rendering with SH environment checkBoxUseRealTimeApprox.Visible = true; checkBoxUseRealTimeApprox.Checked = true; floatTrackbarControlRoughnessSphere.Value = 1; // Show full roughness floatTrackbarControlReflectanceSphere2.Value = 0; // Disturbing if diffuse is showing! groupBoxPlane.Visible = false; // No plane is available in this configuration #elif TEST_LTC_AREA_LIGHT m_shader_Accumulate = new Shader(m_device, new System.IO.FileInfo("Shaders/RenderCompareLTC.hlsl"), VERTEX_FORMAT.Pt4, "VS", null, "PS", null); // Use this to show a rendering with LTC area light checkBoxUseRealTimeApprox.Visible = true; // checkBoxUseRealTimeApprox.Checked = true; checkBoxUseRealTimeApprox.Checked = false; // floatTrackbarControlRoughnessSphere.Value = 1; // Show full roughness // floatTrackbarControlReflectanceSphere2.Value = 0; // Disturbing if diffuse is showing! checkBoxUseLTC.Visible = true; floatTrackbarControlRoughnessSphere.Value = 0.25f; floatTrackbarControlReflectanceSphere.Value = 0.04f; floatTrackbarControlRoughnessSphere2.Value = 0.80f; floatTrackbarControlReflectanceSphere2.Value = 0.5f; floatTrackbarControlRoughnessGround.Value = 0.85f; floatTrackbarControlReflectanceGround.Value = 0.35f; floatTrackbarControlLightElevation.Value = 0.5f; #else m_shader_Accumulate = new Shader(m_device, new System.IO.FileInfo("Shaders/RenderComplete.hlsl"), VERTEX_FORMAT.Pt4, "VS", null, "PS", null); // Use this for a full render #endif } catch (Exception _e) { MessageBox.Show("Shader failed to compile!\n\n" + _e.Message, "MSBRDF Test", MessageBoxButtons.OK, MessageBoxIcon.Error); } uint W = (uint)panelOutput.Width; uint H = (uint)panelOutput.Height; m_CB_Global = new ConstantBuffer <CB_Global>(m_device, 0); m_CB_Camera = new ConstantBuffer <CB_Camera>(m_device, 1); m_CB_Render = new ConstantBuffer <CB_Render>(m_device, 2); m_CB_SH = new ConstantBuffer <CB_SH>(m_device, 3); BuildNoiseTextures(); // Shuffle group indices for (uint groupIndex = 0; groupIndex < GROUPS_COUNT; groupIndex++) { m_groupShuffle[groupIndex] = groupIndex; } for (uint shuffleIndex = 100 * GROUPS_COUNT; shuffleIndex > 0; shuffleIndex--) { for (uint groupIndex = 0; groupIndex < GROUPS_COUNT; groupIndex++) { uint i0 = SimpleRNG.GetUint() % GROUPS_COUNT; uint i1 = SimpleRNG.GetUint() % GROUPS_COUNT; uint temp = m_groupShuffle[i0]; m_groupShuffle[i0] = m_groupShuffle[i1]; m_groupShuffle[i1] = temp; } } // Tables are "built" with Mathematica now // BuildMSBRDF( new DirectoryInfo( @".\Tables\" ) ); LoadMSBRDF(128, new FileInfo("./Tables/MSBRDF_GGX_G2_E128x128.float"), new FileInfo("./Tables/MSBRDF_GGX_G2_Eavg128.float"), out m_tex_MSBRDF_GGX_E, out m_tex_MSBRDF_GGX_Eavg); LoadMSBRDF(32, new FileInfo("./Tables/MSBRDF_OrenNayar_E32x32.float"), new FileInfo("./Tables/MSBRDF_OrenNayar_Eavg32.float"), out m_tex_MSBRDF_OrenNayar_E, out m_tex_MSBRDF_OrenNayar_Eavg); #if TEST_LTC_AREA_LIGHT // Area light m_tex_LTC = LoadLTC(new FileInfo(@".\Tables\LTC.dds")); m_tex_LTC_Unity = LoadUnityLTC(); #endif // Load cube map using (ImageUtility.ImagesMatrix I = new ImageUtility.ImagesMatrix()) { // I.DDSLoadFile( new FileInfo( "garage4_hd.dds" ) ); I.DDSLoadFile(new FileInfo("beach.dds")); EncodeCubeMapIntoSH(I); m_tex_CubeMap = new Texture2D(m_device, I, ImageUtility.COMPONENT_FORMAT.AUTO); } m_tex_Accumulator = new Texture2D(m_device, m_device.DefaultTarget.Width, m_device.DefaultTarget.Height, 1, 1, ImageUtility.PIXEL_FORMAT.RGBA32F, ImageUtility.COMPONENT_FORMAT.AUTO, false, true, null); // Setup camera m_camera.CreatePerspectiveCamera((float)(60.0 * Math.PI / 180.0), (float)panelOutput.Width / panelOutput.Height, 0.01f, 100.0f); m_manipulator.Attach(panelOutput, m_camera); // m_manipulator.InitializeCamera( new float3( 0, 1.5f, 2.0f ), new float3( -0.4f, 0, 0.4f ), float3.UnitY ); // Garage probe m_manipulator.InitializeCamera(new float3(1.46070266f, 1.10467184f, 1.36212754f), new float3(0, 1, 0), float3.UnitY); // Beach probe m_camera.CameraTransformChanged += Camera_CameraTransformChanged; Camera_CameraTransformChanged(null, EventArgs.Empty); // Start game time m_Ticks2Seconds = 1.0 / System.Diagnostics.Stopwatch.Frequency; m_StopWatch.Start(); m_StartGameTime = GetGameTime(); }