Esta página aborda os conceitos básicos da AGSL e diferentes maneiras de usar a AGSL no Android app.
Um sombreador simples da AGSL
O código do shader é chamado para cada pixel desenhado e retorna a cor do pixel
deve ser pintada. Um sombreador extremamente simples é aquele que sempre retorna
uma única cor; neste exemplo usa vermelho. O sombreador é definido dentro de um 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" + "}";
A próxima etapa é criar um RuntimeShader
.
inicializado com a string de sombreador. Isso também compila o sombreador.
Kotlin
val fixedColorShader = RuntimeShader(COLOR_SHADER_SRC)
Java
RuntimeShader fixedColorShader = new RuntimeShader(COLOR_SHADER_SRC);
O RuntimeShader
pode ser usado em qualquer lugar em que um sombreador padrão do Android possa. Como
exemplo, ele pode ser usado para desenhar em uma View
personalizada usando uma
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 } }
Isso desenha uma View
vermelha. Você pode usar um uniform
para transmitir um parâmetro de cor ao
o sombreador a ser desenhado. Primeiro, adicione a cor uniform
ao sombreador:
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" + "}";
Em seguida, chame setColorUniform
no View
personalizado para transmitir a cor desejada.
no sombreador da AGSL.
Kotlin
fixedColorShader.setColorUniform("iColor", Color.GREEN )
Java
fixedColorShader.setColorUniform("iColor", Color.GREEN );
Agora, você recebe uma View
verde. a cor View
é controlada usando um
do código no View
personalizado em vez de ser incorporado na
sombreador.
Em vez disso, você pode criar um efeito de gradiente de cor. Primeiro, você precisará alterar
o sombreador para aceitar a resolução View
como entrada:
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" + "}";
Desenhar o gradiente
Esse shader é um pouco sofisticado. Para cada pixel, ele cria um float2
.
vetor que contém as coordenadas x e y divididas pela resolução, que
vai criar um valor entre zero e um. Em seguida, ele usa esse vetor dimensionado
construa os componentes vermelho e verde da cor de retorno.
Transmita a resolução de View
para um shader uniform
da AGSL chamando
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); } }
Como animar o sombreador
Você pode usar uma técnica semelhante para animar o sombreador modificando-o para receber os uniformes iTime
e iDuration
. O sombreador usará esses valores para criar um
triangular para as cores, fazendo com que elas alternem para frente e para trás em seus valores de 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"+ "}";
No código-fonte da visualização personalizada,
O ValueAnimator
atualiza o
Uniforme 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()); } });
Pintar objetos complexos
Não é necessário desenhar o sombreador para preencher o plano de fundo. pode ser
usada em qualquer lugar que aceite um
objeto Paint
, como
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);
Transformações de sombreamento e tela
É possível aplicar outras transformações Canvas
ao texto sombreado, como
a rotação de chaves. No ValueAnimator
, é possível atualizar uma matriz para rotações 3D
usando o
classe 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);
Como você deseja girar o texto a partir do eixo central e não do canto,
obter os limites de texto e, em seguida, usar preTranslate
e postTranslate
para alterar a
matriz para traduzir o texto de modo que 0,0 seja o centro da rotação sem
alterando a posição em que o texto é desenhado na tela.
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();
Como usar o RuntimeShader com o Jetpack Compose
Fica ainda mais fácil usar RuntimeShader
se você renderiza a interface usando
Jetpack Compose. Começando com o mesmo sombreador de gradiente
antes de:
private const val COLOR_SHADER_SRC =
"""uniform float2 iResolution;
half4 main(float2 fragCoord) {
float2 scaled = fragCoord/iResolution.xy;
return half4(scaled, 0, 1);
}"""
É possível aplicar esse shader a um
ShaderBrush
Você
Em seguida, use o ShaderBrush
como um parâmetro para os comandos de desenho no
Escopo do desenho de 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)
}
Como usar o RuntimeShader com RenderEffect
Você pode usar
RenderEffect
para aplicar uma
RuntimeShader
como pai View
e todas as visualizações filhas. Isso é mais caro do que desenhar uma View
personalizada. mas
permite que você crie facilmente um efeito que incorpora o que teria
originalmente desenhada usando
createRuntimeShaderEffect
Kotlin
view.setRenderEffect(RenderEffect.createRuntimeShaderEffect(myShader, "background"))
Java
view.setRenderEffect(RenderEffect.createRuntimeShaderEffect(myShader, "background"));
O segundo parâmetro é o nome de um uniforme de shader que pode ser eval
com uma
Coordenado do parâmetro (como o passado em fragCoord) para obter a cor original
da
RenderNode
(a visualização e as filhas dela)
de visualização), permitindo que você realize todos os tipos de efeitos.
uniform shader background; // Root node of View tree to be altered
return mix(returnColor, background.eval(fragCoord), 0.5);
Um efeito de grade misturado sobre um botão, mas abaixo de um botão de ação flutuante.
(já que está em uma hierarquia View
diferente).