Pular para o conteúdo
Lente · BloomIntermediário

Bloom de neon

Letreiros que brilham e vazam luz no escuro em volta, a partir de um desfoque em meia resolução só dos pixels mais claros.

UVTelaTela / pós
  1. 01ObjetivoLente
  2. 02TécnicaBloom
  3. 03ExecuçãoBloom de neon
Cena de teste

Parâmetros

1.00
4.0
1.00
0.5

Copie para a sua plataforma

  • GLSLAdaptar
  • GodotAdaptar

Cadeia de nós

Montada passo a passo, no estilo do GLSL, com os nomes de nós dele.

Montada passo a passo, no estilo do Godot, com os nomes de nós dele.

Este card ainda não tem versão para Unreal Engine 5, então os nós mostram os nomes do GLSL.

Este card ainda não tem versão para Unity, então os nós mostram os nomes do GLSL.

Este card ainda não tem versão para Blender, então os nós mostram os nomes do GLSL.

  1. dotDotProductdotdotdot
    UV
    Tela

    01 Brilho

    Passe bright

  2. smoothstepSmoothStepsmoothstepsmoothstepsmoothstep

    02 Manter só o que brilha

    Passe bright

  3. texture * expExpressiontexture * exptexture * exptexture * exp

    03 Espalhar com desfoque

    Passe blurV

  4. +Add+++

    04 Somar sobre a cena

    Passe main

  5. /Divide///

    05 Mapear os tons

    Passe main

Como funciona

A cena de teste tem formas emissivas muito mais claras que 1. Sem ajuda, elas virariam traços brancos chapados. Este card guarda só esses pixels claros, desfoca-os com um raio largo em meia resolução, soma o desfoque de volta ao quadro e depois aplica o tone mapping na soma, para as formas brilharem e o halo sumir suavemente no escuro.

Passo a passo

  1. Brilho. A passada bright lê a cena e tira a luminância, luma: o produto escalar da cor com o quanto cada canal parece claro ao olho (o verde pesa mais, o azul menos). Um número por pixel decide se ele brilha, então uma luz azul saturada e uma branca com o mesmo brilho se comportam igual.
  2. Manter só o que brilha. smoothstep(p_threshold, p_threshold + 0.5, luma) vale 0 abaixo do limiar e 1 meia unidade acima dele, e kept é a cor da cena multiplicada por isso. A rampa suave é um joelho: uma superfície que clareia devagar entra no brilho aos poucos, em vez de acender de uma vez num valor exato.
  3. Espalhar com desfoque. Duas passadas rodam um gaussiano separável sobre kept, as linhas em t_blurH e depois as colunas em t_blurV, cujo resultado é glow. As três passadas têm tamanho half: um quarto dos pixels, e cada amostra cobre dois pixels da tela, então o halo vai duas vezes mais longe com o mesmo número de leituras.
  4. Somar sobre a cena. A última passada soma glow × p_strength à cena em resolução cheia e aplica a exposição, exp2(p_exposure), em stops. Agora hdr passa bem de 1 em volta de cada luz.
  5. Mapear os tons. hdr / (1.0 + hdr) (a curva de Reinhard) traz qualquer valor para 0–1 sem cortar: 1 vira 0,5 e 4 vira 0,8. O núcleo de cada letreiro continua mais claro que o halo, com um degradê entre os dois em vez de um platô branco e chapado.

Por que o limiar precisa de HDR

Uma passada de brilho só funciona se “claro” ainda quiser dizer alguma coisa. Num quadro de 8 bits, uma camisa branca e um tubo de neon valem os dois 1,0, e os dois brilhariam. No alvo de meio float o tubo vale 6,0 e a camisa 0,8, então um limiar de 1 escolhe só o tubo. O mesmo vale para o tone mapping: ele tem que rodar depois da soma, sobre o valor em HDR, senão o brilho não tem folga.

Notas de produção

Contando por pixel de cada passada, a cadeia lê 1 + 25 + 25 + 2 = 53 texels e faz cerca de 565 operações no alcance padrão (σ = 4, 25 amostras por passada de desfoque). Só que três das quatro passadas rodam em meia resolução, então por pixel da tela fica mais perto de 15 leituras: o truque da meia resolução é a maior parte do motivo de o bloom caber no orçamento. A memória são três alvos de meio float em meia resolução, além da cena em HDR que o quadro já tem.

A passada de brilho lê a tela uma vez, no centro de um pixel de meia resolução, que cai bem entre quatro texels da tela, então o filtro bilinear tira a média deles de graça. Essa média ainda pode piscar quando um único pixel muito claro atravessa a grade (os fireflies); um bloom de produção pondera essa redução por 1 / (1 + luma) para domá-los.

Um único tamanho de desfoque dá um halo com uma única queda. Lentes de verdade espalham a luz com um núcleo forte e uma cauda bem longa, que as engines constroem desfocando uma cadeia de cópias cada vez menores e somando todas. Use isso quando um gaussiano só parecer uma névoa uniforme em volta da luz.

Reinhard em cada canal dessatura as cores claras em direção ao branco, o que combina com neon. Num jogo inteiro, use o mesmo tone mapper do resto do quadro (ACES, AgX, Khronos PBR Neutral) e ponha o bloom logo antes dele.

Monte no GLSL

Quatro shaders de fragmento, um por passada. Renderize a cena numa textura de meio float, desenhe as passadas "bright", "blurH" e "blurV" em sequência em três texturas de meio float em meia resolução, cada uma lendo a anterior, e depois desenhe a passada "main" no canvas com a cena e o último desfoque ligados.

Cada passada é um desenho de tela cheia: um triângulo que cobre o viewport, com um shader de vértice que escreve v_uv de 0 a 1 de ponta a ponta. O código copiado traz os quatro shaders de fragmento, cada um com seu main().

  1. Desenhe a cena num framebuffer cujo anexo de cor é uma textura RGBA16F, em luz linear, com as superfícies emissivas bem acima de 1. Esta é a t_screen.
  2. Crie três texturas RGBA16F com metade do tamanho do canvas (arredondado para cima), cada uma no seu framebuffer, com filtragem LINEAR e CLAMP_TO_EDGE.
  3. Desenhe a passada bright na primeira com a t_screen ligada: a t_bright.
  4. Desenhe a passada blurH na segunda com a t_bright ligada: a t_blurH. Depois a passada blurV na terceira com a t_blurH ligada: a t_blurV. As passadas de desfoque acham o tamanho do texel com textureSize(), então funcionam em qualquer tamanho.
  5. Ligue o canvas, volte o viewport ao tamanho cheio e desenhe a passada main com a t_screen e a t_blurV ligadas. Ela soma o brilho, aplica a exposição e a curva de Reinhard e escreve sRGB.
  6. Defina p_threshold, p_spread, p_strength e p_exposure em todo programa que os declara.

Para uma cauda mais longa, repita os passos 3 e 4 numa cópia com um quarto do tamanho e some os dois desfoques na última passada, cada um com sua força.

Monte no Godot

Quatro shaders spatial, encadeados por três SubViewports em meia resolução. A passada "bright" renderiza o mundo a partir de uma câmera que segue a sua; "blurH" e "blurV" desenham cada uma só o seu quad num viewport com Own World 3D, lendo o anterior por uma ViewportTexture; a passada "main" roda num quad de tela cheia na frente da sua câmera. Na maioria dos projetos o Glow do próprio Godot (no Environment) é a melhor escolha; esta cadeia serve para um visual que o Glow não dá, ou para igualar a versão web.

O código copiado são quatro shaders, um por passada. O Godot desenha uma passada de pós como uma malha na frente de uma câmera cujo shader de vértice a estica sobre a tela, e uma passada só lê a anterior por uma ViewportTexture.

  1. Adicione um MeshInstance3D com um QuadMesh 2×2 como filho da sua Camera3D. Dê a ele um ShaderMaterial com a passada main, deixe-o só na camada visual (Visual Layer) 3 e aumente o Extra Cull Margin até o máximo, para ele nunca ser descartado.
  2. Adicione um SubViewport para a bright: Size com metade do tamanho da janela, Use HDR 2D ligado, Update Mode em Always, Own World 3D desligado. Dentro dele, adicione uma Camera3D que a sua câmera conduz por um filho RemoteTransform3D (iguale o FOV) e um quad 2×2 com a passada bright, só na camada visual 2.
  3. Adicione mais dois SubViewports, para a blurH e a blurV: mesmo tamanho e mesmas opções, mas com Own World 3D ligado. Dentro de cada um, adicione uma Camera3D e um quad 2×2 com a sua passada. Os mundos deles não têm mais nada, então eles desenham só o quad.
  4. Ligue as texturas: t_bright no material da blurH é uma ViewportTexture do viewport da bright, t_blurH na blurV é o viewport da blurH, e t_blurV na main é o viewport da blurV.
  5. Ajuste os Cull Masks: a câmera da bright vê as camadas 1 e 2, a sua câmera vê as camadas 1 e 3.
  6. Defina threshold, spread, strength e exposure em todos os materiais; cada um declara todos eles.

Para a passada de brilho pegar alguma coisa, os materiais emissivos precisam de Emission Energy bem acima de 1: o Godot guarda o quadro 3D em HDR, então um tubo de neon com energia 6 aparece como 6 na textura da tela.

Ainda não há tutorial de Unreal Engine 5 para este card.

Ainda não há tutorial de Unity para este card.

Ainda não há tutorial de Blender para este card.

Código

neon-bloom.glslGLSL
#version 300 es// Bloom → Neon bloom, pass "bright" (1 of 4), web GLSL (GLSL ES 3.00). From Creative Center, MIT license.precision highp float;uniform float u_time;uniform vec2 u_resolution; // this pass's output size in pixelsuniform float p_threshold; // Threshold: 1.0 (0.0 to 4.0)uniform float p_spread; // Spread (sigma, half-size px): 4.0 (1.0 to 8.0)uniform float p_strength; // Strength: 1.0 (0.0 to 3.0)uniform float p_exposure; // Exposure (stops): 0.5 (-2.0 to 2.0)uniform sampler2D t_screen; // the scene's color: linear HDR (RGBA16F), linear filter, clamp to edgeuniform sampler2D t_bright; // pass "bright"'s output: linear HDR (RGBA16F), half the output's size, linear filter, clamp to edgeuniform sampler2D t_blurH; // pass "blurH"'s output: linear HDR (RGBA16F), half the output's size, linear filter, clamp to edgeuniform sampler2D t_blurV; // pass "blurV"'s output: linear HDR (RGBA16F), half the output's size, linear filter, clamp to edgein vec2 v_uv;in vec3 v_position;in vec3 v_normal;in vec3 v_view;out vec4 fragColor;struct Inputs {	vec2 uv;	vec3 position;	vec3 normal;	vec3 view;	float time;	vec2 fragCoord;	vec2 resolution;};// Bloom: keep only what is brighter than the threshold, blur it wide at half size, add it back over the scene, then// tone-map, since the sum goes well above 1.vec4 bright(Inputs i) {	// This pass is half size: each of its pixels sits between four screen texels, so one bilinear read averages them	vec3 scene = texture(t_screen, i.uv).rgb;	float luma = dot(scene, vec3(0.2126, 0.7152, 0.0722));	// A soft knee: light fades in over half a unit above the threshold, so a glow doesn't pop on as a surface brightens	vec3 kept = scene * smoothstep(p_threshold, p_threshold + 0.5, luma);	return vec4(kept, 1.0);}// One axis of a Gaussian, taps one texel apart out to three sigmas; both callers pass a pass's own texturevec3 gaussian(sampler2D source, vec2 uv, vec2 axis) {	vec2 texel = axis / vec2(textureSize(source, 0));	int reach = int(ceil(3.0 * p_spread));	vec3 sum = vec3(0.0);	float total = 0.0;	for (int k = -reach; k <= reach; k++) {		float w = exp(-0.5 * float(k * k) / (p_spread * p_spread));		sum += texture(source, uv + texel * float(k)).rgb * w;		total += w;	}	return sum / total;}vec4 blurH(Inputs i) {	vec3 wide = gaussian(t_bright, i.uv, vec2(1.0, 0.0));	return vec4(wide, 1.0);}vec4 blurV(Inputs i) {	vec3 glow = gaussian(t_blurH, i.uv, vec2(0.0, 1.0));	return vec4(glow, 1.0);}vec4 effect(Inputs i) {	vec3 scene = texture(t_screen, i.uv).rgb;	vec3 hdr = (scene + texture(t_blurV, i.uv).rgb * p_strength) * exp2(p_exposure);	// Reinhard: 1 maps to 0.5 and anything brighter approaches 1 without clipping, so the glow's core keeps a gradient	vec3 mapped = hdr / (1.0 + hdr);	return vec4(mapped, 1.0);}void main() {	Inputs i;	i.uv = v_uv;	i.position = v_position;	// Interpolation shortens the vectors between vertices	i.normal = normalize(v_normal);	i.view = normalize(v_view);	i.time = u_time;	i.fragCoord = gl_FragCoord.xy;	i.resolution = u_resolution;	// Linear HDR into this pass's target, read by the passes after it	fragColor = bright(i);}#version 300 es// Bloom → Neon bloom, pass "blurH" (2 of 4), web GLSL (GLSL ES 3.00). From Creative Center, MIT license.precision highp float;uniform float u_time;uniform vec2 u_resolution; // this pass's output size in pixelsuniform float p_threshold; // Threshold: 1.0 (0.0 to 4.0)uniform float p_spread; // Spread (sigma, half-size px): 4.0 (1.0 to 8.0)uniform float p_strength; // Strength: 1.0 (0.0 to 3.0)uniform float p_exposure; // Exposure (stops): 0.5 (-2.0 to 2.0)uniform sampler2D t_screen; // the scene's color: linear HDR (RGBA16F), linear filter, clamp to edgeuniform sampler2D t_bright; // pass "bright"'s output: linear HDR (RGBA16F), half the output's size, linear filter, clamp to edgeuniform sampler2D t_blurH; // pass "blurH"'s output: linear HDR (RGBA16F), half the output's size, linear filter, clamp to edgeuniform sampler2D t_blurV; // pass "blurV"'s output: linear HDR (RGBA16F), half the output's size, linear filter, clamp to edgein vec2 v_uv;in vec3 v_position;in vec3 v_normal;in vec3 v_view;out vec4 fragColor;struct Inputs {	vec2 uv;	vec3 position;	vec3 normal;	vec3 view;	float time;	vec2 fragCoord;	vec2 resolution;};// Bloom: keep only what is brighter than the threshold, blur it wide at half size, add it back over the scene, then// tone-map, since the sum goes well above 1.vec4 bright(Inputs i) {	// This pass is half size: each of its pixels sits between four screen texels, so one bilinear read averages them	vec3 scene = texture(t_screen, i.uv).rgb;	float luma = dot(scene, vec3(0.2126, 0.7152, 0.0722));	// A soft knee: light fades in over half a unit above the threshold, so a glow doesn't pop on as a surface brightens	vec3 kept = scene * smoothstep(p_threshold, p_threshold + 0.5, luma);	return vec4(kept, 1.0);}// One axis of a Gaussian, taps one texel apart out to three sigmas; both callers pass a pass's own texturevec3 gaussian(sampler2D source, vec2 uv, vec2 axis) {	vec2 texel = axis / vec2(textureSize(source, 0));	int reach = int(ceil(3.0 * p_spread));	vec3 sum = vec3(0.0);	float total = 0.0;	for (int k = -reach; k <= reach; k++) {		float w = exp(-0.5 * float(k * k) / (p_spread * p_spread));		sum += texture(source, uv + texel * float(k)).rgb * w;		total += w;	}	return sum / total;}vec4 blurH(Inputs i) {	vec3 wide = gaussian(t_bright, i.uv, vec2(1.0, 0.0));	return vec4(wide, 1.0);}vec4 blurV(Inputs i) {	vec3 glow = gaussian(t_blurH, i.uv, vec2(0.0, 1.0));	return vec4(glow, 1.0);}vec4 effect(Inputs i) {	vec3 scene = texture(t_screen, i.uv).rgb;	vec3 hdr = (scene + texture(t_blurV, i.uv).rgb * p_strength) * exp2(p_exposure);	// Reinhard: 1 maps to 0.5 and anything brighter approaches 1 without clipping, so the glow's core keeps a gradient	vec3 mapped = hdr / (1.0 + hdr);	return vec4(mapped, 1.0);}void main() {	Inputs i;	i.uv = v_uv;	i.position = v_position;	// Interpolation shortens the vectors between vertices	i.normal = normalize(v_normal);	i.view = normalize(v_view);	i.time = u_time;	i.fragCoord = gl_FragCoord.xy;	i.resolution = u_resolution;	// Linear HDR into this pass's target, read by the passes after it	fragColor = blurH(i);}#version 300 es// Bloom → Neon bloom, pass "blurV" (3 of 4), web GLSL (GLSL ES 3.00). From Creative Center, MIT license.precision highp float;uniform float u_time;uniform vec2 u_resolution; // this pass's output size in pixelsuniform float p_threshold; // Threshold: 1.0 (0.0 to 4.0)uniform float p_spread; // Spread (sigma, half-size px): 4.0 (1.0 to 8.0)uniform float p_strength; // Strength: 1.0 (0.0 to 3.0)uniform float p_exposure; // Exposure (stops): 0.5 (-2.0 to 2.0)uniform sampler2D t_screen; // the scene's color: linear HDR (RGBA16F), linear filter, clamp to edgeuniform sampler2D t_bright; // pass "bright"'s output: linear HDR (RGBA16F), half the output's size, linear filter, clamp to edgeuniform sampler2D t_blurH; // pass "blurH"'s output: linear HDR (RGBA16F), half the output's size, linear filter, clamp to edgeuniform sampler2D t_blurV; // pass "blurV"'s output: linear HDR (RGBA16F), half the output's size, linear filter, clamp to edgein vec2 v_uv;in vec3 v_position;in vec3 v_normal;in vec3 v_view;out vec4 fragColor;struct Inputs {	vec2 uv;	vec3 position;	vec3 normal;	vec3 view;	float time;	vec2 fragCoord;	vec2 resolution;};// Bloom: keep only what is brighter than the threshold, blur it wide at half size, add it back over the scene, then// tone-map, since the sum goes well above 1.vec4 bright(Inputs i) {	// This pass is half size: each of its pixels sits between four screen texels, so one bilinear read averages them	vec3 scene = texture(t_screen, i.uv).rgb;	float luma = dot(scene, vec3(0.2126, 0.7152, 0.0722));	// A soft knee: light fades in over half a unit above the threshold, so a glow doesn't pop on as a surface brightens	vec3 kept = scene * smoothstep(p_threshold, p_threshold + 0.5, luma);	return vec4(kept, 1.0);}// One axis of a Gaussian, taps one texel apart out to three sigmas; both callers pass a pass's own texturevec3 gaussian(sampler2D source, vec2 uv, vec2 axis) {	vec2 texel = axis / vec2(textureSize(source, 0));	int reach = int(ceil(3.0 * p_spread));	vec3 sum = vec3(0.0);	float total = 0.0;	for (int k = -reach; k <= reach; k++) {		float w = exp(-0.5 * float(k * k) / (p_spread * p_spread));		sum += texture(source, uv + texel * float(k)).rgb * w;		total += w;	}	return sum / total;}vec4 blurH(Inputs i) {	vec3 wide = gaussian(t_bright, i.uv, vec2(1.0, 0.0));	return vec4(wide, 1.0);}vec4 blurV(Inputs i) {	vec3 glow = gaussian(t_blurH, i.uv, vec2(0.0, 1.0));	return vec4(glow, 1.0);}vec4 effect(Inputs i) {	vec3 scene = texture(t_screen, i.uv).rgb;	vec3 hdr = (scene + texture(t_blurV, i.uv).rgb * p_strength) * exp2(p_exposure);	// Reinhard: 1 maps to 0.5 and anything brighter approaches 1 without clipping, so the glow's core keeps a gradient	vec3 mapped = hdr / (1.0 + hdr);	return vec4(mapped, 1.0);}void main() {	Inputs i;	i.uv = v_uv;	i.position = v_position;	// Interpolation shortens the vectors between vertices	i.normal = normalize(v_normal);	i.view = normalize(v_view);	i.time = u_time;	i.fragCoord = gl_FragCoord.xy;	i.resolution = u_resolution;	// Linear HDR into this pass's target, read by the passes after it	fragColor = blurV(i);}#version 300 es// Bloom → Neon bloom, pass "main" (4 of 4), web GLSL (GLSL ES 3.00). From Creative Center, MIT license.precision highp float;uniform float u_time;uniform vec2 u_resolution; // this pass's output size in pixelsuniform float p_threshold; // Threshold: 1.0 (0.0 to 4.0)uniform float p_spread; // Spread (sigma, half-size px): 4.0 (1.0 to 8.0)uniform float p_strength; // Strength: 1.0 (0.0 to 3.0)uniform float p_exposure; // Exposure (stops): 0.5 (-2.0 to 2.0)uniform sampler2D t_screen; // the scene's color: linear HDR (RGBA16F), linear filter, clamp to edgeuniform sampler2D t_bright; // pass "bright"'s output: linear HDR (RGBA16F), half the output's size, linear filter, clamp to edgeuniform sampler2D t_blurH; // pass "blurH"'s output: linear HDR (RGBA16F), half the output's size, linear filter, clamp to edgeuniform sampler2D t_blurV; // pass "blurV"'s output: linear HDR (RGBA16F), half the output's size, linear filter, clamp to edgein vec2 v_uv;in vec3 v_position;in vec3 v_normal;in vec3 v_view;out vec4 fragColor;struct Inputs {	vec2 uv;	vec3 position;	vec3 normal;	vec3 view;	float time;	vec2 fragCoord;	vec2 resolution;};vec3 u_toSrgb(vec3 c) {	return mix(c * 12.92, 1.055 * pow(c, vec3(1.0 / 2.4)) - 0.055, step(0.0031308, c));}// Bloom: keep only what is brighter than the threshold, blur it wide at half size, add it back over the scene, then// tone-map, since the sum goes well above 1.vec4 bright(Inputs i) {	// This pass is half size: each of its pixels sits between four screen texels, so one bilinear read averages them	vec3 scene = texture(t_screen, i.uv).rgb;	float luma = dot(scene, vec3(0.2126, 0.7152, 0.0722));	// A soft knee: light fades in over half a unit above the threshold, so a glow doesn't pop on as a surface brightens	vec3 kept = scene * smoothstep(p_threshold, p_threshold + 0.5, luma);	return vec4(kept, 1.0);}// One axis of a Gaussian, taps one texel apart out to three sigmas; both callers pass a pass's own texturevec3 gaussian(sampler2D source, vec2 uv, vec2 axis) {	vec2 texel = axis / vec2(textureSize(source, 0));	int reach = int(ceil(3.0 * p_spread));	vec3 sum = vec3(0.0);	float total = 0.0;	for (int k = -reach; k <= reach; k++) {		float w = exp(-0.5 * float(k * k) / (p_spread * p_spread));		sum += texture(source, uv + texel * float(k)).rgb * w;		total += w;	}	return sum / total;}vec4 blurH(Inputs i) {	vec3 wide = gaussian(t_bright, i.uv, vec2(1.0, 0.0));	return vec4(wide, 1.0);}vec4 blurV(Inputs i) {	vec3 glow = gaussian(t_blurH, i.uv, vec2(0.0, 1.0));	return vec4(glow, 1.0);}vec4 effect(Inputs i) {	vec3 scene = texture(t_screen, i.uv).rgb;	vec3 hdr = (scene + texture(t_blurV, i.uv).rgb * p_strength) * exp2(p_exposure);	// Reinhard: 1 maps to 0.5 and anything brighter approaches 1 without clipping, so the glow's core keeps a gradient	vec3 mapped = hdr / (1.0 + hdr);	return vec4(mapped, 1.0);}void main() {	Inputs i;	i.uv = v_uv;	i.position = v_position;	// Interpolation shortens the vectors between vertices	i.normal = normalize(v_normal);	i.view = normalize(v_view);	i.time = u_time;	i.fragCoord = gl_FragCoord.xy;	i.resolution = u_resolution;	vec4 frag_out = effect(i);	// The output is sRGB: clamped and encoded once, here, with no tone mapping; opaque, so alpha is 1	fragColor = vec4(u_toSrgb(clamp(frag_out.rgb, 0.0, 1.0)), 1.0);}
neon-bloom.gdshaderShader do Godot
// Bloom → Neon bloom, pass "bright" (1 of 4), Godot 4. From Creative Center, MIT license.// Godot has no pass chain on one material: draw this pass in its own SubViewport (or a CompositorEffect) and// give its ViewportTexture to the passes that read t_bright.shader_type spatial;// A full-screen quad over the finished frame: no lighting, no culling, never hidden by depthrender_mode unshaded, cull_disabled, depth_test_disabled;uniform float threshold : hint_range(0.0, 4.0, 0.05) = 1.0;uniform float spread : hint_range(1.0, 8.0, 0.1) = 4.0;uniform float strength : hint_range(0.0, 3.0, 0.05) = 1.0;uniform float exposure : hint_range(-2.0, 2.0, 0.1) = 0.5;uniform sampler2D t_screen : hint_screen_texture, filter_linear, repeat_disable;uniform sampler2D t_bright : filter_linear, repeat_disable; // pass "bright"'s output: its SubViewport's ViewportTextureuniform sampler2D t_blurH : filter_linear, repeat_disable; // pass "blurH"'s output: its SubViewport's ViewportTextureuniform sampler2D t_blurV : filter_linear, repeat_disable; // pass "blurV"'s output: its SubViewport's ViewportTexturestruct Inputs {	vec2 uv;	vec3 position;	vec3 normal;	vec3 view;	float time;	vec2 fragCoord;	vec2 resolution;};// Godot's textures start at the top-left and the effect's uv at the bottom-left: each sample flips v backvec2 u_flipUv(vec2 uv) {	return vec2(uv.x, 1.0 - uv.y);}// Bloom: keep only what is brighter than the threshold, blur it wide at half size, add it back over the scene, then// tone-map, since the sum goes well above 1.vec4 bright(Inputs i) {	// This pass is half size: each of its pixels sits between four screen texels, so one bilinear read averages them	vec3 scene = texture(t_screen, u_flipUv(i.uv)).rgb;	float luma = dot(scene, vec3(0.2126, 0.7152, 0.0722));	// A soft knee: light fades in over half a unit above the threshold, so a glow doesn't pop on as a surface brightens	vec3 kept = scene * smoothstep(threshold, threshold + 0.5, luma);	return vec4(kept, 1.0);}// One axis of a Gaussian, taps one texel apart out to three sigmas; both callers pass a pass's own texturevec3 gaussian(sampler2D source, vec2 uv, vec2 axis) {	vec2 texel = axis / vec2(textureSize(source, 0));	int reach = int(ceil(3.0 * spread));	vec3 sum = vec3(0.0);	float total = 0.0;	for (int k = -reach; k <= reach; k++) {		float w = exp(-0.5 * float(k * k) / (spread * spread));		sum += texture(source, u_flipUv(uv + texel * float(k))).rgb * w;		total += w;	}	return sum / total;}vec4 blurH(Inputs i) {	vec3 wide = gaussian(t_bright, i.uv, vec2(1.0, 0.0));	return vec4(wide, 1.0);}vec4 blurV(Inputs i) {	vec3 glow = gaussian(t_blurH, i.uv, vec2(0.0, 1.0));	return vec4(glow, 1.0);}vec4 effect(Inputs i) {	vec3 scene = texture(t_screen, u_flipUv(i.uv)).rgb;	vec3 hdr = (scene + texture(t_blurV, u_flipUv(i.uv)).rgb * strength) * exp2(exposure);	// Reinhard: 1 maps to 0.5 and anything brighter approaches 1 without clipping, so the glow's core keeps a gradient	vec3 mapped = hdr / (1.0 + hdr);	return vec4(mapped, 1.0);}void vertex() {	// Put the shader on a MeshInstance3D with a 2x2 QuadMesh in front of the camera; it covers the screen wherever it is	POSITION = vec4(VERTEX.xy, 1.0, 1.0);}void fragment() {	Inputs i;	// SCREEN_UV starts at the top-left; the effect expects GLSL's bottom-left	i.uv = vec2(SCREEN_UV.x, 1.0 - SCREEN_UV.y);	// A flat quad facing the viewer, the same as the site's 2D preview	i.position = vec3(i.uv * 2.0 - 1.0, 0.0);	i.normal = vec3(0.0, 0.0, 1.0);	i.view = vec3(0.0, 0.0, 1.0);	i.time = TIME;	// FRAGCOORD's origin is the top-left too	i.resolution = VIEWPORT_SIZE;	i.fragCoord = vec2(FRAGCOORD.x, i.resolution.y - FRAGCOORD.y);	vec4 frag_out = bright(i);	ALBEDO = frag_out.rgb;}// Bloom → Neon bloom, pass "blurH" (2 of 4), Godot 4. From Creative Center, MIT license.// Godot has no pass chain on one material: draw this pass in its own SubViewport (or a CompositorEffect) and// give its ViewportTexture to the passes that read t_blurH.shader_type spatial;// A full-screen quad over the finished frame: no lighting, no culling, never hidden by depthrender_mode unshaded, cull_disabled, depth_test_disabled;uniform float threshold : hint_range(0.0, 4.0, 0.05) = 1.0;uniform float spread : hint_range(1.0, 8.0, 0.1) = 4.0;uniform float strength : hint_range(0.0, 3.0, 0.05) = 1.0;uniform float exposure : hint_range(-2.0, 2.0, 0.1) = 0.5;uniform sampler2D t_screen : hint_screen_texture, filter_linear, repeat_disable;uniform sampler2D t_bright : filter_linear, repeat_disable; // pass "bright"'s output: its SubViewport's ViewportTextureuniform sampler2D t_blurH : filter_linear, repeat_disable; // pass "blurH"'s output: its SubViewport's ViewportTextureuniform sampler2D t_blurV : filter_linear, repeat_disable; // pass "blurV"'s output: its SubViewport's ViewportTexturestruct Inputs {	vec2 uv;	vec3 position;	vec3 normal;	vec3 view;	float time;	vec2 fragCoord;	vec2 resolution;};// Godot's textures start at the top-left and the effect's uv at the bottom-left: each sample flips v backvec2 u_flipUv(vec2 uv) {	return vec2(uv.x, 1.0 - uv.y);}// Bloom: keep only what is brighter than the threshold, blur it wide at half size, add it back over the scene, then// tone-map, since the sum goes well above 1.vec4 bright(Inputs i) {	// This pass is half size: each of its pixels sits between four screen texels, so one bilinear read averages them	vec3 scene = texture(t_screen, u_flipUv(i.uv)).rgb;	float luma = dot(scene, vec3(0.2126, 0.7152, 0.0722));	// A soft knee: light fades in over half a unit above the threshold, so a glow doesn't pop on as a surface brightens	vec3 kept = scene * smoothstep(threshold, threshold + 0.5, luma);	return vec4(kept, 1.0);}// One axis of a Gaussian, taps one texel apart out to three sigmas; both callers pass a pass's own texturevec3 gaussian(sampler2D source, vec2 uv, vec2 axis) {	vec2 texel = axis / vec2(textureSize(source, 0));	int reach = int(ceil(3.0 * spread));	vec3 sum = vec3(0.0);	float total = 0.0;	for (int k = -reach; k <= reach; k++) {		float w = exp(-0.5 * float(k * k) / (spread * spread));		sum += texture(source, u_flipUv(uv + texel * float(k))).rgb * w;		total += w;	}	return sum / total;}vec4 blurH(Inputs i) {	vec3 wide = gaussian(t_bright, i.uv, vec2(1.0, 0.0));	return vec4(wide, 1.0);}vec4 blurV(Inputs i) {	vec3 glow = gaussian(t_blurH, i.uv, vec2(0.0, 1.0));	return vec4(glow, 1.0);}vec4 effect(Inputs i) {	vec3 scene = texture(t_screen, u_flipUv(i.uv)).rgb;	vec3 hdr = (scene + texture(t_blurV, u_flipUv(i.uv)).rgb * strength) * exp2(exposure);	// Reinhard: 1 maps to 0.5 and anything brighter approaches 1 without clipping, so the glow's core keeps a gradient	vec3 mapped = hdr / (1.0 + hdr);	return vec4(mapped, 1.0);}void vertex() {	// Put the shader on a MeshInstance3D with a 2x2 QuadMesh in front of the camera; it covers the screen wherever it is	POSITION = vec4(VERTEX.xy, 1.0, 1.0);}void fragment() {	Inputs i;	// SCREEN_UV starts at the top-left; the effect expects GLSL's bottom-left	i.uv = vec2(SCREEN_UV.x, 1.0 - SCREEN_UV.y);	// A flat quad facing the viewer, the same as the site's 2D preview	i.position = vec3(i.uv * 2.0 - 1.0, 0.0);	i.normal = vec3(0.0, 0.0, 1.0);	i.view = vec3(0.0, 0.0, 1.0);	i.time = TIME;	// FRAGCOORD's origin is the top-left too	i.resolution = VIEWPORT_SIZE;	i.fragCoord = vec2(FRAGCOORD.x, i.resolution.y - FRAGCOORD.y);	vec4 frag_out = blurH(i);	ALBEDO = frag_out.rgb;}// Bloom → Neon bloom, pass "blurV" (3 of 4), Godot 4. From Creative Center, MIT license.// Godot has no pass chain on one material: draw this pass in its own SubViewport (or a CompositorEffect) and// give its ViewportTexture to the passes that read t_blurV.shader_type spatial;// A full-screen quad over the finished frame: no lighting, no culling, never hidden by depthrender_mode unshaded, cull_disabled, depth_test_disabled;uniform float threshold : hint_range(0.0, 4.0, 0.05) = 1.0;uniform float spread : hint_range(1.0, 8.0, 0.1) = 4.0;uniform float strength : hint_range(0.0, 3.0, 0.05) = 1.0;uniform float exposure : hint_range(-2.0, 2.0, 0.1) = 0.5;uniform sampler2D t_screen : hint_screen_texture, filter_linear, repeat_disable;uniform sampler2D t_bright : filter_linear, repeat_disable; // pass "bright"'s output: its SubViewport's ViewportTextureuniform sampler2D t_blurH : filter_linear, repeat_disable; // pass "blurH"'s output: its SubViewport's ViewportTextureuniform sampler2D t_blurV : filter_linear, repeat_disable; // pass "blurV"'s output: its SubViewport's ViewportTexturestruct Inputs {	vec2 uv;	vec3 position;	vec3 normal;	vec3 view;	float time;	vec2 fragCoord;	vec2 resolution;};// Godot's textures start at the top-left and the effect's uv at the bottom-left: each sample flips v backvec2 u_flipUv(vec2 uv) {	return vec2(uv.x, 1.0 - uv.y);}// Bloom: keep only what is brighter than the threshold, blur it wide at half size, add it back over the scene, then// tone-map, since the sum goes well above 1.vec4 bright(Inputs i) {	// This pass is half size: each of its pixels sits between four screen texels, so one bilinear read averages them	vec3 scene = texture(t_screen, u_flipUv(i.uv)).rgb;	float luma = dot(scene, vec3(0.2126, 0.7152, 0.0722));	// A soft knee: light fades in over half a unit above the threshold, so a glow doesn't pop on as a surface brightens	vec3 kept = scene * smoothstep(threshold, threshold + 0.5, luma);	return vec4(kept, 1.0);}// One axis of a Gaussian, taps one texel apart out to three sigmas; both callers pass a pass's own texturevec3 gaussian(sampler2D source, vec2 uv, vec2 axis) {	vec2 texel = axis / vec2(textureSize(source, 0));	int reach = int(ceil(3.0 * spread));	vec3 sum = vec3(0.0);	float total = 0.0;	for (int k = -reach; k <= reach; k++) {		float w = exp(-0.5 * float(k * k) / (spread * spread));		sum += texture(source, u_flipUv(uv + texel * float(k))).rgb * w;		total += w;	}	return sum / total;}vec4 blurH(Inputs i) {	vec3 wide = gaussian(t_bright, i.uv, vec2(1.0, 0.0));	return vec4(wide, 1.0);}vec4 blurV(Inputs i) {	vec3 glow = gaussian(t_blurH, i.uv, vec2(0.0, 1.0));	return vec4(glow, 1.0);}vec4 effect(Inputs i) {	vec3 scene = texture(t_screen, u_flipUv(i.uv)).rgb;	vec3 hdr = (scene + texture(t_blurV, u_flipUv(i.uv)).rgb * strength) * exp2(exposure);	// Reinhard: 1 maps to 0.5 and anything brighter approaches 1 without clipping, so the glow's core keeps a gradient	vec3 mapped = hdr / (1.0 + hdr);	return vec4(mapped, 1.0);}void vertex() {	// Put the shader on a MeshInstance3D with a 2x2 QuadMesh in front of the camera; it covers the screen wherever it is	POSITION = vec4(VERTEX.xy, 1.0, 1.0);}void fragment() {	Inputs i;	// SCREEN_UV starts at the top-left; the effect expects GLSL's bottom-left	i.uv = vec2(SCREEN_UV.x, 1.0 - SCREEN_UV.y);	// A flat quad facing the viewer, the same as the site's 2D preview	i.position = vec3(i.uv * 2.0 - 1.0, 0.0);	i.normal = vec3(0.0, 0.0, 1.0);	i.view = vec3(0.0, 0.0, 1.0);	i.time = TIME;	// FRAGCOORD's origin is the top-left too	i.resolution = VIEWPORT_SIZE;	i.fragCoord = vec2(FRAGCOORD.x, i.resolution.y - FRAGCOORD.y);	vec4 frag_out = blurV(i);	ALBEDO = frag_out.rgb;}// Bloom → Neon bloom, pass "main" (4 of 4), Godot 4. From Creative Center, MIT license.// The last pass: it reads the earlier passes' ViewportTextures through their t_ uniforms.shader_type spatial;// A full-screen quad over the finished frame: no lighting, no culling, never hidden by depthrender_mode unshaded, cull_disabled, depth_test_disabled;uniform float threshold : hint_range(0.0, 4.0, 0.05) = 1.0;uniform float spread : hint_range(1.0, 8.0, 0.1) = 4.0;uniform float strength : hint_range(0.0, 3.0, 0.05) = 1.0;uniform float exposure : hint_range(-2.0, 2.0, 0.1) = 0.5;uniform sampler2D t_screen : hint_screen_texture, filter_linear, repeat_disable;uniform sampler2D t_bright : filter_linear, repeat_disable; // pass "bright"'s output: its SubViewport's ViewportTextureuniform sampler2D t_blurH : filter_linear, repeat_disable; // pass "blurH"'s output: its SubViewport's ViewportTextureuniform sampler2D t_blurV : filter_linear, repeat_disable; // pass "blurV"'s output: its SubViewport's ViewportTexturestruct Inputs {	vec2 uv;	vec3 position;	vec3 normal;	vec3 view;	float time;	vec2 fragCoord;	vec2 resolution;};// Godot's textures start at the top-left and the effect's uv at the bottom-left: each sample flips v backvec2 u_flipUv(vec2 uv) {	return vec2(uv.x, 1.0 - uv.y);}// Bloom: keep only what is brighter than the threshold, blur it wide at half size, add it back over the scene, then// tone-map, since the sum goes well above 1.vec4 bright(Inputs i) {	// This pass is half size: each of its pixels sits between four screen texels, so one bilinear read averages them	vec3 scene = texture(t_screen, u_flipUv(i.uv)).rgb;	float luma = dot(scene, vec3(0.2126, 0.7152, 0.0722));	// A soft knee: light fades in over half a unit above the threshold, so a glow doesn't pop on as a surface brightens	vec3 kept = scene * smoothstep(threshold, threshold + 0.5, luma);	return vec4(kept, 1.0);}// One axis of a Gaussian, taps one texel apart out to three sigmas; both callers pass a pass's own texturevec3 gaussian(sampler2D source, vec2 uv, vec2 axis) {	vec2 texel = axis / vec2(textureSize(source, 0));	int reach = int(ceil(3.0 * spread));	vec3 sum = vec3(0.0);	float total = 0.0;	for (int k = -reach; k <= reach; k++) {		float w = exp(-0.5 * float(k * k) / (spread * spread));		sum += texture(source, u_flipUv(uv + texel * float(k))).rgb * w;		total += w;	}	return sum / total;}vec4 blurH(Inputs i) {	vec3 wide = gaussian(t_bright, i.uv, vec2(1.0, 0.0));	return vec4(wide, 1.0);}vec4 blurV(Inputs i) {	vec3 glow = gaussian(t_blurH, i.uv, vec2(0.0, 1.0));	return vec4(glow, 1.0);}vec4 effect(Inputs i) {	vec3 scene = texture(t_screen, u_flipUv(i.uv)).rgb;	vec3 hdr = (scene + texture(t_blurV, u_flipUv(i.uv)).rgb * strength) * exp2(exposure);	// Reinhard: 1 maps to 0.5 and anything brighter approaches 1 without clipping, so the glow's core keeps a gradient	vec3 mapped = hdr / (1.0 + hdr);	return vec4(mapped, 1.0);}void vertex() {	// Put the shader on a MeshInstance3D with a 2x2 QuadMesh in front of the camera; it covers the screen wherever it is	POSITION = vec4(VERTEX.xy, 1.0, 1.0);}void fragment() {	Inputs i;	// SCREEN_UV starts at the top-left; the effect expects GLSL's bottom-left	i.uv = vec2(SCREEN_UV.x, 1.0 - SCREEN_UV.y);	// A flat quad facing the viewer, the same as the site's 2D preview	i.position = vec3(i.uv * 2.0 - 1.0, 0.0);	i.normal = vec3(0.0, 0.0, 1.0);	i.view = vec3(0.0, 0.0, 1.0);	i.time = TIME;	// FRAGCOORD's origin is the top-left too	i.resolution = VIEWPORT_SIZE;	i.fragCoord = vec2(FRAGCOORD.x, i.resolution.y - FRAGCOORD.y);	vec4 frag_out = effect(i);	ALBEDO = frag_out.rgb;}

Ainda não há código de Unreal Engine 5 para este card.

Ainda não há código de Unity para este card.

Ainda não há código de Blender para este card.

Leitura complementar