Auf dieser Seite werden die Grundlagen von AGSL und verschiedene Möglichkeiten erläutert, wie Sie AGSL auf Ihrem Android-Gerät verwenden können.
Ein einfacher AGSL-Shader
Ihr Shader-Code wird für jedes gezeichnete Pixel aufgerufen und gibt die Farbe zurück, die das Pixel
gezeichnet werden sollte. Ein äußerst einfacher Shader
in einer einzigen Farbe; In diesem Beispiel wird Rot verwendet. Der Shader wird in einem String
definiert.
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" + "}";
Im nächsten Schritt erstellen Sie eine RuntimeShader
Objekt, das mit Ihrem Shader-String initialisiert wurde. Dadurch wird auch der Shader kompiliert.
Kotlin
val fixedColorShader = RuntimeShader(COLOR_SHADER_SRC)
Java
RuntimeShader fixedColorShader = new RuntimeShader(COLOR_SHADER_SRC);
RuntimeShader
kann überall dort verwendet werden, wo es ein standardmäßiger Android-Shader kann. Als
Sie können damit beispielsweise in ein benutzerdefiniertes View
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 } }
Dadurch wird ein rotes View
gezeichnet. Mit uniform
können Sie einen Farbparameter an
der zu zeichnende Shader. Fügen Sie dem Shader zuerst die Farbe uniform
hinzu:
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" + "}";
Rufen Sie dann setColorUniform
aus Ihrer benutzerdefinierten View
auf, um die gewünschte Farbe zu übergeben.
in den AGSL-Shader.
Kotlin
fixedColorShader.setColorUniform("iColor", Color.GREEN )
Java
fixedColorShader.setColorUniform("iColor", Color.GREEN );
Jetzt wird ein grünes View
angezeigt. wird die Farbe View
mit einem
aus dem Code Ihrer benutzerdefinierten View
, anstatt in den
Shader.
Sie können stattdessen einen Farbverlaufseffekt erstellen. Sie müssen zunächst
den Shader, um die View
-Auflösung als Eingabe zu akzeptieren:
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" + "}";
Farbverlauf zeichnen
Dieser Shader hat etwas Raffiniertes. Für jedes Pixel wird ein float2
erstellt.
Vektor mit den x- und y-Koordinaten geteilt durch die Auflösung, wobei
einen Wert zwischen null und eins. Anhand dieses skalierten Vektors
die roten und grünen Komponenten der Rückgabefarbe.
Sie übergeben die Auflösung von View
an einen AGSL-Shader uniform
, indem Sie folgenden Befehl aufrufen:
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); } }
Shader animieren
Mit einer ähnlichen Technik kannst du den Shader animieren, indem du ihn so änderst, dass er iTime
- und iDuration
-Uniformen erhält. Der Shader verwendet diese Werte, um einen
dreieckige Welle für die Farben, sodass sie zwischen den Werten des Farbverlaufs hin- und herwechseln.
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"+ "}";
Aus dem Quellcode der benutzerdefinierten Ansicht wird ein
ValueAnimator
aktualisiert die
Trikot von 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()); } });
Komplexe Objekte bemalen
Sie müssen den Shader nicht zeichnen, um den Hintergrund zu füllen. kann es sein
überall verwendet werden,
Paint
-Objekt, z. B.
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);
Shading- und Canvas-Transformationen
Sie können zusätzliche Canvas
-Transformationen auf den schattierten Text anwenden, z. B.
Rotation. Im ValueAnimator
können Sie eine Matrix für 3D-Drehungen aktualisieren.
mit der integrierten
Klasse 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);
Da Sie den Text von der Mittelachse und nicht von der Ecke aus drehen möchten,
Rufen Sie die Textgrenzen ab und ändern Sie mit preTranslate
und postTranslate
Matrix, um den Text so zu übersetzen, dass 0,0 der Mittelpunkt der Drehung ist,
Position ändern, an der der Text auf dem Bildschirm gezeichnet wird.
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();
RuntimeShader mit Jetpack Compose verwenden
Es ist noch einfacher, RuntimeShader
zu verwenden, wenn du deine UI mit
Jetpack Compose. Beginnend mit demselben Farbverlauf-Shader von
vorher:
private const val COLOR_SHADER_SRC =
"""uniform float2 iResolution;
half4 main(float2 fragCoord) {
float2 scaled = fragCoord/iResolution.xy;
return half4(scaled, 0, 1);
}"""
Sie können diesen Shader auf einen
ShaderBrush
Ich
Verwenden Sie dann ShaderBrush
als Parameter für die Zeichenbefehle in Ihrem
Zeichenumfang von 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)
}
<ph type="x-smartling-placeholder">
RuntimeShader mit RenderEffect verwenden
Sie können
RenderEffect
zum Anwenden
RuntimeShader
an das übergeordnete Element View
und alle untergeordneten Ansichten enthält. Das ist teurer als das Zeichnen einer benutzerdefinierten View
. aber
können Sie ganz einfach einen Effekt erzeugen, der das Prinzip der
die ursprünglich mit
createRuntimeShaderEffect
Kotlin
view.setRenderEffect(RenderEffect.createRuntimeShaderEffect(myShader, "background"))
Java
view.setRenderEffect(RenderEffect.createRuntimeShaderEffect(myShader, "background"));
Der zweite Parameter ist der Name einer Shader-Uniform, die Sie eval
mit einem
-Koordinatenparameter wie den in fragCoord übergebenen Parameter, um die Originalfarbe abzurufen
der
RenderNode
(die Datenansicht und ihr untergeordnetes Element)
Ansichten), sodass du alle möglichen Effekte anwenden kannst.
uniform shader background; // Root node of View tree to be altered
return mix(returnColor, background.eval(fragCoord), 0.5);
<ph type="x-smartling-placeholder">
Ein Rastereffekt, gemischt über einer Schaltfläche, aber unter einer unverankerten Aktionsschaltfläche
da es sich in einer anderen View
-Hierarchie befindet.