void BuildNoiseTextures() { PixelsBuffer Content = new PixelsBuffer(NOISE_SIZE * NOISE_SIZE * NOISE_SIZE * 4); PixelsBuffer Content4D = new PixelsBuffer(NOISE_SIZE * NOISE_SIZE * NOISE_SIZE * 16); SimpleRNG.SetSeed(521288629, 362436069); float4 V = float4.Zero; using (BinaryWriter W = Content.OpenStreamWrite()) { using (BinaryWriter W2 = Content4D.OpenStreamWrite()) { for (int Z = 0; Z < NOISE_SIZE; Z++) { for (int Y = 0; Y < NOISE_SIZE; Y++) { for (int X = 0; X < NOISE_SIZE; X++) { V.Set((float)SimpleRNG.GetUniform(), (float)SimpleRNG.GetUniform(), (float)SimpleRNG.GetUniform(), (float)SimpleRNG.GetUniform()); W.Write(V.x); W2.Write(V.x); W2.Write(V.y); W2.Write(V.z); W2.Write(V.w); } } } } } m_Tex_Noise = new Texture3D(m_Device, NOISE_SIZE, NOISE_SIZE, NOISE_SIZE, 1, ImageUtility.PIXEL_FORMAT.R8, ImageUtility.COMPONENT_FORMAT.UNORM, false, false, new PixelsBuffer[] { Content }); m_Tex_Noise4D = new Texture3D(m_Device, NOISE_SIZE, NOISE_SIZE, NOISE_SIZE, 1, ImageUtility.PIXEL_FORMAT.RGBA8, ImageUtility.COMPONENT_FORMAT.UNORM, false, false, new PixelsBuffer[] { Content4D }); }
/// <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="_minEnergyThreshold"></param> /// <param name="_maxIterations"></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="_progress"></param> public void Generate(uint _randomSeed, float _minEnergyThreshold, int _maxIterations, float _standardDeviationImage, float _standardDeviationValue, ProgressDelegate _progress) { m_kernelFactorImage = -1.0 / (_standardDeviationImage * _standardDeviationImage); m_kernelFactorValue = -1.0 / (_standardDeviationValue * _standardDeviationValue); // Generate initial white noise SimpleRNG.SetSeed(_randomSeed); for (int Y = 0; Y < m_textureSize; Y++) { for (int X = 0; X < m_textureSize; X++) { m_textures[0][X, Y] = (float)SimpleRNG.GetUniform(); } } // Perform iterations float bestScore = ComputeScore(m_textures[0]); int iterationIndex = 0; while (iterationIndex < _maxIterations && bestScore > _minEnergyThreshold) { // Copy source to target array Array.Copy(m_textures[0], m_textures[1], m_textureTotalSize); // Swap up to N pixels randomly for (int swapCount = 0; swapCount < MAX_SWAPPED_ELEMENTS_PER_ITERATION; swapCount++) { uint sourceIndex = GetUniformInt(m_textureTotalSize); uint targetIndex = sourceIndex; while (targetIndex == sourceIndex) { targetIndex = GetUniformInt(m_textureTotalSize); // Make sure target index differs! } float temp = Get(m_textures[1], sourceIndex); Set(m_textures[1], sourceIndex, Get(m_textures[1], targetIndex)); Set(m_textures[1], targetIndex, temp); } // Compute new score float score = ComputeScore(m_textures[1]); if (score < bestScore) { // New best score! Swap textures... bestScore = score; float[,] temp = m_textures[0]; m_textures[0] = m_textures[1]; m_textures[1] = temp; } iterationIndex++; if (_progress != null) { _progress(iterationIndex, bestScore, m_textures[0]); // Notify! } } }
public GA(int seed, int popsize, int offsize, double deathRate) { _seed = seed; SimpleRNG.SetSeed((uint)_seed, (uint)_seed + 1); _popsize = popsize; _offsize = offsize; _deathRate = deathRate; population = new ScheduleGenome[_popsize]; offspring = new ScheduleGenome[_offsize]; // Default operator options: realCrossover = RealCrossoverOp.MeanWithNoise; survivalSelection = SurvivalSelectionOp.Elitist; parentSelection = ParentSelectionOp.Tournament; }
public void UpdateStonesAtPatch(IntVector2 patchCoord, Transform patchHolder) { // reset SimpleRNG using cactus seed SimpleRNG.SetSeed((uint)(stoneSeed + patchCoord.x + patchCoord.y * terrainManager.worldPatchesX)); // set RNG seed depending on stone seed, world seed, and patch coordinates stoneRNG = new System.Random(stoneSeed + patchCoord.x + patchCoord.y * terrainManager.worldPatchesX); // in plain terrain float stonePlainDensityHere = stonePlainDensity.min + (float)stoneRNG.NextDouble() * (stonePlainDensity.max - stonePlainDensity.min); for (int i = 0; i < stonePlainDensityHere; i++) { Vector3 point = loadSceneryAndLore.GetValidRandomPositionInPatch(patchCoord, 1f, stoneRNG); float slope = terrainManager.GetSlopeAtPoint(point.x, point.z); //Debug.Log(point + ", " + slope); if (slope > stonePlainSlope.min && slope < stonePlainSlope.max) { GameObject temp = Instantiate(stonePlainPrefabs[stoneRNG.Next(stonePlainPrefabs.Length)], point, Quaternion.identity); temp.transform.localScale = Vector3.one * (stonePlainScale.min + (float)stoneRNG.NextDouble() * (stonePlainScale.max - stonePlainScale.min)); temp.transform.Translate(stonePlainOffset * temp.transform.localScale.x); temp.transform.Rotate(Vector3.up * (float)stoneRNG.NextDouble() * 360f); temp.transform.parent = patchHolder.transform; } } // in cliffs float stoneCliffDensityHere = stoneCliffDensity.min + (float)stoneRNG.NextDouble() * (stoneCliffDensity.max - stoneCliffDensity.min); for (int i = 0; i < stoneCliffDensityHere; i++) { Vector3 point = loadSceneryAndLore.GetValidRandomPositionInPatch(patchCoord, 1f, stoneRNG); float slope = terrainManager.GetSlopeAtPoint(point.x, point.z); if (slope > stoneCliffSlope.min && slope < stoneCliffScale.max) { GameObject temp = Instantiate(stoneCliffPrefabs[stoneRNG.Next(stoneCliffPrefabs.Length)], point, Quaternion.identity); temp.transform.localScale = Vector3.one * (stoneCliffScale.min + (float)stoneRNG.NextDouble() * (stoneCliffScale.max - stoneCliffScale.min)); // re-scale temp.transform.Translate(stoneCliffOffset * temp.transform.localScale.x); // apply downwards offset temp.transform.Rotate(Vector3.up * (float)stoneRNG.NextDouble() * 360f); temp.transform.parent = patchHolder.transform; } } }
public void Initialize() { simpleRng = new SimpleRNG(); animationCurveSampler = new AnimationCurveSampler(probabilityCurve); //Clear LowDiscrepancyList lowDiscrepancyValues.Clear(); //Seed switch (seedType) { case SeedType.GenerateSeed: //generate random seed simpleRng.SetSeedFromSystemTime(); break; case SeedType.CustomSeed: //use custom seed simpleRng.SetSeed(customSeed); break; } }
void BuildNoiseTextures() { PixelsBuffer Content = new PixelsBuffer(NOISE_SIZE * NOISE_SIZE * NOISE_SIZE * 4); PixelsBuffer Content4D = new PixelsBuffer(NOISE_SIZE * NOISE_SIZE * NOISE_SIZE * 16); SimpleRNG.SetSeed(521288629, 362436069); float4 V = float4.Zero; using (BinaryWriter W = Content.OpenStreamWrite()) { using (BinaryWriter W2 = Content4D.OpenStreamWrite()) { for (int Z = 0; Z < NOISE_SIZE; Z++) { for (int Y = 0; Y < NOISE_SIZE; Y++) { for (int X = 0; X < NOISE_SIZE; X++) { V.Set((float)SimpleRNG.GetUniform(), (float)SimpleRNG.GetUniform(), (float)SimpleRNG.GetUniform(), (float)SimpleRNG.GetUniform()); W.Write(V.x); W2.Write(V.x); W2.Write(V.y); W2.Write(V.z); W2.Write(V.w); } } } } } m_tex_Noise = new Texture3D(m_device, NOISE_SIZE, NOISE_SIZE, NOISE_SIZE, 1, ImageUtility.PIXEL_FORMAT.R8, ImageUtility.COMPONENT_FORMAT.UNORM, false, false, new PixelsBuffer[] { Content }); m_tex_Noise4D = new Texture3D(m_device, NOISE_SIZE, NOISE_SIZE, NOISE_SIZE, 1, ImageUtility.PIXEL_FORMAT.RGBA8, ImageUtility.COMPONENT_FORMAT.UNORM, false, false, new PixelsBuffer[] { Content4D }); // Load blue noise using (ImageUtility.ImageFile I = new ImageUtility.ImageFile(new FileInfo("BlueNoise64x64_16bits.png"))) { ImageUtility.ImagesMatrix M = new ImageUtility.ImagesMatrix(new ImageUtility.ImageFile[, ] { { I } }); m_tex_BlueNoise = new Texture2D(m_device, M, ImageUtility.COMPONENT_FORMAT.UNORM); } }
public void UpdateVegetationAtPatch(IntVector2 patchCoord, Transform patchHolder) { // reset SimpleRNG using tree seed SimpleRNG.SetSeed((uint)(treeNoiseSeed + patchCoord.x + patchCoord.y * terrainManager.worldPatchesX)); // set RNG seed depending on tree seed, world seed, and patch coordinates treeRNG = new System.Random(treeNoiseSeed + patchCoord.x + patchCoord.y * terrainManager.worldPatchesX); if (treePrefabs.Length > 0) { // get the number of trees in the patch (vegetation density) int numberOftrees = 0; if (true) { numberOftrees = Mathf.RoundToInt(Mathf.Max(0f, (float)SimpleRNG.GetNormal(treeDensity, treeDensityStdev))); } else { numberOftrees = Mathf.RoundToInt(vegetationNoiseOutsideModel.FractalNoise2D(patchCoord.x, patchCoord.y, 4, treeNoiseFrequency, treeNoiseScale) * treeDensity); } // place trees for (int i = 0; i < numberOftrees; i++) { // scale x and z coordinates for placing prefab prototypes in terrain. Note that this is not needed when placing GameObjects //Vector3 point = terrainManager.GetRandomPointInPatchSurface(patchCoord, treeRNG, scaleToTerrain: true); Vector3 point = loadSceneryAndLore.GetValidRandomPositionInPatch(patchCoord, 1f, treeRNG); float slope = terrainManager.GetSlopeAtPoint(point.x, point.z); if (slope < 0.7f) { //make sure tree are not on cliffs GameObject temp = Instantiate(treePrefabs[Random.Range(0, treePrefabs.Length)], point, Quaternion.identity); //temp.transform.Translate(Vector3.down * 1f); temp.transform.localScale = Vector3.one * (treeScale.min + (float)treeRNG.NextDouble() * (treeScale.max - treeScale.min)); temp.transform.Rotate(Vector3.up * Random.Range(0f, 360f)); temp.transform.parent = patchHolder.transform; // TreeInstance temp = new TreeInstance (); // temp.position = point; // temp.rotation = Random.Range (0f, 360f) * Mathf.Deg2Rad; // temp.prototypeIndex = Random.Range (0, treePrefabs.Length); // temp.color = Color.white; // temp.lightmapColor = Color.white; // // actTerrain.AddTreeInstance (temp); } } } //Debug.Log (terrainData.treeInstanceCount); // reset SimpleRNG using cactus seed SimpleRNG.SetSeed((uint)(cactusNoiseSeed + patchCoord.x + patchCoord.y * terrainManager.worldPatchesX)); // set RNG seed depending on cactus seed, world seed, and patch coordinates cactusRNG = new System.Random(cactusNoiseSeed + patchCoord.x + patchCoord.y * terrainManager.worldPatchesX); if (cactusPrefabs.Length > 0) { // get the number of cactus in the patch (vegetation density) int numberOfCactus = 0; if (true) { numberOfCactus = Mathf.RoundToInt(Mathf.Max((float)SimpleRNG.GetNormal(cactusDensity, cactusDensityStdev))); } else { numberOfCactus = Mathf.RoundToInt(vegetationNoiseOutsideModel.FractalNoise2D(patchCoord.x, patchCoord.y, 4, cactusNoiseFrequency, cactusNoiseScale) * cactusDensity); } // place trees for (int i = 0; i < numberOfCactus; i++) { //Debug.Log(patchCoord.ToString()); // scale x and z coordinates for placing prefab prototypes in terrain. Note that this is not needed when placing GameObjects //Vector3 point = terrainManager.GetRandomPointInPatchSurface(patchCoord, cactusRNG, scaleToTerrain: true); Vector3 point = loadSceneryAndLore.GetValidRandomPositionInPatch(patchCoord, 1f, cactusRNG); // normal diffusion //point = new Vector3(point.x + (float)SimpleRNG.GetNormal(0, .01f), 0f, point.z + (float)SimpleRNG.GetNormal(0, 0.01f)); float slope = terrainManager.GetSlopeAtPoint(point.x, point.z); if (slope < 0.7f) { //make sure cactus are not on cliffs GameObject temp = Instantiate(cactusPrefabs[cactusRNG.Next(0, cactusPrefabs.Length)], point, Quaternion.identity); //temp.transform.Translate(Vector3.down * 0.2f); temp.transform.localScale = Vector3.one * (cactusScale.min + (float)cactusRNG.NextDouble() * (cactusScale.max - cactusScale.min)); temp.transform.Rotate(Vector3.up * Random.Range(0f, 360f)); temp.transform.parent = patchHolder.transform; //TreeInstance temp = new TreeInstance(); //temp.position = point; //temp.rotation = Random.Range(0f, 360f) * Mathf.Deg2Rad; //temp.prototypeIndex = Random.Range(treePrefabs.Length, treePrefabs.Length + cactusPrefabs.Length); //float randomScale = Random.Range(1f, cactusMaxScale); //temp.widthScale = randomScale; //temp.heightScale = randomScale; //temp.color = Color.white; //temp.lightmapColor = Color.white; //actTerrain.AddTreeInstance(temp); } } } }
/// <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 { // Initialize the device m_device = new Device(); m_device.Init(graphPanel.Handle, false, true); // Create the render shaders try { Shader.WarningAsError = false; m_shader_RenderSphere = new Shader(m_device, new System.IO.FileInfo(@"./Shaders/RenderSphere.hlsl"), VERTEX_FORMAT.Pt4, "VS", null, "PS"); m_shader_RenderScene = new Shader(m_device, new System.IO.FileInfo(@"./Shaders/RenderScene.hlsl"), VERTEX_FORMAT.Pt4, "VS", null, "PS"); m_shader_RenderLDR = new Shader(m_device, new System.IO.FileInfo(@"./Shaders/RenderLDR.hlsl"), VERTEX_FORMAT.Pt4, "VS", null, "PS"); } catch (Exception _e) { throw new Exception("Failed to compile shader! " + _e.Message); } // Create CB m_CB_Render = new ConstantBuffer <CB_Main>(m_device, 0); // Create textures LoadHDRImage(); m_Tex_HDRBuffer = new Texture2D(m_device, (uint)graphPanel.Width, (uint)graphPanel.Height, 2, 1, ImageUtility.PIXEL_FORMAT.RGBA32F, ImageUtility.COMPONENT_FORMAT.AUTO, false, false, null); { // Build noise texture SimpleRNG.SetSeed(1U); PixelsBuffer content = new PixelsBuffer(256 * 256 * 16); using (System.IO.BinaryWriter W = content.OpenStreamWrite()) for (int i = 0; i < 256 * 256; i++) { W.Write((float)SimpleRNG.GetUniform()); W.Write((float)SimpleRNG.GetUniform()); W.Write((float)SimpleRNG.GetUniform()); W.Write((float)SimpleRNG.GetUniform()); } m_Tex_Noise = new Texture2D(m_device, 256, 256, 1, 1, ImageUtility.PIXEL_FORMAT.RGBA32F, ImageUtility.COMPONENT_FORMAT.AUTO, false, false, new PixelsBuffer[] { content }); } // Build SH coeffs const int ORDERS = 20; { const int TABLE_SIZE = 64; // Load A coeffs into a texture array float[,,] A = new float[TABLE_SIZE, TABLE_SIZE, ORDERS]; // using ( System.IO.FileStream S = new System.IO.FileInfo( @"ConeTable_cosAO_order20.float" ).OpenRead() ) using (System.IO.FileStream S = new System.IO.FileInfo(@"ConeTable_cosTheta_order20.float").OpenRead()) using (System.IO.BinaryReader R = new System.IO.BinaryReader(S)) { for (int thetaIndex = 0; thetaIndex < TABLE_SIZE; thetaIndex++) { for (int AOIndex = 0; AOIndex < TABLE_SIZE; AOIndex++) { for (int order = 0; order < ORDERS; order++) { A[thetaIndex, AOIndex, order] = R.ReadSingle(); } } } } PixelsBuffer[] coeffSlices = new PixelsBuffer[5]; // 5 slices of 4 coeffs each to get our 20 orders for (int sliceIndex = 0; sliceIndex < coeffSlices.Length; sliceIndex++) { PixelsBuffer coeffSlice = new PixelsBuffer(TABLE_SIZE * TABLE_SIZE * 16); coeffSlices[sliceIndex] = coeffSlice; using (System.IO.BinaryWriter W = coeffSlice.OpenStreamWrite()) { for (int thetaIndex = 0; thetaIndex < TABLE_SIZE; thetaIndex++) { for (int AOIndex = 0; AOIndex < TABLE_SIZE; AOIndex++) { W.Write(A[thetaIndex, AOIndex, 4 * sliceIndex + 0]); W.Write(A[thetaIndex, AOIndex, 4 * sliceIndex + 1]); W.Write(A[thetaIndex, AOIndex, 4 * sliceIndex + 2]); W.Write(A[thetaIndex, AOIndex, 4 * sliceIndex + 3]); } } } } m_Tex_ACoeffs = new Texture2D(m_device, 64, 64, coeffSlices.Length, 1, ImageUtility.PIXEL_FORMAT.RGBA32F, ImageUtility.COMPONENT_FORMAT.AUTO, false, false, coeffSlices); } { // Load environment coeffs into a constant buffer float3[] coeffs = new float3[ORDERS * ORDERS]; using (System.IO.FileStream S = new System.IO.FileInfo(@"Ennis_order20.float3").OpenRead()) using (System.IO.BinaryReader R = new System.IO.BinaryReader(S)) for (int coeffIndex = 0; coeffIndex < ORDERS * ORDERS; coeffIndex++) { coeffs[coeffIndex].Set(R.ReadSingle(), R.ReadSingle(), R.ReadSingle()); } // Write into a raw byte[] byte[] rawContent = new byte[400 * 4 * 4]; using (System.IO.MemoryStream MS = new System.IO.MemoryStream(rawContent)) using (System.IO.BinaryWriter W = new System.IO.BinaryWriter(MS)) { for (int coeffIndex = 0; coeffIndex < ORDERS * ORDERS; coeffIndex++) { W.Write(coeffs[coeffIndex].x); W.Write(coeffs[coeffIndex].y); W.Write(coeffs[coeffIndex].z); W.Write(0.0f); } } m_CB_Coeffs = new RawConstantBuffer(m_device, 1, rawContent.Length); m_CB_Coeffs.UpdateData(rawContent); } // Create camera + manipulator m_camera.CreatePerspectiveCamera(0.5f * (float)Math.PI, (float)graphPanel.Width / graphPanel.Height, 0.01f, 100.0f); m_camera.CameraTransformChanged += m_camera_CameraTransformChanged; m_cameraManipulator.Attach(graphPanel, m_camera); m_cameraManipulator.InitializeCamera(-2.0f * float3.UnitZ, float3.Zero, float3.UnitY); m_camera_CameraTransformChanged(null, EventArgs.Empty); // Start rendering Application.Idle += Application_Idle; } catch (Exception _e) { MessageBox.Show("Failed to initialize D3D renderer!\r\nReason: " + _e.Message); } }
/// <summary> /// Generates blue noise distribution using the void-and-cluster method /// </summary> /// <param name="_randomSeed"></param> /// <param name="_standardDeviation">Standard deviation for gaussian kernel. If not sure, use 2.1</param> /// <param name="_notifyProgressInterval">Will read back the GPU texture to the CPU and notify of progress each time we run for that much progress interval</param> /// <param name="_progress"></param> public void Generate(uint _randomSeed, float _standardDeviation, float _notifyProgressInterval, ProgressDelegate _progress) { if (m_device == null) { // Use the CPU version! GenerateCPU(_randomSeed, _standardDeviation, _notifyProgressInterval, _progress); return; } uint progressNotificationInterval = Math.Max(1, (uint)Math.Ceiling(_notifyProgressInterval * m_textureTotalSize)); 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._kernelFactor = -1.0f / (2.0f * _standardDeviation * _standardDeviation); m_texBinaryPattern.SetCSUAV(0); m_texDitheringArray.SetCSUAV(1); SimpleRNG.SetSeed(_randomSeed, 362436069U); List <float> statistics = new List <float>((int)m_textureTotalSize); for (uint iterationIndex = 0; iterationIndex < m_textureTotalSize; iterationIndex++) { m_CB_Main.m._randomOffsetX = GetUniformInt(m_textureSize); m_CB_Main.m._randomOffsetY = GetUniformInt(m_textureSize); m_CB_Main.m._iterationIndex = iterationIndex; m_CB_Main.UpdateData(); // 1] Filter binary texture and compute "clustering score" if (m_CS_Filter.Use()) { m_texScore0.SetCSUAV(2); uint groupsCount = m_textureSize >> 4; m_CS_Filter.Dispatch(groupsCount, groupsCount, 1); m_texScore0.RemoveFromLastAssignedSlotUAV(); } // 2] Downsample score and keep lowest value and the position where to find it #if !BYPASS_GPU_DOWNSAMPLING if (m_CS_DownsampleScore16.Use()) { uint groupsCount = m_textureSize; uint mipLevelIndex = 0; uint mipLevelsCount = (uint)m_texturePOT; m_CB_Mips.m._textureMipTarget = 0; // 2.1) Downsample by groups of 4 mips while (mipLevelsCount >= 4) { mipLevelIndex += 4; mipLevelsCount -= 4; groupsCount >>= 4; m_texScore0.SetCS(0); m_texScore1.GetView(mipLevelIndex, 1, 0, 1).SetCSUAV(2); m_CB_Mips.m._textureMipSource = m_CB_Mips.m._textureMipTarget; m_CB_Mips.m._textureMipTarget = mipLevelIndex; m_CB_Mips.UpdateData(); m_CS_DownsampleScore16.Dispatch(groupsCount, groupsCount, 1); m_texScore0.RemoveFromLastAssignedSlots(); m_texScore1.RemoveFromLastAssignedSlotUAV(); // Swap Texture2D temp = m_texScore0; m_texScore0 = m_texScore1; m_texScore1 = temp; // Clear any random offset after first pass m_CB_Main.m._randomOffsetX = 0; m_CB_Main.m._randomOffsetY = 0; m_CB_Main.UpdateData(); } // 2.2) Downsample to last mip ComputeShader CSLastMip = null; switch (mipLevelsCount) { case 3: CSLastMip = m_CS_DownsampleScore8; break; case 2: CSLastMip = m_CS_DownsampleScore4; break; case 1: CSLastMip = m_CS_DownsampleScore2; break; } if (CSLastMip != null && CSLastMip.Use()) { m_texScore0.SetCS(0); m_texScore1.GetView((uint)m_texturePOT, 1, 0, 1).SetCSUAV(2); m_CB_Mips.m._textureMipSource = m_CB_Mips.m._textureMipTarget; m_CB_Mips.m._textureMipTarget = (uint)m_texturePOT; m_CB_Mips.UpdateData(); CSLastMip.Dispatch(1, 1, 1); m_texScore0.RemoveFromLastAssignedSlots(); m_texScore1.RemoveFromLastAssignedSlotUAV(); // Swap Texture2D temp = m_texScore0; m_texScore0 = m_texScore1; m_texScore1 = temp; } // #if DEBUG // DebugDownsampling(); // //DebugSplatPosition( iterationIndex ); // #endif } #else DownsampleCPU(iterationIndex, statistics); m_CB_Mips.m._textureMipTarget = (uint)m_texturePOT; #endif // 3] Splat a new pixel where we located the best score if (m_CS_Splat.Use()) { m_texScore0.SetCS(0); m_CB_Mips.m._textureMipSource = m_CB_Mips.m._textureMipTarget; m_CB_Mips.UpdateData(); m_CS_Splat.Dispatch(1, 1, 1); m_texScore0.RemoveFromLastAssignedSlots(); } if (_progress == null || iterationIndex % progressNotificationInterval != 0) { continue; } ReadBackTexture(); _progress((float)iterationIndex / m_textureTotalSize, m_ditheringArray, statistics); } m_texBinaryPattern.RemoveFromLastAssignedSlotUAV(); m_texDitheringArray.RemoveFromLastAssignedSlotUAV(); // Read back final result ReadBackTexture(); // Notify one last time if (_progress != null) { _progress(1.0f, m_ditheringArray, statistics); } }
void GenWalls() { if (useRandomSeed) { SimpleRNG.SetSeedFromSystemTime(); } else { SimpleRNG.SetSeed((uint)manualSeed); } foreach (Cell c in cells) { unassignedRooms.Add(c); } Cell startRoom = null; int curRoom = 1; while (unassignedRooms.Count > 0) { // add room of random size int nextRoomSize = (int)(SimpleRNG.GetUniform() * (maxRoomSize - minRoomSize)) + minRoomSize; Cell centreCell = unassignedRooms[(int)(SimpleRNG.GetUniform() * (unassignedRooms.Count - 1))]; if (startRoom == null) { startRoom = centreCell; } // work out ideal bounds of new room int startX = centreCell.x - Mathf.CeilToInt(nextRoomSize / 2f) + 1; int endX = Mathf.Min(startX + nextRoomSize, cellsPerSide); startX = Mathf.Max(0, startX); int startY = centreCell.y - Mathf.CeilToInt(nextRoomSize / 2f) + 1; int endY = Mathf.Min(startY + nextRoomSize, cellsPerSide); startY = Mathf.Max(0, startY); var roomCells = new List <Cell>(); var lastInRoom = new List <int>(); // which rows in the last column had a room on it? If no rows match, column won't be inited and room will stop. Avoids split rooms for (int x = startX; x < endX; x++) { var cellsThisColumn = new List <int>(); if (lastInRoom.Count == 0) { // no cells in room yet, add first block bool started = false; for (int y = startY; y < endY; y++) { if (cells[x, y].room == 0) { cellsThisColumn.Add(y); started = true; } else if (started) { break; } } } else { // add last column's rooms to this column if valid, then spread up and down until hits another room foreach (int roomRow in lastInRoom) { if (!cellsThisColumn.Contains(roomRow) && cells[x, roomRow].room == 0) { cellsThisColumn.Add(roomRow); for (int south = roomRow - 1; south >= startY; south--) { if (cells[x, south].room == 0) { cellsThisColumn.Add(south); } else { break; } } for (int north = roomRow + 1; north < endY; north++) { if (cells[x, north].room == 0) { cellsThisColumn.Add(north); } else { break; } } } } // if no valid connection after room has started, stop making room if (cellsThisColumn.Count == 0) { break; } } // actually make rooms foreach (int row in cellsThisColumn) { // for each cell within room edges, add walls between neighbouring rooms (if not in another room already) // add each valid room to list, and if can't path to first room after all rooms done, make holes Cell roomCell = cells[x, row]; if (AddCellToRoom(roomCell, curRoom)) { roomCells.Add(roomCell); } } lastInRoom = cellsThisColumn; } Debug.Log("Room made"); PrintLayout(); // try to path to start room if (roomCells.Count > 0 && CellPath.PathTo(startRoom.centrePosition, roomCells[0].centrePosition) == null) { // no path, make corridor to first cell Cell pathEnd = null; int distToTarg = int.MaxValue; foreach (Cell edgeCell in roomCells) { int newDist = Mathf.Abs(edgeCell.x - startRoom.x) + Mathf.Abs(edgeCell.y - startRoom.y); if (newDist < distToTarg) { distToTarg = newDist; pathEnd = edgeCell; } } while (pathEnd.room == curRoom) { Debug.Log("Opening path from " + pathEnd); int xDist = startRoom.x - pathEnd.x; int yDist = startRoom.y - pathEnd.y; if (xDist >= Mathf.Abs(yDist)) { pathEnd = OpenCellInDirection(pathEnd, Direction.East); } else if (xDist <= -Mathf.Abs(yDist)) { pathEnd = OpenCellInDirection(pathEnd, Direction.West); } else if (yDist > Mathf.Abs(xDist)) { pathEnd = OpenCellInDirection(pathEnd, Direction.North); } else if (yDist < -Mathf.Abs(xDist)) { pathEnd = OpenCellInDirection(pathEnd, Direction.South); } } // check if can path. JUST IN CASE if (CellPath.PathTo(startRoom.centrePosition, roomCells[0].centrePosition) == null) { Debug.LogWarning("Still no path from room " + curRoom); PrintLayout(); } } curRoom++; } Debug.Log("Layout complete..."); PrintLayout(); // Instantiate walls? var verticalWalls = new Cell[cellsPerSide, cellsPerSide]; for (int x = 0; x < cellsPerSide - 1; x++) { int wallType = Random.Range(0, wallPrefabs.Length); for (int y = 0; y < cellsPerSide; y++) { if (!cells[x, y].canGoEast) { CreateWall(cells[x, y], Direction.East, wallType); verticalWalls[x, y] = cells[x, y]; if (y > 0 && verticalWalls[x, y - 1] == null) { CreateWallCap(cells[x, y], true); } } else { wallType = Random.Range(0, wallPrefabs.Length); if (y > 0 && verticalWalls[x, y - 1] != null) { CreateWallCap(cells[x, y], true); } } } } var horizontalWalls = new Cell[cellsPerSide, cellsPerSide]; for (int y = 0; y < cellsPerSide - 1; y++) { int wallType = Random.Range(0, wallPrefabs.Length); for (int x = 0; x < cellsPerSide; x++) { if (!cells[x, y].canGoNorth) { CreateWall(cells[x, y], Direction.North, wallType); horizontalWalls[x, y] = cells[x, y]; if (x > 0 && horizontalWalls[x - 1, y] == null) { CreateWallCap(cells[x, y], false); } } else { wallType = Random.Range(0, wallPrefabs.Length); if (x > 0 && horizontalWalls[x - 1, y] != null) { CreateWallCap(cells[x, y], false); } } } } }