Skip to content
React Native Skia ガイドブック
Esc
navigateopen⌘Jpreview
On this page

シェーダーの実践

ImageShaderによる画像の塗り、4種類のグラデーション、パーリンノイズ、Blend/ColorShaderによる合成など、SkSLと組み込みシェーダーの実践的な使い方をまとめます。

前章でSkSLの基本構文を見たので、この章では実際によく使うシェーダーを一通り見ていきます。グラデーションやノイズは自分でSkSLを書かなくても組み込みコンポーネントとして用意されているので、まずはそちらから押さえていきましょう。

ImageShader: 画像をシェーダーとして塗る

ImageShaderは画像を塗り(shader)として扱うためのコンポーネントです。Imageコンポーネントが「矩形に画像を描く」のに対して、ImageShaderは任意の図形の塗りとして画像を使えます。円や複雑なパスの中に写真を収めたいときに便利です。

Name Type Description
image SkImage 画像インスタンス
tx? / ty? TileMode X・Y方向のタイリング方法(clamprepeatmirrordecal)
fit? Fit rectにフィットさせる方法
rect? SkRect フィット計算の基準となる出力先の矩形
transform? Transforms2d 追加の変換
sampling? Sampling 拡大縮小時のサンプリング方法
import {
  Canvas,
  Circle,
  ImageShader,
  useImage,
} from "@shopify/react-native-skia";

const ImageShaderDemo = () => {
  const image = useImage(require("./assets/photo.jpg"));
  if (image === null) {
    return null;
  }
  return (
    <Canvas style={{ flex: 1 }}>
      <Circle cx={128} cy={128} r={128}>
        <ImageShader
          image={image}
          fit="cover"
          rect={{ x: 0, y: 0, width: 256, height: 256 }}
        />
      </Circle>
    </Canvas>
  );
};

Skiaを読み込み中…

グラデーション

グラデーションもシェーダーの一種です。すべてのグラデーションコンポーネントに共通するpropは次の通りです。

Name Type Description
colors string[] 分布させる色の配列
positions? number[] 各色の相対位置(colorsと同じ長さで指定)
mode? TileMode clamprepeatmirrordecal
transform? Transforms2d 追加の変換

4種類のグラデーションを並べてみます。

Skiaを読み込み中…

  • LinearGradient: startendの2点を結ぶ直線状に色を分布させる、もっとも基本的なグラデーション
  • RadialGradient: 中心cと半径rで、中心から外側へ円状に色を分布させる
  • SweepGradient: 中心cを軸に、角度(startend、デフォルトは0〜360度)に沿って色を分布させる。時計の針が回るようなグラデーション
  • TwoPointConicalGradient: 開始円(startstartR)と終了円(endendR)という2つの円の間を補間する、もっとも自由度の高いグラデーション。中心をずらしたりRadiusを変えたりすると光源のような表現になる
import { Canvas, Rect, LinearGradient, vec } from "@shopify/react-native-skia";

const LinearGradientDemo = () => {
  return (
    <Canvas style={{ flex: 1 }}>
      <Rect x={0} y={0} width={256} height={256}>
        <LinearGradient
          start={vec(0, 0)}
          end={vec(256, 256)}
          colors={["blue", "yellow"]}
        />
      </Rect>
    </Canvas>
  );
};

パーリンノイズ

パーリンノイズ(Perlin noise)は自然な起伏を持つ乱数パターンを生成するアルゴリズムで、雲・煙・大理石模様や、前章のDisplacementMapの変位マップなど幅広い用途に使われます。React Native SkiaにはFractalNoise(フラクタルノイズ)とTurbulence(乱流)の2種類が用意されています。

Name Type Description
freqX / freqY number X・Y方向の基本周波数(0.0〜1.0)
octaves number 重ね合わせる周波数成分の数。多いほど細かいディテールが増える
seed number 乱数のシード値
tileWidth? / tileHeight? number 両方0以外の場合、指定サイズでタイル状に繰り返し可能なノイズにする

Skiaを読み込み中…

FractalNoiseは滑らかな雲のような模様に、Turbulenceはより鋭くコントラストの強い模様になります。octavesを増やすほど計算コストが上がるので、リアルタイムに動かす場合は必要最小限に留めるのが無難です。

import { Canvas, Rect, FractalNoise, Fill } from "@shopify/react-native-skia";

const FractalNoiseDemo = () => {
  return (
    <Canvas style={{ flex: 1 }}>
      <Fill color="white" />
      <Rect x={0} y={0} width={256} height={256}>
        <FractalNoise freqX={0.05} freqY={0.05} octaves={4} />
      </Rect>
    </Canvas>
  );
};

Blend: シェーダー同士を合成する

Blendは2つのシェーダーを、指定したブレンドモード(前章で見たblendModeと同じ一覧)で合成するシェーダーです。グラデーションとノイズを重ねてマーブル模様のような表現を作れます。

Name Type Description
mode BlendMode ブレンドモード
children ReactNode 合成する2つのシェーダー
import { Canvas, Rect, Turbulence, RadialGradient, Blend, vec } from "@shopify/react-native-skia";

const BlendDemo = () => {
  return (
    <Canvas style={{ flex: 1 }}>
      <Rect x={0} y={0} width={256} height={256}>
        <Blend mode="difference">
          <RadialGradient r={128} c={vec(128, 128)} colors={["blue", "yellow"]} />
          <Turbulence freqX={0.05} freqY={0.05} octaves={4} />
        </Blend>
      </Rect>
    </Canvas>
  );
};

ColorShader: 単色をシェーダーとして扱う

ColorShaderは単色を返すだけのシェーダーです。単独で使うことはほとんどありませんが、BlendShaderのuniform shaderとして「一定の色」を渡したい場合に使います。

import { Canvas, Fill, ColorShader } from "@shopify/react-native-skia";

const ColorShaderDemo = () => {
  return (
    <Canvas style={{ flex: 1 }}>
      <Fill>
        <ColorShader color="lightBlue" />
      </Fill>
    </Canvas>
  );
};

以下のデモではBlendのブレンドモードと、2番目のシェーダーをTurbulence(ノイズ)かColorShader(単色)かで切り替えて試せます。

Skiaを読み込み中…

自作シェーダーを動かしてみる

最後に、前章のuniformの知識を使って、自作のSkSLシェーダーをアニメーションパラメーター付きで動かしてみます。ピクセルごとのループを持たない単純な三角関数の合成なので、以下のデモ自体は軽量ですが、RuntimeShaderを使ったより複雑な自作シェーダー(多重ループを含むもの、複数のシェーダーを何段にも重ねるものなど)は画素数分の計算が毎フレーム走るため処理コストが高くなりがちです。ブラウザのプレビューで滑らかに動いていても実機では重く感じることがあるので、複雑なシェーダーを実装した際は実機での確認を推奨します。

Skiaを読み込み中…

const source = Skia.RuntimeEffect.Make(`
uniform float2 resolution;
uniform float frequency;
uniform float amplitude;

vec4 main(vec2 pos) {
  vec2 uv = pos / resolution;
  float wave = sin((uv.x + uv.y) * frequency) * amplitude;
  vec3 color = vec3(0.4 + wave, 0.5 - wave, 0.8);
  return vec4(color, 1.0);
}
`)!;

frequencyは波の細かさ、amplitudeは色の変化の強さに対応するuniformです。どちらもJavaScript側のスライダー操作でそのまま再計算されるので、シェーダーがReactのstateと地続きで動くことを体感できるはずです。

注意点

この章で扱ったグラデーションやノイズは、いずれもShaderとしてPaintの子要素に渡せます。カラーフィルターやイメージフィルターと組み合わせるとさらに表現の幅が広がりますが、重ねるほど処理コストも積み上がる点は常に意識してください。特にRuntimeShaderを含むチェーンをリスト内の多数のアイテムに適用するような使い方は、パフォーマンスへの影響が大きくなりやすいので避けるのが無難です。

公式ドキュメントのImage ShadersGradientsPerlin Noise ShadersBlending and Colorsも参照してください。