SkiaSharp noise and composing
Einfache Vektorgrafiken neigen dazu, unnatürlich zu aussehen. Die geraden Linien, glatten Kurven und Volltonfarben ähneln nicht den Unvollkommenheiten von realen Objekten. Während der Arbeit an den computergenerierten Grafiken für den Film Tron 1982 begann der Informatiker Ken Perlin mit der Entwicklung von Algorithmen, die zufällige Prozesse verwendeten, um diesen Bildern realistischere Texturen zu verleihen. 1997 gewann Ken Perlin einen Academy Award für technische Leistungen. Seine Arbeit ist bekannt als Perlin Noise, und es wird in SkiaSharp unterstützt. Ein Beispiel:
Wie Sie sehen können, ist jedes Pixel kein zufälliger Farbwert. Die Kontinuität von Pixel zu Pixel führt zu zufälligen Formen.
Die Unterstützung von Perlin-Rauschen in Skia basiert auf einer W3C-Spezifikation für CSS und SVG. Abschnitt 8.20 des Filtereffektemoduls Ebene 1 enthält die zugrunde liegenden Perlin-Rauschalgorithmen im C-Code.
Erkunden von Perlin-Rauschen
Die SKShader
Klasse definiert zwei verschiedene statische Methoden zum Generieren von Perlin-Rauschen: CreatePerlinNoiseFractalNoise
und CreatePerlinNoiseTurbulence
. Die Parameter sind identisch:
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);
Beide Methoden sind auch in überladenen Versionen mit einem zusätzlichen SKPointI
Parameter vorhanden. Im Abschnitt "Tiling Perlin-Rauschen " werden diese Überladungen erläutert.
Die beiden baseFrequency
Argumente sind positive Werte, die in der SkiaSharp-Dokumentation von 0 bis 1 definiert sind, aber sie können auch auf höhere Werte festgelegt werden. Je höher der Wert ist, desto größer ist die Änderung des zufälligen Bilds in horizontaler und vertikaler Richtung.
Der numOctaves
Wert ist eine ganze Zahl von 1 oder höher. Sie bezieht sich auf einen Iterationsfaktor in den Algorithmen. Jede zusätzliche Oktave trägt einen Effekt bei, der die Hälfte der vorherigen Oktave ist, sodass der Effekt mit höheren Oktavwerten abnimmt.
Der seed
Parameter ist der Ausgangspunkt für den Zufallszahlengenerator. Obwohl als Gleitkommawert angegeben, wird der Bruch vor der Verwendung abgeschnitten, und 0 ist identisch mit 1.
Auf der Seite "Perlin-Rauschen " im Beispiel können Sie mit verschiedenen Werten der baseFrequency
Argumente numOctaves
experimentieren. Dies ist die XAML-Datei:
<?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>
Es verwendet zwei Slider
Ansichten für die beiden baseFrequency
Argumente. Um den Bereich der niedrigeren Werte zu erweitern, sind die Schieberegler logarithmisch. Die CodeBehind-Datei berechnet die Argumente für die Methoden aus den SKShader
Mächten der Slider
Werte. In Label
den Ansichten werden die berechneten Werte angezeigt:
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);
Ein Slider
Wert von 1 entspricht 0,001, ein Slider
Wert os 2 entspricht 0,01, ein Slider
Wert von 3 entspricht 0,1 und ein Slider
Wert von 4 entspricht 1.
Dies ist die CodeBehind-Datei, die diesen Code enthält:
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);
}
}
}
Hier sehen Sie das Programm, das auf iOS-, Android- und Universelle Windows-Plattform-Geräten (UWP) ausgeführt wird. Das fraktale Rauschen wird in der oberen Hälfte des Zeichenbereichs angezeigt. Der Turbulenzenrausch liegt in der unteren Hälfte:
Dieselben Argumente erzeugen immer das gleiche Muster, das in der oberen linken Ecke beginnt. Diese Konsistenz ist offensichtlich, wenn Sie die Breite und Höhe des UWP-Fensters anpassen. Während Windows 10 den Bildschirm neu gezeichnet, wird das Muster in der oberen Hälfte des Zeichenbereichs erneut Standard identisch.
Das Rauschmuster enthält verschiedene Transparenzgrade. Die Transparenz wird offensichtlich, wenn Sie eine Farbe im canvas.Clear()
Anruf festlegen. Diese Farbe wird im Muster hervorgehoben. Dieser Effekt wird auch im Abschnitt "Kombinieren mehrerer Shader" angezeigt.
Diese Perlin-Rauschmuster werden selten von sich selbst verwendet. Häufig unterliegen sie Mischmodi und Farbfiltern, die in späteren Artikeln erläutert werden.
Tiling Perlin-Rauschen
Die beiden statischen SKShader
Methoden zum Erstellen von Perlin-Rauschen sind auch in Überladungsversionen vorhanden. Die CreatePerlinNoiseFractalNoise
Überladungen CreatePerlinNoiseTurbulence
weisen einen zusätzlichen SKPointI
Parameter auf:
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);
Die SKPointI
Struktur ist die ganzzahlige Version der vertrauten SKPoint
Struktur. SKPointI
definiert und Y
Eigenschaften des Typs int
X
und nicht .float
Diese Methoden erstellen ein wiederholtes Muster der angegebenen Größe. In jeder Kachel entspricht der rechte Rand dem linken Rand, und der obere Rand entspricht dem unteren Rand. Diese Eigenschaft wird auf der Seite "Nebeneinander angeordnetes Perlin-Rauschen " veranschaulicht. Die XAML-Datei ähnelt dem vorherigen Beispiel, hat aber nur eine Stepper
Ansicht zum Ändern des seed
Arguments:
<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>
Die CodeBehind-Datei definiert eine Konstante für die Kachelgröße. Der PaintSurface
Handler erstellt eine Bitmap dieser Größe und eine SKCanvas
zum Zeichnen in diese Bitmap. Die SKShader.CreatePerlinNoiseTurbulence
Methode erstellt einen Shader mit dieser Kachelgröße. Dieser Shader wird auf der Bitmap gezeichnet:
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);
}
}
}
}
Nachdem die Bitmap erstellt wurde, wird ein weiteres SKPaint
Objekt verwendet, um ein nebeneinander angeordnetes Bitmapmuster durch Aufrufen SKShader.CreateBitmap
zu erstellen. Beachten Sie die beiden Argumente von SKShaderTileMode.Repeat
:
paint.Shader = SKShader.CreateBitmap(bitmap,
SKShaderTileMode.Repeat,
SKShaderTileMode.Repeat);
Dieser Shader wird verwendet, um den Zeichenbereich abzudecken. Schließlich wird ein weiteres SKPaint
Objekt verwendet, um ein Rechteck mit der Größe der ursprünglichen Bitmap zu strichen.
Nur der seed
Parameter kann über die Benutzeroberfläche ausgewählt werden. Wenn dasselbe seed
Muster auf jeder Plattform verwendet wird, würden sie dasselbe Muster anzeigen. Unterschiedliche seed
Werte führen zu unterschiedlichen Mustern:
Das quadratische Muster von 200 Pixeln in der oberen linken Ecke fließt nahtlos in die anderen Kacheln.
Kombinieren mehrerer Shader
Die SKShader
Klasse enthält eine CreateColor
Methode, mit der ein Shader mit einer angegebenen Volltonfarbe erstellt wird. Dieser Shader ist nicht sehr nützlich, da Sie diese Farbe einfach auf die Color
Eigenschaft des SKPaint
Objekts festlegen und die Shader
Eigenschaft auf NULL festlegen können.
Diese CreateColor
Methode wird in einer anderen Methode nützlich, die SKShader
definiert wird. Diese Methode ist CreateCompose
, die zwei Shader kombiniert. Dies ist die Syntax:
public static SKShader CreateCompose (SKShader dstShader, SKShader srcShader);
Der srcShader
(Quell-Shader) wird effektiv über dem dstShader
(Ziel-Shader) gezeichnet. Wenn der Quell-Shader eine Volltonfarbe oder ein Farbverlauf ohne Transparenz ist, wird der Ziel-Shader vollständig verdeckt.
Ein Perlin-Rausch-Shader enthält Transparenz. Wenn dieser Shader die Quelle ist, wird der Ziel-Shader durch die transparenten Bereiche angezeigt.
Die Seite "Zusammengesetztes Perlin-Rauschen " verfügt über eine XAML-Datei, die nahezu identisch mit der ersten Perlin Noise-Seite ist. Die CodeBehind-Datei ist ebenfalls ähnlich. Die ursprüngliche Perlin Noise-Seite legt jedoch die Shader
Eigenschaft des SKPaint
Shaders fest, der von der statischen CreatePerlinNoiseFractalNoise
Und CreatePerlinNoiseTurbulence
Methoden zurückgegeben wird. Diese zusammengesetzte Perlin-Rauschseite ruft einen Kombinations-Shader auf CreateCompose
. Das Ziel ist ein einfarbiger blauer Shader, der mit CreateColor
. Die Quelle ist ein Perlin-Rausch-Shader:
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);
}
}
}
Der Fraktal-Rausch-Shader befindet sich oben; Der Turbulenzenshader befindet sich unten:
Beachten Sie, wie viel Blauer diese Shader sind als die von der Perlin Noise-Seite angezeigten. Der Unterschied veranschaulicht die Transparenz in den Rausch-Shadern.
Es gibt auch eine Überladung der CreateCompose
Methode:
public static SKShader CreateCompose (SKShader dstShader, SKShader srcShader, SKBlendMode blendMode);
Der letzte Parameter ist ein Element der SKBlendMode
Enumeration, eine Aufzählung mit 29 Membern, die in der nächsten Reihe von Artikeln über SkiaSharp Compositing und Blend-Modi behandelt wird.