Android アプリでの AGSL の使用

このページでは、AGSL の基本と、Android で AGSL を使用するさまざまな方法について説明します。 。

シンプルな AGSL シェーダー

シェーダーのコードは描画されたピクセルごとに呼び出され、ピクセルの ペイントする必要があります非常にシンプルなシェーダーは、常に 単色この例では赤を使用しています。シェーダーは String 内で定義されます。

KotlinJava
private const val COLOR_SHADER_SRC =
   
"""half4 main(float2 fragCoord) {
      return half4(1,0,0,1);
   }"""

private static final String COLOR_SHADER_SRC =
   
"half4 main(float2 fragCoord) {\n" +
     
"return half4(1,0,0,1);\n" +
   
"}";

次に、RuntimeShader を作成します。 シェーダーの文字列で初期化されたオブジェクト。これにより、シェーダーもコンパイルされます。

KotlinJava
val fixedColorShader = RuntimeShader(COLOR_SHADER_SRC)
RuntimeShader fixedColorShader = new RuntimeShader(COLOR_SHADER_SRC);

RuntimeShader は、標準の Android シェーダーが使用できる場所で使用できます。として たとえば、カスタム View Canvas

KotlinJava
val paint = Paint()
paint
.shader = fixedColorShader
override fun onDrawForeground(canvas: Canvas?) {
   canvas
?.let {
      canvas
.drawPaint(paint) // fill the Canvas with the shader
   
}
}
Paint paint = new Paint();
paint
.setShader(fixedColorShader);
public void onDrawForeground(@Nullable Canvas canvas) {
   
if (canvas != null) {
      canvas
.drawPaint(paint); // fill the Canvas with the shader
   
}
}

これにより、赤色の View が描画されます。uniform を使用して、色パラメータを 描画するシェーダーを指定します。まず、色 uniform をシェーダーに追加します。

KotlinJava
private const val COLOR_SHADER_SRC =
"""layout(color) uniform half4 iColor;
   half4 main(float2 fragCoord) {
      return iColor;
   }"""

private static final String COLOR_SHADER_SRC =
   
"layout(color) uniform half4 iColor;\n"+
     
"half4 main(float2 fragCoord) {\n" +
     
"return iColor;\n" +
   
"}";

次に、カスタムの View から setColorUniform を呼び出して、目的の色を渡します。 AGSL シェーダーに統合します

KotlinJava
fixedColorShader.setColorUniform("iColor", Color.GREEN )
fixedColorShader.setColorUniform("iColor", Color.GREEN );

これで、緑色の View が表示されます。View の色は、 パラメータをカスタム View のコードから 作成します。

代わりに、カラー グラデーション効果を作成できます。まず、 シェーダーで View 解像度を入力として受け入れます。

KotlinJava
private const val COLOR_SHADER_SRC =
"""uniform float2 iResolution;
   half4 main(float2 fragCoord) {
      float2 scaled = fragCoord/iResolution.xy;
      return half4(scaled, 0, 1);
   }"""

private static final String COLOR_SHADER_SRC =
   
"uniform float2 iResolution;\n" +
     
"half4 main(float2 fragCoord) {\n" +
     
"float2 scaled = fragCoord/iResolution.xy;\n" +
     
"return half4(scaled, 0, 1);\n" +
   
"}";

グラデーションの描画

このシェーダーは、ちょっとした工夫をしています。ピクセルごとに float2 が作成されます。 x 座標と y 座標を解像度で割った値を含むベクトル。 0 ~ 1 の値が作成されます。そのスケーリングされたベクトルを使用して、 戻り値の色の赤と緑のコンポーネントを作成します。

次のコマンドを呼び出して、View の解像度を AGSL シェーダーの uniform に渡します。 setFloatUniform

KotlinJava
val paint = Paint()
paint
.shader = fixedColorShader
override fun onDrawForeground(canvas: Canvas?) {
   canvas
?.let {
      fixedColorShader
.setFloatUniform("iResolution", width.toFloat(), height.toFloat())
      canvas
.drawPaint(paint)
   
}
}
Paint paint = new Paint();
paint
.setShader(fixedColorShader);
public void onDrawForeground(@Nullable Canvas canvas) {
   
if (canvas != null) {
      fixedColorShader
.setFloatUniform("iResolution", (float)getWidth(), (float()getHeight()));
      canvas
.drawPaint(paint);
   
}
}
<ph type="x-smartling-placeholder">
</ph> 赤と緑のグラデーション
赤と緑のグラデーション

シェーダーをアニメーション化する

同様の手法を使用して、iTime ユニフォームと iDuration ユニフォームを受け取るようにシェーダーを変更することで、シェーダーをアニメーション化できます。シェーダーはこれらの値を使用して、 三角波を使って色をグラデーションします。

KotlinJava
private const val DURATION = 4000f
private const val COLOR_SHADER_SRC = """
   uniform float2 iResolution;
   uniform float iTime;
   uniform float iDuration;
   half4 main(in float2 fragCoord) {
      float2 scaled = abs(1.0-mod(fragCoord/iResolution.xy+iTime/(iDuration/2.0),2.0));
      return half4(scaled, 0, 1.0);
   }
"""

private static final float DURATION = 4000f;
private static final String COLOR_SHADER_SRC =
   
"uniform float2 iResolution;\n"+
   
"uniform float iTime;\n"+
   
"uniform float iDuration;\n"+
   
"half4 main(in float2 fragCoord) {\n"+
     
"float2 scaled = abs(1.0-mod(fragCoord/iResolution.xy+iTime/(iDuration/2.0),2.0));\n"+
     
"return half4(scaled, 0, 1.0);\n"+
   
"}";

カスタムビューのソースコードから、 ValueAnimatoriTime ユニフォーム。

KotlinJava
// declare the ValueAnimator
private val shaderAnimator = ValueAnimator.ofFloat(0f, DURATION)

// use it to animate the time uniform
shaderAnimator
.duration = DURATION.toLong()
shaderAnimator
.repeatCount = ValueAnimator.INFINITE
shaderAnimator
.repeatMode = ValueAnimator.RESTART
shaderAnimator
.interpolator = LinearInterpolator()

animatedShader
.setFloatUniform("iDuration", DURATION )
shaderAnimator
.addUpdateListener { animation ->
    animatedShader
.setFloatUniform("iTime", animation.animatedValue as Float )
}
shaderAnimator
.start()
// declare the ValueAnimator
private final ValueAnimator shaderAnimator = ValueAnimator.ofFloat(0f, DURATION);

// use it to animate the time uniform
shaderAnimator
.setDuration((long)DURATION);
shaderAnimator
.setRepeatCount(ValueAnimator.INFINITE);
shaderAnimator
.setRepeatMode(ValueAnimator.RESTART);
shaderAnimator
.setInterpolator(new LinearInterpolator());

animatedShader
.setFloatUniform("iDuration", DURATION );
shaderAnimator
.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
   
public final void onAnimationUpdate(ValueAnimator animation) {
      animatedShader
.setFloatUniform("iTime", (float)animation.getAnimatedValue());
   
}
});
<ph type="x-smartling-placeholder">
</ph> 赤と緑のアニメーション グラデーション
赤と緑のアニメーション グラデーション

複雑なオブジェクトの描画

背景全体にシェーダーを描画する必要はありません。できる 使用できるように、 Paint オブジェクト( drawText

KotlinJava
canvas.drawText(ANIMATED_TEXT, TEXT_MARGIN_DP, TEXT_MARGIN_DP + bounds.height(),
   paint
)
canvas.drawText(ANIMATED_TEXT, TEXT_MARGIN_DP, TEXT_MARGIN_DP + bounds.height(),
   paint
);
<ph type="x-smartling-placeholder">
</ph> 赤と緑のアニメーション グラデーション テキスト
赤と緑のアニメーション グラデーション テキスト

シェーディングとキャンバスの変換

シェーディングされたテキストには、次のような追加の Canvas 変換を適用できます。 作成できます。ValueAnimator で、3D 回転のマトリックスを更新できます。 組み込みの android.graphics.Camera クラス。

KotlinJava
// in the ValueAnimator
camera
.rotate(0.0f, animation.animatedValue as Float / DURATION * 360f, 0.0f)
// in the ValueAnimator
camera
.rotate(0.0f, (Float)animation.getAnimatedValue() / DURATION * 360f, 0.0f);

テキストを隅からではなく中心軸から回転させるため、 テキスト境界を取得してから、preTranslatepostTranslate を使用して 行列を使用してテキストを翻訳し、0,0 を 画面上のテキストが描画される位置を変更する。

KotlinJava
linearColorPaint.getTextBounds(ANIMATED_TEXT, 0, ANIMATED_TEXT.length, bounds)
camera
.getMatrix(rotationMatrix)
val centerX = (bounds.width().toFloat())/2
val centerY = (bounds.height().toFloat())/2
rotationMatrix
.preTranslate(-centerX, -centerY)
rotationMatrix
.postTranslate(centerX, centerY)
canvas
.save()
canvas
.concat(rotationMatrix)
canvas
.drawText(ANIMATED_TEXT, 0f, 0f + bounds.height(), paint)
canvas
.restore()
linearColorPaint.getTextBounds(ANIMATED_TEXT, 0, ANIMATED_TEXT.length(), bounds);
camera
.getMatrix(rotationMatrix);
float centerX = (float)bounds.width()/2.0f;
float centerY = (float)bounds.height()/2.0f;
rotationMatrix
.preTranslate(-centerX, -centerY);
rotationMatrix
.postTranslate(centerX, centerY);
canvas
.save();
canvas
.concat(rotationMatrix);
canvas
.drawText(ANIMATED_TEXT, 0f, 0f + bounds.height(), paint);
canvas
.restore();
<ph type="x-smartling-placeholder">
</ph> 赤と緑の回転アニメーション グラデーション テキスト
赤と緑の回転アニメーション グラデーション テキスト

Jetpack Compose で RuntimeShader を使用する

を使用して UI をレンダリングする場合は、RuntimeShader を使用する方がさらに簡単です。 Jetpack Compose。まずは、先ほどと同じグラデーション シェーダーを before:

private const val COLOR_SHADER_SRC =
   
"""uniform float2 iResolution;
   half4 main(float2 fragCoord) {
   float2 scaled = fragCoord/iResolution.xy;
   return half4(scaled, 0, 1);
}"""

そのシェーダーを ShaderBrush。マイページ 次に、ShaderBrushCanvas の描画スコープ。

// created as top level constants
val colorShader = RuntimeShader(COLOR_SHADER_SRC)
val shaderBrush = ShaderBrush(colorShader)

Canvas(
   modifier
= Modifier.fillMaxSize()
) {
   colorShader
.setFloatUniform("iResolution",
   size
.width, size.height)
   drawCircle
(brush = shaderBrush)
}
<ph type="x-smartling-placeholder">
</ph> AGSL Compose のグラデーション円
赤と緑のグラデーション円

RenderEffect で RuntimeShader を使用する

次を使用: RenderEffect: RuntimeShader を親 Viewおよびすべての子ビュー。この方法は、カスタムの View を描画するよりもコストがかかります。 そうすると、作成したばかりのコンポーネントに 元々は createRuntimeShaderEffect

KotlinJava
view.setRenderEffect(RenderEffect.createRuntimeShaderEffect(myShader, "background"))
view.setRenderEffect(RenderEffect.createRuntimeShaderEffect(myShader, "background"));

2 つ目のパラメータは、シェーダーのユニフォームの名前です。eval (fragCoord で渡された値など)の座標パラメータを使って、 RenderNode(ビューとその子ビュー) ビューなど)を使用して、あらゆる種類のエフェクトを実行できます。

uniform shader background;       // Root node of View tree to be altered
return mix(returnColor, background.eval(fragCoord), 0.5);
<ph type="x-smartling-placeholder">
</ph> ボタンの上にグリッドを統合しました
ボタンの上に AGSL のグリッドを重ねて表示

ボタンの上に、フローティング アクション ボタンの下にあるグリッド エフェクトが混在している (View 階層が異なるため)。