Na tej stronie znajdziesz podstawowe informacje o języku migowym AGSL i o różnych sposobach korzystania z niego na Androidzie. .
Prosty program do cieniowania AGSL
Kod cieniowania jest wywoływany dla każdego widocznego piksela i zwraca kolor,
do malowania. Wyjątkowo prosty cieniowanie to taki, który zawsze się powraca
jeden kolor; W tym przykładzie użyto koloru czerwonego. Moduł cieniujący jest zdefiniowany w elemencie String
.
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" +
"}";
Następnym krokiem jest utworzenie RuntimeShader
.
zainicjowany ciągiem znaków cieniowania. Spowoduje to również skompilowanie cieniowania.
val fixedColorShader = RuntimeShader(COLOR_SHADER_SRC)
RuntimeShader fixedColorShader = new RuntimeShader(COLOR_SHADER_SRC);
RuntimeShader
można używać wszędzie tam, gdzie można zastosować standardowy cieniowanie Androida. Jako
Możesz go natomiast narysować w niestandardowym elemencie View
za pomocą
Canvas
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
}
}
Powoduje to wyświetlenie czerwonego elementu View
. Za pomocą parametru uniform
możesz przekazać parametr koloru do:
program do cieniowania, który chcesz narysować. Najpierw dodaj kolor uniform
do cieniowania:
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" +
"}";
Następnie wywołaj setColorUniform
z niestandardowego View
, aby przekazać wybrany kolor
do cieniowania AGSL.
fixedColorShader.setColorUniform("iColor", Color.GREEN )
fixedColorShader.setColorUniform("iColor", Color.GREEN );
Teraz otrzymasz zielony View
; kolorem View
kontroluje się za pomocą
z kodu w niestandardowym View
zamiast umieszczać w
program do cieniowania.
Zamiast tego możesz utworzyć efekt gradientu koloru. Najpierw musisz zmienić
program do cieniowania akceptuje rozdzielczość View
jako dane wejściowe:
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" +
"}";
Rysowanie gradientu
Ten cieniowanie robi coś fantazyjnego. Dla każdego piksela powstaje float2
który zawiera współrzędne x i y podzielone przez rozdzielczość, która
zwróci wartość od 0 do 1. Następnie używa tego przeskalowanego wektora,
utworzyć czerwone i zielone składowe koloru zwracanego.
Przekazujesz rozdzielczość zasobu View
do cieniowania AGSL uniform
, wywołując
setFloatUniform
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);
}
}

Animowanie cieniowania
W podobny sposób możesz animować cieniowanie, modyfikując go tak, aby otrzymywały uniformy iTime
i iDuration
. Mechanizm cieniujący użyje tych wartości do utworzenia
trójkątna fala kolorów, powodując przesuwanie się w tył i z powrotem po wartościach gradientu.
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"+
"}";
W kodzie źródłowym widoku niestandardowego komponent
ValueAnimator
aktualizuje
Strój iTime
.
// 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());
}
});

Malowanie złożonych obiektów
Nie musisz rysować cieniowania, aby wypełnić tło. może być
w dowolnym miejscu akceptującym
Paint
, taki jak
drawText
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);

Cieniowanie i przekształcenia w Canvas
Możesz zastosować dodatkowe przekształcenia Canvas
do zacienionego tekstu, takie jak
i rotacji. W narzędziu ValueAnimator
możesz zaktualizować macierz obrotów 3D
za pomocą wbudowanego
Zajęcia: android.graphics.Camera
.
// 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);
Tekst chcesz obrócić od osi środkowej, a nie od rogu,
pobierz granice tekstu, a następnie użyj preTranslate
i postTranslate
, aby zmienić
macierz do przetłumaczenia tekstu tak, aby 0,0 stanowiło środek obrotu bez
przez zmianę pozycji, w której tekst jest rysowany na ekranie.
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();

Korzystanie z RuntimeShader w Jetpack Compose
Korzystanie z narzędzia RuntimeShader
jest jeszcze łatwiejsze, jeśli renderujesz interfejs za pomocą
Jetpack Compose Zaczynam od tego samego cieniowania gradientu z
before:
private const val COLOR_SHADER_SRC =
"""uniform float2 iResolution;
half4 main(float2 fragCoord) {
float2 scaled = fragCoord/iResolution.xy;
return half4(scaled, 0, 1);
}"""
Możesz zastosować ten program do cieniowania
ShaderBrush
Ty
użyj ShaderBrush
jako parametru w poleceniach rysowania w
Zakres rysowania (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)
}

Używanie RuntimeShader z funkcją RenderEffect
Za pomocą
RenderEffect
, aby zastosować
RuntimeShader
dla elementu nadrzędnego (View
)
i wszystkie widoki podrzędne. To droższe niż rysowanie niestandardowego elementu View
. ale
pozwala łatwo stworzyć efekt, który obejmuje
pierwotnie narysowano przy użyciu
createRuntimeShaderEffect
view.setRenderEffect(RenderEffect.createRuntimeShaderEffect(myShader, "background"))
view.setRenderEffect(RenderEffect.createRuntimeShaderEffect(myShader, "background"));
Drugi parametr to nazwa jednostki cieniującej, której możesz eval
za pomocą
parametru współrzędnych (np. przekazywanego we fragCoord), aby uzyskać kolor oryginalny
z
RenderNode
(widok i jego element podrzędny
widoków danych), dzięki czemu możesz używać różnych efektów.
uniform shader background; // Root node of View tree to be altered
return mix(returnColor, background.eval(fragCoord), 0.5);

Efekt siatki złożony z przycisku, ale pod pływającym przyciskiem polecenia
(ponieważ znajduje się w innej hierarchii View
).