Last time we learned about generating normal maps. In this lesson we're going to return to post processing effects (also called image effects) to learn how to make a retro-styled scanline effect!
If you're old (like me) you remember back in the day when TVs and monitors were made outta vacuum tubes and dreams. These things shot beams at glass in patterns and the rows of these "raster scans" would create visible lines across the screen. So what we need to do to evoke this feel in our game is to make a shader that will put lots of horizontal lines on the screen. Let's get started!
Make sure you have the post processing package installed in your project. If you don't know how to do that, read up on it here. I'm also assuming you have your project set up with the PostProcessing layer as directed in this lesson. If not, go back and skim those and come back when you have a Post Process Layer on your camera and a Post-process Volume enabled in your scene.
Go to the folder Assets/Shaders/Post Effects/Scripts in your project files (or make this folder) and add a new script called Scanline.cs. In Assets/Shaders/Post Effects/Shaders (or similar) add a new Image Effect Shader called Scanline.shader.
Open Scanline.shader in Visual Studio. We'll first start by changing the first line to Shader "Hidden/Xibanya/Effects/Scanline" and then we'll replace the template code with our own starting point for a post processing effect.
If you want to know how this blank post effect works or why we're putting any of this here, check the first lesson we did on the v2 post effects stack!
Now we'll stub out the effect renderer. I already wrote about how to do that in the lesson on depth-based effects, so check that if you need a refresher. The stubbed out effect renderer will look like this:
In Unity, go to your post-process volume and click Add effect... then go to Xibanya > Scanline (as indicated in our stubbed out renderer).
If you did it right, you'll have an empty post effect added to your volume and nothing onscreen will have changed.
Now we can see our changes to the shader happen in Unity as we make them.
Making the shader
Okay, all that initial housekeeping out of the way, let's start making this effect! Hmm, so we need a ton of lines on the screen, right? Well we could turn every other pixel vertically into a black bar by doing this!
floor rounds whatever is in it to the nearest integer (whole number) and % is a modulus operator, which is a fancy way of saying it gets us the remainder after dividing what's in front of it by what's after it.
And this is what that gets us
well it's...a lot of horizontal lines, yeah...but it doesn't really look, like, retro, you know what I'm sayin'? I think it's like...there's too many lines. Well we can adjust that by feeding in a different screen height from our renderer.
In Scanline.cs, we'll add an IntParameter to adjust the height of the effect.
And we'll use that parameter to adjust the value in the shader.
Then we'll tweak the shader to have a _Height property for the renderer to write to.
Mmm yeah, no, I'm not feeling it. There has to be more to making scanlines than just alternating bars across the screen.
Back in the day it wasn't like this super sharp line, it was caused by light beams and stuff, it was analog, gotta have some fade to it. Weren't we just working on something that had a smooth regular fade in and out? If we could fade our line in and out going down the screen, that would be perfect. I'm talking of course about sine!
Let's change our frag function to this instead
(Remember, we're using that * 0.5 + 0.5 to put the range of the output between 0 and 1 rather than -1 and 1!)
Okay, that looks better already.
But something about it still doesn't quite feel right. The scanlines just seem a little too dark, the effect is too harsh. Back in my day, how visible the scanlines were depended on what kind of screen it was. When I used to sit too close to the TV watching Animaniacs or whatever I could distinctly see the color split, but it wasn't obvious if I was sitting on the couch. If I was with my mom at the ATM I could see pretty distinctly visible scanlines even from a healthy distance away. So we should be able to adjust the strength of this effect via our post effect!
While we're at it, why not allow ourselves to control the color of the scanline too? Might want a green tinge for like, some stealthy shootyman game. Let's further tweak our shader to let us do that.
The sine function will determine the strength of the blend of our scanline color with the original screen color.
We'll then add a ColorParameter to the Scanline settings and write to it in the ScanlineRenderer.
Save and head back to Unity to check it out, and mess with the values in the post process volume. Nifty!
We're still missing something though. For that proper retro look, the lines have to be moving! This is supposed to be beams sweeping over glass from behind, right? So just like we did in the earlier lesson with sine, we'll add some looping time to the effect.
We'll get our scroll loop then subtract it from our y coordinate to make it appear to be moving vertically.
Then we just need to add a new FloatParameter to our render settings,
then write to it in the Render function!
If the lines don't appear to be moving in Unity, go into playmode to check if they're working.
The shader and effect we made in this tutorial are attached as Scanline.shader and Scanline.cs. If you have any questions or want to share what you come up with, let me know in the comments here, on Twitter, or in Discord. And if this tutorial helped you out, please consider becoming a patron!
This tutorial is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike4.0 International License. The code shared with this tutorial is licensed under a CreativeCommonsAttribution 4.0 International License.