Pular para o conteúdo
Luz · FresnelIniciante

Holograma

Uma casca translúcida que brilha, um contorno Fresnel dos dois lados mais linhas de varredura subindo pela altura do mundo.

NormalVisãoPosiçãoTempoSuperfície
Abrir no Playground
  1. 01ObjetivoLuz
  2. 02TécnicaFresnel
  3. 03ExecuçãoHolograma
Malha de teste

Parâmetros

#4dd9ff
2.5
0.02
12.0
0.60
0.35

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.

  1. absAbsabsabsabs
    Normal
    Visão
    Posição
    Tempo

    01 De frente, dos dois lados

  2. powPowpowpowpow

    02 Contorno Fresnel

  3. fractFractfractfractfract

    03 Repetir ao longo da altura

  4. smoothstepSmoothStepsmoothstepsmoothstepsmoothstep

    04 Linhas finas e suaves

  5. *Multiply***

    05 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 aditivo, somando a sua luz ao que está atrás em vez de cobri-lo, então o meio escuro da casca soma só um brilho fraco. E o culling está desligado, então as faces de trás também são desenhadas e o fundo da casca aparece através da frente, o que dá profundidade a ela.

Passo a passo

  1. De frente, dos dois lados. O produto escalar entre a normal e a direção de visão, como na luz de contorno, mas com abs() em vez de max(…, 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. Com max() o fundo inteiro contaria como borda e brilharia chapado.
  2. Contorno Fresnel. pow(1.0 - facing, p_power), a forma do Fresnel: 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.
  3. Repetir ao longo da altura. fract() da altura no mundo vezes uma densidade repete uma rampa de 0 a 1 a cada 1 / p_density unidades, 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.
  4. 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: um step() seco pisca enquanto as linhas atravessam os pixels.
  5. 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

hologram.fragGLSL
#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));}
hologram.gdshaderShader do Godot
// 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