I’ve put together a shader that draws outlines of shapes by passing the normal and depth buffers of the viewport through a Sobel convolution. You can adjust the size of the kernel to make the lines thicker or thinner and even taper.
Draw object outlines in Godot 3D scenes.
6 Likes
wchc
July 4, 2025, 1:13pm
2
I know just some of these words
Amazing work! You should put a link to the repo too, as I’m sure someone will be interested in it. I’ll definitely play around myself.
Draw object outlines in Godot 3D scenes.
1 Like
Thanks! I’ve added the link.
1 Like
Really cool stuff. This is a hard problem to solve. Have you seen the new feature added in Godot 4.5 beta 1?
They’ve added stencil support.
master
← apples:7174-apples-stencil
opened 10:48AM - 17 Aug 23 UTC
Adds stencil buffer support to spatial materials.
## Linked Issues
- Super… sedes https://github.com/godotengine/godot/pull/78542
- Resolves https://github.com/godotengine/godot-proposals/issues/7174
- *Bugsquad edit:* Resolves https://github.com/godotengine/godot-proposals/issues/1298
- ~~Currently integrates https://github.com/godotengine/godot/pull/73527~~ The depth function design included here has deviated from its original PR.
Testing with the sample project https://github.com/apples/godot-stencil-demo
## Screenshots

Standard outline, standard x-ray, and a custom outline effect.
Made entirely with the Material settings.

A more interesting x-ray example with a custom material.
[ww_light.webm](https://github.com/user-attachments/assets/768ad9c0-313d-46d4-80af-5067399b64df)
A cheap light effect that mimics the lighting effects in Wind Waker.
[campfire_480.webm](https://github.com/user-attachments/assets/834a11b4-bdc8-40e2-8030-00d4d6ea1859)
A simple stylized fire effect implemented entirely with `StandardMaterial3D`.
## Example shader
```glsl
shader_type spatial;
render_mode depth_draw_never, cull_back, unshaded;
stencil_mode write_depth_fail, 2;
void fragment() {
ALPHA = 0.0; // Currently needed because we can't control color mask.
}
```
## Differences from the design
- Added compare operator `ALWAYS`. The `ALWAYS` operator is needed for some effects.
- Added outline and xray presets ahead of schedule because it was easy and made testing easier.
- The way that `next_pass` materials are handled is a bit strange right now, but I tried to make it as non-destructive as possible.
## Currently implemented
- Added `stencil_mode` to shaders, which works very similarly to `render_mode`.
- `read` - enables stencil comparisons.
- `write` - enables stencil writes on depth pass.
- `write_depth_fail` - enables stencil writes on depth fail.
- `compare_(never|less|equal|less_or_equal|greater|not_equal|greater_or_equal|always)` - sets comparison operator.
- (integer) - sets the reference value.
- Modified the `depth_test_disabled` render mode to be split into `depth_test_{default,disabled,inverted}` modes.
- `depth_test_default` - Depth test enabled, standard sorting.
- `depth_test_disabled` - Depth test disabled, same behavior as currently implemented.
- `depth_test_inverted` - Depth test enabled, inverted sorting.
- `VisualShader` now has special handling for `depth_test_` modes: The `disabled` mode is kept as-is and presented as a bool flag, while the other two modes are presented as a enum mode dropdown which excludes the `disabled` mode.
- `BaseMaterial3D` stencil properties.
- `depth_test` - Determines whether the depth test is inverted or not. Hidden when `no_depth_test` is true.
- `stencil_mode` - choose between disabled, custom, or presets.
- `stencil_flags` - set read/write/write_depth_fail flags.
- `stencil_compare` - set stencil comparison operator.
- `stencil_reference` - set stencil reference value.
- `stencil_effect_color` - used by outline and xray presets.
- `stencil_outline_thickness` - used by outline preset.
- `BaseMaterial3D` stencil presets.
- `STENCIL_MODE_OUTLINE` - adds a next pass which uses the `grow` property to create an outline.
- `STENCIL_MODE_XRAY` - adds a next pass which uses `depth_test_disabled` to draw a silhouette of the object behind other geometry.
## Supported Renderers
- [x] Forward+
- [x] Mobile
- [x] Compatibility
## Depth Prepass
~~Stencil effects work well when rendered with a second opaque pass immediately following the current opaque pass. However, with depth prepass enabled, the depth information from any stencil materials is not available for screen-space effects. For instance, with SSAO enabled, opaque stencil materials have strong visual artifacts. Due to this, there might not be a good way to support opaque stencil effects when depth prepass is enabled.~~
This has been resolved by simply not supporting opaque-pass stencil-read materials.
## GLES3
~~More work is needed for GLES3 support. The depth buffer attachment for the scene FBO must be changed to a depth-stencil attachment. Making this change without breaking existing projects will be difficult.~~
- ~~See https://github.com/godotengine/godot/commit/f2ec38d26d69943c1e53828f8df88a78f187d106 for an experimental implementation, and find concerns in this comment: https://github.com/godotengine/godot/pull/80710#issuecomment-1694398430.~~
Update: GLES3 and therefore the Compatibility renderer are now included in this PR. It may potentially cause problems for XR, but requires further testing.
## Sorting Problems
Currently the biggest annoyance with sorting, is that `next_pass` materials aren't necessarily rendered immediately after their parent. Users have to rely entirely on `render_priority` to get correct sorting.
Individual stencil effects which have material passes in both the opaque and the alpha passes will have more difficulty being correctly sorted. This might make effects such as portals and impossible geometry more challenging to implement.
TBH, other than the screenshots, I don’t know a lot about it, but it seems pretty exciting specifically for the purpose of outlining. Personally I’ve tried about half a dozen versions and all of them are a bit janky where corners don’t always line up. Yours definitely seems better. But as @wshs says you seem to know your stuff. So I’m curious what do you think of this new feature?
1 Like
I didn’t realize the Godot team was adding that for 4.5. It might be a good replacement for the depth test in this shader. I’d like to know how they’re exposing it to the shader API.
1 Like