Partager via


Bruit skiaSharp et composition

Les graphiques vectoriels simples ont tendance à paraître non naturels. Les lignes droites, les courbes lisses et les couleurs unie ne ressemblent pas aux imperfections des objets réels. Tout en travaillant sur les graphiques générés par l’ordinateur pour le film Tron 1982, Ken Perlin a commencé à développer des algorithmes qui utilisaient des processus aléatoires pour donner à ces images des textures plus réalistes. En 1997, Ken Perlin a remporté un Prix d’Académie pour la réussite technique. Son travail est devenu le bruit Perlin, et il est pris en charge dans SkiaSharp. Voici un exemple :

Exemple de bruit perlin

Comme vous pouvez le voir, chaque pixel n’est pas une valeur de couleur aléatoire. La continuité du pixel au pixel entraîne des formes aléatoires.

La prise en charge du bruit Perlin dans Skia est basée sur une spécification W3C pour CSS et SVG. La section 8.20 du module d’effets de filtre 1 inclut les algorithmes de bruit Perlin sous-jacents dans le code C.

Exploration du bruit perlin

La SKShader classe définit deux méthodes statiques différentes pour générer le bruit Perlin : CreatePerlinNoiseFractalNoise et CreatePerlinNoiseTurbulence. Les paramètres sont identiques :

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);

Les deux méthodes existent également dans les versions surchargées avec un paramètre supplémentaire SKPointI . La section Tiling Perlin noise traite de ces surcharges.

Les deux baseFrequency arguments sont des valeurs positives définies dans la documentation SkiaSharp comme allant de 0 à 1, mais elles peuvent également être définies sur des valeurs plus élevées. Plus la valeur est élevée, plus la modification de l’image aléatoire est élevée dans les directions horizontales et verticales.

La numOctaves valeur est un entier de 1 ou supérieur. Il est lié à un facteur d’itération dans les algorithmes. Chaque octave supplémentaire contribue à un effet qui correspond à la moitié de l’octave précédente, de sorte que l’effet diminue avec des valeurs octaves plus élevées.

Le seed paramètre est le point de départ du générateur de nombres aléatoires. Bien que spécifiée comme valeur à virgule flottante, la fraction est tronquée avant son utilisation, et 0 est identique à 1.

La page Perlin Noise de l’exemple vous permet d’expérimenter différentes valeurs des baseFrequency arguments et numOctaves des valeurs. Voici le fichier 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>

Il utilise deux Slider vues pour les deux baseFrequency arguments. Pour développer la plage des valeurs inférieures, les curseurs sont logarithmiques. Le fichier code-behind calcule les arguments des SKShaderméthodes à partir des puissances des Slider valeurs. Les Label vues affichent les valeurs calculées :

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);

La Slider valeur 1 correspond à 0,001, une Slider valeur os 2 correspond à 0,01, une Slider valeur de 3 correspond à 0,1 et une Slider valeur de 4 correspond à 1.

Voici le fichier code-behind qui inclut ce code :

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);
        }
    }
}

Voici le programme en cours d’exécution sur les appareils iOS, Android et plateforme Windows universelle (UWP). Le bruit fractal s’affiche dans la moitié supérieure de la toile. Le bruit de turbulence se trouve dans la moitié inférieure :

Bruit perlin

Les mêmes arguments produisent toujours le même modèle qui commence en haut à gauche. Cette cohérence est évidente lorsque vous ajustez la largeur et la hauteur de la fenêtre UWP. Lorsque Windows 10 redessine l’écran, le modèle dans la moitié supérieure du canevas reste le même.

Le modèle de bruit intègre différents degrés de transparence. La transparence devient évidente si vous définissez une couleur dans l’appel canvas.Clear() . Cette couleur devient visible dans le motif. Vous verrez également cet effet dans la section Combinant plusieurs nuanceurs.

Ces modèles de bruit Perlin sont rarement utilisés par eux-mêmes. Souvent, ils sont soumis à des modes de fusion et des filtres de couleurs abordés dans les articles ultérieurs.

Bruit de perlin de mosaïne

Les deux méthodes statiques SKShader de création de bruit Perlin existent également dans les versions de surcharge. Les surcharges et CreatePerlinNoiseTurbulence les CreatePerlinNoiseFractalNoise surcharges ont un paramètre supplémentaire 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 structure est la version entière de la structure familière SKPoint . SKPointI définit X et Y propriétés de type int plutôt que float.

Ces méthodes créent un modèle répétitif de la taille spécifiée. Dans chaque vignette, le bord droit est le même que le bord gauche, et le bord supérieur est le même que le bord inférieur. Cette caractéristique est illustrée dans la page De bruit perlin en mosaïque. Le fichier XAML est similaire à l’exemple précédent, mais il n’a qu’une Stepper vue pour modifier l’argument 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>

Le fichier code-behind définit une constante pour la taille de la vignette. Le PaintSurface gestionnaire crée une bitmap de cette taille et un SKCanvas dessin dans cette bitmap. La SKShader.CreatePerlinNoiseTurbulence méthode crée un nuanceur avec cette taille de vignette. Ce nuanceur est dessiné sur la 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);
            }
        }
    }
}

Une fois la bitmap créée, un autre SKPaint objet est utilisé pour créer un modèle bitmap en mosaïque en appelant SKShader.CreateBitmap. Notez les deux arguments suivants SKShaderTileMode.Repeat:

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

Ce nuanceur est utilisé pour couvrir le canevas. Enfin, un autre SKPaint objet est utilisé pour traiter un rectangle montrant la taille de la bitmap d’origine.

Seul le seed paramètre est sélectionnable à partir de l’interface utilisateur. Si le même seed modèle est utilisé sur chaque plateforme, il affiche le même modèle. Différentes seed valeurs entraînent des modèles différents :

Bruit perlin en mosaïque

Le modèle carré de 200 pixels dans l’angle supérieur gauche circule en toute transparence dans les autres vignettes.

Combinaison de plusieurs nuanceurs

La SKShader classe inclut une méthode qui crée un CreateColor nuanceur avec une couleur unie spécifiée. Ce nuanceur n’est pas très utile par lui-même, car vous pouvez simplement définir cette couleur sur la Color propriété de l’objet SKPaint et définir la Shader propriété sur Null.

Cette CreateColor méthode devient utile dans une autre méthode qui SKShader définit. Cette méthode est CreateCompose, qui combine deux nuanceurs. Voici la syntaxe :

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

Le srcShader nuanceur (nuanceur source) est effectivement dessiné en haut de la dstShader (nuanceur de destination). Si le nuanceur source est une couleur unie ou un dégradé sans transparence, le nuanceur de destination est complètement masqué.

Un nuanceur de bruit Perlin contient la transparence. Si ce nuanceur est la source, le nuanceur de destination s’affiche dans les zones transparentes.

La page Bruit perlin composé a un fichier XAML qui est pratiquement identique à la première page De bruit Perlin. Le fichier code-behind est également similaire. Toutefois, la page Perlin Noise d’origine définit la Shader propriété du SKPaint nuanceur retourné par les méthodes et CreatePerlinNoiseTurbulence statiquesCreatePerlinNoiseFractalNoise. Cette page Perlin Noise composée appelle CreateCompose un nuanceur combiné. La destination est un nuanceur bleu unie créé à l’aide CreateColorde . La source est un nuanceur de bruit 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);
        }
    }
}

Le nuanceur de bruit fractal est en haut ; le nuanceur de turbulence est en bas :

Bruit perlin composé

Notez combien ces nuanceurs sont bleus que ceux affichés par la page Perlin Noise . La différence illustre la quantité de transparence dans les nuanceurs de bruit.

Il existe également une surcharge de la CreateCompose méthode :

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

Le paramètre final est membre de l’énumération SKBlendMode , une énumération avec 29 membres qui est abordée dans la série suivante d’articles sur les modes de composition et de fusion SkiaSharp.