Skip to main content

Text crawl and other effects GLSL shader

For my project, I've been working on a kind of 80s style sci-fi monitor display.In game, this is going to be handled by a single texture effect with a GLSL shader.

Different sections of the texture will be cut down and used on different view screens. Here's a preview of how it looks in action:

Screen capture in Linux Mint is always a bit buggy. :( Or maybe it's because my computer is over ten years old...

Here's another set of screens with different colors and contents, for the planet that the player gets to visit in the first episode (you can see in this preview how the "alert effect" is activated to change colors of the scan):

You can see the original effect I'm trying to duplicate in the Star Trek movies:

Anyway, it's a nice set of old school effects, and I'm going to explain how they are achieved in a game engine with a GLSL shader (In this case, GODOT, and awesome free game engine).

First you need to set up the different layers of your image. You can use three separate greyscale images if you like, but it won't make things better AFAIK.

Red layer is the text crawl and scan effect:

You can see how the scan is acheived with a gradient, and the same is done with the text, shading from white to black. Make sure the gradient is done with linear interpolation, or it won't scan evenly over time.

Next is green, which controls a pulsing effect on the waveforms and some other effects, like pulsing buttons:

Again, they are treated like gradients, shaded from the center outwards.

Lastly is the blue channel, which is not animated, and represents background elements:

This one can be modified with blur effects to get that classic CRT glow... don't use any post processing on the other channels though, because it will make their effects turn out wonky.

When combined into one image with three channels it looks like this:

You can use more textures or even try using the alpha channel to get more effects, it's up to you. I left it at 2 because it was already starting to get a bit busy. Less is sometimes more.

Now lets' look at some of the GLSL code:

shader_type spatial; 

render_mode unshaded, blend_add; 

uniform sampler2D lcars

uniform float alert;

We are writing a spatial shader, and we want it to be unshaded and "additive" so that it will glow over the background elements. I'm going to be using modeled props for the screens and I want to see only the glowing elements of the shader. Where it's black, the underlying screen prop should show through.

lcars is the name of the viewscreens in Star Trek. Here it's the name of our RGB image. The float, alert, is to blend between two colors, so we can have the scanner effect come out in red, or hide it or have it come in a different color. See the second gif above to see how that works.

The next bit is a little more complicated. 

There are several steps here that need to be taken in order to get the effect to come out right. I'm just going to be showing the basic text crawl /scan effect, which you can then modify for your own needs.

vec4 lc = texture(lcars, UV);

float crawl = 1.0 - fract(TIME * 0.5);

float t2 = lc.r;
float c1 = smoothstep(crawl * 0.8, crawl, t2);
float c2 = smoothstep(crawl + 0.1, crawl, t2);
float c3 = mix(0.0, c2, c1);
float c4 = c1 * 0.5;
float c5 = max(c3, c4);
vec3 alert_off = vec3(c5 * 0.2, c5 * 0.4, c5 * 0.5);
vec3 alert_on = vec3(c5, 0.0, 0.0);
vec3 text_crawl = mix(alert_off, alert_on, alert);

We start out by getting our texture. We only need one part here, the red channel. We're going to be working with floats.

Next we need to use the TIME built-in object to make the animation work.

frac will return just the fraction part of time. So it will always give a figure between 0.0 and 0.9999.. This will keep the effect repeating. 

We want to to go "up" instead of down, so we want a value of 1.0 - fract(TIME).

Scaling time (in this case by 0.5) will make the effect cycle faster or slower. TIME * 4.0 would be really fast. TIME * 0.1 would be much slower.

We're going to use smoothstep to remap the gradient:

c1 maps the gradient from 1.0 to 0.0. You can stick with this basic effect if you like. It will make the text crawl work, but it's all just appearing.

We want it to glow as it appears and then fade away. So we use c2 as a negative filter. With smoothstep, if you set the first argument higher than the second one, it will invert the output  when mapping the values (1.0 to 0.0). You could do this instead of inverting the crawl value... but you still need one negative and one positive gradient.

We then mix these with black (0.0) so that c3 will create a pulse that rides the edge of the gradient. Again, you can use this effect as you like. If you want the text to only display as it prints and then fade to black, you could use c3 as the final value.

I don't want that though, so I mix c3 with a weaker form of c1 to get the final effect.

Finally, I want to be able to change from a blue color to a red color effect when alert is set to 1.0. So I set two vec3 and mix between them with the alert uniform.

If you want the effect to pulse instead of crawl, you can use sin(TIME) instead of frac(TIME). That's what I used for the waveform effects.

Once you've put together each of your effects with the background color, just mix them together into a final vec3.

For my own shader, I put all this in a function (get_pixel), then called it twice. Adding a slight UV offset (UV + 0.001) and putting the elements together in a different order (get_pixel().brg instead of .rgb) gives a nice CRT chromatic aberration effect that duplicates the feel of some 80s sci-fi special effects.

As a last mention, you can really increase the quality of the effect by using uncompressed textures... but it might not be worth the benefit, since it will use a lot more resources once you've got a few of these shaders in your game.

Otherwise, use VIDEO RAM mode for textures and turn off filter and mipmaps to avoid the worst artifacts.

Here's a shot of how that looks when compared to uncompressed textures: 

It's up to you whether you can live with the artifacts generated. It's honestly still probably better than the quality you could expect from a real CRT monitor, so it might be worth just going with it.

Note: all these screenshots were created in a project running GLES2. Post processing effects such as bloom will change how they look in GLES3, so you need to choose different values for colors. You could use uniforms to set them on the fly when you switch modes, or you could write different shaders for different modes.

 

Comments

Popular posts from this blog

Automating Level imports from Blender to Godot

  Recently I've been making some levels in Blender an importing them into Godot. There are only about 7 or 8 shaders for each level, not counting dynamic objects which will be added later. But to improve rendering performance, it can be a good idea to split the meshes up into sections. At that point you might be faced with a list like this: Or it might be even more chaotic, if you didn't use simple names for the objects in your level. So it can take a long time to sort out all the meshes, make them unique and add textures and so on. Blender imports with simple Blender textures, or with placeholder materials. This is sometimes OK, but if your Godot shaders are very different to those used by Blender, it means applying new materials to every mesh object in the level when you import the scene. I found that during the design process, I was importing and readying a level several times before I was happy with the final layout. So at first I was wasting a lot of time. In Blender, I us

Upstairs / Downstairs.

I've decided to make my prefabs multilevel. Later this should allow me to add pit traps and other great stuff. It also makes it easier to line up stairs so that you can exit them on the same co-ordinates where you entered them. The prefab editor is pretty much finished, it just needs some code for loading up prefabs from a saved dictionary, so that they can be checked or edited. The entries will need to be forwards compatible, so I'll be loading each tile and then translating the indexes to a new array, that way if I add extra indexes or extra info (like traps or puzzles) I'll be able to update existing prefabs to work with the new standard. Click for a video.

Make your game models POP with fake rim lighting.

I was watching one of my son's cartoons today and I noticed they models were using serious amounts of simulated rim lighting . Even though it wasn't a dark scene where you'd usually see such an effect, the result was actually quite effective. The white edge highlighting and ambient occluded creases give a kind of high contrast that is similar to, but different from traditional comic book ink work. I'll be honest, I don't know if there's a specific term for this effect in 3d design, since my major at university was in traditional art. I learned it as part of photography. You can find plenty of tutorials on "what is rim lighting" for photography. It basically means putting your main sources of light behind your subject so that they are lit around the edges. It can produce very arresting photographs, either with an obvious effect when used on a dark subject ... ..,or as part of a fully lit scene to add some subtle highlights. See ho