DOTS计算Voronoi图形生成,根据点自动划分区域生成多边形

 

 如图,生成Voronoi图形,代码如下。

 

using UnityEngine;
using Unity.Mathematics;
using Unity.Jobs;
using Unity.Collections;
using Unity.Profiling;

[ExecuteInEditMode]
public class VoronoiTextureBurstJobComponent : MonoBehaviour
{
    [SerializeField][Min(1)] uint _seed = 769;
    [SerializeField][Range(2,512)] int _width = 300, _height = 300;
    [SerializeField][Range(1,64)] int _numRandomPoints = 3;

    Texture2D _texture;
    NativeList<ushort2> _points;
    uint lastSeedUsed = 0;
    
    void OnValidate ()
    {
        bool inputChanged = false;

        if( !_points.IsCreated )
        {
            _points = new NativeList<ushort2>( Allocator.Persistent );
            inputChanged = true;
        }
        if( _points.Length!=_numRandomPoints )
        {
            _points.Length = _numRandomPoints;
            inputChanged = true;
        }

        if( _texture==null || _texture.width!=_width || _texture.height!=_height )
        {
            if( _texture==null ) DestroyItWillYou( _texture );
            _texture = new Texture2D( _width , _height , TextureFormat.RGBA32 , mipCount:0 , linear:true );
            inputChanged = true;
        }

        if( inputChanged || _seed!=lastSeedUsed )
        {
            lastSeedUsed = _seed;
            var watch = System.Diagnostics.Stopwatch.StartNew();
            var dep = new FillRandomPointsJob{
                TextureWidth    = _width ,
                TextureHeight    = _height ,
                Seed            = _seed ,
                Points            = _points ,
            }.Schedule();
            NativeArray<RGBA32> textureData = _texture.GetRawTextureData<RGBA32>();
            new GenerateVoronoiDiagramJob{
                Points            = _points ,
                TextureWidth    = _width ,
                TextureHeight    = _height ,
                TextureOutput    = textureData ,
            }.Schedule( textureData.Length , _width , dep ).Complete();
            _texture.Apply();
            Debug.Log( $"jobs took {watch.ElapsedMilliseconds} [ms] to complete");
        }
    }

    void OnEnable ()
        => OnValidate();

    void OnDisable ()
    {
        if( _texture!=null ) DestroyItWillYou(_texture);
        if( _points.IsCreated ) _points.Dispose();
    }

    void OnGUI ()
        => GUI.DrawTexture( new Rect(0,0,_width,_height) , _texture );

    void DestroyItWillYou ( Object obj )
    {
        if( Application.isPlaying ) Destroy( obj );
        else DestroyImmediate( obj );
    }

    [Unity.Burst.BurstCompile]
    struct FillRandomPointsJob : IJob
    {
        public int TextureWidth, TextureHeight;
        public uint Seed;
        [WriteOnly] public NativeArray<ushort2> Points;
        void IJob.Execute ()
        {
            var rnd = new Unity.Mathematics.Random( Seed );
            int len = Points.Length;
            for( int i=0 ; i<len ; i++ )
                Points[i] = new ushort2{ x=(ushort)rnd.NextInt(TextureWidth) , y=(ushort)rnd.NextInt(TextureHeight) };
        }
    }

    [Unity.Burst.BurstCompile]
    struct GenerateVoronoiDiagramJob : IJobParallelFor
    {
        [ReadOnly] public NativeArray<ushort2> Points;
        public int TextureWidth, TextureHeight;
        [WriteOnly] public NativeArray<RGBA32> TextureOutput;
        void IJobParallelFor.Execute ( int jobIndex )
        {
            float2 pixelPos = new int2{ x=jobIndex%TextureWidth , y=jobIndex/TextureWidth };

            float pointDist = float.MaxValue;
            int pointIndex = -1;
            for( int i=Points.Length-1 ; i!=-1 ; i-- )
            {
                float2 pointPos = new float2{ x=Points[i].x , y=Points[i].y };
                
                float dist = math.lengthsq( pointPos - pixelPos );// dist sq
                
                // float2 vec =  math.abs( pointPos - pixelPos );
                // float dist = vec.x + vec.y;// manhattan distance
                
                if( dist<pointDist )
                {
                    pointDist = dist;
                    pointIndex = i;
                }

                if( dist<math.max(math.min(TextureWidth*0.01f,TextureHeight*0.01f),1) )
                {
                    TextureOutput[jobIndex] = new RGBA32{ r=0 , g=0 , b=0 , a=255 };
                    return;
                }
            }

            RGBA32 colorFromCoord;
            {
                ushort2 pointCoord = Points[pointIndex];
                float2 f = new float2{ x=pointCoord.x , y=pointCoord.y } / ( new float2{ x=TextureWidth , y=TextureHeight } - new float2{ x=1 , y=1 } );
                byte r = (byte)( 255*f.x );
                byte g = (byte)( 255*f.y );
                byte b = (byte)( (r+g)%255 );
                colorFromCoord = new RGBA32{ r=r , g=g , b=b , a=255 };
            }
            TextureOutput[jobIndex] = colorFromCoord;
        }
    }

    struct ushort2 { public ushort x, y; }

    struct RGBA32 { public byte r, g, b, a; }

}

 

posted @ 2024-09-24 20:56  Flamesky  阅读(18)  评论(0编辑  收藏  举报