Update SkSL docs slightly
- Use .eval() in all example fiddles - Add some more explanation of how the parts of a draw/paint contribute to the GPU fragment shader. Bug: skia:12302 Change-Id: Ib69b9af39368c980e1aa9206af585f26498d083e Reviewed-on: https://skia-review.googlesource.com/c/skia/+/445640 Commit-Queue: Brian Osman <brianosman@google.com> Reviewed-by: John Stiles <johnstiles@google.com>
This commit is contained in:
parent
0ed278b42d
commit
56273c9248
@ -7,8 +7,9 @@ linkTitle: 'SkSL'
|
||||
|
||||
**SkSL** is Skia's
|
||||
[shading language](https://en.wikipedia.org/wiki/Shading_language).
|
||||
**`SkRuntimeEffect`** is a Skia C++ object that can be used to create `SkShader`
|
||||
and `SkColorFilter` objects with behavior controlled by SkSL code.
|
||||
**`SkRuntimeEffect`** is a Skia C++ object that can be used to create
|
||||
`SkShader`, `SkColorFilter`, and `SkBlender` objects with behavior controlled by
|
||||
SkSL code.
|
||||
|
||||
You can experiment with SkSL at https://shaders.skia.org/. The syntax is very
|
||||
similar to GLSL. When using SkSL effects in your Skia application, there are
|
||||
@ -18,26 +19,65 @@ stage of the
|
||||
[GPU pipeline](https://www.khronos.org/opengl/wiki/Rendering_Pipeline_Overview).
|
||||
With SkSL, you are programming a stage of the Skia pipeline.**
|
||||
|
||||
In particular, a GLSL fragment shader controls the entire behavior of the GPU
|
||||
between the rasterizer and the blending hardware. That shader does all of the
|
||||
work to compute a color, and the color it generates is exactly what is fed to
|
||||
the fixed-function blending stage of the pipeline.
|
||||
|
||||
SkSL effects exist as part of the larger Skia pipeline. When you issue a canvas
|
||||
drawing operation, Skia (generally) assembles a single GPU fragment shader to do
|
||||
all of the required work. This shader typically includes several pieces. For
|
||||
example, it might include:
|
||||
|
||||
- Evaluating whether a pixel falls inside or outside of the shape being drawn
|
||||
(or on the border, where it might apply antialiasing).
|
||||
- Evaluating whether a pixel falls inside or outside of the clipping region
|
||||
(again, with possible antialiasing logic for border pixels).
|
||||
- Logic for the `SkShader` on the `SkPaint`. The `SkShader` can actually be a
|
||||
tree of objects (due to `SkShaders::Blend` and other features described
|
||||
below).
|
||||
- Similar logic for the `SkColorFilter` (which can also be a tree, due to
|
||||
`SkColorFilters::Compose`, `SkColorFilters::Blend`, and features described
|
||||
below).
|
||||
- Blending code (for certain `SkBlendMode`s, or for custom blending specified
|
||||
with `SkPaint::setBlender`).
|
||||
|
||||
Even if the `SkPaint` has a complex tree of objects in the `SkShader`,
|
||||
`SkColorFilter`, or `SkBlender` fields, there is still only a *single* GPU
|
||||
fragment shader. Each node in that tree creates a single function. The clipping
|
||||
code and geometry code each create a function. The blending code might create a
|
||||
function. The overall fragment shader then calls all of these functions (which
|
||||
may call other functions, e.g. in the case of an `SkShader` tree).
|
||||
|
||||
**Your SkSL effect contributes a function to the GPU's fragment shader.**
|
||||
|
||||
---
|
||||
|
||||
## <span id="children">Sampling other SkShaders</span>
|
||||
## <span id="children">Evaluating (sampling) other SkShaders</span>
|
||||
|
||||
In GLSL, a fragment shader can sample a texture. With runtime effects, the
|
||||
object that you bind (in C++) and sample (in SkSL) is an `SkShader`. Skia has
|
||||
simple methods for creating an `SkShader` from an `SkImage`, so it's easy to use
|
||||
images in your runtime effects:
|
||||
object that you bind (in C++) is an `SkShader`, represented by a `shader` in
|
||||
SkSL. To make it clear that you are operating on an object that will emit its
|
||||
own shader code, you don't use `sample`. Instead, the `shader` object has a
|
||||
`.eval()` method. Regardless, Skia has simple methods for creating an
|
||||
`SkShader` from an `SkImage`, so it's easy to use images in your runtime
|
||||
effects:
|
||||
|
||||
<fiddle-embed name='f2885d3af66e5589fab19017953ac59e'></fiddle-embed>
|
||||
<fiddle-embed name='8a895f12c8fd7b976bb68e6002f85a8e'></fiddle-embed>
|
||||
|
||||
Because the object you bind and sample is an `SkShader`, you can directly use
|
||||
any Skia shader, without necessarily turning it into an image (texture) first.
|
||||
For example, you can sample a linear gradient:
|
||||
Because the object you bind and evaluate is an `SkShader`, you can directly
|
||||
use any Skia shader, without necessarily turning it into an image (texture)
|
||||
first. For example, you can evaluate a linear gradient. In this example,
|
||||
there is no texture created to hold the gradient. Skia generates a single
|
||||
fragment shader that computes the gradient color, samples from the image's
|
||||
texture, and then multiplies the two together:
|
||||
|
||||
<fiddle-embed name='5fa8d1ec3af346e7c16872d138598f87'></fiddle-embed>
|
||||
<fiddle-embed name='f282a4411782ed92057350e339586502'></fiddle-embed>
|
||||
|
||||
You can even sample another runtime effect:
|
||||
Of course, you can even invoke another runtime effect, allowing you to combine
|
||||
shader snippets dynamically:
|
||||
|
||||
<fiddle-embed name='08745d5050b250c2d8a826a739ea0ee1'></fiddle-embed>
|
||||
<fiddle-embed name='2151b061428f47844a2500b57c887ddf'></fiddle-embed>
|
||||
|
||||
---
|
||||
|
||||
@ -59,7 +99,7 @@ if the blend function were `(GL_ONE, GL_ONE_MINUS_SRC_ALPHA)`.
|
||||
Skia's use of premultiplied alpha implies:
|
||||
|
||||
- If you start with an unpremultiplied `SkImage` (like a PNG), turn that into an
|
||||
`SkImageShader`, and sample that shader... the resulting colors will be
|
||||
`SkImageShader`, and evaluate that shader... the resulting colors will be
|
||||
`[R*A, G*A, B*A, A]`, **not** `[R, G, B, A]`.
|
||||
- If your SkSL will return transparent colors, it must be sure to multiply the
|
||||
`RGB` by `A`.
|
||||
@ -68,7 +108,7 @@ Skia's use of premultiplied alpha implies:
|
||||
both kinds of color together.
|
||||
|
||||
Skia _enforces_ that the color produced by your SkSL is a valid premultiplied
|
||||
color. In other words, `RGB <= A`. If your SkSL returns colors where that is not
|
||||
color. In other words, `RGB ≤ A`. If your SkSL returns colors where that is not
|
||||
true, they will be clamped, leading to incorrect colors. The image below
|
||||
demonstrates this: properly premultiplied colors produce a smooth gradient as
|
||||
alpha decreases. Unpremultipled colors cause the gradient to display
|
||||
@ -90,9 +130,9 @@ invoked by another, that parent shader may modify them arbitrarily.
|
||||
|
||||
In addition, the `SkShader` produced from an `SkImage` does not use normalized coordinates (like a
|
||||
texture in GLSL). It uses `(0, 0)` in the upper-left corner, and `(w, h)` in the bottom-right
|
||||
corner. Normally, this is exactly what you want. If you're sampling an `SkImageShader` with
|
||||
corner. Normally, this is exactly what you want. If you're evaluating an `SkImageShader` with
|
||||
coordinates based on the ones passed to you, the scale is correct. However, if you want to adjust
|
||||
those coordinates (to do some kind of re-mapping of the image), remember that the coordinates are
|
||||
scaled up to the dimensions of the image:
|
||||
|
||||
<fiddle-embed name='7ae4fd94835aa957aa6beb15f1774b6a'></fiddle-embed>
|
||||
<fiddle-embed name='cc49d5a7b6b88d6a4dca1619e6df8763'></fiddle-embed>
|
||||
|
Loading…
Reference in New Issue
Block a user