Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
361b7f7
docs: added note how to resolve, when melos install commands fail (no…
kornellapu Jan 17, 2026
9d2057e
docs: Added basic shader tutorial
kornellapu Jan 19, 2026
fc6a1af
docs: Fixed formatting in files
kornellapu Jan 19, 2026
3a15cec
docs: Fixed formatting in files
kornellapu Jan 19, 2026
52925e6
docs: Fixed wording
kornellapu Jan 20, 2026
7072d47
docs: Fixed wording
kornellapu Jan 20, 2026
0ab2ed8
docs: Added cspell ignore to: human and account names and sounds
kornellapu Jan 20, 2026
2bd05c6
docs: Changed .gif resource to looping .webp
kornellapu Jan 20, 2026
5c2977c
docs: revert to original (reverting note in contributing.md)
kornellapu Jan 20, 2026
e3fcbf5
docs: Changed backtick triplets to singles around non code words.
kornellapu Jan 21, 2026
527d8a1
docs: Fixed wording by spydon suggestion
kornellapu Jan 21, 2026
ddbbea1
docs: Added usernames to .cspell dictionary
kornellapu Jan 21, 2026
fe2d2c6
docs: Removed ignoring usernames in .cspell
kornellapu Jan 21, 2026
3bfb578
docs: Added shader words to .cspell dictionary
kornellapu Jan 21, 2026
e6af71a
docs: Removed .cspell ignore from shader
kornellapu Jan 21, 2026
c2b04cc
docs: Removed all remaining .cspell ignores
kornellapu Jan 21, 2026
039ddf3
docs: Ordered .cspell usernames dictionary
kornellapu Jan 21, 2026
2b922cf
docs: Removed separate repo link
kornellapu Jan 21, 2026
8817b2c
docs: Address review comments on basic shader tutorial
spydon Mar 1, 2026
81efa89
docs: Polish shader tutorial style and code quality
spydon Mar 1, 2026
aaf72ab
docs: Rewrap tutorial prose lines to fill 100-char width
spydon Mar 1, 2026
3338e15
docs: Rewrite shader tutorial prose for clarity
spydon Mar 1, 2026
e922b05
docs: Fix markdown lint line-length in note and code blocks
spydon Mar 1, 2026
dc0c353
Merge branch 'main' into docs/docs_basic_shader_tutorial
spydon Mar 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/.cspell/gamedev_dictionary.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ jank # stutter or inconsistent gap or timing
lerp # short for linear interpolation
LTRBR # left top right bottom radius
LTWH # left top width height
mediump # medium GLSL float precision
metalness # a measure of how much a surface reflects light for the purposes of physically based rendering
Minkowski # Minkowski sum, a sum of two sets of vectors, A and B, where the result is the sum of each vector pair
multitap # support from a device to recognize many taps at the same time
Expand All @@ -49,6 +50,7 @@ subfolders # plural of subfolders
sublists # plural of sublist
subrange # a range entirely contained on a given range
SVGs # plural of SVG
texel # texture pixel (unit of texture map)
texels # plural of texel
tileset # image with a collection of tiles. in games, tiles are small square sprites laid out in a grid to form the game map
tilesets # plural of tileset
Expand Down
4 changes: 4 additions & 0 deletions .github/.cspell/people_usernames.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,12 @@ erickzanardo # github.com/erickzanardo
feroult # github.com/feroult
fröber # github.com/Brixto
gnarhard # github.com/gnarhard
Hoodead # github.com/kornellapu
kenney # kenney.nl
Klingsbo # github.com/spydon
Kornél # github.com/kornellapu
lapu # Artist of reference art (basic shader tutorial)
Lapu # github.com/kornellapu
luan # github.com/luanpotter
luanpotter # github.com/luanpotter
Lukas # github.com/spydon
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/images/tutorials/basic_shader/sword.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
29 changes: 29 additions & 0 deletions doc/tutorials/basic_shader/basic_shader.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Basic shader tutorial

This tutorial will give you a brief understanding of how to create and use basic shaders on
`SpriteComponent`s with `PostProcess` and `PostProcessComponent` using Dart/Flutter and the Flame
engine.

This tutorial assumes that you have a working Flame project set up. If you don't, please follow
the [](bare_flame_game.md) tutorial first.

The tutorial consists of 4 steps. We will create a simple outline shader for sprites which have a
transparent background layer.

```{note}
This tutorial is intended to work on images with transparent
background, like `.png` files.
```

*Created by Kornél (Hoodead) Lapu.*


```{toctree}
:hidden:

1. Sprite Component <step1.md>
2. Outline Post Process <step2.md>
3. Shader <step3.md>
4. User Input <step4.md>
5. Takeaways <takeaways.md>
```
96 changes: 96 additions & 0 deletions doc/tutorials/basic_shader/step1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# 1. Sprite Component


## Architecture and Responsibilities

Let's create the component where we render our sprite and apply the shader. We will split this
into two classes:

- a `SpriteComponent` subclass that loads the image and handles input events
- a `PostProcessComponent` subclass that wraps the sprite and applies the shader

This separation means that shader changes only require editing the wrapper class, while sprite
changes like adding input event mixins or additional children only require editing the sprite
class.


## Image resource

For this tutorial we need an image with a transparent background to apply the outline shader to.
Create an `assets/images/` directory in your project and add your `.png` image there.

Don't forget to register the assets folder in `pubspec.yaml`:

```yaml
flutter:
assets:
- assets/images/
```


## Sprite

Create a new file named `sword_component.dart` (replace "sword" with your own image name):

```dart
import 'package:flame/components.dart';

class SwordSprite extends SpriteComponent {
@override
Future<void> onLoad() async {
sprite = await Sprite.load('sword.png');
size = sprite!.srcSize;
}
}
```


## Wrapper

Next, add the wrapper class that applies the post process. In the same file, create:

```dart
import 'package:flame/components.dart';
import 'package:flame/post_process.dart';

import 'package:basic_shader_tutorial/outline_postprocess.dart';

class OutlinedSwordSprite extends PostProcessComponent {
OutlinedSwordSprite({super.position, super.anchor})
: super(
children: [SwordSprite()],
postProcess: OutlinePostProcess(anchor: anchor ?? Anchor.topLeft),
);
}
```


## Result

The final `sword_component.dart` file looks like this:

```dart
import 'package:flame/components.dart';
import 'package:flame/post_process.dart';

import 'package:basic_shader_tutorial/outline_postprocess.dart';

class OutlinedSwordSprite extends PostProcessComponent {
OutlinedSwordSprite({super.position, super.anchor})
: super(
children: [SwordSprite()],
postProcess: OutlinePostProcess(anchor: anchor ?? Anchor.topLeft),
);
}

class SwordSprite extends SpriteComponent {
@override
Future<void> onLoad() async {
sprite = await Sprite.load('sword.png');
size = sprite!.srcSize;
}
}
```

This won't compile yet because `OutlinePostProcess` doesn't exist. Let's create it in the next
step!
135 changes: 135 additions & 0 deletions doc/tutorials/basic_shader/step2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
# 2. Outline Post Process


## Responsibility

The `PostProcess` class manages the fragment (pixel) shader. It is responsible for loading the
shader program, creating GPU resources, and keeping uniform variables up to date each frame. You
can also expose runtime settings through uniforms, for example to enable or disable effects.


## Post process

Create a new file named `outline_postprocess.dart`. This class loads the shader program in
`onLoad()` and passes uniform values to the GPU each frame in `postProcess()`:

```dart
import 'dart:ui';

import 'package:flutter/material.dart';

import 'package:flame/components.dart';
import 'package:flame/post_process.dart';

extension on Color {
Vector4 toVector4() {
return Vector4(r, g, b, a);
}
}

class OutlinePostProcess extends PostProcess {
final double outlineSize;
Color outlineColor;
final Anchor anchor;

OutlinePostProcess({
this.outlineSize = 7.0,
this.outlineColor = Colors.purpleAccent,
this.anchor = Anchor.topLeft,
});

late final FragmentProgram _fragmentProgram;
late final FragmentShader _fragmentShader =
_fragmentProgram.fragmentShader();
late final Paint _myPaint = Paint()..shader = _fragmentShader;

@override
Future<void> onLoad() async {
await super.onLoad();

_fragmentProgram =
await FragmentProgram.fromAsset('assets/shaders/outline.frag');
}

@override
void postProcess(Vector2 size, Canvas canvas) {
final preRenderedSubtree = rasterizeSubtree();

_fragmentShader.setFloatUniforms((value) {
value
..setVector(size)
..setFloat(outlineSize)
..setVector(outlineColor.toVector4());
});

_fragmentShader.setImageSampler(0, preRenderedSubtree);

canvas
..save()
..translate(-size.x * anchor.x, -size.y * anchor.y)
..drawRect(Offset.zero & size.toSize(), _myPaint)
..restore();
}
}
```

With this file in place, the syntax error from the previous step will go away.

Since the `PostProcessComponent` is the parent of the `SpriteComponent`, the post process renders
first and the sprite is drawn on top. The `rasterizeSubtree()` call captures all children into an
image that the shader can sample from.


## Usage

Now we need to wire everything together. Open `main.dart` and add both a plain sprite and an
outlined sprite to the world so we can compare them side by side:

```dart
import 'package:flutter/material.dart';

import 'package:flame/components.dart';
import 'package:flame/game.dart';

import 'package:basic_shader_tutorial/sword_component.dart';

void main() {
runApp(
GameWidget(game: MyGame()),
);
}

class MyGame extends FlameGame {
MyGame() : super(world: MyWorld());

@override
Color backgroundColor() => Colors.green;
}

class MyWorld extends World {
@override
Future<void> onLoad() async {
add(
SwordSprite()
..position = Vector2(-200, 0)
..anchor = Anchor.center,
);

add(
OutlinedSwordSprite(
position: Vector2(200, 0),
anchor: Anchor.center,
),
);
}
}
```

Here we use a custom `FlameGame` subclass to override the background color. Adjust the positions
and color to suit your own images.

Run the application. You should see only one sprite, the outlined one is missing. The console
will show why:
`[...] Unhandled Exception: Exception: Asset 'assets/shaders/outline.frag' not found [...]`

We haven't created the shader file yet. Let's do that in the next step.
Loading