Iluminação 3D no Godot 4 Explicada: Luzes, Sombras e WorldEnvironment

Aprenda iluminação 3D no Godot 4: DirectionalLight, OmniLight, SpotLight, sombras, WorldEnvironment e iluminação global (SDFGI, VoxelGI, LightmapGI).
Iluminação 3D no Godot 4 Explicada: Luzes, Sombras e WorldEnvironment
Iluminação 3D no Godot é o que separa uma cena que parece protótipo de uma cena que parece jogo. O modelo pode ser simples, a textura pode ser básica: com luz bem colocada, sombra coerente e um ambiente configurado, o cérebro do jogador aceita o mundo. Sem isso, até asset caro fica com cara de showroom cinza.
A boa notícia é que o sistema de luz do Godot 4 é pequeno de aprender. São três nós de luz, um nó de ambiente (o WorldEnvironment) e três técnicas de iluminação global pra escolher conforme o hardware alvo. O resto é olho e ajuste. Esse tutorial cobre cada peça na ordem em que eu monto numa cena real.
Uma observação antes de começar: parte do que aparece aqui depende do renderer escolhido na criação do projeto. Forward+ tem tudo; Mobile corta algumas técnicas (SDFGI, VoxelGI, fog volumétrico); Compatibility corta ainda mais. Vou marcar essas diferenças quando importar.
Os três tipos de luz do Godot
Todo nó de luz 3D herda de Light3D e compartilha as propriedades centrais: light_color (cor), light_energy (intensidade) e shadow_enabled (se projeta sombra). O que muda entre eles é a forma como a luz se espalha.
DirectionalLight3D: o sol
Luz que vem de uma direção única e atinge a cena inteira, como o sol ou a lua. A posição do nó não importa, só a rotação. É quase sempre a primeira luz de qualquer cena externa, e muitas cenas ficam prontas só com ela mais um ambiente decente.
Por afetar tudo, é também a luz mais barata por metro quadrado de cena. Regra prática: uma DirectionalLight por cena. Duas só em casos estilizados (um "sol" principal forte e uma luz de preenchimento fraca vinda do lado oposto, sem sombra).
Como a direção é só rotação, um ciclo de dia e noite básico é girar o nó:
extends DirectionalLight3D
@export var duracao_do_ciclo := 120.0 # segundos pra um dia completo
func _process(delta):
rotate_x(deg_to_rad(360.0 / duracao_do_ciclo) * delta)
OmniLight3D: a lâmpada
Luz pontual que emite em todas as direções dentro de um raio. Lâmpada, tocha, fogueira, explosão. As propriedades que você vai mexer:
omni_range: até onde a luz alcança. Mantenha o menor valor que funciona visualmente, porque alcance é custo.omni_attenuation: a curva de decaimento. Valores maiores concentram a luz perto da fonte.
Uma tocha que tremula é uma OmniLight com a energia variando de leve. O truque pra não virar estrobo é sortear um alvo de vez em quando e interpolar até ele:
extends OmniLight3D
@export var energia_base := 2.0
@export var variacao := 0.25
@export var velocidade := 8.0
var alvo := energia_base
func _process(delta):
if randf() < 0.1:
alvo = energia_base + randf_range(-variacao, variacao)
light_energy = lerpf(light_energy, alvo, velocidade * delta)
SpotLight3D: o cone
Luz em cone, como lanterna, farol de carro ou holofote de palco. Além do alcance (spot_range), você controla a abertura do cone com spot_angle e a suavidade da borda com spot_angle_attenuation. É a luz mais "dramática" das três: ótima pra dirigir o olhar do jogador pra um ponto específico do cenário.
WorldEnvironment: a alma da cena
Aqui mora a diferença entre cena iluminada e cena com atmosfera. O WorldEnvironment é um nó único na cena que carrega um resource Environment, e esse resource controla tudo que não é luz direta: o fundo, o céu, a luz ambiente, o tone mapping e os efeitos de pós-processamento.
Sem ele, lugares fora do alcance das luzes ficam pretos absolutos, o que quase nunca é o que você quer. As três seções que eu configuro primeiro:
Background e Sky. O modo Sky com um ProceduralSkyMaterial dá um céu com horizonte e sol em segundos, e serve de base pra luz ambiente. Pra interiores, o modo Color com um cinza escuro funciona melhor que céu nenhum.
Ambient Light. A luz indireta "de graça" que preenche as áreas sem luz direta. Com a fonte em Sky, o ambiente herda as cores do céu: azulado pra cima, tom do horizonte pros lados. Fica natural sem esforço. A ambient_light_energy controla o quanto isso clareia a cena; exagerar aqui deixa tudo lavado e sem contraste.
Tonemap. Converte o resultado em HDR pra cores de tela. O padrão Linear estoura branco com facilidade. Filmic ou ACES seguram os realces e dão aparência mais profissional na hora. A partir do Godot 4.3 existe também o AgX, pensado pra cenas com luzes muito intensas. Trocar o tonemap é a mudança de um clique que mais altera o visual do jogo inteiro.
Dá pra montar tudo isso por código quando a cena é gerada em runtime:
func _ready():
var env = Environment.new()
env.background_mode = Environment.BG_SKY
var sky = Sky.new()
sky.sky_material = ProceduralSkyMaterial.new()
env.sky = sky
env.ambient_light_source = Environment.AMBIENT_SOURCE_SKY
env.tonemap_mode = Environment.TONE_MAPPER_FILMIC
$WorldEnvironment.environment = env
Na prática, porém, o lugar do Environment é o editor, onde você vê o resultado ao vivo. Código é pra exceção, não pra regra.
Sombras sem matar a performance
Sombra é o item mais caro da conta de iluminação. Cada luz com shadow_enabled ligado obriga a engine a redesenhar a profundidade da cena do ponto de vista daquela luz. Com uma luz é tranquilo. Com dez, o frame time vai embora.
O processo que eu sigo:
Ligue sombra só onde ela conta história. A DirectionalLight quase sempre merece sombra: é ela que ancora os objetos no chão. Omni e Spot, só as que o jogador olha de perto. Uma fileira de postes no fundo do cenário ilumina sem sombra e ninguém nota.
Ajuste o alcance da sombra direcional. A propriedade directional_shadow_max_distance define até que distância da câmera a sombra do "sol" é desenhada. O padrão é generoso; em cena fechada, baixar esse valor concentra a resolução da sombra perto do jogador e ela fica visivelmente mais nítida, de graça.
Escolha o modo de split certo. A sombra direcional tem três modos: Orthogonal (mais barato, qualidade uniforme, bom pra cena pequena), PSSM 2 Splits e PSSM 4 Splits (dividem a distância em faixas com resoluções diferentes, melhor pra mundo aberto). Mais splits, mais qualidade ao longe, mais custo.
Conheça os dois defeitos clássicos. Shadow acne são listras escuras na superfície iluminada: aumente o shadow_bias da luz aos poucos até sumir. Peter-panning é a sombra "descolando" da base do objeto: sinal de bias alto demais, reduza. Em superfícies curvas, o shadow_normal_bias resolve o acne com menos efeito colateral. É um cabo de guerra entre os dois valores, e o ajuste fino é por luz, não global.
A resolução geral das sombras fica em Project Settings > Rendering > Lights and Shadows. Subir o atlas melhora tudo de uma vez, mas o custo de memória de vídeo sobe junto. Em mobile, é dos primeiros lugares onde se corta.
Iluminação global: escolhendo entre SDFGI, VoxelGI e LightmapGI
Luz direta mais ambiente flat resolve muita cena, mas o salto de realismo vem da iluminação global (GI): a luz que bate numa parede vermelha e mancha o chão de vermelho, o interior que recebe claridade indireta da janela. O Godot 4 oferece três caminhos, e a escolha é basicamente uma troca entre dinamismo e custo.
SDFGI (só Forward+): totalmente dinâmico, sem bake nenhum. Liga no Environment e funciona, inclusive com luzes se movendo e ciclo de dia e noite. É a opção natural pra mundo aberto. Os poréns: custo de GPU considerável, atualização da luz indireta com certo atraso visível, e vazamento de luz através de paredes finas (a solução costuma ser engrossar as paredes da geometria).
VoxelGI: um nó que voxeliza uma região da cena num bake rápido feito no editor. Depois do bake, luzes podem mudar e objetos dinâmicos recebem luz indireta de verdade. Qualidade ótima pra cenas de tamanho pequeno a médio (interiores, arenas), mas a região coberta e a resolução dos voxels limitam o alcance. Também é exclusivo do Forward+.
LightmapGI: o método clássico, a luz indireta é calculada uma vez no editor e gravada em textura. Custo de runtime quase zero, por isso é o caminho pra mobile e hardware fraco. A troca: só geometria estática entra no bake (e precisa de UV2, que o importador do Godot gera pra você), e luz que muda em runtime não atualiza o lightmap. Objetos dinâmicos são iluminados por sondas (probes) que o bake espalha pela cena.
Minha regra de bolso: jogo desktop com cenário dinâmico, SDFGI; interior controlado com exigência de qualidade, VoxelGI ou LightmapGI; mobile, LightmapGI sem discussão. E nenhuma vergonha em não usar GI nenhum: jogo estilizado com ambiente bem escolhido e luzes de preenchimento manuais segura o visual em qualquer hardware.
Pós-processamento: o acabamento
De volta ao Environment, três efeitos completam o pacote de iluminação:
Glow. Faz áreas brilhantes sangrarem luz, essencial pra visual neon, magia, painéis de nave. Só dispara em valores acima do branco, então combina com materiais emissivos com emission_energy_multiplier acima de 1. Sutileza é a regra: glow alto demais é o jeito mais rápido de deixar o jogo com cara de 2008.
Fog volumétrico (só Forward+). Névoa de verdade, que reage às luzes: o cone do SpotLight vira um facho visível no ar. Caro, mas o efeito em cena noturna ou interior empoeirado é imbatível. Pra névoa localizada (um porão, um vale), o nó FogVolume limita o efeito a uma região.
SSAO. Oclusão de ambiente em screen space: escurece cantos, frestas e pontos de contato entre objetos. É o efeito que tira a aparência de "objetos flutuando" sem custo de bake.
E um nó que vale citar junto: ReflectionProbe. Materiais metálicos e pisos polidos refletem o que essa sonda captura da região ao redor, em vez de refletirem só o céu. Um probe por cômodo muda o nível de qualidade percebida de interiores.
Erros que eu vejo toda semana
- Compensar ambiente fraco com dez OmniLights. Resolva primeiro o WorldEnvironment, depois adicione luzes pontuais com propósito. Menos luzes fortes contam mais que muitas luzes fracas.
- Sombra ligada em tudo. Custo silencioso que estoura o frame em hardware mediano. Ligue por decisão, não por padrão.
- Cena lavada. Ambient energy alto mais tonemap Linear é a receita do visual sem contraste. Escuro faz parte da composição: o olho precisa de área escura pra valorizar a clara.
- Testar só no editor. A janela do editor não reflete o custo real. Rode o jogo no hardware alvo e acompanhe o frame time antes de fechar decisões de luz.
- Esquecer o renderer alvo. Montar a cena inteira em cima de SDFGI e fog volumétrico e só depois descobrir que o port mobile não suporta nenhum dos dois. Decida o renderer no início do projeto.
Fechando
Iluminação 3D no Godot 4 se resume a uma sequência: DirectionalLight pra luz principal, WorldEnvironment pra céu, ambiente e tonemap, Omni e Spot pra pontos de interesse, sombra só onde paga o custo, e uma técnica de GI compatível com o hardware alvo. Glow, fog e SSAO entram por último, como tempero.
Nada disso exige decorar valores. Exige montar uma cena de teste, mexer num parâmetro por vez e olhar o resultado. Pegue qualquer cena sua que esteja com cara de protótipo e gaste uma hora só em luz, sem tocar em mais nada. A diferença no final costuma convencer mais que qualquer tutorial.


