Clarão branco
Deixe um sprite branco por uma fração de segundo quando ele é atingido, mantendo a silhueta.
- 01ObjetivoCor
- 02TécnicaClarão de dano
- 03ExecuçãoClarão branco
Parâmetros
Copie para a sua plataforma
- GLSLCopiar
- GodotCopiar
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.
textureTexture2Dtexturetexturetexture UVTextura01 Ler o sprite
mixMixmixmixmix Entrada02 Levar à cor do clarão
vec4(rgb, a)Output (Color, Alpha)vec4(rgb, a)vec4(rgb, a)vec4(rgb, a) Entrada03 Manter o alfa do sprite
Como funciona
A prévia desenha um cogumelo original de 32 por 32 pixels com o clarão em 0,7; arraste “Intensidade do clarão” até 0 para ver o sprite puro e até 1 para o quadro todo branco que um jogo mostra no impacto.
- Ler o sprite.
texture(t_sprite, i.uv)lê a cor e o alfa do sprite neste pixel. A textura usa filtro nearest, então cada pixel da tela pega um texelUm elemento de uma textura, a versão do pixel numa textura. Uma amostra mistura texels próximos ou devolve o mais próximo.FundamentoTexel
Um elemento de uma textura, a versão do pixel numa textura. Uma amostra mistura texels próximos ou devolve o mais próximo.
texel size = 1 / textureSize(sampler, 0)Fontesinteiro e a pixel art continua nítida em qualquer escala.- texture (abre em uma nova aba)Referência
- texelFetch (abre em uma nova aba)Referência
- Levar à cor do clarão.
mix(own.rgb, p_color, p_amount)move cada canal uma fração do caminho até a cor do clarão: nada em 0, tudo em 1. Mostrado sozinho, este passo preenche o quad inteiro, os cantos transparentes também, porque a cor deles existe (preto) mesmo onde nada é desenhado. - Manter o alfa do sprite.
vec4(flashed, own.a)devolve o alfa do próprio sprite. O passe mescla com alfa comum, então os texels vazios continuam invisíveis e só a silhueta pisca.
Um mix é a operação certa porque chega à cor do clarão por igual, de qualquer ponto de partida. Multiplicar, que é o que uma cor de tint ou de modulate faz, escala cada pixel. Abaixo de 1 só escurece. Acima de 1 as partes claras estouram no branco muito antes das escuras, e o preto puro nunca muda. Somar branco estoura do mesmo jeito desigual.
O shader não sabe quando o golpe aconteceu. O código do jogo põe p_amount em 1 no quadro do golpe e o traz de volta a
0 em cerca de 0,1 segundo: um fade linear, 1 - elapsed / duration, ou uma queda mais rápida que demora um pouco no
fim. Muitos jogos o seguram em 1 por dois ou três quadros antes, para o clarão sobreviver a um quadro perdido. Como o
valor pertence a um sprite, cada sprite precisa da própria cópia do material, senão todos os inimigos piscam juntos.
O filtro nearest nunca mistura texels vizinhos, então o preto guardado nos texels transparentes nunca chega à borda. Com
filtro linear e alfa direto, os texels da borda se misturariam com esse preto e mostrariam uma franja escura; o
alfa pré-multiplicadoUma cor guardada já multiplicada pela opacidade. Ela filtra e mescla sem franjas escuras e consegue expressar luz aditiva. Alfa pré-multiplicado Uma cor guardada já multiplicada pela opacidade. Ela filtra e mescla sem franjas escuras e consegue expressar luz aditiva.over: out = src + dst · (1 − src.a)
Monte no GLSL
O código copiado é um shader de fragmento GLSL ES 3.00 completo para WebGL2, para um quad com textura: o sprite que você desenha.
- Compile-o com um shader de vértice que posicione o quad e passe
v_uv, 0 no canto inferior esquerdo do sprite e 1 no superior direito (maisv_position,v_normalev_view, que um shader de vértice 2D pode preencher com constantes). - Envie o sprite como textura
SRGB8_ALPHA8com filtroNEARESTe modo de bordaCLAMP_TO_EDGE, ligue-a e apontet_spritepara a unidade dela. As imagens do WebGL carregam a linha de cima primeiro, então ativeUNPACK_FLIP_Y_WEBGLantes do envio ou invertav_uv.y. - Envie
p_colorem luz linear (branco é branco nos dois) e ajustep_amountpelo loop do jogo: 1 no quadro do golpe, depois de volta a 0 em cerca de 0,1 segundo. - Desenhe com a mesclagem alfa ligada. O
main()limita e codifica a cor para sRGB e deixa o alfa do sprite como está.
Monte no Godot
Ponha em t_sprite a mesma textura do próprio Sprite2D: o TEXTURE do Godot não pode ser lido dentro de uma função, e o effect() lê o sprite numa. O shader supõe o HDR 2D desligado (o padrão do projeto) e ele mesmo decodifica o sprite e a cor; com HDR 2D ligado, faça u_toLinear() e u_toSrgb() devolverem c sem mudança.
A exportação é um shader canvas_item para um Sprite2D.
- Selecione o Sprite2D. Em CanvasItem > Material, crie um ShaderMaterial com um Shader novo e cole o código.
- Em Shader Parameters, arraste a textura do sprite para T Sprite e deixe Color em branco.
- Controle Amount pelo script que trata o dano, com um Tween:
func flash() -> void:
var flash_material := material as ShaderMaterial
create_tween().tween_method(func(v: float): flash_material.set_shader_parameter("amount", v), 1.0, 0.0, 0.12)
Para um tom de dano, ponha Color em vermelho e alongue o tween; para uma piscada de invencibilidade, leve Amount entre 0 e 0,8 algumas vezes seguidas. No VisualShader a cadeia é Texture2DParameter (T Sprite) → Texture2D → Mix com um ColorParameter por um FloatParameter no Color do nó Output, com o alfa da textura no Alpha dele.
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
#version 300 es// Hit flash → White flash, 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_amount; // Flash amount: 0.7 (0.0 to 1.0)uniform vec3 p_color; // Flash color: vec3(1.0, 1.0, 1.0), sRGB; upload it decoded to linearuniform sampler2D t_sprite; // mushroom.png: sRGB color, uploaded as SRGB8_ALPHA8 so it samples linear; nearest 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));}// Game code drives p_amount: 1 on the frame of the hit, back to 0 over a tenth of a second or so.vec4 effect(Inputs i) { vec4 own = texture(t_sprite, i.uv); vec3 flashed = mix(own.rgb, p_color, p_amount); // Only the color flashes; the sprite's own alpha keeps its silhouette, so the empty texels stay empty vec4 result = vec4(flashed, own.a); return result;}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 fragColor = vec4(u_toSrgb(clamp(frag_out.rgb, 0.0, 1.0)), frag_out.a);}// Hit flash → White flash, Godot 4. From Creative Center, MIT license.shader_type canvas_item;// Unshaded shows the effect's color as is, like the site's preview; scene lights don't touch itrender_mode unshaded, blend_mix;uniform float amount : hint_range(0.0, 1.0, 0.01) = 0.7;uniform vec3 color : source_color = vec3(1.0, 1.0, 1.0);uniform sampler2D t_sprite : source_color, filter_nearest, repeat_disable; // the sprite: the Sprite2D's own texture again (TEXTURE can't be read inside a function)struct Inputs { vec2 uv; vec3 position; vec3 normal; vec3 view; float time; vec2 fragCoord; vec2 resolution;};// Godot's 2D renderer works in sRGB (HDR 2D off, the default), so the frame decodes color params and sRGB// textures to linear itself and encodes the result. With HDR 2D on, make these return c unchanged.vec3 u_toLinear(vec3 c) { return mix(c / 12.92, pow((c + 0.055) / 1.055, vec3(2.4)), step(0.04045, c));}vec4 u_toLinear4(vec4 c) { return vec4(u_toLinear(c.rgb), c.a);}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));}// 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);}// Game code drives amount: 1 on the frame of the hit, back to 0 over a tenth of a second or so.vec4 effect(Inputs i) { vec4 own = u_toLinear4(texture(t_sprite, u_flipUv(i.uv))); vec3 flashed = mix(own.rgb, u_toLinear(color), amount); // Only the color flashes; the sprite's own alpha keeps its silhouette, so the empty texels stay empty vec4 result = vec4(flashed, own.a); return result;}void fragment() { Inputs i; // UV starts at the top-left; the effect expects GLSL's bottom-left i.uv = vec2(UV.x, 1.0 - 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 = 1.0 / SCREEN_PIXEL_SIZE; i.fragCoord = vec2(FRAGCOORD.x, i.resolution.y - FRAGCOORD.y); vec4 frag_out = effect(i); COLOR = vec4(u_toSrgb(clamp(frag_out.rgb, 0.0, 1.0)), frag_out.a);}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
- texture (abre em uma nova aba)Referência
- mix (abre em uma nova aba)Referência
- CanvasItem shaders (abre em uma nova aba)Documentação
- Resource (abre em uma nova aba)Documentação
- Blending (abre em uma nova aba)Artigo