Utilizzare AGSL nell'app per Android

Questa pagina illustra le nozioni di base di AGSL e i vari modi per utilizzarlo in Android dell'app.

Un semplice Shar AGSL

Il codice dello shaker viene chiamato per ogni pixel disegnato e restituisce il colore del pixel deve essere dipinto. Uno Shar estremamente semplice è un tipo che ritorna sempre un solo colore; in questo esempio viene usato il rosso. Lo streamr è definito all'interno di un String.

Kotlin

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

Java

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

Il passaggio successivo consiste nel creare una RuntimeShader inizializzato con la stringa shaker. In questo modo viene compilato anche lo shaker.

Kotlin

val fixedColorShader = RuntimeShader(COLOR_SHADER_SRC)

Java

RuntimeShader fixedColorShader = new RuntimeShader(COLOR_SHADER_SRC);

Il tuo RuntimeShader può essere utilizzato ovunque possa fare uno shaker Android standard. Come ad esempio, puoi usarlo per disegnare in un elemento View personalizzato utilizzando Canvas

Kotlin

val paint = Paint()
paint.shader = fixedColorShader
override fun onDrawForeground(canvas: Canvas?) {
   canvas?.let {
      canvas.drawPaint(paint) // fill the Canvas with the shader
   }
}

Java

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

In questo modo viene tracciata una View rossa. Puoi utilizzare un uniform per passare un parametro di colore a lo shaker da disegnare. Innanzitutto, aggiungi il colore uniform allo shaker:

Kotlin

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

Java

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

Poi, chiama setColorUniform dal tuo View personalizzato per trasmettere il colore desiderato nello streamr AGSL.

Kotlin

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

Java

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

Ora, ottieni un View verde; il colore View viene controllato utilizzando un del codice nel tuo asset View personalizzato anziché essere incorporato nel Shar.

In alternativa, puoi creare un effetto a gradiente di colore. Devi prima modificare lo shaker accetti la risoluzione View come input:

Kotlin

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

Java

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" +
   "}";

Disegno del gradiente

Questo Shar fa qualcosa di leggermente particolare. Per ogni pixel, viene creato un float2 vettore contenente le coordinate x e y divise per la risoluzione, creerà un valore compreso tra zero e uno. Quindi utilizza il vettore scalato i componenti rosso e verde del colore restituito.

Puoi passare la risoluzione di View in uno shaker AGSL uniform chiamando setFloatUniform.

Kotlin

val paint = Paint()
paint.shader = fixedColorShader
override fun onDrawForeground(canvas: Canvas?) {
   canvas?.let {
      fixedColorShader.setFloatUniform("iResolution", width.toFloat(), height.toFloat())
      canvas.drawPaint(paint)
   }
}

Java

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);
   }
}
Gradiente rosso e verde
Sfumatura rosso e verde

Animazione dello shaker

Puoi utilizzare una tecnica simile per animare lo shaker modificandolo in modo da ricevere le uniformi di iTime e iDuration. Lo shaker userà questi valori per creare un'onda triangolare per i colori, che li fa scorrere avanti e indietro attraverso i valori del gradiente.

Kotlin

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

Java

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"+
   "}";

Nel codice sorgente della visualizzazione personalizzata, ValueAnimator aggiorna la uniforme da iTime.

Kotlin

// 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()

Java

// 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());
   }
});
Gradiente animato rosso e verde
Sfumatura animato rosso e verde

Pittura di oggetti complessi

Non è necessario disegnare lo shaker per riempire lo sfondo; può essere utilizzate in qualsiasi luogo che accetti Oggetto Paint, ad esempio drawText.

Kotlin

canvas.drawText(ANIMATED_TEXT, TEXT_MARGIN_DP, TEXT_MARGIN_DP + bounds.height(),
   paint)

Java

canvas.drawText(ANIMATED_TEXT, TEXT_MARGIN_DP, TEXT_MARGIN_DP + bounds.height(),
   paint);
Testo gradiente animato rosso e verde
Testo a gradiente animato rosso e verde

Trasformazioni di ombreggiatura e canvas

Puoi applicare ulteriori trasformazioni Canvas al testo ombreggiato, ad esempio la rotazione. In ValueAnimator, puoi aggiornare una matrice per le rotazioni 3D usando i modelli android.graphics.Camera.

Kotlin

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

Java

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

Poiché vuoi ruotare il testo dall'asse centrale anziché dall'angolo, ottenere i limiti di testo e quindi usare preTranslate e postTranslate per modificare per tradurre il testo in modo che 0,0 sia il centro della rotazione senza cambiare la posizione in cui il testo viene disegnato sullo schermo.

Kotlin

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

Java

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();
Testo gradiente animato rotante rosso e verde
Testo gradiente animato rosso e verde a rotazione

Utilizzo di RuntimeShader con Jetpack Compose

È ancora più facile usare RuntimeShader se esegui il rendering dell'UI con Jetpack Compose. A partire dallo stesso Shader gradiente prima del:

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

Puoi applicare lo shaker a una ShaderBrush Tu poi usa ShaderBrush come parametro per i comandi di disegno all'interno delle Ambito di disegno di Canvas.

// 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)
}
Cerchio gradiente AGSL Compose
Cerchio sfumato rosso e verde

Utilizzo di RuntimeShader con RenderEffect

Puoi utilizzare RenderEffect per applicare uno RuntimeShader a un genitore View e tutte le viste secondarie. È più costoso che disegnare un View personalizzato. ma consente di creare facilmente un effetto che incorpora ciò che sono state originariamente disegnate createRuntimeShaderEffect

Kotlin

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

Java

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

Il secondo parametro è il nome di un'uniforme shaker che puoi eval con un (come quello passato in fragCoord) per ottenere il colore originale del RenderNode (la Vista e le relative schede secondarie di visualizzazione), consentendoti di eseguire qualsiasi tipo di effetto.

uniform shader background;       // Root node of View tree to be altered
return mix(returnColor, background.eval(fragCoord), 0.5);
Pulsante Griglia combinata sopra
Pulsante AGSL combinato con griglia AGSL

Effetto griglia combinato su un pulsante, ma sotto un pulsante di azione mobile (poiché si trova in una gerarchia View diversa).