Rumore skiaSharp e composizione
La grafica vettoriale semplice tende a sembrare innaturale. Le linee rette, le curve uniformi e i colori a tinta unita non assomigliano alle imperfezioni degli oggetti reali. Mentre si lavora sulla grafica generata dal computer per il film Tron del 1982, ken perlin ha iniziato a sviluppare algoritmi che usavano processi casuali per dare a queste immagini trame più realistiche. Nel 1997, Ken Perlin ha vinto un Premio Oscar per il successo tecnico. Il suo lavoro è venuto a essere conosciuto come rumore Perlin, ed è supportato in SkiaSharp. Ecco un esempio:
Come si può notare, ogni pixel non è un valore di colore casuale. La continuità da pixel a pixel comporta forme casuali.
Il supporto del rumore Perlin in Skia si basa su una specifica W3C per CSS e SVG. La sezione 8.20 del modulo Filter Effects Module 1 include gli algoritmi di disturbo Perlin sottostanti nel codice C.
Esplorazione del rumore di Perlin
La SKShader
classe definisce due metodi statici diversi per generare rumore Perlin: CreatePerlinNoiseFractalNoise
e CreatePerlinNoiseTurbulence
. I parametri sono identici:
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);
Entrambi i metodi esistono anche nelle versioni di overload con un parametro aggiuntivo SKPointI
. La sezione Tiling Perlin noise illustra questi overload.
I due baseFrequency
argomenti sono valori positivi definiti nella documentazione skiaSharp, compresi tra 0 e 1, ma possono essere impostati anche su valori più elevati. Maggiore è il valore, maggiore è la modifica nell'immagine casuale nelle direzioni orizzontali e verticali.
Il numOctaves
valore è un numero intero di 1 o superiore. Si riferisce a un fattore di iterazione negli algoritmi. Ogni ottava aggiuntiva contribuisce a un effetto che è metà dell'ottavo precedente, quindi l'effetto diminuisce con valori di ottave più elevati.
Il seed
parametro è il punto iniziale per il generatore di numeri casuali. Anche se specificato come valore a virgola mobile, la frazione viene troncata prima dell'uso e 0 è uguale a 1.
La pagina Perlin Noise nell'esempio consente di sperimentare diversi valori degli baseFrequency
argomenti e numOctaves
. Ecco il file 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>
Usa due Slider
visualizzazioni per i due baseFrequency
argomenti. Per espandere l'intervallo dei valori inferiori, i dispositivi di scorrimento sono logaritmici. Il file code-behind calcola gli argomenti dei SKShader
metodi dai poteri dei Slider
valori. Le Label
visualizzazioni visualizzano i valori calcolati:
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);
Il Slider
valore 1 corrisponde a 0,001, un Slider
valore os 2 corrisponde a 0,01, un Slider
valore pari a 3 corrisponde a 0,1 e il Slider
valore 4 corrisponde a 1.
Ecco il file code-behind che include tale codice:
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);
}
}
}
Ecco il programma in esecuzione su dispositivi iOS, Android e piattaforma UWP (Universal Windows Platform) (UWP). Il rumore frattale viene visualizzato nella metà superiore dell'area di disegno. Il rumore della turbolenza è nella metà inferiore:
Gli stessi argomenti producono sempre lo stesso modello che inizia nell'angolo superiore sinistro. Questa coerenza è ovvia quando si regola la larghezza e l'altezza della finestra UWP. Quando Windows 10 ridisegna lo schermo, il motivo nella metà superiore dell'area di disegno rimane invariato.
Il modello di rumore incorpora vari gradi di trasparenza. La trasparenza diventa evidente se si imposta un colore nella canvas.Clear()
chiamata. Tale colore diventa prominente nel motivo. Questo effetto verrà visualizzato anche nella sezione Combinazione di più shader.
Questi modelli di rumore perlin vengono usati raramente da soli. Spesso sono soggetti a modalità di fusione e filtri di colore descritti negli articoli successivi.
Disturbo perlin di legatura
I due metodi statici SKShader
per la creazione del disturbo Perlin esistono anche nelle versioni di overload. Gli CreatePerlinNoiseFractalNoise
overload e CreatePerlinNoiseTurbulence
hanno un parametro aggiuntivo 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);
La SKPointI
struttura è la versione integer della struttura familiare SKPoint
. SKPointI
definisce X
le proprietà e Y
di tipo int
anziché float
.
Questi metodi creano un modello ripetuto delle dimensioni specificate. In ogni riquadro, il bordo destro è uguale al bordo sinistro e il bordo superiore è uguale al bordo inferiore. Questa caratteristica è illustrata nella pagina Rumore perlin affiancato. Il file XAML è simile all'esempio precedente, ma ha solo una Stepper
visualizzazione per modificare l'argomento 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>
Il file code-behind definisce una costante per le dimensioni del riquadro. Il PaintSurface
gestore crea una bitmap di tale dimensione e un oggetto SKCanvas
per il disegno in tale bitmap. Il SKShader.CreatePerlinNoiseTurbulence
metodo crea uno shader con le dimensioni del riquadro. Questo shader viene disegnato sulla bitmap:
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);
}
}
}
}
Dopo aver creato la bitmap, viene usato un altro SKPaint
oggetto per creare un modello bitmap affiancato chiamando SKShader.CreateBitmap
. Si notino i due argomenti di SKShaderTileMode.Repeat
:
paint.Shader = SKShader.CreateBitmap(bitmap,
SKShaderTileMode.Repeat,
SKShaderTileMode.Repeat);
Questo shader viene usato per coprire l'area di disegno. Infine, viene usato un altro SKPaint
oggetto per tracciare un rettangolo che mostra le dimensioni della bitmap originale.
Solo il seed
parametro è selezionabile dall'interfaccia utente. Se in ogni piattaforma viene usato lo stesso seed
modello, viene visualizzato lo stesso modello. I valori diversi seed
generano modelli diversi:
Il modello quadrato da 200 pixel nell'angolo superiore sinistro scorre senza problemi negli altri riquadri.
Combinazione di più shader
La SKShader
classe include un CreateColor
metodo che crea uno shader con un colore a tinta unita specificato. Questo shader non è molto utile da solo perché è sufficiente impostare tale colore sulla Color
proprietà dell'oggetto SKPaint
e impostare la Shader
proprietà su null.
Questo CreateColor
metodo diventa utile in un altro metodo che SKShader
definisce. Questo metodo è CreateCompose
, che combina due shader. Ecco la sintassi:
public static SKShader CreateCompose (SKShader dstShader, SKShader srcShader);
( srcShader
shader di origine) viene disegnato in modo efficace sopra lo dstShader
shader di destinazione. Se lo shader di origine è un colore a tinta unita o una sfumatura senza trasparenza, lo shader di destinazione verrà completamente oscurato.
Un perlin noise shader contiene trasparenza. Se tale shader è l'origine, lo shader di destinazione verrà visualizzato attraverso le aree trasparenti.
La pagina Composta perlin Noise ha un file XAML praticamente identico alla prima pagina Perlin Noise . Anche il file code-behind è simile. Tuttavia, la pagina Originale Perlin Noise imposta la Shader
proprietà di SKPaint
sullo shader restituito dai metodi statici CreatePerlinNoiseFractalNoise
e CreatePerlinNoiseTurbulence
. Questa pagina Composta perlin Noise chiama CreateCompose
un shader combinato. La destinazione è uno shader blu solido creato usando CreateColor
. L'origine è uno shader del rumore 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);
}
}
}
Il disturbo frattale è in cima; lo shader turbolenza si trova nella parte inferiore:
Si noti quanto blu sono questi shader rispetto a quelli visualizzati dalla pagina Perlin Noise . La differenza illustra la quantità di trasparenza negli shader del rumore.
Esiste anche un overload del CreateCompose
metodo :
public static SKShader CreateCompose (SKShader dstShader, SKShader srcShader, SKBlendMode blendMode);
Il parametro finale è un membro dell'enumerazione, un'enumerazione SKBlendMode
con 29 membri discussa nella serie successiva di articoli sulle modalità di composizione e fusione SkiaSharp.