Holograma
Uma casca translúcida que brilha, um contorno Fresnel dos dois lados mais linhas de varredura subindo pela altura do mundo.
- 01ObjetivoLuz
- 02TécnicaFresnel
- 03ExecuçãoHolograma
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.
absAbsabsabsabs NormalVisãoPosiçãoTempo01 De frente, dos dois lados
powPowpowpowpow Entrada02 Contorno Fresnel
fractFractfractfractfract Entrada03 Repetir ao longo da altura
smoothstepSmoothStepsmoothstepsmoothstepsmoothstep Entrada04 Linhas finas e suaves
*Multiply*** Entrada05 Colorir e somar
Como funciona
Um holograma é luz sem superfície: você enxerga através dele, e o que vê é o contorno. Três escolhas do card fazem isso
funcionar. A cor é escrita como emissão, então nada a ilumina. A passada desenha com
blending aditivoCombinar a cor que o shader de fragmento devolve com a cor que já está no framebuffer, em vez de substituí-la. Transparência, brilho aditivo e multiplicação vêm daí. Blending Combinar a cor que o shader de fragmento devolve com a cor que já está no framebuffer, em vez de substituí-la. Transparência, brilho aditivo e multiplicação vêm daí.result = src × srcFactor + dst × dstFactor
Passo a passo
- De frente, dos dois lados. O produto escalarMultiplica dois vetores componente a componente e soma os resultados. Para vetores unitários é o cosseno do ângulo entre eles, de 1 (mesma direção) a -1 (opostos).Fundamento
Produto escalar
Multiplica dois vetores componente a componente e soma os resultados. Para vetores unitários é o cosseno do ângulo entre eles, de 1 (mesma direção) a -1 (opostos).
dot(a, b) = |a| |b| cos θFontesentre a normalUm vetor unitário perpendicular a uma superfície, apontando para onde ela está virada. Iluminação e efeitos Fresnel o comparam com outras direções.- dot (abre em uma nova aba)Referência
- Dot product (abre em uma nova aba)Referência
FundamentoNormal
Um vetor unitário perpendicular a uma superfície, apontando para onde ela está virada. Iluminação e efeitos Fresnel o comparam com outras direções.
Fontese a direção de visão, como na luz de contorno, mas comabs()em vez demax(…, 0.0). A normal de uma face de trás aponta para longe da câmera, então o produto escalar é negativo;abs()a trata como uma face da frente, e o lado de lá da casca ganha o mesmo contorno. Commax()o fundo inteiro contaria como borda e brilharia chapado. - Contorno Fresnel.
pow(1.0 - facing, p_power), a forma do FresnelSuperfícies refletem mais luz vistas em ângulo rasante do que de frente. Shaders costumam imitar isso com o produto escalar entre a normal e a direção de visão.TécnicaEfeito Fresnel
Superfícies refletem mais luz vistas em ângulo rasante do que de frente. Shaders costumam imitar isso com o produto escalar entre a normal e a direção de visão.
F = F0 + (1 − F0)(1 − cos θ)^5Fontes: escuro no meio, claro no contorno. Na prévia as faces de trás caem sobre o mesmo contorno das da frente (numa esfera o raio de visão encontra a superfície no mesmo ângulo ao entrar e ao sair), então o contorno fica duas vezes mais claro; o que aparece através da frente são as linhas da camada de trás, curvadas para o outro lado. - Repetir ao longo da altura.
fract()da altura no mundo vezes uma densidade repete uma rampa de 0 a 1 a cada1 / p_densityunidades, e subtrair o tempo faz as rampas subirem. A posição no mundo, e não a UV, mantém as linhas niveladas e com espaçamento igual em qualquer malha, e a mesma altura nas faces da frente e de trás alinha as duas camadas. - Linhas finas e suaves. A distância até o meio de cada rampa,
abs(band - 0.5), vale 0 no centro da linha;1.0 - smoothstep(0.0, 0.15, …)a transforma numa linha que se apaga ao longo de 0,15 do espaçamento de cada lado. A borda suave importa: umstep()seco pisca enquanto as linhas atravessam os pixels. - Colorir e somar. O contorno, um preenchimento constante para o corpo nunca sumir de todo e as linhas com o próprio brilho, somados e multiplicados pela cor. Como o blending soma, camadas sobrepostas só ficam mais claras, e não há ordenação para errar.
Gire a prévia: os contornos ficam nas silhuetas enquanto as linhas continuam niveladas no mundo.
Monte no GLSL
Desenhe-o depois da cena opaca com gl.enable(gl.BLEND), gl.blendFunc(gl.ONE, gl.ONE), gl.disable(gl.CULL_FACE) e gl.depthMask(false), mantendo o teste de profundidade ligado para que objetos sólidos na frente ainda o escondam. O main() gerado escreve a luz pré-multiplicada, como ONE, ONE espera.
O botão de copiar entrega um shader de fragmento WebGL2 completo. O seu shader de vértice escreve a posição, a normal
e a direção de visão (para a câmera) em espaço de mundo em v_position, v_normal e v_view, e declara v_uv; o
main() gerado normaliza as duas direções por pixel. Passe para u_time os segundos desde o início, a cada quadro,
senão as linhas ficam paradas.
Desenhe-o com o estado de blending e profundidade da nota acima. Como a soma não depende da ordem, vários hologramas podem se sobrepor sem ordenação, que é o principal motivo de efeitos de brilho usarem blending aditivo em vez de alfa.
Monte no Godot
O export define render_mode blend_add, cull_disabled e escreve EMISSION sobre um ALBEDO preto, com especular e luz ambiente desligados. blend_add põe o material na passada transparente do Godot, então ele é desenhado depois da cena opaca, não escreve profundidade e nunca é escondido pelas próprias faces.
Crie um MeshInstance3D (uma SphereMesh ou a malha de um personagem), dê a ele um novo ShaderMaterial e cole o shader num novo Shader. Ponha-o sobre um fundo escuro, ou num WorldEnvironment com céu escuro, para ver o brilho aditivo; ligue o Glow no WorldEnvironment para um halo em volta dos contornos.
No VisualShader, o contorno é o nó Fresnel alimentado por NORMAL e VIEW, mas com o culling desligado as faces de trás
precisam da versão com abs(): um DotProduct das duas entradas, um FloatFunc Abs, um FloatFunc OneMinus e um FloatOp
Pow. As linhas são o Y em espaço de mundo (um Input VERTEX por um TransformVecMult com INV_VIEW_MATRIX, depois um
VectorDecompose), um MultiplyAdd com a densidade e menos TIME vezes a velocidade (um FloatOp Multiply pela velocidade
negada), um FloatFunc Fract, um FloatOp Subtract de 0,5, um FloatFunc Abs, um SmoothStep de 0,0 a 0,15 e um FloatFunc
OneMinus para as linhas, depois um FloatOp Multiply por um FloatParameter com o brilho das linhas. Some o contorno, um
FloatParameter de preenchimento e as linhas, multiplique por um ColorParameter na porta Emission e defina Blend Mode
como Add e Cull Mode como Disabled nos render modes do shader.
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// Fresnel → Hologram, 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 vec3 p_color; // Color: vec3(0.3, 0.85, 1.0), sRGB; upload it decoded to linearuniform float p_power; // Rim power: 2.5 (0.5 to 8.0)uniform float p_fill; // Body glow: 0.02 (0.0 to 0.5)uniform float p_density; // Lines per unit: 12.0 (1.0 to 40.0)uniform float p_speed; // Line speed: 0.6 (-3.0 to 3.0)uniform float p_lineStrength; // Line brightness: 0.35 (0.0 to 1.5)in 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));}vec4 effect(Inputs i) { // With culling off the back faces draw too, and their normals point away from the camera: abs() gives them the // same rim as the front, so the far side of the shell glows through. float facing = abs(dot(i.normal, i.view)); float rim = pow(1.0 - facing, p_power); // World height instead of uv: the lines stay level and evenly spaced whatever the mesh's unwrap looks like. float band = fract(i.position.y * p_density - i.time * p_speed); float lines = 1.0 - smoothstep(0.0, 0.15, abs(band - 0.5)); vec3 emitted = p_color * (rim + p_fill + p_lineStrength * lines); return vec4(emitted, 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 // Additive light over a transparent target: premultiplied, as bright as its alpha, so dark adds nothing vec3 frag_light = u_toSrgb(clamp(frag_out.rgb, 0.0, 1.0)) * frag_out.a; fragColor = vec4(frag_light, max(max(frag_light.r, frag_light.g), frag_light.b));}// Fresnel → Hologram, Godot 4. From Creative Center, MIT license.shader_type spatial;// Emission: a black base color, no specular or ambient light, and the effect as EMISSION, which glow catchesrender_mode specular_disabled, ambient_light_disabled, blend_add, cull_disabled;uniform vec3 color : source_color = vec3(0.3, 0.85, 1.0);uniform float power : hint_range(0.5, 8.0, 0.1) = 2.5;uniform float fill : hint_range(0.0, 0.5, 0.01) = 0.02;uniform float density : hint_range(1.0, 40.0, 0.5) = 12.0;uniform float speed : hint_range(-3.0, 3.0, 0.05) = 0.6;uniform float lineStrength : hint_range(0.0, 1.5, 0.05) = 0.35;struct Inputs { vec2 uv; vec3 position; vec3 normal; vec3 view; float time; vec2 fragCoord; vec2 resolution;};vec4 effect(Inputs i) { // With culling off the back faces draw too, and their normals point away from the camera: abs() gives them the // same rim as the front, so the far side of the shell glows through. float facing = abs(dot(i.normal, i.view)); float rim = pow(1.0 - facing, power); // World height instead of uv: the lines stay level and evenly spaced whatever the mesh's unwrap looks like. float band = fract(i.position.y * density - i.time * speed); float lines = 1.0 - smoothstep(0.0, 0.15, abs(band - 0.5)); vec3 emitted = color * (rim + fill + lineStrength * lines); return vec4(emitted, 1.0);}void fragment() { Inputs i; // Godot's UV starts at the top-left; the effect expects GLSL's bottom-left i.uv = vec2(UV.x, 1.0 - UV.y); // VERTEX, NORMAL and VIEW are in view space here; the effect works in world space i.position = (INV_VIEW_MATRIX * vec4(VERTEX, 1.0)).xyz; i.normal = normalize((INV_VIEW_MATRIX * vec4(NORMAL, 0.0)).xyz); i.view = normalize((INV_VIEW_MATRIX * vec4(VIEW, 0.0)).xyz); 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 = vec3(0.0); EMISSION = frag_out.rgb; ALPHA = 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
- PBR: Theory (abre em uma nova aba)Artigo
- Blending (abre em uma nova aba)Artigo
- Face culling (abre em uma nova aba)Artigo
- abs (abre em uma nova aba)Referência
- fract (abre em uma nova aba)Referência
- smoothstep (abre em uma nova aba)Referência
- Spatial shaders (abre em uma nova aba)Documentação