SkiaSharp 노이즈 및 구성

간단한 벡터 그래픽은 부자연스럽게 보이는 경향이 있습니다. 직선, 부드러운 곡선 및 단색은 실제 개체의 불완전성과 유사하지 않습니다. 컴퓨터 과학자 켄 펄린은 1982년 영화 트론을 위해 컴퓨터로 생성된 그래픽을 작업하는 동안 임의 프로세스를 사용하여 이러한 이미지에 보다 사실적인 질감을 제공하는 알고리즘을 개발하기 시작했습니다. 1997년 켄 펄린은 아카데미 기술 공로상을 수상했습니다. 그의 작품은 펄린 소음으로 알려지게되었으며 SkiaSharp에서 지원됩니다. 예를 들면 다음과 같습니다.

Perlin 노이즈 샘플

보듯이 각 픽셀은 임의 색 값이 아닙니다. 픽셀에서 픽셀 사이의 연속성은 임의 셰이프를 생성합니다.

Skia의 Perlin 노이즈 지원은 CSS 및 SVG에 대한 W3C 사양을 기반으로 합니다. 필터 효과 모듈 수준 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 매개 변수를 사용하여 오버로드된 버전에도 존재합니다. 타일링 펄린 노이즈 섹션에서는 이러한 오버로드에 대해 설명합니다.

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 뷰를 사용합니다. 낮은 값의 범위를 확장하기 위해 슬라이더는 로그입니다. 코드 숨김 파일은 값의 힘에서 메서드에 대한 SKShader인수를 Slider 계산합니다. 뷰에는 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);

값 1은 Slider 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 및 UWP(유니버설 Windows 플랫폼) 디바이스에서 실행되는 프로그램입니다. 프랙탈 노이즈는 캔버스의 상반부에 표시됩니다. 난기류 소음은 아래쪽 절반에 있습니다.

Perlin 노이즈

동일한 인수는 항상 왼쪽 위 모서리에서 시작하는 동일한 패턴을 생성합니다. 이 일관성은 UWP 창의 너비와 높이를 조정할 때 분명합니다. Windows 10에서 화면을 다시 그릴 때 캔버스의 위쪽 절반에 있는 패턴은 동일하게 기본.

노이즈 패턴은 다양한 수준의 투명도를 통합합니다. 호출에서 색을 설정하면 투명도가 canvas.Clear() 분명해집니다. 해당 색은 패턴에서 두드러집니다. 여러 셰이더 결합 섹션에도 이 효과가 표시됩니다.

이러한 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 아닌 float형식 int 의 정의 및 속성을 정의합니다Y.

이러한 메서드는 지정된 크기의 반복 패턴을 만듭니다. 각 타일에서 오른쪽 가장자리는 왼쪽 가장자리와 같고 위쪽 가장자리는 아래쪽 가장자리와 동일합니다. 이 특성은 타일식 Perlin 노이 페이지에서 보여 집니다. XAML 파일은 이전 샘플과 유사하지만 인수를 변경 seed 하기 위한 보기만 있습니다Stepper.

<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 됩니다. 이 셰이더는 개체의 SKPaint 속성으로 해당 색을 설정하고 속성을 null로 Color 설정 Shader 하기만 하면 되므로 그 자체로는 유용하지 않습니다.

CreateColor 메서드는 정의하는 다른 메서드 SKShader 에서 유용합니다. 이 메서드는 두 셰이더를 결합하는 메서드입니다 CreateCompose. 구문은 다음과 같습니다.

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

srcShader (원본 셰이더)는 (대상 셰이더) 위에 dstShader 효과적으로 그려집니다. 원본 셰이더가 단색이거나 투명도가 없는 그라데이션인 경우 대상 셰이더가 완전히 가려집니다.

Perlin 노이즈 셰이더에는 투명도가 포함됩니다. 해당 셰이더가 원본이면 대상 셰이더가 투명 영역을 통해 표시됩니다.

구성된 Perlin 노이즈 페이지에는 첫 번째 Perlin 노이즈 페이지와 거의 동일한 XAML 파일이 있습니다. 코드 숨김 파일도 비슷합니다. 그러나 원래 Perlin 노이즈 페이지는 정적 CreatePerlinNoiseFractalNoiseCreatePerlinNoiseTurbulence 메서드에서 반환된 셰이더의 속성을 SKPaint 설정합니다Shader. 이 구성된 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 노이즈 페이지에 표시되는 셰이더보다 얼마나 파란색인지 확인합니다. 차이점은 노이즈 셰이더의 투명도 양을 보여 줍니다.

메서드의 오버로드도 있습니다.CreateCompose

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

마지막 매개 변수는 열거형의 SKBlendMode 멤버이며, SkiaSharp 작성 및 혼합 모드에 대한 다음 문서 시리즈에서 설명된 29명의 멤버가 포함된 열거형입니다.