private void CopyDataToTextures()
    {
        //Transmittance is responsible for the change in the sun color as it moves
        //The raw file is a 2D array of 32 bit floats with a range of 0 to 1
        string        path   = Application.streamingAssetsPath + "/Textures/transmittance.raw";
        ComputeBuffer buffer = new ComputeBuffer(TRANSMITTANCE_WIDTH * TRANSMITTANCE_HEIGHT, sizeof(float) * TRANSMITTANCE_CHANNELS);

        CBUtility.WriteIntoRenderTexture(m_transmittance, TRANSMITTANCE_CHANNELS, path, buffer, m_writeData);
        buffer.Release();

        //Inscatter is responsible for the change in the sky color as the sun moves
        //The raw file is a 4D array of 32 bit floats with a range of 0 to 1.589844
        //As there is not such thing as a 4D texture the data is packed into a 3D texture
        //and the shader manually performs the sample for the 4th dimension
        path   = Application.streamingAssetsPath + "/Textures/inscatter.raw";
        buffer = new ComputeBuffer(INSCATTER_WIDTH * INSCATTER_HEIGHT * INSCATTER_DEPTH, sizeof(float) * INSCATTER_CHANNELS);
        CBUtility.WriteIntoRenderTexture(m_inscatter, INSCATTER_CHANNELS, path, buffer, m_writeData);
        buffer.Release();

        //The raw file is a 2D array of 32 bit floats with a range of 0 to 1
        path   = Application.streamingAssetsPath + "/Textures/irradiance.raw";
        buffer = new ComputeBuffer(IRRADIANCE_WIDTH * IRRADIANCE_HEIGHT, sizeof(float) * IRRADIANCE_CHANNELS);
        CBUtility.WriteIntoRenderTexture(m_irradiance, IRRADIANCE_CHANNELS, path, buffer, m_writeData);
        buffer.Release();
    }
    private void CreateFinalSkyboxLUT()
    {
        int texDepth  = (int)_skyboxLUTSize.z;
        int texWidth  = (int)_skyboxLUTSize.x;
        int texHeight = (int)_skyboxLUTSize.y;
        int floatSize = sizeof(float);
        int channels  = 4;
        int texSize   = texWidth * texHeight * texDepth;

        if (_skyboxLUT == null)
        {
            _skyboxLUT                   = new RenderTexture((int)_skyboxLUTSize.x, (int)_skyboxLUTSize.y, 0, RenderTextureFormat.ARGBHalf, RenderTextureReadWrite.Linear);
            _skyboxLUT.volumeDepth       = (int)_skyboxLUTSize.z;
            _skyboxLUT.dimension         = UnityEngine.Rendering.TextureDimension.Tex3D;
            _skyboxLUT.enableRandomWrite = true;
            _skyboxLUT.name              = "SkyboxLUT";
            _skyboxLUT.Create();
        }

        ComputeBuffer buffer = new ComputeBuffer(texSize, floatSize * channels); // 4 bytes for float and 4 channels

        buffer.SetData(_skyboxData);
        CBUtility.WriteIntoRenderTexture(_skyboxLUT, channels, buffer, FrostbiteWriteShader);

        buffer.Release();
        //SaveTextureAsKTX(_skyboxLUT, "skyboxlut", true);
    }
    public void ReadTextureFromKTX(RenderTexture rtex, String name)
    {
        FileStream   fs     = new FileStream("Assets/Textures/" + name + ".ktx", FileMode.Open);
        BinaryReader reader = new BinaryReader(fs);

        // skip header and meta data
        reader.ReadBytes(36);
        int texWidth     = (int)reader.ReadUInt32();
        int texHeight    = (int)reader.ReadUInt32();
        int texDepth     = (int)reader.ReadUInt32();
        int channelCount = 4;
        int channelSize  = 1;
        int texSize      = texWidth * texHeight * texDepth;

        if (rtex == null)
        {
            rtex                   = new RenderTexture(texWidth, texHeight, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear);
            rtex.volumeDepth       = texDepth;
            rtex.dimension         = UnityEngine.Rendering.TextureDimension.Tex3D;
            rtex.enableRandomWrite = true;
            rtex.name              = "Noise3DTex";
            rtex.filterMode        = FilterMode.Trilinear;
            rtex.wrapMode          = TextureWrapMode.Repeat;
            rtex.Create();
        }
        reader.ReadBytes(16);
        int bufferSize = (int)reader.ReadUInt32();

        ComputeBuffer buffer = new ComputeBuffer(texSize, channelSize * channelCount); // 4 bytes for float and 4 channels

        buffer.SetData(reader.ReadBytes(bufferSize));
        CBUtility.WriteIntoRenderTexture(rtex, channelCount, buffer, FrostbiteWriteShader);

        buffer.Release();
    }
예제 #4
0
        /// <summary>
        /// Save the actual data in table as a raw file to be loaded and used during run time.
        /// </summary>
        private void SaveAsRaw(int size, int channels, string fileName, RenderTexture rtex)
        {
            ComputeBuffer buffer = new ComputeBuffer(size, sizeof(float) * channels);

            CBUtility.ReadFromRenderTexture(rtex, channels, buffer, m_readData);

            float[] data = new float[size * channels];

            buffer.GetData(data);

            byte[] byteArray = new byte[size * 4 * channels];
            System.Buffer.BlockCopy(data, 0, byteArray, 0, byteArray.Length);
            System.IO.File.WriteAllBytes(Application.dataPath + m_filePath + fileName + ".raw", byteArray);

            buffer.Release();
        }
예제 #5
0
    public static void SaveAsRaw(int size, CBUtility.Channels channels, string fileName, string filePath, RenderTexture rtex, ComputeShader readDataComputeShader)
    {
        var channelsSize = (byte)channels;
        var buffer       = new ComputeBuffer(size, sizeof(float) * channelsSize);

        CBUtility.ReadFromRenderTexture(rtex, channels, buffer, readDataComputeShader);

        var data = new float[size * channelsSize];

        buffer.GetData(data);

        var byteArray = new byte[size * 4 * channelsSize];

        Buffer.BlockCopy(data, 0, byteArray, 0, byteArray.Length);
        File.WriteAllBytes(Application.dataPath + filePath + fileName + ".raw", byteArray);

        buffer.Release();
    }
예제 #6
0
        /// <summary>
        /// Updates the ground height below camera.
        /// </summary>
        private void UpdateGroundHeight()
        {
            if (TerrainNode.LocalCameraPosition.z > TerrainNode.TerrainQuadRoot.ZMax)
            {
                return;
            }

            var localCameraPosition = TerrainNode.LocalCameraPosition;

            // If camera has moved update ground height
            if ((localCameraPosition - OldLocalCamera).Magnitude() > 1.0 && CameraQuad != null && CameraQuad.Tile != null && EnableGroundHeightUpdate)
            {
                var slot = CameraQuad.Tile.Slot[0] as GPUTileStorage.GPUSlot;

                if (slot != null)
                {
                    var border   = Producer.GetBorder();
                    var tileSize = Producer.GetTileSizeMinBorder(0);

                    var dx = CameraQuadCoordinates.x * tileSize;
                    var dy = CameraQuadCoordinates.y * tileSize;

                    // x,y are the non-normalized position in the elevations texture where the ground height below the camera is.
                    var x = dx + (float)border;
                    var y = dy + (float)border;

                    // Read the single value from the render texture
                    CBUtility.ReadSingleFromRenderTexture(slot.Texture, x, y, 0, GroundBuffer, GodManager.Instance.ReadData, true);

                    // Get single height value from buffer
                    var height = new Vector4[1];

                    GroundBuffer.GetData(height);

                    TerrainNode.ParentBody.HeightZ = Math.Max(0.0, height[0].x);

                    OldLocalCamera.x = localCameraPosition.x;
                    OldLocalCamera.y = localCameraPosition.y;
                    OldLocalCamera.z = localCameraPosition.z;
                }
            }

            CameraQuad = null;
        }
예제 #7
0
        private void ReadFromGPU(int numGrids)
        {
            if (!this.disableReadBack && this.readSdr == null)
            {
                Ocean.LogWarning("Trying to read GPU displacement data but the read shader is null");
            }
            bool flag = SystemInfo.graphicsShaderLevel >= 50 && SystemInfo.supportsComputeShaders;

            if (!this.disableReadBack && this.readSdr != null && this.m_readBuffer != null && flag)
            {
                InterpolatedArray2f[] readDisplacements = this.DisplacementBuffer.GetReadDisplacements();
                if (numGrids > 0)
                {
                    CBUtility.ReadFromRenderTexture(this.m_displacementMaps[0], 3, this.m_readBuffer, this.readSdr);
                    this.m_readBuffer.GetData(readDisplacements[0].Data);
                }
                else
                {
                    readDisplacements[0].Clear();
                }
                if (numGrids > 1)
                {
                    CBUtility.ReadFromRenderTexture(this.m_displacementMaps[1], 3, this.m_readBuffer, this.readSdr);
                    this.m_readBuffer.GetData(readDisplacements[1].Data);
                }
                else
                {
                    readDisplacements[1].Clear();
                }
                if (numGrids > 2)
                {
                    CBUtility.ReadFromRenderTexture(this.m_displacementMaps[2], 3, this.m_readBuffer, this.readSdr);
                    this.m_readBuffer.GetData(readDisplacements[2].Data);
                }
                else
                {
                    readDisplacements[2].Clear();
                }
                if (numGrids > 3)
                {
                }
            }
        }
예제 #8
0
    public static void SaveAs8bit(int width, int height, CBUtility.Channels channels, string fileName, string filePath, RenderTexture rtex, ComputeShader readDataComputeShader, float scale = 1.0f)
    {
        var channelsSize = (byte)channels;
        var buffer       = new ComputeBuffer(width * height, sizeof(float) * channelsSize);

        CBUtility.ReadFromRenderTexture(rtex, channels, buffer, readDataComputeShader);

        var data = new float[width * height * channelsSize];

        buffer.GetData(data);

        var texture = new Texture2D(width, height);

        for (int x = 0; x < width; x++)
        {
            for (int y = 0; y < height; y++)
            {
                var color = new Color(0, 0, 0, 1);

                color.r = data[(x + y * width) * channelsSize + 0];

                if (channelsSize > 1)
                {
                    color.g = data[(x + y * width) * channelsSize + 1];
                }

                if (channelsSize > 2)
                {
                    color.b = data[(x + y * width) * channelsSize + 2];
                }

                texture.SetPixel(x, y, color * scale);
            }
        }

        texture.Apply();

        var bytes = texture.EncodeToPNG();

        File.WriteAllBytes(Application.dataPath + filePath + fileName + ".png", bytes);

        buffer.Release();
    }
예제 #9
0
        private void CreateWTable()
        {
            // Some values need for the InitWaveSpectrum function can be precomputed
            var uv = Vector2.zero;
            var st = Vector2.zero;

            float k1, k2, k3, k4, w1, w2, w3, w4;

            var table = new float[FourierGridSize * FourierGridSize * 4];

            for (int x = 0; x < FourierGridSize; x++)
            {
                for (int y = 0; y < FourierGridSize; y++)
                {
                    uv = new Vector2(x, y) / MapSize;

                    st.x = uv.x > 0.5f ? uv.x - 1.0f : uv.x;
                    st.y = uv.y > 0.5f ? uv.y - 1.0f : uv.y;

                    k1 = (st * InverseGridSizes.x).magnitude;
                    k2 = (st * InverseGridSizes.y).magnitude;
                    k3 = (st * InverseGridSizes.z).magnitude;
                    k4 = (st * InverseGridSizes.w).magnitude;

                    w1 = Mathf.Sqrt(9.81f * k1 * (1.0f + k1 * k1 / (WAVE_KM * WAVE_KM)));
                    w2 = Mathf.Sqrt(9.81f * k2 * (1.0f + k2 * k2 / (WAVE_KM * WAVE_KM)));
                    w3 = Mathf.Sqrt(9.81f * k3 * (1.0f + k3 * k3 / (WAVE_KM * WAVE_KM)));
                    w4 = Mathf.Sqrt(9.81f * k4 * (1.0f + k4 * k4 / (WAVE_KM * WAVE_KM)));

                    table[(x + y * FourierGridSize) * 4 + 0] = w1;
                    table[(x + y * FourierGridSize) * 4 + 1] = w2;
                    table[(x + y * FourierGridSize) * 4 + 2] = w3;
                    table[(x + y * FourierGridSize) * 4 + 3] = w4;
                }
            }

            // Write floating point data into render texture
            var buffer = new ComputeBuffer(FourierGridSize * FourierGridSize, sizeof(float) * 4);

            buffer.SetData(table);
            CBUtility.WriteIntoRenderTexture(WTable, CBUtility.Channels.RGBA, buffer, GodManager.Instance.WriteData);
            buffer.ReleaseAndDisposeBuffer();
        }
예제 #10
0
        /// <summary>
        /// Saves a 8 bit version of the table.
        /// Only used to get a visible image for debugging.
        /// </summary>
        private void SaveAs8bit(int width, int height, int channels, string fileName, RenderTexture rtex, float scale = 1.0f)
        {
            ComputeBuffer buffer = new ComputeBuffer(width * height, sizeof(float) * channels);

            CBUtility.ReadFromRenderTexture(rtex, channels, buffer, m_readData);

            float[] data = new float[width * height * channels];

            buffer.GetData(data);

            Texture2D tex = new Texture2D(width, height);

            for (int x = 0; x < width; x++)
            {
                for (int y = 0; y < height; y++)
                {
                    Color col = new Color(0, 0, 0, 1);

                    col.r = data[(x + y * width) * channels + 0];

                    if (channels > 1)
                    {
                        col.g = data[(x + y * width) * channels + 1];
                    }

                    if (channels > 2)
                    {
                        col.b = data[(x + y * width) * channels + 2];
                    }

                    tex.SetPixel(x, y, col * scale);
                }
            }

            tex.Apply();

            byte[] bytes = tex.EncodeToPNG();

            System.IO.File.WriteAllBytes(Application.dataPath + m_filePath + fileName + ".png", bytes);

            buffer.Release();
        }
예제 #11
0
        /// <summary>
        /// Some of the values needed in the InitWaveSpectrum function can be precomputed.
        /// If the grid sizes change this function must called again.
        /// </summary>
        void CreateWTable()
        {
            //Some values need for the InitWaveSpectrum function can be precomputed
            Vector2 uv, st;
            float   k1, k2, k3, k4, w1, w2, w3, w4;

            float[] table = new float[m_size * m_size * 4];

            for (int x = 0; x < m_size; x++)
            {
                for (int y = 0; y < m_size; y++)
                {
                    uv = new Vector2(x, y) / m_fsize;

                    st.x = uv.x > 0.5f ? uv.x - 1.0f : uv.x;
                    st.y = uv.y > 0.5f ? uv.y - 1.0f : uv.y;

                    k1 = (st * m_inverseGridSizes.x).magnitude;
                    k2 = (st * m_inverseGridSizes.y).magnitude;
                    k3 = (st * m_inverseGridSizes.z).magnitude;
                    k4 = (st * m_inverseGridSizes.w).magnitude;

                    w1 = Mathf.Sqrt(9.81f * k1 * (1.0f + k1 * k1 / (WAVE_KM * WAVE_KM)));
                    w2 = Mathf.Sqrt(9.81f * k2 * (1.0f + k2 * k2 / (WAVE_KM * WAVE_KM)));
                    w3 = Mathf.Sqrt(9.81f * k3 * (1.0f + k3 * k3 / (WAVE_KM * WAVE_KM)));
                    w4 = Mathf.Sqrt(9.81f * k4 * (1.0f + k4 * k4 / (WAVE_KM * WAVE_KM)));

                    table[(x + y * m_size) * 4 + 0] = w1;
                    table[(x + y * m_size) * 4 + 1] = w2;
                    table[(x + y * m_size) * 4 + 2] = w3;
                    table[(x + y * m_size) * 4 + 3] = w4;
                }
            }

            //Write floating point data into render texture
            ComputeBuffer buffer = new ComputeBuffer(m_size * m_size, sizeof(float) * 4);

            buffer.SetData(table);
            CBUtility.WriteIntoRenderTexture(m_WTable, 4, buffer, m_writeData);

            buffer.Release();
        }
    private void PrecomputeSkyboxAlltogether()
    {
        int  texDepth  = (int)_skyboxLUTSize.z;
        int  texWidth  = (int)_skyboxLUTSize.x;
        int  texHeight = (int)_skyboxLUTSize.y;
        int  floatSize = sizeof(float);
        int  channels  = 4;
        int  texSize   = texWidth * texHeight * texDepth;
        bool firstTime = false;

        if (_skyboxData == null)
        {
            firstTime   = true;
            _skyboxData = new float[texSize * channels];
        }

        ComputeBuffer buffer = new ComputeBuffer(texSize, floatSize * channels); // 4 bytes for float and 4 channels

        CBUtility.ReadFromRenderTexture(_skyboxLUT2, channels, buffer, FrostbiteReadShader);
        float[] data = new float[texSize * channels];
        buffer.GetData(data);

        if (firstTime)
        {
            for (int i = 0; i < _skyboxData.Length; ++i)
            {
                _skyboxData[i] = data[i];
            }
        }
        else
        {
            for (int i = 0; i < _skyboxData.Length; ++i)
            {
                _skyboxData[i] += data[i];
            }
        }

        buffer.Release();
    }
예제 #13
0
        protected override void Start()
        {
            base.Start();

            m_mesh        = MeshFactory.MakePlane(2, 2, MeshFactory.PLANE.XY, false);
            m_mesh.bounds = new Bounds(Vector3.zero, new Vector3(1e8f, 1e8f, 1e8f));

            //The sky map is used to create a reflection of the sky for objects that need it (like the ocean)
            m_skyMap            = new RenderTexture(512, 512, 0, RenderTextureFormat.ARGBHalf);
            m_skyMap.filterMode = FilterMode.Trilinear;
            m_skyMap.wrapMode   = TextureWrapMode.Clamp;
            m_skyMap.anisoLevel = 9;
            m_skyMap.useMipMap  = true;
            //m_skyMap.mipMapBias = -0.5f;
            m_skyMap.Create();

            //Transmittance is responsible for the change in the sun color as it moves
            //The raw file is a 2D array of 32 bit floats with a range of 0 to 1
            string path = Application.dataPath + m_filePath + "/transmittance.raw";

            m_transmittance                   = new RenderTexture(TRANSMITTANCE_W, TRANSMITTANCE_H, 0, RenderTextureFormat.ARGBHalf);
            m_transmittance.wrapMode          = TextureWrapMode.Clamp;
            m_transmittance.filterMode        = FilterMode.Bilinear;
            m_transmittance.enableRandomWrite = true;
            m_transmittance.Create();

            ComputeBuffer buffer = new ComputeBuffer(TRANSMITTANCE_W * TRANSMITTANCE_H, sizeof(float) * 3);

            CBUtility.WriteIntoRenderTexture(m_transmittance, 3, path, buffer, World.WriteData);
            buffer.Release();

            //Iirradiance is responsible for the change in the sky color as the sun moves
            //The raw file is a 2D array of 32 bit floats with a range of 0 to 1
            path = Application.dataPath + m_filePath + "/irradiance.raw";

            m_irradiance                   = new RenderTexture(SKY_W, SKY_H, 0, RenderTextureFormat.ARGBHalf);
            m_irradiance.wrapMode          = TextureWrapMode.Clamp;
            m_irradiance.filterMode        = FilterMode.Bilinear;
            m_irradiance.enableRandomWrite = true;
            m_irradiance.Create();

            buffer = new ComputeBuffer(SKY_W * SKY_H, sizeof(float) * 3);
            CBUtility.WriteIntoRenderTexture(m_irradiance, 3, path, buffer, World.WriteData);
            buffer.Release();

            //Inscatter is responsible for the change in the sky color as the sun moves
            //The raw file is a 4D array of 32 bit floats with a range of 0 to 1.589844
            //As there is not such thing as a 4D texture the data is packed into a 3D texture
            //and the shader manually performs the sample for the 4th dimension
            path = Application.dataPath + m_filePath + "/inscatter.raw";

            m_inscatter                   = new RenderTexture(RES_MU_S * RES_NU, RES_MU, 0, RenderTextureFormat.ARGBHalf);
            m_inscatter.volumeDepth       = RES_R;
            m_inscatter.wrapMode          = TextureWrapMode.Clamp;
            m_inscatter.filterMode        = FilterMode.Bilinear;
            m_inscatter.dimension         = TextureDimension.Tex3D;
            m_inscatter.enableRandomWrite = true;
            m_inscatter.Create();

            buffer = new ComputeBuffer(RES_MU_S * RES_NU * RES_MU * RES_R, sizeof(float) * 4);
            CBUtility.WriteIntoRenderTexture(m_inscatter, 4, path, buffer, World.WriteData);
            buffer.Release();

            InitUniforms(m_skyMaterial);
            InitUniforms(m_skyMapMaterial);
        }
예제 #14
0
        /// <summary>
        /// Creates a series of textures that contain random noise.
        /// These texture tile together using the Wang Tiling method.
        /// Used by the UpSample shader to create fractal noise for the terrain elevations.
        /// </summary>
        private void CreateDemNoise()
        {
            var tileWidth = Cache.GetStorage(0).TileSize;

            NoiseTextures = new RenderTexture[6];

            var layers = new int[] { 0, 1, 3, 5, 7, 15 };
            var rand   = 1234567;

            for (byte nl = 0; nl < 6; ++nl)
            {
                var noiseArray = new float[tileWidth * tileWidth];
                var l          = layers[nl];
                var buffer     = new ComputeBuffer(tileWidth * tileWidth, sizeof(float));

                for (int j = 0; j < tileWidth; ++j)
                {
                    for (int i = 0; i < tileWidth; ++i)
                    {
                        noiseArray[i + j * tileWidth] = Noise.Noise2D(i, j);
                    }
                }

                // Corners
                for (int j = 0; j < tileWidth; ++j)
                {
                    for (int i = 0; i < tileWidth; ++i)
                    {
                        noiseArray[i + j * tileWidth] = 0.0f;
                    }
                }

                // Bottom border
                Random.InitState((l & 1) == 0 ? 7654321 : 5647381);

                for (int h = 5; h <= tileWidth / 2; ++h)
                {
                    var N = RandomValue();

                    noiseArray[h + 2 * tileWidth] = N;
                    noiseArray[(tileWidth - 1 - h) + 2 * tileWidth] = N;
                }

                for (int v = 3; v < 5; ++v)
                {
                    for (int h = 5; h < tileWidth - 5; ++h)
                    {
                        var N = RandomValue();

                        noiseArray[h + v * tileWidth] = N;
                        noiseArray[(tileWidth - 1 - h) + (4 - v) * tileWidth] = N;
                    }
                }

                // Right border
                Random.InitState((l & 2) == 0 ? 7654321 : 5647381);

                for (int v = 5; v <= tileWidth / 2; ++v)
                {
                    var N = RandomValue();

                    noiseArray[(tileWidth - 3) + v * tileWidth] = N;
                    noiseArray[(tileWidth - 3) + (tileWidth - 1 - v) * tileWidth] = N;
                }

                for (int h = tileWidth - 4; h >= tileWidth - 5; --h)
                {
                    for (int v = 5; v < tileWidth - 5; ++v)
                    {
                        var N = RandomValue();

                        noiseArray[h + v * tileWidth] = N;
                        noiseArray[(2 * tileWidth - 6 - h) + (tileWidth - 1 - v) * tileWidth] = N;
                    }
                }

                // Top border
                Random.InitState((l & 4) == 0 ? 7654321 : 5647381);

                for (int h = 5; h <= tileWidth / 2; ++h)
                {
                    var N = RandomValue();

                    noiseArray[h + (tileWidth - 3) * tileWidth] = N;
                    noiseArray[(tileWidth - 1 - h) + (tileWidth - 3) * tileWidth] = N;
                }

                for (int v = tileWidth - 2; v < tileWidth; ++v)
                {
                    for (int h = 5; h < tileWidth - 5; ++h)
                    {
                        var N = RandomValue();

                        noiseArray[h + v * tileWidth] = N;
                        noiseArray[(tileWidth - 1 - h) + (2 * tileWidth - 6 - v) * tileWidth] = N;
                    }
                }

                // Left border
                Random.InitState((l & 8) == 0 ? 7654321 : 5647381);

                for (int v = 5; v <= tileWidth / 2; ++v)
                {
                    var N = RandomValue();

                    noiseArray[2 + v * tileWidth] = N;
                    noiseArray[2 + (tileWidth - 1 - v) * tileWidth] = N;
                }

                for (int h = 1; h >= 0; --h)
                {
                    for (int v = 5; v < tileWidth - 5; ++v)
                    {
                        var N = RandomValue();

                        noiseArray[h + v * tileWidth] = N;
                        noiseArray[(4 - h) + (tileWidth - 1 - v) * tileWidth] = N;
                    }
                }

                // Center
                Random.InitState(rand);

                for (int v = 5; v < tileWidth - 5; ++v)
                {
                    for (int h = 5; h < tileWidth - 5; ++h)
                    {
                        var N = RandomValue();
                        noiseArray[h + v * tileWidth] = N;
                    }
                }

                // Randomize for next texture
                rand = (rand * 1103515245 + 12345) & 0x7FFFFFFF;

                NoiseTextures[nl] = RTExtensions.CreateRTexture(new Vector2(tileWidth, tileWidth), 0, RenderTextureFormat.RHalf, FilterMode.Point, TextureWrapMode.Repeat);

                // Write data into render texture
                buffer.SetData(noiseArray);

                CBUtility.WriteIntoRenderTexture(NoiseTextures[nl], 1, buffer, GodManager.Instance.WriteData);

                buffer.ReleaseAndDisposeBuffer();
            }
        }
예제 #15
0
        private void CalculateAO()
        {
            Debug.Log("Precomputing AO Started...");

            int GRIDRES_AO = 128;
            int N_AO       = 2;

            var options = new ParallelOptions {
                MaxDegreeOfParallelism = 4
            };

            float[] buf = new float[GRIDRES_AO * GRIDRES_AO * GRIDRES_AO * 4];

            for (int i = 0; i < GRIDRES_AO; ++i)
            {
                for (int j = 0; j < GRIDRES_AO; ++j)
                {
                    for (int k = 0; k < GRIDRES_AO; ++k)
                    {
                        int off = i + j * GRIDRES_AO + k * GRIDRES_AO * GRIDRES_AO;

                        buf[4 * off]     = 0;
                        buf[4 * off + 1] = 0;
                        buf[4 * off + 2] = 0;
                        buf[4 * off + 3] = 0;
                    }
                }
            }

            var indices  = PreProcessMesh.GetIndices(0);
            var vertices = PreProcessMesh.vertices;

            for (int ni = 0; ni < indices.Length; ni += 3)
            {
                int a = indices[ni];
                int b = indices[ni + 1];
                int c = indices[ni + 2];

                float x1 = vertices[a].x, y1 = vertices[a].y, z1 = vertices[a].z;
                float x2 = vertices[b].x, y2 = vertices[b].y, z2 = vertices[b].z;
                float x3 = vertices[c].x, y3 = vertices[c].y, z3 = vertices[c].z;

                x1 = (x1 + 1.0f) / 2.0f;
                x2 = (x2 + 1.0f) / 2.0f;
                x3 = (x3 + 1.0f) / 2.0f;
                y1 = (y1 + 1.0f) / 2.0f;
                y2 = (y2 + 1.0f) / 2.0f;
                y3 = (y3 + 1.0f) / 2.0f;
                z1 = (z1 + 1.0f) / 2.0f;
                z2 = (z2 + 1.0f) / 2.0f;
                z3 = (z3 + 1.0f) / 2.0f;

                double l12 = Mathf.Sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1) + (z2 - z1) * (z2 - z1));
                double l23 = Mathf.Sqrt((x3 - x2) * (x3 - x2) + (y3 - y2) * (y3 - y2) + (z3 - z2) * (z3 - z2));
                double l31 = Mathf.Sqrt((x1 - x3) * (x1 - x3) + (y1 - y3) * (y1 - y3) + (z1 - z3) * (z1 - z3));

                if (l12 > l23 && l12 > l31)
                {
                    Swap(ref a, ref c);
                    Swap(ref x1, ref x3); Swap(ref y1, ref y3); Swap(ref z1, ref z3);
                    Swap(ref l12, ref l23);
                }
                else if (l31 > l12 && l31 > l23)
                {
                    Swap(ref a, ref b);
                    Swap(ref x1, ref x2); Swap(ref y1, ref y2); Swap(ref z1, ref z2);
                    Swap(ref l31, ref l23);
                }

                int n12 = (int)(Math.Ceiling(l12 * GRIDRES_AO) * 2.0);
                int n13 = (int)(Math.Ceiling(l31 * GRIDRES_AO) * 2.0);

                Parallel.For(0, n12 - 1, i =>
                {
                    var u = (double)i / n12;

                    Parallel.For(0, n13 - 1, j =>
                    {
                        var v = (double)j / n13;

                        if (u + v < 1.0)
                        {
                            var x = x1 + u * (x2 - x1) + v * (x3 - x1);
                            var y = y1 + u * (y2 - y1) + v * (y3 - y1);
                            var z = z1 + u * (z2 - z1) + v * (z3 - z1);

                            int ix = (int)(x * GRIDRES_AO);
                            int iy = (int)(y * GRIDRES_AO);
                            int iz = (int)(z * GRIDRES_AO);

                            if (ix >= 0 && ix < GRIDRES_AO && iy >= 0 && iy < GRIDRES_AO && iz >= 0 && iz < GRIDRES_AO)
                            {
                                int off = 4 * (ix + iy * GRIDRES_AO + iz * GRIDRES_AO * GRIDRES_AO);

                                buf[off]     = 255;
                                buf[off + 1] = 255;
                                buf[off + 2] = 255;
                                buf[off + 3] = 255;
                            }
                        }
                    });
                });
            }

            Debug.Log("Precomputing AO Mesh Passed...");

            double[] vocc = new double[GRIDRES_AO * GRIDRES_AO * GRIDRES_AO];

            for (int i = 0; i < GRIDRES_AO * GRIDRES_AO * GRIDRES_AO; ++i)
            {
                vocc[i] = 1.0;
            }

            double zmax = Math.Abs(Z);
            double zmin = -Math.Abs(Z);

            Parallel.For(0, N_AO - 1, options, i =>
            {
                var theta  = (i + 0.5) / N_AO * Math.PI / 2.0;
                var dtheta = 1.0 / N_AO * Math.PI / 2.0;

                Parallel.For(0, (4 * N_AO) - 1, options, j =>
                {
                    var phi  = (j + 0.5) / (4 * N_AO) * 2.0 * Math.PI;
                    var dphi = 1.0 / (4 * N_AO) * 2.0 * Math.PI;
                    var docc = Math.Cos(theta) * Math.Sin(theta) * dtheta * dphi / Math.PI;

                    if ((i * 4 * N_AO + j) % 4 == 0)
                    {
                        Debug.Log(string.Format("Precomputing AO Step {0} of {1}", i * 4 * N_AO + j, 4 * N_AO * N_AO));
                    }

                    Vector3d uz = new Vector3d(Math.Cos(phi) * Math.Sin(theta), Math.Sin(phi) * Math.Sin(theta), Math.Cos(theta));
                    Vector3d ux = uz.z.EpsilonEquals(1.0, 0.0000001) ? new Vector3d(1.0, 0.0, 0.0) : new Vector3d(-uz.y, uz.x, 0.0).Normalized();
                    Vector3d uy = uz.Cross(ux);

                    Matrix3x3d toView = new Matrix3x3d(ux.x, ux.y, ux.z, uy.x, uy.y, uy.z, uz.x, uz.y, uz.z);
                    Matrix3x3d toVol  = new Matrix3x3d(ux.x, uy.x, uz.x, ux.y, uy.y, uz.y, ux.z, uy.z, uz.z);

                    Box3d b = new Box3d();
                    b       = b.Enlarge(toView * new Vector3d(-1.0, -1.0, zmin));
                    b       = b.Enlarge(toView * new Vector3d(+1.0, -1.0, zmin));
                    b       = b.Enlarge(toView * new Vector3d(-1.0, +1.0, zmin));
                    b       = b.Enlarge(toView * new Vector3d(+1.0, +1.0, zmin));
                    b       = b.Enlarge(toView * new Vector3d(-1.0, -1.0, zmax));
                    b       = b.Enlarge(toView * new Vector3d(+1.0, -1.0, zmax));
                    b       = b.Enlarge(toView * new Vector3d(-1.0, +1.0, zmax));
                    b       = b.Enlarge(toView * new Vector3d(+1.0, +1.0, zmax));

                    int nx = (int)((b.Max.x - b.Min.x) * GRIDRES_AO / 2);
                    int ny = (int)((b.Max.y - b.Min.y) * GRIDRES_AO / 2);
                    int nz = (int)((b.Max.z - b.Min.z) * GRIDRES_AO / 2);

                    int[] occ = new int[nx * ny * nz];
                    for (int v = 0; v < nx * ny * nz; ++v)
                    {
                        occ[v] = 0;
                    }

                    for (int iz = nz - 1; iz >= 0; --iz)
                    {
                        var z = b.Min.z + (iz + 0.5) / nz * (b.Max.z - b.Min.z);

                        for (int iy = 0; iy < ny; ++iy)
                        {
                            var y = b.Min.y + (iy + 0.5) / ny * (b.Max.y - b.Min.y);

                            for (int ix = 0; ix < nx; ++ix)
                            {
                                var x = b.Min.x + (ix + 0.5) / nx * (b.Max.x - b.Min.x);

                                Vector3d p = toVol * new Vector3d(x, y, z);

                                int val = 0;
                                int vx  = (int)((p.x + 1.0) / 2.0 * GRIDRES_AO);
                                int vy  = (int)((p.y + 1.0) / 2.0 * GRIDRES_AO);
                                int vz  = (int)((p.z + 1.0) / 2.0 * GRIDRES_AO);

                                if (vx >= 0 && vx < GRIDRES_AO && vy >= 0 && vy < GRIDRES_AO && vz >= 0 && vz < GRIDRES_AO)
                                {
                                    val = buf[4 * (vx + vy * GRIDRES_AO + vz * GRIDRES_AO * GRIDRES_AO) + 3].EpsilonEquals(255.0f) ? 1 : 0;
                                }

                                occ[ix + iy * nx + iz * nx * ny] = val;

                                if (iz != nz - 1)
                                {
                                    occ[ix + iy * nx + iz * nx * ny] += occ[ix + iy * nx + (iz + 1) * nx * ny];
                                }
                            }
                        }
                    }

                    Parallel.For(0, GRIDRES_AO - 1, options, ix =>
                    {
                        var x = -1.0 + (ix + 0.5) / GRIDRES_AO * 2.0;

                        Parallel.For(0, GRIDRES_AO - 1, options, iy =>
                        {
                            var y = -1.0 + (iy + 0.5) / GRIDRES_AO * 2.0;

                            Parallel.For(0, GRIDRES_AO - 1, options, iz =>
                            {
                                var z = -1.0 + (iz + 0.5) / GRIDRES_AO * 2.0;

                                Vector3d p = toView * new Vector3d(x, y, z);

                                int vx = (int)((p.x - b.Min.x) / (b.Max.x - b.Min.x) * nx);
                                int vy = (int)((p.y - b.Min.y) / (b.Max.y - b.Min.y) * ny);
                                int vz = (int)((p.z - b.Min.z) / (b.Max.z - b.Min.z) * nz);

                                if (vx >= 0 && vx < nx && vy >= 0 && vy < ny && vz >= 0 && vz < nz)
                                {
                                    int occN = occ[vx + vy * nx + vz * nx * ny];

                                    if (occN > 6)
                                    {
                                        vocc[ix + iy * GRIDRES_AO + iz * GRIDRES_AO * GRIDRES_AO] -= docc;
                                    }
                                }
                            });
                        });
                    });
                });
            });

            for (int i = 0; i < GRIDRES_AO; ++i)
            {
                for (int j = 0; j < GRIDRES_AO; ++j)
                {
                    for (int k = 0; k < GRIDRES_AO; ++k)
                    {
                        int off = i + j * GRIDRES_AO + k * GRIDRES_AO * GRIDRES_AO;

                        if (buf[4 * off + 3].EpsilonEquals(255.0f))
                        {
                            var v = Math.Max(vocc[off], 0.0f) * 255;

                            buf[4 * off]     = (float)v;
                            buf[4 * off + 1] = (float)v;
                            buf[4 * off + 2] = (float)v;
                        }
                    }
                }
            }

            GC.Collect();

            var cb = new ComputeBuffer(GRIDRES_AO * GRIDRES_AO * GRIDRES_AO, sizeof(float) * 4);

            PreProcessAORT = RTExtensions.CreateRTexture(GRIDRES_AO, 0, RenderTextureFormat.ARGBFloat, FilterMode.Bilinear, TextureWrapMode.Clamp, GRIDRES_AO);

            CBUtility.WriteIntoRenderTexture(PreProcessAORT, CBUtility.Channels.RGBA, cb, GodManager.Instance.WriteData);
            RTUtility.SaveAs8bit(GRIDRES_AO, GRIDRES_AO * GRIDRES_AO, CBUtility.Channels.RGBA, "TreeAO", DestinationFolder, buf, 0.00392156863f);

            cb.ReleaseAndDisposeBuffer();

            Debug.Log("Precomputing AO Completed!");
        }
예제 #16
0
        /// <summary>
        /// Updates the <see cref="TerrainQuad.ZMin"/> and <see cref="TerrainQuad.ZMax"/> values.
        /// Used to create a better fitting bounding box.
        /// Is not essental and can be disabled if retriving the heights data from the GPU is causing performance issues.
        /// </summary>
        private void UpdateMinMax()
        {
            // If no quads need read back or if disabled return
            if (NeedsReadBackDictionary.Count == 0 || !EnableReadBack)
            {
                return;
            }

            // Make a copy of all the keys of the tiles that need to be read back
            var ids = new Tile.Id[NeedsReadBackDictionary.Count];

            NeedsReadBackDictionary.Keys.CopyTo(ids, 0);

            // Sort the keys by there level, lowest -> highest
            Array.Sort(ids, new Tile.ComparerID());

            var count = 0;

            // Foreach key read back the tiles data until the maxReadBacksPerFrame limit is reached
            foreach (var id in ids)
            {
                var treeZ = NeedsReadBackDictionary[id];

                // If elevations container already contains key then data has been read back before so just reapply the [min, max] values to TerranQuad
                if (ElevationsDicionary.ContainsKey(id))
                {
                    var info = ElevationsDicionary.Get(id);

                    treeZ.TerrainQuad.ZMin = info.Min;
                    treeZ.TerrainQuad.ZMax = info.Max;

                    NeedsReadBackDictionary.Remove(id);
                }
                else
                {
                    // If for some reason the tile is null remove from container and continue
                    if (treeZ.Tile == null)
                    {
                        NeedsReadBackDictionary.Remove(id);

                        continue;
                    }

                    var slot = treeZ.Tile.Slot[0] as GPUTileStorage.GPUSlot;

                    // If for some reason this is not a GPUSlot remove and continue
                    if (slot == null)
                    {
                        NeedsReadBackDictionary.Remove(id);

                        continue;
                    }

                    var texture = slot.Texture;
                    var size    = texture.width * texture.height;

                    var elevationInfo = new ElevationInfo();
                    elevationInfo.Elevations = new float[size];

                    // Read back heights data from texture
                    CBUtility.ReadFromRenderTexture(texture, CBUtility.Channels.R, ElevationsBuffer, GodManager.Instance.ReadData);

                    // Copy into elevations info
                    ElevationsBuffer.GetData(elevationInfo.Elevations);

                    // Find the min/max values
                    for (int i = 0; i < size; i++)
                    {
                        if (elevationInfo.Elevations[i] < elevationInfo.Min)
                        {
                            elevationInfo.Min = elevationInfo.Elevations[i];
                        }
                        if (elevationInfo.Elevations[i] > elevationInfo.Max)
                        {
                            elevationInfo.Max = elevationInfo.Elevations[i];
                        }
                    }

                    // Update TerrainQuad
                    treeZ.TerrainQuad.ZMin = elevationInfo.Min;
                    treeZ.TerrainQuad.ZMax = elevationInfo.Max;

                    // Store elevations to prevent having to read back again soon
                    // Add to end of container
                    ElevationsDicionary.AddLast(id, elevationInfo);
                    NeedsReadBackDictionary.Remove(id);

                    count++;

                    // If the number of rad back to do per frame has hit the limit stop loop.
                    if (count >= MaxReadBacksPerFrame)
                    {
                        break;
                    }
                }
            }

            // If the number of elevation info to store has exceded limit remove from start of container
            while (ElevationsDicionary.Count() > MaxStoredElevations)
            {
                ElevationsDicionary.RemoveFirst();
            }
        }
예제 #17
0
        /// <summary>
        /// This function creates the elevations data and is called by the <see cref="Tile.Tasks.CreateTileTask"/> when the task is run by the <see cref="Utilities.Schedular"/>.
        /// The functions needs the tiles parent data to have already been created. If it has not the program will abort.
        /// </summary>
        /// <param name="level"></param>
        /// <param name="tx"></param>
        /// <param name="ty"></param>
        /// <param name="slot"></param>
        public override void DoCreateTile(int level, int tx, int ty, List <TileStorage.Slot> slot)
        {
            var gpuSlot = slot[0] as GPUTileStorage.GPUSlot;

            if (gpuSlot == null)
            {
                throw new NullReferenceException("gpuSlot");
            }

            var tileWidth = gpuSlot.Owner.TileSize;
            var tileSize  = tileWidth - (1 + GetBorder() * 2);

            GPUTileStorage.GPUSlot parentGpuSlot = null;

            var upsample   = level > 0;
            var parentTile = FindTile(level - 1, tx / 2, ty / 2, false, true);

            // TODO : Make it classwide...
            var residualTileSize = GetTileSize(0);
            var residualTexture  = RTExtensions.CreateRTexture(residualTileSize, 0, RenderTextureFormat.RFloat, FilterMode.Point, TextureWrapMode.Clamp);
            var residualBuffer   = new ComputeBuffer(residualTileSize * residualTileSize, sizeof(float));

            if (ResidualProducer != null)
            {
                if (ResidualProducer.HasTile(level, tx, ty))
                {
                    if (ResidualProducer.IsGPUProducer)
                    {
                        GPUTileStorage.GPUSlot residualGpuSlot = null;

                        var residualTile = ResidualProducer.FindTile(level, tx, ty, false, true);

                        if (residualTile != null)
                        {
                            residualGpuSlot = residualTile.GetSlot(0) as GPUTileStorage.GPUSlot;
                        }
                        else
                        {
                            throw new MissingTileException("Find residual tile failed");
                        }

                        if (residualGpuSlot == null)
                        {
                            throw new MissingTileException("Find parent tile failed");
                        }

                        UpSampleMaterial.SetTexture("_ResidualSampler", residualGpuSlot.Texture);
                        UpSampleMaterial.SetVector("_ResidualOSH", new Vector4(0.25f / (float)tileWidth, 0.25f / (float)tileWidth, 2.0f / (float)tileWidth, 1.0f));
                    }
                    else
                    {
                        CPUTileStorage.CPUSlot <float> residualCPUSlot = null;

                        var residualTile = ResidualProducer.FindTile(level, tx, ty, false, true);

                        if (residualTile != null)
                        {
                            residualCPUSlot = residualTile.GetSlot(0) as CPUTileStorage.CPUSlot <float>;
                        }
                        else
                        {
                            throw new MissingTileException("Find residual tile failed");
                        }

                        if (residualCPUSlot == null)
                        {
                            throw new MissingTileException("Find parent tile failed");
                        }

                        residualBuffer.SetData(residualCPUSlot.Data);

                        RTUtility.ClearColor(residualTexture);
                        CBUtility.WriteIntoRenderTexture(residualTexture, CBUtility.Channels.R, residualBuffer, GodManager.Instance.WriteData);
                        //RTUtility.SaveAs8bit(residualTileSize, residualTileSize, CBUtility.Channels.R, string.Format("Residual_{0}_{1}-{2}-{3}", TerrainNode.name, level, tx, ty), "/Resources/Preprocess/Textures/Debug/", residualCPUSlot.Data);

                        UpSampleMaterial.SetTexture("_ResidualSampler", residualTexture);
                        UpSampleMaterial.SetVector("_ResidualOSH", new Vector4(0.25f / (float)tileWidth, 0.25f / (float)tileWidth, 2.0f / (float)tileWidth, 1.0f));
                    }
                }
                else
                {
                    UpSampleMaterial.SetTexture("_ResidualSampler", null);
                    UpSampleMaterial.SetVector("_ResidualOSH", new Vector4(0.0f, 0.0f, 1.0f, 0.0f));
                }
            }
            else
            {
                UpSampleMaterial.SetTexture("_ResidualSampler", null);
                UpSampleMaterial.SetVector("_ResidualOSH", new Vector4(0.0f, 0.0f, 1.0f, 0.0f));
            }

            if (upsample)
            {
                if (parentTile != null)
                {
                    parentGpuSlot = parentTile.GetSlot(0) as GPUTileStorage.GPUSlot;
                }
                else
                {
                    throw new MissingTileException(string.Format("Find parent tile failed! {0}:{1}-{2}", level - 1, tx / 2, ty / 2));
                }
            }

            if (parentGpuSlot == null && upsample)
            {
                throw new NullReferenceException("parentGpuSlot");
            }

            var rootQuadSize = TerrainNode.TerrainQuadRoot.Length;

            var tileWSD = Vector4.zero;

            tileWSD.x = (float)tileWidth;
            tileWSD.y = (float)rootQuadSize / (float)(1 << level) / (float)tileSize;
            tileWSD.z = (float)tileSize / (float)(TerrainNode.ParentBody.GridResolution - 1);
            tileWSD.w = 0.0f;

            var tileScreenSize = (0.5 + (float)GetBorder()) / (tileWSD.x - 1 - (float)GetBorder() * 2);
            var tileSD         = new Vector2d(tileScreenSize, 1.0 + tileScreenSize * 2.0);

            UpSampleMaterial.SetVector("_TileWSD", tileWSD);
            UpSampleMaterial.SetVector("_TileSD", tileSD.ToVector2());

            if (upsample)
            {
                var parentTexture = parentGpuSlot.Texture;

                var dx = (float)(tx % 2) * (float)(tileSize / 2.0f);
                var dy = (float)(ty % 2) * (float)(tileSize / 2.0f);

                var coarseLevelOSL = new Vector4(dx / (float)parentTexture.width, dy / (float)parentTexture.height, 1.0f / (float)parentTexture.width, 0.0f);

                UpSampleMaterial.SetTexture("_CoarseLevelSampler", parentTexture);
                UpSampleMaterial.SetVector("_CoarseLevelOSL", coarseLevelOSL);
            }
            else
            {
                UpSampleMaterial.SetTexture("_CoarseLevelSampler", null);
                UpSampleMaterial.SetVector("_CoarseLevelOSL", new Vector4(-1.0f, -1.0f, -1.0f, -1.0f));
            }

            var rs = level < NoiseAmplitudes.Length ? NoiseAmplitudes[level] : 0.0f;

            var offset = new Vector4d(((double)tx / (1 << level) - 0.5) * rootQuadSize,
                                      ((double)ty / (1 << level) - 0.5) * rootQuadSize,
                                      rootQuadSize / (1 << level),
                                      TerrainNode.ParentBody.Size);

            UpSampleMaterial.SetFloat("_Amplitude", rs / (TerrainNode.ParentBody.Amplitude / 10.0f));
            UpSampleMaterial.SetFloat("_Frequency", TerrainNode.ParentBody.Frequency * (1 << level));
            UpSampleMaterial.SetVector("_Offset", offset.ToVector4());
            UpSampleMaterial.SetMatrix("_LocalToWorld", TerrainNode.FaceToLocal.ToMatrix4x4());

            if (TerrainNode.ParentBody.TCCPS != null)
            {
                TerrainNode.ParentBody.TCCPS.SetUniforms(UpSampleMaterial);
            }

            Graphics.Blit(null, gpuSlot.Texture, UpSampleMaterial);

            residualTexture.ReleaseAndDestroy();
            residualBuffer.ReleaseAndDisposeBuffer();

            base.DoCreateTile(level, tx, ty, slot);
        }
예제 #18
0
        private void GenerateWavesSpectrum()
        {
            // Slope variance due to all waves, by integrating over the full spectrum.
            // Used by the BRDF rendering model
            float theoreticSlopeVariance = 0.0f;
            float k = 5e-3f;

            while (k < 1e3f)
            {
                float nextK = k * 1.001f;
                theoreticSlopeVariance += k * k * Spectrum(k, 0, true) * (nextK - k);
                k = nextK;
            }

            float[] spectrum01 = new float[m_fourierGridSize * m_fourierGridSize * 4];
            float[] spectrum23 = new float[m_fourierGridSize * m_fourierGridSize * 4];

            int     idx;
            float   i;
            float   j;
            float   totalSlopeVariance = 0.0f;
            Vector2 sample12XY;
            Vector2 sample12ZW;
            Vector2 sample34XY;
            Vector2 sample34ZW;

            Random.InitState(0);


            for (int x = 0; x < m_fourierGridSize; x++)
            {
                for (int y = 0; y < m_fourierGridSize; y++)
                {
                    idx = x + y * m_fourierGridSize;
                    i   = (x >= m_fourierGridSize / 2) ? (float)(x - m_fourierGridSize) : (float)x;
                    j   = (y >= m_fourierGridSize / 2) ? (float)(y - m_fourierGridSize) : (float)y;

                    sample12XY = GetSpectrumSample(i, j, m_gridSizes.x, Mathf.PI / m_gridSizes.x);
                    sample12ZW = GetSpectrumSample(i, j, m_gridSizes.y, Mathf.PI * m_fsize / m_gridSizes.x);
                    sample34XY = GetSpectrumSample(i, j, m_gridSizes.z, Mathf.PI * m_fsize / m_gridSizes.y);
                    sample34ZW = GetSpectrumSample(i, j, m_gridSizes.w, Mathf.PI * m_fsize / m_gridSizes.z);

                    spectrum01[idx * 4 + 0] = sample12XY.x;
                    spectrum01[idx * 4 + 1] = sample12XY.y;
                    spectrum01[idx * 4 + 2] = sample12ZW.x;
                    spectrum01[idx * 4 + 3] = sample12ZW.y;

                    spectrum23[idx * 4 + 0] = sample34XY.x;
                    spectrum23[idx * 4 + 1] = sample34XY.y;
                    spectrum23[idx * 4 + 2] = sample34ZW.x;
                    spectrum23[idx * 4 + 3] = sample34ZW.y;

                    i *= 2.0f * Mathf.PI;
                    j *= 2.0f * Mathf.PI;

                    totalSlopeVariance += GetSlopeVariance(i / m_gridSizes.x, j / m_gridSizes.x, sample12XY);
                    totalSlopeVariance += GetSlopeVariance(i / m_gridSizes.y, j / m_gridSizes.y, sample12ZW);
                    totalSlopeVariance += GetSlopeVariance(i / m_gridSizes.z, j / m_gridSizes.z, sample34XY);
                    totalSlopeVariance += GetSlopeVariance(i / m_gridSizes.w, j / m_gridSizes.w, sample34ZW);
                }
            }

            //Write floating point data into render texture
            ComputeBuffer buffer = new ComputeBuffer(m_fourierGridSize * m_fourierGridSize, sizeof(float) * 4);

            buffer.SetData(spectrum01);
            CBUtility.WriteIntoRenderTexture(m_spectrum01, 4, buffer, World.WriteData);

            buffer.SetData(spectrum23);
            CBUtility.WriteIntoRenderTexture(m_spectrum23, 4, buffer, World.WriteData);

            buffer.Release();

            m_varianceShader.SetFloat("_SlopeVarianceDelta", 0.5f * (theoreticSlopeVariance - totalSlopeVariance));
            m_varianceShader.SetFloat("_VarianceSize", (float)m_varianceSize);
            m_varianceShader.SetFloat("_Size", m_fsize);
            m_varianceShader.SetVector("_GridSizes", m_gridSizes);
            m_varianceShader.SetTexture(0, "_Spectrum01", m_spectrum01);
            m_varianceShader.SetTexture(0, "_Spectrum23", m_spectrum23);
            m_varianceShader.SetTexture(0, "des", m_variance);

            m_varianceShader.Dispatch(0, m_varianceSize / 4, m_varianceSize / 4, m_varianceSize / 4);

            //Find the maximum value for slope variance

            buffer = new ComputeBuffer(m_varianceSize * m_varianceSize * m_varianceSize, sizeof(float));
            CBUtility.ReadFromRenderTexture(m_variance, 1, buffer, World.ReadData);

            float[] varianceData = new float[m_varianceSize * m_varianceSize * m_varianceSize];
            buffer.GetData(varianceData);

            m_maxSlopeVariance = 0.0f;
            for (int v = 0; v < m_varianceSize * m_varianceSize * m_varianceSize; v++)
            {
                m_maxSlopeVariance = Mathf.Max(m_maxSlopeVariance, varianceData[v]);
            }

            buffer.Release();
        }
        /// <summary>
        /// This function creates the elevations data and is called by the CreateTileTask when the task is run by the schedular
        /// The functions needs the tiles parent data to have already been created. If it has not the program will abort.
        /// </summary>
        public override void DoCreateTile(int level, int tx, int ty, List <Slot> slot)
        {
            GPUSlot gpuSlot = slot[0] as GPUSlot;

            int tileWidth = gpuSlot.Owner.TileSize;
            int b         = Border * 2 + 1;
            int tileSize  = tileWidth - b;

            GPUSlot parentGpuSlot = null;
            Tile    parentTile    = null;

            if (level > 0)
            {
                parentTile = FindTile(level - 1, tx / 2, ty / 2, false, true);

                if (parentTile != null)
                {
                    parentGpuSlot = parentTile.GetSlot(0) as GPUSlot;
                }
                else
                {
                    throw new MissingTileException("Find parent tile failed");
                }
            }

            float rootQuadSize = (float)TerrainNode.Root.Length;

            Vector4 tileWSD = new Vector4();

            tileWSD.x = tileWidth;
            tileWSD.y = rootQuadSize / (1 << level) / tileSize;
            tileWSD.z = (tileWidth - b) / (World.GridResolution - 1.0f);
            tileWSD.w = 0.0f;

            m_upsampleMat.SetVector(m_uniforms.tileWSD, tileWSD);

            if (level > 0)
            {
                RenderTexture tex = parentGpuSlot.Texture;

                m_upsampleMat.SetTexture(m_uniforms.coarseLevelSampler, tex);

                float dx = (tx % 2) * (tileSize / 2);
                float dy = (ty % 2) * (tileSize / 2);

                Vector4 coarseLevelOSL = new Vector4(dx / tex.width, dy / tex.height, 1.0f / tex.width, 0.0f);

                m_upsampleMat.SetVector(m_uniforms.coarseLevelOSL, coarseLevelOSL);
            }
            else
            {
                m_upsampleMat.SetVector(m_uniforms.coarseLevelOSL, new Vector4(-1.0f, -1.0f, -1.0f, -1.0f));
            }

            if (m_residualProducer != null && m_residualProducer.HasTile(level, tx, ty))
            {
                Tile            residualTile = m_residualProducer.FindTile(level, tx, ty, false, true);
                CPUSlot <float> residualSlot = null;

                if (residualTile != null)
                {
                    residualSlot = residualTile.GetSlot(0) as CPUSlot <float>;
                }
                else
                {
                    throw new MissingTileException("Find residual tile failed");
                }

                //Must clear residual tex before use or terrain will have artifacts at the seams. Not sure why.
                RTUtility.ClearColor(m_residualTex, Color.clear);

                m_residualBuffer.SetData(residualSlot.Data);
                CBUtility.WriteIntoRenderTexture(m_residualTex, 1, m_residualBuffer, World.WriteData);

                m_upsampleMat.SetTexture(m_uniforms.residualSampler, m_residualTex);
                m_upsampleMat.SetVector(m_uniforms.residualOSH, new Vector4(0.25f / tileWidth, 0.25f / tileWidth, 2.0f / tileWidth, 1.0f));
            }
            else
            {
                m_upsampleMat.SetTexture(m_uniforms.residualSampler, null);
                m_upsampleMat.SetVector(m_uniforms.residualOSH, new Vector4(0.0f, 0.0f, 1.0f, 0.0f));
            }

            float rs = level < m_noiseAmp.Length ? m_noiseAmp[level] : 0.0f;

            int noiseL = 0;
            int face   = TerrainNode.Face;

            if (rs != 0.0f)
            {
                if (face == 1)
                {
                    int offset  = 1 << level;
                    int bottomB = m_noise.Noise2D(tx + 0.5f, ty + offset) > 0.0f ? 1 : 0;
                    int rightB  = (tx == offset - 1 ? m_noise.Noise2D(ty + offset + 0.5f, offset) : m_noise.Noise2D(tx + 1.0f, ty + offset + 0.5f)) > 0.0f ? 2 : 0;
                    int topB    = (ty == offset - 1 ? m_noise.Noise2D((3.0f * offset - 1.0f - tx) + 0.5f, offset) : m_noise.Noise2D(tx + 0.5f, ty + offset + 1.0f)) > 0.0f ? 4 : 0;
                    int leftB   = (tx == 0 ? m_noise.Noise2D((4.0f * offset - 1.0f - ty) + 0.5f, offset) : m_noise.Noise2D(tx, ty + offset + 0.5f)) > 0.0f ? 8 : 0;
                    noiseL = bottomB + rightB + topB + leftB;
                }
                else if (face == 6)
                {
                    int offset  = 1 << level;
                    int bottomB = (ty == 0 ? m_noise.Noise2D((3.0f * offset - 1.0f - tx) + 0.5f, 0) : m_noise.Noise2D(tx + 0.5f, ty - offset)) > 0.0f ? 1 : 0;
                    int rightB  = (tx == offset - 1.0f ? m_noise.Noise2D((2.0f * offset - 1.0f - ty) + 0.5f, 0) : m_noise.Noise2D(tx + 1.0f, ty - offset + 0.5f)) > 0.0f ? 2 : 0;
                    int topB    = m_noise.Noise2D(tx + 0.5f, ty - offset + 1.0f) > 0.0f ? 4 : 0;
                    int leftB   = (tx == 0 ? m_noise.Noise2D(3.0f * offset + ty + 0.5f, 0) : m_noise.Noise2D(tx, ty - offset + 0.5f)) > 0.0f ? 8 : 0;
                    noiseL = bottomB + rightB + topB + leftB;
                }
                else
                {
                    int offset  = (1 << level) * (face - 2);
                    int bottomB = m_noise.Noise2D(tx + offset + 0.5f, ty) > 0.0f ? 1 : 0;
                    int rightB  = m_noise.Noise2D((tx + offset + 1) % (4 << level), ty + 0.5f) > 0.0f ? 2 : 0;
                    int topB    = m_noise.Noise2D(tx + offset + 0.5f, ty + 1.0f) > 0.0f ? 4 : 0;
                    int leftB   = m_noise.Noise2D(tx + offset, ty + 0.5f) > 0.0f ? 8 : 0;
                    noiseL = bottomB + rightB + topB + leftB;
                }
            }

            int[] noiseRs = new int[] { 0, 0, 1, 0, 2, 0, 1, 0, 3, 3, 1, 3, 2, 2, 1, 0 };
            int   noiseR  = noiseRs[noiseL];

            int[] noiseLs = new int[] { 0, 1, 1, 2, 1, 3, 2, 4, 1, 2, 3, 4, 2, 4, 4, 5 };
            noiseL = noiseLs[noiseL];

            m_upsampleMat.SetTexture(m_uniforms.noiseSampler, m_noiseTextures[noiseL]);
            m_upsampleMat.SetVector(m_uniforms.noiseUVLH, new Vector4(noiseR, (noiseR + 1) % 4, 0, rs));

            Graphics.Blit(null, gpuSlot.Texture, m_upsampleMat);

            base.DoCreateTile(level, tx, ty, slot);
        }
예제 #20
0
        /// <summary>
        /// Generates the wave spectrum based on the
        /// settings wind speed, wave amp and wave age.
        /// If these values change this function must be called again.
        /// </summary>
        void GenerateWavesSpectrum()
        {
            // Slope variance due to all waves, by integrating over the full spectrum.
            float theoreticSlopeVariance = 0.0f;
            float k = 5e-3f;

            while (k < 1e3f)
            {
                float nextK = k * 1.001f;
                theoreticSlopeVariance += k * k * Spectrum(k, 0, true) * (nextK - k);
                k = nextK;
            }

            float[] spectrum01 = new float[m_size * m_size * 4];
            float[] spectrum23 = new float[m_size * m_size * 4];

            int     idx;
            float   i;
            float   j;
            float   totalSlopeVariance = 0.0f;
            Vector2 sample12XY;
            Vector2 sample12ZW;
            Vector2 sample34XY;
            Vector2 sample34ZW;

            UnityEngine.Random.seed = 0;

            for (int x = 0; x < m_size; x++)
            {
                for (int y = 0; y < m_size; y++)
                {
                    idx = x + y * m_size;
                    i   = (x >= m_size / 2) ? (float)(x - m_size) : (float)x;
                    j   = (y >= m_size / 2) ? (float)(y - m_size) : (float)y;

                    sample12XY = GetSpectrumSample(i, j, m_gridSizes.x, Mathf.PI / m_gridSizes.x);
                    sample12ZW = GetSpectrumSample(i, j, m_gridSizes.y, Mathf.PI * m_fsize / m_gridSizes.x);
                    sample34XY = GetSpectrumSample(i, j, m_gridSizes.z, Mathf.PI * m_fsize / m_gridSizes.y);
                    sample34ZW = GetSpectrumSample(i, j, m_gridSizes.w, Mathf.PI * m_fsize / m_gridSizes.z);

                    spectrum01[idx * 4 + 0] = sample12XY.x;
                    spectrum01[idx * 4 + 1] = sample12XY.y;
                    spectrum01[idx * 4 + 2] = sample12ZW.x;
                    spectrum01[idx * 4 + 3] = sample12ZW.y;

                    spectrum23[idx * 4 + 0] = sample34XY.x;
                    spectrum23[idx * 4 + 1] = sample34XY.y;
                    spectrum23[idx * 4 + 2] = sample34ZW.x;
                    spectrum23[idx * 4 + 3] = sample34ZW.y;

                    i *= 2.0f * Mathf.PI;
                    j *= 2.0f * Mathf.PI;

                    totalSlopeVariance += GetSlopeVariance(i / m_gridSizes.x, j / m_gridSizes.x, sample12XY);
                    totalSlopeVariance += GetSlopeVariance(i / m_gridSizes.y, j / m_gridSizes.y, sample12ZW);
                    totalSlopeVariance += GetSlopeVariance(i / m_gridSizes.z, j / m_gridSizes.z, sample34XY);
                    totalSlopeVariance += GetSlopeVariance(i / m_gridSizes.w, j / m_gridSizes.w, sample34ZW);
                }
            }

            //Write floating point data into render texture
            ComputeBuffer buffer = new ComputeBuffer(m_size * m_size, sizeof(float) * 4);

            buffer.SetData(spectrum01);
            CBUtility.WriteIntoRenderTexture(m_spectrum01, 4, buffer, m_writeData);

            buffer.SetData(spectrum23);
            CBUtility.WriteIntoRenderTexture(m_spectrum23, 4, buffer, m_writeData);

            buffer.Release();

            m_varianceShader.SetFloat("_SlopeVarianceDelta", 0.5f * (theoreticSlopeVariance - totalSlopeVariance));
            m_varianceShader.SetFloat("_VarianceSize", (float)m_varianceSize);
            m_varianceShader.SetFloat("_Size", m_fsize);
            m_varianceShader.SetVector("_GridSizes", m_gridSizes);
            m_varianceShader.SetTexture(0, "_Spectrum01", m_spectrum01);
            m_varianceShader.SetTexture(0, "_Spectrum23", m_spectrum23);
            m_varianceShader.SetTexture(0, "des", m_variance);

            m_varianceShader.Dispatch(0, m_varianceSize / 4, m_varianceSize / 4, m_varianceSize / 4);
        }
예제 #21
0
        void Start()
        {
            ComputeShader writeData = Resources.Load <ComputeShader>("Ocean/WriteData");

            if (writeData == null)
            {
                throw new ArgumentException("Could not find Write compute shader in the resources folder");
            }

            m_skyMap            = new RenderTexture(512, 512, 0, RenderTextureFormat.ARGBHalf);
            m_skyMap.filterMode = FilterMode.Trilinear;
            m_skyMap.wrapMode   = TextureWrapMode.Clamp;
            m_skyMap.anisoLevel = 9;
            m_skyMap.useMipMap  = true;
            m_skyMap.Create();

            m_transmittance                   = new RenderTexture(TRANSMITTANCE_WIDTH, TRANSMITTANCE_HEIGHT, 0, RenderTextureFormat.ARGBHalf);
            m_transmittance.wrapMode          = TextureWrapMode.Clamp;
            m_transmittance.filterMode        = FilterMode.Bilinear;
            m_transmittance.enableRandomWrite = true;
            m_transmittance.Create();

            m_irradiance                   = new RenderTexture(IRRADIANCE_WIDTH, IRRADIANCE_HEIGHT, 0, RenderTextureFormat.ARGBHalf);
            m_irradiance.wrapMode          = TextureWrapMode.Clamp;
            m_irradiance.filterMode        = FilterMode.Bilinear;
            m_irradiance.enableRandomWrite = true;
            m_irradiance.Create();

            m_inscatter                   = new RenderTexture(INSCATTER_WIDTH, INSCATTER_HEIGHT, 0, RenderTextureFormat.ARGBHalf);
            m_inscatter.volumeDepth       = INSCATTER_DEPTH;
            m_inscatter.wrapMode          = TextureWrapMode.Clamp;
            m_inscatter.filterMode        = FilterMode.Bilinear;
            m_inscatter.isVolume          = true;
            m_inscatter.enableRandomWrite = true;
            m_inscatter.Create();


            //Transmittance is responsible for the change in the sun color as it moves
            //The raw file is a 2D array of 32 bit floats with a range of 0 to 1
            string path = Application.dataPath + "/Resources/Textures/transmittance.raw";

            ComputeBuffer buffer = new ComputeBuffer(TRANSMITTANCE_WIDTH * TRANSMITTANCE_HEIGHT, sizeof(float) * TRANSMITTANCE_CHANNELS);

            CBUtility.WriteIntoRenderTexture(m_transmittance, TRANSMITTANCE_CHANNELS, path, buffer, writeData);
            buffer.Release();

            //Iirradiance is responsible for the change in the sky color as the sun moves
            //The raw file is a 2D array of 32 bit floats with a range of 0 to 1
            path = Application.dataPath + "/Resources/Textures/irradiance.raw";

            buffer = new ComputeBuffer(IRRADIANCE_WIDTH * IRRADIANCE_HEIGHT, sizeof(float) * IRRADIANCE_CHANNELS);
            CBUtility.WriteIntoRenderTexture(m_irradiance, IRRADIANCE_CHANNELS, path, buffer, writeData);
            buffer.Release();

            //Inscatter is responsible for the change in the sky color as the sun moves
            //The raw file is a 4D array of 32 bit floats with a range of 0 to 1.589844
            //As there is not such thing as a 4D texture the data is packed into a 2D texture
            //and the shader manually performs the sample for the 3rd and 4th dimension
            path = Application.dataPath + "/Resources/Textures/inscatter.raw";

            buffer = new ComputeBuffer(INSCATTER_WIDTH * INSCATTER_HEIGHT * INSCATTER_DEPTH, sizeof(float) * INSCATTER_CHANNELS);
            CBUtility.WriteIntoRenderTexture(m_inscatter, INSCATTER_CHANNELS, path, buffer, writeData);
            buffer.Release();

            InitMaterial(m_skyMapMaterial);
            InitMaterial(m_skyMaterial);
            //ocean mat needs some of these uniforms so may as well set them here
            InitMaterial(m_oceanMaterial);
        }
        /// <summary>
        /// Creates a series of textures that contain random noise.
        /// These texture tile together using the Wang Tiling method.
        /// Used by the UpSample shader to create fractal noise for the terrain elevations.
        /// </summary>
        private void CreateDemNoise()
        {
            int tileWidth = Cache.GetStorage(0).TileSize;

            m_noiseTextures = new RenderTexture[6];

            int[] layers = new int[] { 0, 1, 3, 5, 7, 15 };
            int   rand   = 1234567;

            for (int nl = 0; nl < 6; ++nl)
            {
                float[] noiseArray = new float[tileWidth * tileWidth];
                int     l          = layers[nl];

                ComputeBuffer buffer = new ComputeBuffer(tileWidth * tileWidth, sizeof(float));

                // corners
                for (int j = 0; j < tileWidth; ++j)
                {
                    for (int i = 0; i < tileWidth; ++i)
                    {
                        noiseArray[i + j * tileWidth] = 0.0f;
                    }
                }

                // bottom border
                Random.InitState((l & 1) == 0 ? 7654321 : 5647381);
                for (int h = 5; h <= tileWidth / 2; ++h)
                {
                    float N = Rand();
                    noiseArray[h + 2 * tileWidth] = N;
                    noiseArray[(tileWidth - 1 - h) + 2 * tileWidth] = N;
                }

                for (int v = 3; v < 5; ++v)
                {
                    for (int h = 5; h < tileWidth - 5; ++h)
                    {
                        float N = Rand();
                        noiseArray[h + v * tileWidth] = N;
                        noiseArray[(tileWidth - 1 - h) + (4 - v) * tileWidth] = N;
                    }
                }

                // right border
                Random.InitState((l & 2) == 0 ? 7654321 : 5647381);
                for (int v = 5; v <= tileWidth / 2; ++v)
                {
                    float N = Rand();
                    noiseArray[(tileWidth - 3) + v * tileWidth] = N;
                    noiseArray[(tileWidth - 3) + (tileWidth - 1 - v) * tileWidth] = N;
                }

                for (int h = tileWidth - 4; h >= tileWidth - 5; --h)
                {
                    for (int v = 5; v < tileWidth - 5; ++v)
                    {
                        float N = Rand();
                        noiseArray[h + v * tileWidth] = N;
                        noiseArray[(2 * tileWidth - 6 - h) + (tileWidth - 1 - v) * tileWidth] = N;
                    }
                }

                // top border
                Random.InitState((l & 4) == 0 ? 7654321 : 5647381);
                for (int h = 5; h <= tileWidth / 2; ++h)
                {
                    float N = Rand();
                    noiseArray[h + (tileWidth - 3) * tileWidth] = N;
                    noiseArray[(tileWidth - 1 - h) + (tileWidth - 3) * tileWidth] = N;
                }

                for (int v = tileWidth - 2; v < tileWidth; ++v)
                {
                    for (int h = 5; h < tileWidth - 5; ++h)
                    {
                        float N = Rand();
                        noiseArray[h + v * tileWidth] = N;
                        noiseArray[(tileWidth - 1 - h) + (2 * tileWidth - 6 - v) * tileWidth] = N;
                    }
                }

                // left border
                Random.InitState((l & 8) == 0 ? 7654321 : 5647381);
                for (int v = 5; v <= tileWidth / 2; ++v)
                {
                    float N = Rand();
                    noiseArray[2 + v * tileWidth] = N;
                    noiseArray[2 + (tileWidth - 1 - v) * tileWidth] = N;
                }

                for (int h = 1; h >= 0; --h)
                {
                    for (int v = 5; v < tileWidth - 5; ++v)
                    {
                        float N = Rand();
                        noiseArray[h + v * tileWidth] = N;
                        noiseArray[(4 - h) + (tileWidth - 1 - v) * tileWidth] = N;
                    }
                }

                // center
                Random.InitState(rand);
                for (int v = 5; v < tileWidth - 5; ++v)
                {
                    for (int h = 5; h < tileWidth - 5; ++h)
                    {
                        float N = Rand();
                        noiseArray[h + v * tileWidth] = N;
                    }
                }

                //randomize for next texture
                rand = (rand * 1103515245 + 12345) & 0x7FFFFFFF;

                m_noiseTextures[nl]                   = new RenderTexture(tileWidth, tileWidth, 0, RenderTextureFormat.RHalf);
                m_noiseTextures[nl].wrapMode          = TextureWrapMode.Repeat;
                m_noiseTextures[nl].filterMode        = FilterMode.Point;
                m_noiseTextures[nl].enableRandomWrite = true;
                m_noiseTextures[nl].Create();
                //write data into render texture
                buffer.SetData(noiseArray);
                CBUtility.WriteIntoRenderTexture(m_noiseTextures[nl], 1, buffer, World.WriteData);
                buffer.Release();
            }
        }
예제 #23
0
        private void GenerateWavesSpectrum()
        {
            // Slope variance due to all waves, by integrating over the full spectrum.
            // Used by the BRDF rendering model

            var theoreticSlopeVariance = 0.0f;
            var k = 5e-3f;

            while (k < 1e3f)
            {
                var nextK = k * 1.001f;

                theoreticSlopeVariance += k * k * Spectrum(k, 0, true) * (nextK - k);

                k = nextK;
            }

            var spectrum01 = new float[FourierGridSize * FourierGridSize * 4];
            var spectrum23 = new float[FourierGridSize * FourierGridSize * 4];

            int   idx;
            float i;
            float j;
            float totalSlopeVariance = 0.0f;

            Vector2 sample12XY = Vector2.zero;
            Vector2 sample12ZW = Vector2.zero;
            Vector2 sample34XY = Vector2.zero;
            Vector2 sample34ZW = Vector2.zero;

            Random.InitState(0);

            for (int x = 0; x < FourierGridSize; x++)
            {
                for (int y = 0; y < FourierGridSize; y++)
                {
                    idx = x + y * FourierGridSize;
                    i   = (x >= FourierGridSize / 2) ? (float)(x - FourierGridSize) : (float)x;
                    j   = (y >= FourierGridSize / 2) ? (float)(y - FourierGridSize) : (float)y;

                    sample12XY = GetSpectrumSample(i, j, GridSizes.x, Mathf.PI / GridSizes.x);
                    sample12ZW = GetSpectrumSample(i, j, GridSizes.y, Mathf.PI * MapSize / GridSizes.x);
                    sample34XY = GetSpectrumSample(i, j, GridSizes.z, Mathf.PI * MapSize / GridSizes.y);
                    sample34ZW = GetSpectrumSample(i, j, GridSizes.w, Mathf.PI * MapSize / GridSizes.z);

                    spectrum01[idx * 4 + 0] = sample12XY.x;
                    spectrum01[idx * 4 + 1] = sample12XY.y;
                    spectrum01[idx * 4 + 2] = sample12ZW.x;
                    spectrum01[idx * 4 + 3] = sample12ZW.y;

                    spectrum23[idx * 4 + 0] = sample34XY.x;
                    spectrum23[idx * 4 + 1] = sample34XY.y;
                    spectrum23[idx * 4 + 2] = sample34ZW.x;
                    spectrum23[idx * 4 + 3] = sample34ZW.y;

                    i *= 2.0f * Mathf.PI;
                    j *= 2.0f * Mathf.PI;

                    totalSlopeVariance += GetSlopeVariance(i / GridSizes.x, j / GridSizes.x, sample12XY);
                    totalSlopeVariance += GetSlopeVariance(i / GridSizes.y, j / GridSizes.y, sample12ZW);
                    totalSlopeVariance += GetSlopeVariance(i / GridSizes.z, j / GridSizes.z, sample34XY);
                    totalSlopeVariance += GetSlopeVariance(i / GridSizes.w, j / GridSizes.w, sample34ZW);
                }
            }

            //Write floating point data into render texture
            var buffer = new ComputeBuffer(FourierGridSize * FourierGridSize, sizeof(float) * 4);

            buffer.SetData(spectrum01);
            CBUtility.WriteIntoRenderTexture(Spectrum01, CBUtility.Channels.RGBA, buffer, GodManager.Instance.WriteData);

            buffer.SetData(spectrum23);
            CBUtility.WriteIntoRenderTexture(Spectrum23, CBUtility.Channels.RGBA, buffer, GodManager.Instance.WriteData);

            buffer.Release();

            var varianceShader = GodManager.Instance.Variance;

            varianceShader.SetFloat("_SlopeVarianceDelta", 0.5f * (theoreticSlopeVariance - totalSlopeVariance));
            varianceShader.SetFloat("_VarianceSize", (float)VarianceSize);
            varianceShader.SetFloat("_Size", MapSize);
            varianceShader.SetVector("_GridSizes", GridSizes);
            varianceShader.SetTexture(0, "_Spectrum01", Spectrum01);
            varianceShader.SetTexture(0, "_Spectrum23", Spectrum23);
            varianceShader.SetTexture(0, "des", Variance);

            varianceShader.Dispatch(0, VarianceSize / 4, VarianceSize / 4, VarianceSize / 4);

            // Find the maximum value for slope variance

            buffer = new ComputeBuffer(VarianceSize * VarianceSize * VarianceSize, sizeof(float));
            CBUtility.ReadFromRenderTexture(Variance, CBUtility.Channels.R, buffer, GodManager.Instance.ReadData);

            var varianceData = new float[VarianceSize * VarianceSize * VarianceSize];

            buffer.GetData(varianceData);

            MaxSlopeVariance = 0.0f;

            for (int v = 0; v < VarianceSize * VarianceSize * VarianceSize; v++)
            {
                MaxSlopeVariance = Mathf.Max(MaxSlopeVariance, varianceData[v]);
            }

            buffer.Release();
        }
    public void SaveTextureAsKTX(RenderTexture rtex, String name, bool tile3D = false)
    {
        int texDepth  = rtex.volumeDepth;
        int texWidth  = rtex.width;
        int texHeight = rtex.height;
        int floatSize = sizeof(float);
        int channels  = 4;

        bool tileEnabled = tile3D && texDepth > 1;

        if (tileEnabled)
        {
            texWidth *= texDepth;
            texDepth  = 1;
        }


        int texSize = texWidth * texHeight * texDepth;

        ComputeBuffer buffer = new ComputeBuffer(texSize, floatSize * channels); // 4 bytes for float and 4 channels

        CBUtility.ReadFromRenderTexture(rtex, channels, buffer, FrostbiteReadShader);

        float[] data = new float[texSize * channels];

        buffer.GetData(data);

        Byte[] header =
        {
            0xAB, 0x4B, 0x54, 0x58, // first four bytes of Byte[12] identifier
            0x20, 0x31, 0x31, 0xBB, // next four bytes of Byte[12] identifier
            0x0D, 0x0A, 0x1A, 0x0A, // final four bytes of Byte[12] identifier
            0x01, 0x02, 0x03, 0x04, // Byte[4] endianess (Big endian in this case)
        };

        FileStream   fs     = new FileStream("Assets/Textures/" + name + ".ktx", FileMode.OpenOrCreate);
        BinaryWriter writer = new BinaryWriter(fs);

        writer.Write(header);

        UInt32 glType               = 0x140B; // HALF
        UInt32 glTypeSize           = 2;      // 2 bytes
        UInt32 glFormat             = 0x1908; // RGBA
        UInt32 glInterformat        = 0x881A; // RGBA FLOAT16
        UInt32 glBaseInternalFormat = 0x1908; // RGBA
        UInt32 width         = (UInt32)texWidth;
        UInt32 height        = (UInt32)texHeight;
        UInt32 depth         = (UInt32)(texDepth == 1 ? 0 : texDepth);
        UInt32 numOfArrElem  = 0;
        UInt32 numOfFace     = 1;
        UInt32 numOfMip      = 1;
        UInt32 bytesOfKeyVal = 0;

        writer.Write(glType);
        writer.Write(glTypeSize);
        writer.Write(glFormat);
        writer.Write(glInterformat);
        writer.Write(glBaseInternalFormat);
        writer.Write(width);
        writer.Write(height);
        writer.Write(depth);
        writer.Write(numOfArrElem);
        writer.Write(numOfFace);
        writer.Write(numOfMip);
        writer.Write(bytesOfKeyVal);

        UInt32 imageSize = (UInt32)(texSize * channels * glTypeSize);

        writer.Write(imageSize);

        if (tileEnabled)
        {
            for (int j = 0; j < rtex.height; j++)
            {
                for (int k = 0; k < rtex.volumeDepth; k++)
                {
                    for (int i = 0; i < rtex.width; i++)
                    {
                        int startIndex = k * rtex.width * rtex.height * channels + j * rtex.width * channels + i * channels;
                        writer.Write(Half.GetBytes((Half)data[startIndex]));
                        writer.Write(Half.GetBytes((Half)data[startIndex + 1]));
                        writer.Write(Half.GetBytes((Half)data[startIndex + 2]));
                        writer.Write(Half.GetBytes((Half)data[startIndex + 3]));
                    }
                }
            }
        }
        else
        {
            for (int i = 0; i < data.Length; ++i)
            {
                writer.Write(Half.GetBytes((Half)data[i]));
            }
        }

        writer.Close();
        fs.Close();
        buffer.Release();
    }