This post contains references to self harm. I remember the exact moment, in the summer
If you know anything about the implementation of custom shaders in the game engine Godot, you know that they have consistently pushed to become more approachable and easier to use through some traditional and some untraditional methods.
These efforts are important and bring a new level of power to a lot of game devs, however the efforts have also resulted in a lot of churn between Godot versions. In July, 2018 we saw the re-introduction of the VisualShader tool which allows us to use a simple UI to build up complex shaders visually.
The tool is simple and leaves a bit to be desired (for example, it's not possible to have a
uniform boolean flag (e.g. IsGreyscale) to toggle shader behavior in VisualShader, but it is in a shader written in the normal shader language.
Regardless, I think VisualShader is great for what it is and could benefit from more documentation so I'd here is a simple example of a VisualShader which modifies the rendering of a sprite to make it greyscale.
At the end of this tutorial, hopefully you'll be able to turn any sprite in your Godot game to be grey.
This example will focus on the "Spin" button in my game.
This is a
Sprite node with the texture set to a full color asset. Once we're done, we'll have an identical button with all the colors turned to grey!
In your project, select your
Sprite and check out the inspector. You'll want to look for the
CanvasItem header, which contains low-level rendering options for this
Sprite. Under the
Material dropdown, you'll want to click
New ShaderMaterial. If you're not familiar with materials, the idea is that a material holds various properties related to the rendering of your object like whether or not it interacts with light or in our case, what shader to use.
Once you've gotten your new
ShaderMaterial click on the dropdown near the
Shader field and select
New VisualShader which will give you access to the graph-based shader editor. If you pick
New Shader instead, you'll get access to the text-based shader editor.
Look in the bottom of your editor, near the other tabs like
Debugger, etc you should now see
VisualShader (as long as your
Sprite is still selected in the inspector.)
By default, your graph will be empty except for an
Output node. but it's easy to create new nodes by clicking the
Add Node... button. However, before we dive into that, look back in your inspector and make sure the
Mode of your shader is set to
CanvasItem, this gives our graph different
Inputs which will let us deal with our 2D sprite.
Once you're all set up your inspector should look something like this...
If your inspector looks different, for example you don't see the hierarchical view of
CanvasItem > Material > Shader then make sure you're using Godot 3.1 and not an older 3.1 release.
At this point we can build up a graph in the VisualShader editor.
Above we see four nodes, each added via the
Add Node... button. If you've never written a shader before, it's important to understand that this logical graph will be ran once for each pixel of your sprite. So when we're building up this logic we're only thinking about one given point on the sprite and the modifications we're doing are only happening to that one point. The graph is re-ran for each pixel and the results of all those runs is what's rendered to the screen at the end of a frame.
The first node is an
Input node with the
uv type selected.
Input value is something coming from outside our shader, they're the values which are provided by settings things like the
Texture of the
If you're not familiar with the concept of a uv then a simple way to think of it is that in a 2D world we'd use
y to denote a pixel on a texture, but in the 3D world we already use
z to position an object, so we use
uv data is just coordinates on a texture. Since they're just coordinates (i.e. numbers) they're not much use until we map them to the data from our
Texture. The second node takes care of this.
As an aside, once you're created the second Node via the
Add Node... button, you'll have a few options about where this texture's data comes from. If you select the default
Texture2D option, then you're saying the data wont come from the screen (i.e. the entire game screen) and it wont come from the sprite's Texture2D, it'll come from a new parameter to the shader. If you grab the output
rgb value from that texture and connect it to the final
Output node, you'll see a new
Shader Param in the inspector. This is an automatically created
uniform, any time you want to pass data into your shader from your other scripts, you'll use a
uniform of some form.
If you want to pass in other data which isn't supplied in
Inputs then check out the
Uniforms section in the
Add Node... menu. Keep in mind that you wont see the
Shader Param field from your
uniform show up unless it's connected to the
Anywho, flip your
Texture node to
Texture2D and that will be the
Sprite's texture. Once we have that we'll have our first output ready, the
alpha field. Connect the
alpha to the
alpha. We're not interested in messing with any
alpha values so there's nothing to do here except pass the original value through.
rgb value is of interest to us. We'll make our final node, a
DotProduct. I'll be honest, I don't know why exactly this works to turn the
rgb value to grey. The base of this VisualShader is a port of this answer on the Godot forums. All credit goes to timoschwarzer. If you want, you can try to read the Wikipedia page on dot products, but I doubt it'll help.
With that bit of mystery, let's connect up our last node.
We've hard-coded the same value's from Timo's answer. Then finally we can take the dot product and allow it to be implicitly converted back into a vector to fulfill the
color field on the
There we go, we've created our first VisualShader in Godot 3.1! If you want, you can go into the inspector and click the dropdown near the
Material to save this material so you can easily apply it to other sprites.