SkiaSharp 雜訊和撰寫
簡單的向量圖形通常看起來不自然。 直線、平滑曲線和純色不會與真實世界物件的不完美相似。 在為 1982 年電影 Tron 處理電腦產生的圖形時,計算機科學家 Ken Perlin 開始開發演算法,這些演算法會使用隨機程式為這些影像提供更真實的紋理。 1997年,肯·佩林榮獲奧斯卡技術成就獎。 他的作品被稱為佩林噪音,在SkiaSharp中得到了支援。 以下是範例:
如您所見,每個圖元不是隨機色彩值。 從像素到像素的連續性會產生隨機圖形。
Skia 中的 Perlin 雜訊支援是以 CSS 和 SVG 的 W3C 規格為基礎。 Filter Effects 模組層級 1 的第 8.20 節包含 C 程式代碼中的基礎 Perlin 雜訊演算法。
探索 Perlin 雜訊
類別 SKShader
會定義兩個不同的靜態方法,以產生 Perlin 雜訊: CreatePerlinNoiseFractalNoise
和 CreatePerlinNoiseTurbulence
。 參數完全相同:
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>
它會針對這兩個自變數使用兩Slider
baseFrequency
個檢視。 若要展開較低值的範圍,滑桿是對數。 程序代碼後置檔案會從值的力量計算方法的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、Slider
os 2 的值對應至 0.01、Slider
3 的值對應至 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) 裝置上執行的程式。 分形噪音會顯示在畫布的上半部。 動蕩噪音位於下半場:
相同的自變數一律會產生從左上角開始的相同模式。 當您調整 UWP 視窗的寬度和高度時,此一致性是顯而易見的。 當 Windows 10 重繪螢幕時,畫布上半部的圖樣會維持不變。
雜訊模式會納入各種程度的透明度。 如果您在呼叫中 canvas.Clear()
設定色彩,透明度就會變得很明顯。 該色彩在圖案中變得突出。 您也會在結合多個著色器一節中看到此效果。
這些 Perlin 雜訊模式本身很少使用。 它們通常會受限於後續文章中討論的混合模式和色彩篩選條件。
波狀 Perlin 雜訊
建立 Perlin 雜訊的兩個靜態 SKShader
方法也存在於多載版本中。 CreatePerlinNoiseFractalNoise
和 CreatePerlinNoiseTurbulence
多載有額外的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
結構的整數版本。 SKPointI
會X
定義 型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
值會產生不同的模式:
左上角的 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 頁面會將 Shader
的 SKPaint
屬性設定為從靜態 CreatePerlinNoiseFractalNoise
和 CreatePerlinNoiseTurbulence
方法傳回的著色器。 這個 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 Noise 頁面所顯示的著色器要藍多少。 差異說明雜訊著色器中的透明度。
方法也有多載 CreateCompose
:
public static SKShader CreateCompose (SKShader dstShader, SKShader srcShader, SKBlendMode blendMode);
最後一個參數是列舉的成員SKBlendMode
,列舉具有 29 個成員,會在下一系列關於 SkiaSharp 撰寫和混合模式的文章中討論。