Pulsing forcefield

At this point, the forcefield is pretty and functional, and we could stop. But some extra features can go a long way. In this tutorial, we add a scanline that periodically crosses down the sphere, and we make the shield grow and shrink slightly over time.

Pulsing size

We start with uniforms to control the speed and distance of any given pulse.

uniform float pulsing_strength = 0.25;
uniform float pulsing_speed = 1.0;

In the vertex shader, we generate a sine wave with sin. TIME multiplied by the pulsing speed is the frequency, and the pulsing strength is the amplitude.

We add the product of the vertex NORMAL and this distance to VERTEX to make it grow and shrink over time.

void vertex() {
    float pulse_distance = ((sin(TIME * pulsing_speed) * 0.1)) * pulsing_strength;
    VERTEX += NORMAL * pulse_distance;
}

Scanline

We specify some uniforms: - scanline_period is the period that will affect the speed and frequency of the scanline. - scanline_width is the width of the scanline. - scanline_intensity is the intensity that controls how bright the scanline makes the shield when it passes by.

uniform float scanline_period = 0.5;
uniform float scanline_width : hint_range(0, 0.49) = 0.1;
uniform float scanline_intensity = 0.35;

As the scanline will be a band that goes from black to white, then black again, we double the final width. We also don’t want it to go over 1.0.

We start by calculating the offset with TIME. Time is negative, so the scanline travels downwards. Making the period negative makes it go up.

Modulo, with the mod function, forces the offset to repeat in the [0..2] range. When we subtract 1 from that, it converts it to the [-1..1] range, making the scanline invisible half the time. We don’t modulo with 1 because the effect would snap from 1 to 0 instantly, looking jarring.

float uv_offset = mod(-TIME * scanline_period, 2.0) - 1.0;

With this offset, we can finally calculate the band itself using two smoothstep calls. UV.y plus our offset gives a vertical scan. The bottom half smoothstep is subtracted from 1 to invert its colors, resulting in black into white into black. Once multiplied by pattern, the hexagonal keeps showing through.

float scanline = smoothstep(0.5 - scanline_width, 0.5, UV.y + uv_offset) * (1.0 - smoothstep(0.5, 0.5 + scanline_width, UV.y + uv_offset)) * pattern.r;

We add it to the fresnel inside the smoothstep that builds the ALPHA.

EMISSION = color.rgb;
ALPHA = smoothstep(0.0, 1.0, fresnel + scanline * scanline_intensity);

Final code

shader_type spatial;
render_mode depth_draw_opaque, cull_disabled, ambient_light_disabled, blend_add, shadows_disabled;

uniform vec4 color : hint_color;
uniform float fresnel_power = 1.0;
uniform float edge_intensity = 2.0;
uniform float fill_amount : hint_range(0, 1) = 0.1;

uniform float pulsing_strength = 0.25;
uniform float pulsing_speed = 1.0;

uniform float scanline_period = 0.5;
uniform float scanline_width : hint_range(0, 0.49) = 0.1;
uniform float scanline_intensity = 0.35;

uniform float pattern_scroll_speed = 0.025;
uniform vec2 pattern_uv_offset = vec2(6.0, 3.0);

uniform sampler2D pattern_texture : hint_albedo;

void vertex() {
    float pulse_distance = ((sin(TIME * pulsing_speed) * 0.1)) * pulsing_strength;
    VERTEX += NORMAL * pulse_distance;
}

void fragment() {
    //Create a fresnel effect from the NORMAL and VIEW vectors.
  float fresnel = pow(1.0 - dot(NORMAL, VIEW), fresnel_power) * edge_intensity;

    //Add back transparency in the middle
  fresnel = fresnel + fill_amount;

    //Get the raw linear depth from the depth texture into a  [-1, 1] range
  float depth = texture(DEPTH_TEXTURE, SCREEN_UV).r * 2.0 - 1.0;
    //Recreate linear depth of the intersecting geometry using projection matrix, and subtract the vertex of the sphere
  depth = PROJECTION_MATRIX[3][2] / (depth + PROJECTION_MATRIX[2][2]) + VERTEX.z;
    //Intensity intersection effect
  depth = pow(1.0 - clamp(depth, 0, 1), fresnel_power) * edge_intensity;

    //Calculate final alpha using fresnel and depth joined together
  fresnel = fresnel + depth;

    //Calculate UV scrolling pattern
  float scrolling_time = TIME * pattern_scroll_speed;
    vec4 pattern = texture(pattern_texture, (UV * pattern_uv_offset) + vec2(scrolling_time));

    //Use pattern to cut holes in alpha
  fresnel *= pattern.r;

    float uv_offset = mod(-TIME * scanline_period, 2.0) - 1.0;
    float scanline = smoothstep(0.5 - scanline_width, 0.5, UV.y + uv_offset) * (1.0 - smoothstep(0.5, 0.5 + scanline_width, UV.y + uv_offset)) * pattern.r;

    //Apply final color
  ALBEDO = vec3(0);
    EMISSION = color.rgb;
    ALPHA = smoothstep(0.0, 1.0, fresnel + scanline * scanline_intensity);
}