Decals — наклейки в 3D
Decal-узел для bullet holes, граффити, dirt-масок, footprint'ов и других накладок на геометрию.
Decal — узел в Godot, который проецирует текстуру на поверхности под собой. Это классический приём для:
- Дырок от пуль на стене
- Грязи на полу, лужи
- Граффити, постеров
- Следов от шин
- Cracks, кровь, scorch marks от взрывов
Реализовано как отдельный render pass, не лежит на геометрии — поэтому можно “клеить” поверх любой поверхности без модификации mesh’ей.
CSS mix-blend-mode: multiply или Canvas globalCompositeOperation = 'multiply' поверх
background. В 3D у браузеров аналога нет — это специфика real-time graphics.
Базовое использование
- Добавьте на сцену узел Decal.
- В Inspector задайте
texture_albedo— основная текстура. - (Опционально)
texture_normal— карта нормалей для рельефа. - (Опционально)
texture_orm— Occlusion/Roughness/Metallic mask. - Поверните и масштабируйте узел: bounding box decal’а определяет, на какую область он проецируется.
cull_mask— фильтр по visual layers (только определённые объекты получают decal).
World3D
├── Floor (StaticBody3D + MeshInstance3D)
└── BulletHole (Decal)
texture_albedo: bullet_hole.png (RGBA)
size: (0.3, 0.3, 0.3) # 30×30 cm область
rotation: looking at -Y (вниз — на пол)
Программный spawn
extends Node
@export var decal_scene: PackedScene # Decal в виде PackedScene
@export var lifetime: float = 30.0
func spawn_bullet_hole(at: Vector3, normal: Vector3) -> void:
var decal := decal_scene.instantiate() as Decal
add_child(decal)
decal.global_position = at + normal * 0.01 # чуть выше поверхности
# Ориентируем decal так, чтобы он смотрел "вниз" (по -Y) на нормаль
decal.look_at(at - normal, Vector3.UP if abs(normal.y) < 0.9 else Vector3.BACK)
# Маленькая рандомизация поворота для естественности
decal.rotate_object_local(Vector3.UP, randf() * TAU)
# Удаляем через lifetime — типовой подход для "тает" эффекта
var tween := create_tween()
tween.tween_interval(lifetime * 0.7)
tween.tween_property(decal, "albedo_mix", 0.0, lifetime * 0.3)
tween.tween_callback(decal.queue_free)
albedo_mix — float 0..1, контролирует “силу” decal’а. Анимируете в 0 → decal плавно исчезает.
Параметры Decal’а
size: Vector3— bounding box проекции (X×Y по поверхности, Z — глубина проекции).texture_albedo+albedo_mix— цветовая текстура и её сила.texture_normal+normal_fade— карта нормалей и насколько она применяется.texture_orm— комбинированная Occlusion/Roughness/Metallic.texture_emission+emission_energy— самосветящиеся decal’ы (огни, экраны).modulate: Color— общий цвет умножения (для перекраски без замены текстуры).upper_fade/lower_fade— fade в углах bounding box’а.distance_fade_*— fade на расстоянии (для оптимизации; дальние decal’ы исчезают).cull_mask— битмаска visual layers; decal видим только на этих слоях.
В сценах с активной стрельбой накапливаются сотни decal’ов. Без distance_fade_enabled = true
- разумных
begin/lengthрендерер будет процессить их все. С fade — дальние плавно становятся прозрачными и пропускаются, оставляя видимыми только ~30-50 ближних.
Rendering требования
- Forward+ или Mobile renderer. На Compatibility (WebGL) decal’ы работают, но в урезанном виде и через эмуляцию.
- Decal не пишет в depth buffer — он лежит поверх пикселя. Это значит: за углом или сквозь стены он не виден, и сортировка с прозрачными объектами может быть сложной.
Когда decal — НЕ выбор
- Большие, точные знаки на полу (например, разметка для рендера-на-карту) — может быть дешевле включить как часть текстуры пола или второй UV-слой.
- Геометрия, которая должна влиять на физику (выпуклость, статуэтка) — Decal только визуальный, не меняет collision shape.
- Анимированные decals — поддерживаются (sprite sheet через UV-shift в
texture_albedo), но через ShaderMaterial; стандартный Decal node не имеет анимации в Inspector.
Сравнение с Unity
Unity имеет аналог: Decal Projector в URP (com.unity.render-pipelines.universal/Decals).
Концепция и API почти 1-в-1: тоже projector, texture_albedo, distance fade, cull mask. Различия —
в деталях performance: Unity URP Decal Projector обычно дороже, чем Godot Decal в Forward+.