共用方式為


SkiaSharp 雜訊和撰寫

簡單的向量圖形通常看起來不自然。 直線、平滑曲線和純色不會與真實世界物件的不完美相似。 在為 1982 年電影 Tron 處理電腦產生的圖形時,計算機科學家 Ken Perlin 開始開發演算法,這些演算法會使用隨機程式為這些影像提供更真實的紋理。 1997年,肯·佩林榮獲奧斯卡技術成就獎。 他的作品被稱為佩林噪音,在SkiaSharp中得到了支援。 以下是範例:

Perlin 雜訊範例

如您所見,每個圖元不是隨機色彩值。 從像素到像素的連續性會產生隨機圖形。

Skia 中的 Perlin 雜訊支援是以 CSS 和 SVG 的 W3C 規格為基礎。 Filter Effects 模組層級 1 的第 8.20 節包含 C 程式代碼中的基礎 Perlin 雜訊演算法。

探索 Perlin 雜訊

類別 SKShader 會定義兩個不同的靜態方法,以產生 Perlin 雜訊: CreatePerlinNoiseFractalNoiseCreatePerlinNoiseTurbulence。 參數完全相同:

public static SkiaSharp CreatePerlinNoiseFractalNoise (float baseFrequencyX, float baseFrequencyY, int numOctaves, float seed);

public static SkiaSharp.SKShader CreatePerlinNoiseTurbulence (float baseFrequencyX, float baseFrequencyY, int numOctaves, float seed);

這兩種方法也存在於具有額外 SKPointI 參數的多載版本中。 Tiling Perlin 雜訊一節會討論這些多載。

這兩 baseFrequency 個自變數是SkiaSharp檔中定義的正值,範圍從0到1,但也可以設定為較高的值。 值越高,水平和垂直方向隨機影像的變更就越大。

numOctaves 是 1 或更新版本的整數。 它與演算法中的反覆專案因數有關。 每個額外的八度都貢獻了前一個八位的一半效果,因此效果會隨著較高的八進位值而減少。

參數 seed 是隨機數產生器的起點。 雖然指定為浮點值,但在使用分數之前會截斷,而 0 與 1 相同。

範例中的 [Perlin 雜訊] 頁面可讓您實驗 和 numOctaves 自變數的各種值baseFrequency。 以下是 XAML 檔案:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:skia="clr-namespace:SkiaSharp.Views.Forms;assembly=SkiaSharp.Views.Forms"
             x:Class="SkiaSharpFormsDemos.Effects.PerlinNoisePage"
             Title="Perlin Noise">

    <StackLayout>
        <skia:SKCanvasView x:Name="canvasView"
                           VerticalOptions="FillAndExpand"
                           PaintSurface="OnCanvasViewPaintSurface" />

        <Slider x:Name="baseFrequencyXSlider"
                Maximum="4"
                Margin="10, 0"
                ValueChanged="OnSliderValueChanged" />

        <Label x:Name="baseFrequencyXText"
               HorizontalTextAlignment="Center" />

        <Slider x:Name="baseFrequencyYSlider"
                Maximum="4"
                Margin="10, 0"
                ValueChanged="OnSliderValueChanged" />

        <Label x:Name="baseFrequencyYText"
               HorizontalTextAlignment="Center" />

        <StackLayout Orientation="Horizontal"
                     HorizontalOptions="Center"
                     Margin="10">

            <Label Text="{Binding Source={x:Reference octavesStepper},
                                  Path=Value,
                                  StringFormat='Number of Octaves: {0:F0}'}"
                   VerticalOptions="Center" />

            <Stepper x:Name="octavesStepper"
                     Minimum="1"
                     ValueChanged="OnStepperValueChanged" />
        </StackLayout>
    </StackLayout>
</ContentPage>

它會針對這兩個自變數使用兩SliderbaseFrequency個檢視。 若要展開較低值的範圍,滑桿是對數。 程序代碼後置檔案會從值的力量計算方法的Slider自變數SKShader。 檢視 Label 會顯示匯出值:

float baseFreqX = (float)Math.Pow(10, baseFrequencyXSlider.Value - 4);
baseFrequencyXText.Text = String.Format("Base Frequency X = {0:F4}", baseFreqX);

float baseFreqY = (float)Math.Pow(10, baseFrequencyYSlider.Value - 4);
baseFrequencyYText.Text = String.Format("Base Frequency Y = {0:F4}", baseFreqY);

Slider 1 的值對應至 0.001、Slideros 2 的值對應至 0.01、Slider3 的值對應至 0.1,而 Slider 4 的值對應至 1。

以下是包含該程式代碼的程式代碼後置檔案:

public partial class PerlinNoisePage : ContentPage
{
    public PerlinNoisePage()
    {
        InitializeComponent();
    }

    void OnSliderValueChanged(object sender, ValueChangedEventArgs args)
    {
        canvasView.InvalidateSurface();
    }

    void OnStepperValueChanged(object sender, ValueChangedEventArgs args)
    {
        canvasView.InvalidateSurface();
    }

    void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
    {
        SKImageInfo info = args.Info;
        SKSurface surface = args.Surface;
        SKCanvas canvas = surface.Canvas;

        canvas.Clear();

        // Get values from sliders and stepper
        float baseFreqX = (float)Math.Pow(10, baseFrequencyXSlider.Value - 4);
        baseFrequencyXText.Text = String.Format("Base Frequency X = {0:F4}", baseFreqX);

        float baseFreqY = (float)Math.Pow(10, baseFrequencyYSlider.Value - 4);
        baseFrequencyYText.Text = String.Format("Base Frequency Y = {0:F4}", baseFreqY);

        int numOctaves = (int)octavesStepper.Value;

        using (SKPaint paint = new SKPaint())
        {
            paint.Shader =
                SKShader.CreatePerlinNoiseFractalNoise(baseFreqX,
                                                       baseFreqY,
                                                       numOctaves,
                                                       0);

            SKRect rect = new SKRect(0, 0, info.Width, info.Height / 2);
            canvas.DrawRect(rect, paint);

            paint.Shader =
                SKShader.CreatePerlinNoiseTurbulence(baseFreqX,
                                                     baseFreqY,
                                                     numOctaves,
                                                     0);

            rect = new SKRect(0, info.Height / 2, info.Width, info.Height);
            canvas.DrawRect(rect, paint);
        }
    }
}

以下是在 iOS、Android 和 通用 Windows 平台 (UWP) 裝置上執行的程式。 分形噪音會顯示在畫布的上半部。 動蕩噪音位於下半場:

Perlin 雜訊

相同的自變數一律會產生從左上角開始的相同模式。 當您調整 UWP 視窗的寬度和高度時,此一致性是顯而易見的。 當 Windows 10 重繪螢幕時,畫布上半部的圖樣會維持不變。

雜訊模式會納入各種程度的透明度。 如果您在呼叫中 canvas.Clear() 設定色彩,透明度就會變得很明顯。 該色彩在圖案中變得突出。 您也會在結合多個著色器一節中看到此效果。

這些 Perlin 雜訊模式本身很少使用。 它們通常會受限於後續文章中討論的混合模式和色彩篩選條件。

波狀 Perlin 雜訊

建立 Perlin 雜訊的兩個靜態 SKShader 方法也存在於多載版本中。 CreatePerlinNoiseFractalNoiseCreatePerlinNoiseTurbulence 多載有額外的SKPointI參數:

public static SKShader CreatePerlinNoiseFractalNoise (float baseFrequencyX, float baseFrequencyY, int numOctaves, float seed, SKPointI tileSize);

public static SKShader CreatePerlinNoiseTurbulence (float baseFrequencyX, float baseFrequencyY, int numOctaves, float seed, SKPointI tileSize);

結構 SKPointI 是熟悉 SKPoint 結構的整數版本。 SKPointIX定義 型int別的 和 Y 屬性,而不是 float

這些方法會建立指定大小的重複模式。 在每個磚中,右邊緣與左邊緣相同,上邊緣與下邊緣相同。 此特性會在 [並排式 Perlin 雜訊 ] 頁面中示範。 XAML 檔案與上一個範例類似,但只有檢視 Stepper 可變更 seed 自變數:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:skia="clr-namespace:SkiaSharp.Views.Forms;assembly=SkiaSharp.Views.Forms"
             x:Class="SkiaSharpFormsDemos.Effects.TiledPerlinNoisePage"
             Title="Tiled Perlin Noise">

    <StackLayout>
        <skia:SKCanvasView x:Name="canvasView"
                           VerticalOptions="FillAndExpand"
                           PaintSurface="OnCanvasViewPaintSurface" />

        <StackLayout Orientation="Horizontal"
                     HorizontalOptions="Center"
                     Margin="10">

            <Label Text="{Binding Source={x:Reference seedStepper},
                                  Path=Value,
                                  StringFormat='Seed: {0:F0}'}"
                   VerticalOptions="Center" />

            <Stepper x:Name="seedStepper"
                     Minimum="1"
                     ValueChanged="OnStepperValueChanged" />

        </StackLayout>
    </StackLayout>
</ContentPage>

程序代碼後置檔案會定義磚大小的常數。 處理程式 PaintSurface 會建立該大小的點陣圖,以及 SKCanvas 繪製到該點陣圖的 。 方法 SKShader.CreatePerlinNoiseTurbulence 會建立具有該磚大小的著色器。 此著色器會在點陣圖上繪製:

public partial class TiledPerlinNoisePage : ContentPage
{
    const int TILE_SIZE = 200;

    public TiledPerlinNoisePage()
    {
        InitializeComponent();
    }

    void OnStepperValueChanged(object sender, ValueChangedEventArgs args)
    {
        canvasView.InvalidateSurface();
    }

    void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
    {
        SKImageInfo info = args.Info;
        SKSurface surface = args.Surface;
        SKCanvas canvas = surface.Canvas;

        canvas.Clear();

        // Get seed value from stepper
        float seed = (float)seedStepper.Value;

        SKRect tileRect = new SKRect(0, 0, TILE_SIZE, TILE_SIZE);

        using (SKBitmap bitmap = new SKBitmap(TILE_SIZE, TILE_SIZE))
        {
            using (SKCanvas bitmapCanvas = new SKCanvas(bitmap))
            {
                bitmapCanvas.Clear();

                // Draw tiled turbulence noise on bitmap
                using (SKPaint paint = new SKPaint())
                {
                    paint.Shader = SKShader.CreatePerlinNoiseTurbulence(
                                        0.02f, 0.02f, 1, seed,
                                        new SKPointI(TILE_SIZE, TILE_SIZE));

                    bitmapCanvas.DrawRect(tileRect, paint);
                }
            }

            // Draw tiled bitmap shader on canvas
            using (SKPaint paint = new SKPaint())
            {
                paint.Shader = SKShader.CreateBitmap(bitmap,
                                                     SKShaderTileMode.Repeat,
                                                     SKShaderTileMode.Repeat);
                canvas.DrawRect(info.Rect, paint);
            }

            // Draw rectangle showing tile
            using (SKPaint paint = new SKPaint())
            {
                paint.Style = SKPaintStyle.Stroke;
                paint.Color = SKColors.Black;
                paint.StrokeWidth = 2;

                canvas.DrawRect(tileRect, paint);
            }
        }
    }
}

建立位圖之後,會呼叫 來建立另一個 SKPaint 物件來建立並排的點陣圖模式 SKShader.CreateBitmap。 請注意 的兩個 SKShaderTileMode.Repeat自變數:

paint.Shader = SKShader.CreateBitmap(bitmap,
                                     SKShaderTileMode.Repeat,
                                     SKShaderTileMode.Repeat);

此著色器用來涵蓋畫布。 最後,另一個 SKPaint 對像是用來繪製矩形,以顯示原始點陣圖的大小。

seed只有參數可從使用者介面中選取。 seed如果在每個平臺上使用相同的模式,它們會顯示相同的模式。 不同的 seed 值會產生不同的模式:

並排的 Perlin 雜訊

左上角的 200 像素方形圖樣會順暢地流入其他磚。

結合多個著色器

類別 SKShader 包含方法 CreateColor ,這個方法會建立具有指定純色的著色器。 這個著色器本身並不十分有用,因為您可以直接將該色彩設定為 Color 對象的 屬性 SKPaint ,並將 屬性設定 Shader 為 null。

此方法 CreateColor 在定義的另一個方法 SKShader 中會變得很有用。 這個方法是 CreateCompose,結合兩個著色器。 以下是語法:

public static SKShader CreateCompose (SKShader dstShader, SKShader srcShader);

srcShader (來源著色器) 實際上是在 (目的地著色器) 之上dstShader繪製的。 如果來源著色器是純色或沒有透明度的漸層,目的地著色器將會完全遮蔽。

Perlin 雜訊著色器包含透明度。 如果該著色器是來源,目的地著色器將會透過透明區域顯示。

[撰寫 Perlin 雜訊] 頁面的 XAML 檔案幾乎與第一個 Perlin 雜訊頁面相同。 程序代碼後置檔案也類似。 但是原始 的 Perlin Noise 頁面會將 ShaderSKPaint 屬性設定為從靜態 CreatePerlinNoiseFractalNoiseCreatePerlinNoiseTurbulence 方法傳回的著色器。 這個 Composed Perlin 雜訊 頁面會呼叫 CreateCompose 組合著色器。 目的地是使用 CreateColor建立的純藍色著色器。 來源是 Perlin 雜訊著色器:

public partial class ComposedPerlinNoisePage : ContentPage
{
    public ComposedPerlinNoisePage()
    {
        InitializeComponent();
    }

    void OnSliderValueChanged(object sender, ValueChangedEventArgs args)
    {
        canvasView.InvalidateSurface();
    }

    void OnStepperValueChanged(object sender, ValueChangedEventArgs args)
    {
        canvasView.InvalidateSurface();
    }

    void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
    {
        SKImageInfo info = args.Info;
        SKSurface surface = args.Surface;
        SKCanvas canvas = surface.Canvas;

        canvas.Clear();

        // Get values from sliders and stepper
        float baseFreqX = (float)Math.Pow(10, baseFrequencyXSlider.Value - 4);
        baseFrequencyXText.Text = String.Format("Base Frequency X = {0:F4}", baseFreqX);

        float baseFreqY = (float)Math.Pow(10, baseFrequencyYSlider.Value - 4);
        baseFrequencyYText.Text = String.Format("Base Frequency Y = {0:F4}", baseFreqY);

        int numOctaves = (int)octavesStepper.Value;

        using (SKPaint paint = new SKPaint())
        {
            paint.Shader = SKShader.CreateCompose(
                SKShader.CreateColor(SKColors.Blue),
                SKShader.CreatePerlinNoiseFractalNoise(baseFreqX,
                                                       baseFreqY,
                                                       numOctaves,
                                                       0));

            SKRect rect = new SKRect(0, 0, info.Width, info.Height / 2);
            canvas.DrawRect(rect, paint);

            paint.Shader = SKShader.CreateCompose(
                SKShader.CreateColor(SKColors.Blue),
                SKShader.CreatePerlinNoiseTurbulence(baseFreqX,
                                                     baseFreqY,
                                                     numOctaves,
                                                     0));

            rect = new SKRect(0, info.Height / 2, info.Width, info.Height);
            canvas.DrawRect(rect, paint);
        }
    }
}

分形雜訊著色器位於頂端;動蕩著色器位於底部:

撰寫的 Perlin 雜訊

請注意,這些著色器比 Perlin Noise 頁面所顯示的著色器要藍多少。 差異說明雜訊著色器中的透明度。

方法也有多載 CreateCompose

public static SKShader CreateCompose (SKShader dstShader, SKShader srcShader, SKBlendMode blendMode);

最後一個參數是列舉的成員SKBlendMode,列舉具有 29 個成員,會在下一系列關於 SkiaSharp 撰寫和混合模式的文章中討論。