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.
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; }
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);
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); }