次の方法で共有


SkiaSharp の円形グラデーション

SKShader クラスでは、4 種類のグラデーションを作成する静的メソッドが定義されます。 「SkiaSharp 線形グラデーション」の記事では、CreateLinearGradient メソッドについて説明されています。 この記事では、他の 3 種類のグラデーションについて説明します。これらはすべて円に基づいています。

CreateRadialGradient メソッドでは、円の中心から発生するグラデーションが作成されます。

放射状グラデーションのサンプル

CreateSweepGradient メソッドでは、円の中心の周りをスイープするグラデーションが作成されます。

スイープ グラデーションのサンプル

3 番目の種類のグラデーションは非常に珍しいものです。 これは 2 点円錐状グラデーションと呼ばれ、CreateTwoPointConicalGradient メソッドによって定義されます。 グラデーションは 1 つの円から別の円に広がります。

円錐状グラデーションのサンプル

2 つの円のサイズが異なる場合、グラデーションは円錐形になります。

この記事では、これらのグラデーションについて詳しく説明します。

放射状グラデーション

CreateRadialGradient メソッドには次の構文があります。

public static SKShader CreateRadialGradient (SKPoint center,
                                             Single radius,
                                             SKColor[] colors,
                                             Single[] colorPos,
                                             SKShaderTileMode mode)

CreateRadialGradient オーバーロードには、変換マトリックス パラメーターも含まれます。

最初の 2 つの引数では、円の中心と半径が指定されます。 グラデーションはその中心から始まり、radius ピクセルに対して外側に広がります。 radius を超えて何が起こるかは、SKShaderTileMode 引数によって異なります。 colors パラメーターは 2 つ以上の色の配列であり (線状グラデーション メソッドと同様)、colorPos は 0 から 1 の範囲の整数の配列です。 これらの整数は、その radius 線に沿った色の相対的な位置を示します。 その引数を null に設定して、色を均等に配置できます。

CreateRadialGradient を使用して円を塗りつぶす場合は、グラデーションの中心を円の中心に設定し、グラデーションの半径を円の半径に設定できます。 その場合、SKShaderTileMode 引数はグラデーションのレンダリングには影響しません。 ただし、グラデーションで塗りつぶされた領域がグラデーションで定義された円よりも大きい場合、SKShaderTileMode 引数は円の外側で何が起こるかについて大きな影響を与えます。

SKShaderMode の効果は、サンプルの [Radial Gradient] ページで示されています。 このページの XAML ファイルでは、SKShaderTileMode 列挙体の 3 つのメンバーのいずれかを選択できる Picker をインスタンス化します。

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:skia="clr-namespace:SkiaSharp;assembly=SkiaSharp"
             xmlns:skiaforms="clr-namespace:SkiaSharp.Views.Forms;assembly=SkiaSharp.Views.Forms"
             x:Class="SkiaSharpFormsDemos.Effects.RadialGradientPage"
             Title="Radial Gradient">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>

        <skiaforms:SKCanvasView x:Name="canvasView"
                                Grid.Row="0"
                                PaintSurface="OnCanvasViewPaintSurface" />

        <Picker x:Name="tileModePicker"
                Grid.Row="1"
                Title="Shader Tile Mode"
                Margin="10"
                SelectedIndexChanged="OnPickerSelectedIndexChanged">
            <Picker.ItemsSource>
                <x:Array Type="{x:Type skia:SKShaderTileMode}">
                    <x:Static Member="skia:SKShaderTileMode.Clamp" />
                    <x:Static Member="skia:SKShaderTileMode.Repeat" />
                    <x:Static Member="skia:SKShaderTileMode.Mirror" />
                </x:Array>
            </Picker.ItemsSource>

            <Picker.SelectedIndex>
                0
            </Picker.SelectedIndex>
        </Picker>
    </Grid>
</ContentPage>

分離コード ファイルでは、キャンバス全体を放射状グラデーションで色付けします。 グラデーションの中心はキャンバスの中心に設定され、半径は 100 ピクセルに設定されます。 グラデーションは、黒と白の 2 つの色だけで構成されます。

public partial class RadialGradientPage : ContentPage
{
    public RadialGradientPage ()
    {
        InitializeComponent ();
    }

    void OnPickerSelectedIndexChanged(object sender, EventArgs args)
    {
        canvasView.InvalidateSurface();
    }

    void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
    {
        SKImageInfo info = args.Info;
        SKSurface surface = args.Surface;
        SKCanvas canvas = surface.Canvas;

        canvas.Clear();

        SKShaderTileMode tileMode =
            (SKShaderTileMode)(tileModePicker.SelectedIndex == -1 ?
                                        0 : tileModePicker.SelectedItem);

        using (SKPaint paint = new SKPaint())
        {
            paint.Shader = SKShader.CreateRadialGradient(
                                new SKPoint(info.Rect.MidX, info.Rect.MidY),
                                100,
                                new SKColor[] { SKColors.Black, SKColors.White },
                                null,
                                tileMode);

            canvas.DrawRect(info.Rect, paint);
        }
    }
}

このコードでは、中心に黒いグラデーションが作成され、中心から 100 ピクセルの白に徐々にフェードされます。 その半径を超えて何が起こるかは、SKShaderTileMode 引数によって異なります。

放射状グラデーション

3 つのケースすべてで、グラデーションによってキャンバスが塗りつぶされます。 左側の iOS 画面では、半径を超えるグラデーションは最後の色 (白) で続きます。 それが SKShaderTileMode.Clamp の結果です。 Android の画面には SKShaderTileMode.Repeat の効果が示されています。中心から 100 ピクセルの位置で、グラデーションは最初の色 (黒) で再び始まります。 グラデーションは、半径 100 ピクセルごとに繰り返されます。

右側のユニバーサル Windows プラットフォームの画面には、SKShaderTileMode.Mirror によってグラデーションの方向がどのように入れ替わるかが示されています。 最初のグラデーションは黒 (中心) から白 (半径 100 ピクセル) になります。 次は白 (半径 100 ピクセル) から黒 (半径 200 ピクセル) になり、次のグラデーションは再び反転します。

放射状グラデーションでは 3 つ以上の色を使用できます。 [レインボー アーク グラデーション] サンプルでは、虹の色に対応し、赤で終わる 8 色の配列と、8 つの位置値の配列が作成されます。

public class RainbowArcGradientPage : ContentPage
{
    public RainbowArcGradientPage ()
    {
        Title = "Rainbow Arc Gradient";

        SKCanvasView canvasView = new SKCanvasView();
        canvasView.PaintSurface += OnCanvasViewPaintSurface;
        Content = canvasView;
    }

    void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
    {
        SKImageInfo info = args.Info;
        SKSurface surface = args.Surface;
        SKCanvas canvas = surface.Canvas;

        canvas.Clear();

        using (SKPaint paint = new SKPaint())
        {
            float rainbowWidth = Math.Min(info.Width, info.Height) / 4f;

            // Center of arc and gradient is lower-right corner
            SKPoint center = new SKPoint(info.Width, info.Height);

            // Find outer, inner, and middle radius
            float outerRadius = Math.Min(info.Width, info.Height);
            float innerRadius = outerRadius - rainbowWidth;
            float radius = outerRadius - rainbowWidth / 2;

            // Calculate the colors and positions
            SKColor[] colors = new SKColor[8];
            float[] positions = new float[8];

            for (int i = 0; i < colors.Length; i++)
            {
                colors[i] = SKColor.FromHsl(i * 360f / 7, 100, 50);
                positions[i] = (i + (7f - i) * innerRadius / outerRadius) / 7f;
            }

            // Create sweep gradient based on center and outer radius
            paint.Shader = SKShader.CreateRadialGradient(center,
                                                         outerRadius,
                                                         colors,
                                                         positions,
                                                         SKShaderTileMode.Clamp);
            // Draw a circle with a wide line
            paint.Style = SKPaintStyle.Stroke;
            paint.StrokeWidth = rainbowWidth;

            canvas.DrawCircle(center, radius, paint);
        }
    }
}

キャンバスの幅と高さの最小値が 1000 であるとします。これは、rainbowWidth 値が 250 であることを意味します。 outerRadiusinnerRadius の値はそれぞれ 1000 と 750 に設定されます。 これらの値は、positions 配列の計算に使用されます。8 つの値の範囲は 0.75f から 1 です。 radius 値は、円をストロークするために使用されます。 875 の値は、250 ピクセルのストローク幅が 750 ピクセルの半径と 1000 ピクセルの半径の間で拡張されることを意味します。

レインボー アーク グラデーション

キャンバス全体をこのグラデーションで塗りつぶすと、内半径内は赤いことがわかります。 これは、positions 配列が 0 で始まらないためです。 最初の色は、0 から最初の配列値までのオフセットに使用されます。 また、グラデーションは外半径を超えると赤になります。 これが Clamp タイル モードの結果です。 グラデーションは太線をストロークする場合に使用されるため、これらの赤い領域は表示されません。

マスキングの放射状グラデーション

線状グラデーションと同様に、放射状グラデーションには透明または部分的に透明な色を組み込むことができます。 この機能は、ある画像の一部を非表示にしてその画像の別の部分を強調する、"マスキング" と呼ばれるプロセスに役立ちます。

[放射状グラデーション マスク] ページに例が示されています。 プログラムではリソース ビットマップの 1 つが読み込まれます。 CENTER および RADIUS フィールドは、ビットマップを調べて決定されたものであり、強調表示する必要がある領域が示されます。 PaintSurface ハンドラーではまず、ビットマップを表示するために四角形を計算し、その四角形に表示します。

public class RadialGradientMaskPage : ContentPage
{
    SKBitmap bitmap = BitmapExtensions.LoadBitmapResource(
        typeof(RadialGradientMaskPage),
        "SkiaSharpFormsDemos.Media.MountainClimbers.jpg");

    static readonly SKPoint CENTER = new SKPoint(180, 300);
    static readonly float RADIUS = 120;

    public RadialGradientMaskPage ()
    {
        Title = "Radial Gradient Mask";

        SKCanvasView canvasView = new SKCanvasView();
        canvasView.PaintSurface += OnCanvasViewPaintSurface;
        Content = canvasView;
    }

    void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
    {
        SKImageInfo info = args.Info;
        SKSurface surface = args.Surface;
        SKCanvas canvas = surface.Canvas;

        canvas.Clear();

        // Find rectangle to display bitmap
        float scale = Math.Min((float)info.Width / bitmap.Width,
                                (float)info.Height / bitmap.Height);

        SKRect rect = SKRect.Create(scale * bitmap.Width, scale * bitmap.Height);

        float x = (info.Width - rect.Width) / 2;
        float y = (info.Height - rect.Height) / 2;
        rect.Offset(x, y);

        // Display bitmap in rectangle
        canvas.DrawBitmap(bitmap, rect);

        // Adjust center and radius for scaled and offset bitmap
        SKPoint center = new SKPoint(scale * CENTER.X + x,
                                     scale * CENTER.Y + y);
        float radius = scale * RADIUS;

        using (SKPaint paint = new SKPaint())
        {
            paint.Shader = SKShader.CreateRadialGradient(
                                center,
                                radius,
                                new SKColor[] { SKColors.Transparent,
                                                SKColors.White },
                                new float[] { 0.6f, 1 },
                                SKShaderTileMode.Clamp);

            // Display rectangle using that gradient
            canvas.DrawRect(rect, paint);
        }
    }
}

ビットマップの描画後、単純なコードで CENTERRADIUScenterradius に変換されます。これは、表示のために拡大縮小およびシフトされたビットマップ内の強調表示された領域を示します。 これらの値は、その中心と半径を持つ放射状グラデーションを作成するために使用されます。 2 つの色は、その中心と半径の最初の 60% では透明で始まります。 その後、グラデーションは白にフェードします。

放射状グラデーション マスク

このアプローチは、ビットマップをマスキングする最適な方法ではありません。 問題は、マスクの色は主に白 (キャンバスの背景に合わせて選択されたもの) であることです。 背景が他の色 (おそらくグラデーション自体の色) の場合、一致しません。 マスキングに対するより良いアプローチは、SkiaSharp の Porter-Duff ブレンド モードに関する記事に示されています。

反射ハイライトの放射状グラデーション

光は、丸い面に当たると、多くの方向に反射しますが、光の一部は目に直接跳ね返ります。 これにより、多くの場合、表面上の領域が白くぼやけて見えます。これを "反射ハイライト" といいます。

3 次元グラフィックスでは、多くの場合、反射ハイライトは光パスとシェーディングを決定するために使用されるアルゴリズムに起因します。 2 次元グラフィックスでは、3D 表面の外観を示すために反射ハイライトが追加されることがあります。 反射ハイライトでは、フラットな赤い円を丸い赤いボールに変換できます。

[放射状反射ハイライト] ページでは、放射状グラデーションを使用してそれが正確に行われます。 PaintSurface ハンドラーではまず、円の半径、および 2 つの SKPoint 値 (center と、円の中心と左上隅の中間にある offCenter) を計算します。

public class RadialSpecularHighlightPage : ContentPage
{
    public RadialSpecularHighlightPage()
    {
        Title = "Radial Specular Highlight";

        SKCanvasView canvasView = new SKCanvasView();
        canvasView.PaintSurface += OnCanvasViewPaintSurface;
        Content = canvasView;
    }

    void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
    {
        SKImageInfo info = args.Info;
        SKSurface surface = args.Surface;
        SKCanvas canvas = surface.Canvas;

        canvas.Clear();

        float radius = 0.4f * Math.Min(info.Width, info.Height);
        SKPoint center = new SKPoint(info.Rect.MidX, info.Rect.MidY);
        SKPoint offCenter = center - new SKPoint(radius / 2, radius / 2);

        using (SKPaint paint = new SKPaint())
        {
            paint.Shader = SKShader.CreateRadialGradient(
                                offCenter,
                                radius / 2,
                                new SKColor[] { SKColors.White, SKColors.Red },
                                null,
                                SKShaderTileMode.Clamp);

            canvas.DrawCircle(center, radius, paint);
        }
    }
}

CreateRadialGradient 呼び出しでは、白 (offCenter 点にある) で始まり、赤 (半径の半分の距離にある) で終わるグラデーションが作成されます。 次のように表示されます。

放射状反射ハイライト

このグラデーションをよく見ると、欠陥があると判断する可能性があります。 グラデーションは特定の点を中心としており、丸い面を反射させるには、少し対称性が低い方が望ましい場合があります。 その場合、「反射ハイライトの円錐状グラデーション」セクションに示される反射ハイライトの方が望ましい場合があります。

スイープ グラデーション

CreateSweepGradient メソッドには、すべてのグラデーション作成メソッドの最も簡単な構文があります。

public static SKShader CreateSweepGradient (SKPoint center,
                                            SKColor[] colors,
                                            Single[] colorPos)

これは、単なる中心、色の配列、および色の位置です。 グラデーションは中心点の右側から始まり、中心を時計回りに 360 度スイープします。 SKShaderTileMode パラメーターがないことに注目してください。

マトリックス変換パラメーターを使った CreateSweepGradient オーバーロードも使用できます。 グラデーションに回転変換を適用して、開始点を変更することができます。 スケール変換を適用して、時計回りから反時計回りに方向を変更することもできます。

[スイープ グラデーション] ページでは、スイープ グラデーションを使用して、ストローク幅が 50 ピクセルの円が色付けされます。

スイープ グラデーション

SweepGradientPage クラスでは、異なる色相値を持つ 8 色の配列が定義されます。 配列は赤で始まり終わる (色相値は 0 または 360) ことに注目してください。これはスクリーンショットの右端に示されています。

public class SweepGradientPage : ContentPage
{
    bool drawBackground;

    public SweepGradientPage ()
    {
        Title = "Sweep Gradient";

        SKCanvasView canvasView = new SKCanvasView();
        canvasView.PaintSurface += OnCanvasViewPaintSurface;
        Content = canvasView;

        TapGestureRecognizer tap = new TapGestureRecognizer();
        tap.Tapped += (sender, args) =>
        {
            drawBackground ^= true;
            canvasView.InvalidateSurface();
        };
        canvasView.GestureRecognizers.Add(tap);
    }

    void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
    {
        SKImageInfo info = args.Info;
        SKSurface surface = args.Surface;
        SKCanvas canvas = surface.Canvas;

        canvas.Clear();

        using (SKPaint paint = new SKPaint())
        {
            // Define an array of rainbow colors
            SKColor[] colors = new SKColor[8];

            for (int i = 0; i < colors.Length; i++)
            {
                colors[i] = SKColor.FromHsl(i * 360f / 7, 100, 50);
            }

            SKPoint center = new SKPoint(info.Rect.MidX, info.Rect.MidY);

            // Create sweep gradient based on center of canvas
            paint.Shader = SKShader.CreateSweepGradient(center, colors, null);

            // Draw a circle with a wide line
            const int strokeWidth = 50;
            paint.Style = SKPaintStyle.Stroke;
            paint.StrokeWidth = strokeWidth;

            float radius = (Math.Min(info.Width, info.Height) - strokeWidth) / 2;
            canvas.DrawCircle(center, radius, paint);

            if (drawBackground)
            {
                // Draw the gradient on the whole canvas
                paint.Style = SKPaintStyle.Fill;
                canvas.DrawRect(info.Rect, paint);
            }
        }
    }
}

また、プログラムでは、PaintSurface ハンドラーの末尾にあるコードを有効にする TapGestureRecognizer も実装されます。 このコードでは、キャンバスを塗りつぶすために同じグラデーションが使用されます。

スイープ グラデーション フル

これらのスクリーンショットは、グラデーションによって色付けされた領域がすべて塗りつぶされていることを示しています。 グラデーションが同じ色で始まり終わらない場合は、中心点の右側が不連続となります。

2 点円錐状グラデーション

CreateTwoPointConicalGradient メソッドには次の構文があります。

public static SKShader CreateTwoPointConicalGradient (SKPoint startCenter,
                                                      Single startRadius,
                                                      SKPoint endCenter,
                                                      Single endRadius,
                                                      SKColor[] colors,
                                                      Single[] colorPos,
                                                      SKShaderTileMode mode)

パラメーターは、2 つの円 ("開始" 円と "終了" 円という) の中心点と半径で始まります。 残りの 3 つのパラメーターは、CreateLinearGradient および CreateRadialGradient の場合と同じです。 CreateTwoPointConicalGradient オーバーロードには行列変換が含まれます。

グラデーションは開始円から始まり、終了円で終わります。 SKShaderTileMode パラメーターによって、2 つの円を超えて何が起こるかが制御されます。 2 点円錐状のグラデーションは、領域を完全に塗りつぶさない唯一のグラデーションです。 2 つの円の半径が同じである場合、グラデーションは円の直径と同じ幅の四角形に制限されます。 2 つの円の半径が異なる場合、グラデーションでは円錐が形成されます。

2 点円錐状グラデーションを試す可能性があるため、[円錐状グラデーション] ページは、2 つの円の半径に対して 2 つの接点を移動できるように、InteractivePage から派生します。

<local:InteractivePage xmlns="http://xamarin.com/schemas/2014/forms"
                       xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                       xmlns:local="clr-namespace:SkiaSharpFormsDemos"
                       xmlns:skia="clr-namespace:SkiaSharp;assembly=SkiaSharp"
                       xmlns:skiaforms="clr-namespace:SkiaSharp.Views.Forms;assembly=SkiaSharp.Views.Forms"
                       xmlns:tt="clr-namespace:TouchTracking"
                       x:Class="SkiaSharpFormsDemos.Effects.ConicalGradientPage"
                       Title="Conical Gradient">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>

        <Grid BackgroundColor="White"
              Grid.Row="0">
            <skiaforms:SKCanvasView x:Name="canvasView"
                                    PaintSurface="OnCanvasViewPaintSurface" />
            <Grid.Effects>
                <tt:TouchEffect Capture="True"
                                TouchAction="OnTouchEffectAction" />
            </Grid.Effects>
        </Grid>

        <Picker x:Name="tileModePicker"
                Grid.Row="1"
                Title="Shader Tile Mode"
                Margin="10"
                SelectedIndexChanged="OnPickerSelectedIndexChanged">
            <Picker.ItemsSource>
                <x:Array Type="{x:Type skia:SKShaderTileMode}">
                    <x:Static Member="skia:SKShaderTileMode.Clamp" />
                    <x:Static Member="skia:SKShaderTileMode.Repeat" />
                    <x:Static Member="skia:SKShaderTileMode.Mirror" />
                </x:Array>
            </Picker.ItemsSource>

            <Picker.SelectedIndex>
                0
            </Picker.SelectedIndex>
        </Picker>
    </Grid>
</local:InteractivePage>

分離コード ファイルでは、固定半径が 50 と 100 の 2 つの TouchPoint オブジェクトが定義されます。

public partial class ConicalGradientPage : InteractivePage
{
    const int RADIUS1 = 50;
    const int RADIUS2 = 100;

    public ConicalGradientPage ()
    {
        touchPoints = new TouchPoint[2];

        touchPoints[0] = new TouchPoint
        {
            Center = new SKPoint(100, 100),
            Radius = RADIUS1
        };

        touchPoints[1] = new TouchPoint
        {
            Center = new SKPoint(300, 300),
            Radius = RADIUS2
        };

        InitializeComponent();
        baseCanvasView = canvasView;
    }

    void OnPickerSelectedIndexChanged(object sender, EventArgs args)
    {
        canvasView.InvalidateSurface();
    }

    void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
    {
        SKImageInfo info = args.Info;
        SKSurface surface = args.Surface;
        SKCanvas canvas = surface.Canvas;

        canvas.Clear();

        SKColor[] colors = { SKColors.Red, SKColors.Green, SKColors.Blue };
        SKShaderTileMode tileMode =
            (SKShaderTileMode)(tileModePicker.SelectedIndex == -1 ?
                                        0 : tileModePicker.SelectedItem);

        using (SKPaint paint = new SKPaint())
        {
            paint.Shader = SKShader.CreateTwoPointConicalGradient(touchPoints[0].Center,
                                                                  RADIUS1,
                                                                  touchPoints[1].Center,
                                                                  RADIUS2,
                                                                  colors,
                                                                  null,
                                                                  tileMode);
            canvas.DrawRect(info.Rect, paint);
        }

        // Display the touch points here rather than by TouchPoint
        using (SKPaint paint = new SKPaint())
        {
            paint.Style = SKPaintStyle.Stroke;
            paint.Color = SKColors.Black;
            paint.StrokeWidth = 3;

            foreach (TouchPoint touchPoint in touchPoints)
            {
                canvas.DrawCircle(touchPoint.Center, touchPoint.Radius, paint);
            }
        }
    }
}

colors 配列は赤、緑、青です。 PaintSurface ハンドラーの下部にあるコードでは、グラデーションを妨げないように、2 つの接点が黒い円として描画されます。

DrawRect 呼び出しでは、グラデーションを使用してキャンバス全体が色付けされることに注目してください。 しかし、一般的なケースでは、キャンバスの多くはグラデーションによって色付けされません。 考えられる 3 つの構成を示すプログラムを以下に示します。

円錐状グラデーション

左側の iOS 画面には、ClampSKShaderTileMode 設定の効果が示されています。 グラデーションは赤 (2 番目の円に最も近い側の反対にある小さな円の端の内側) で始まります。 Clamp 値を指定すると、円錐の点まで赤が続きます。 グラデーションは青 (最初の円に最も近い大きな円の外端) で終わりますが、その円内およびそれ以降も青が続きます。

Android の画面は似ていますが、RepeatSKShaderTileMode が使用されています。 これで、グラデーションが最初の円の内側から始まり、2 番目の円の外側で終わることがより明確になりました。 Repeat を設定すると、グラデーションは大きな円の内側から赤で繰り返されます。

UWP 画面には、小さな円が大きな円の内側に完全に移動した場合に何が起こるかが示されています。 グラデーションは円錐にならず、代わりに領域全体が塗りつぶされます。 効果は放射状グラデーションと似ていますが、小さな円が大きな円の中で正確に中央に配置されていない場合は非対称となります。

ある円が別の円に入れ子になっている場合、グラデーションの実用的有用性を疑う可能性がありますが、反射ハイライトには最適です。

反射ハイライトの円錐状グラデーション

この記事の前半では、放射状グラデーションを使用して反射ハイライトを作成する方法を確認しました。 また、この目的のために 2 点円錐状グラデーションを使用することもでき、次の外観が好ましい場合があります。

円錐状反射ハイライト

非対称な外観は、オブジェクトの丸い面をより適切に示しています。

[円錐状反射ハイライト] ページの描画コードは、シェーダーを除き、[放射状反射ハイライト] ページと同じです。

public class ConicalSpecularHighlightPage : ContentPage
{
    ···
    void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
    {
        ···
        using (SKPaint paint = new SKPaint())
        {
            paint.Shader = SKShader.CreateTwoPointConicalGradient(
                                offCenter,
                                1,
                                center,
                                radius,
                                new SKColor[] { SKColors.White, SKColors.Red },
                                null,
                                SKShaderTileMode.Clamp);

            canvas.DrawCircle(center, radius, paint);
        }
    }
}

2つの円には offCentercenter の中心があります。 center に中心がある円はボール全体を含む半径に関連付けられますが、offCenter に中心がある円の半径は 1 ピクセルだけです。 グラデーションは実質的にその点から始まり、ボールの端で終わります。