You might want to read this blog post.
https://alexanderameye.github.io/notes/rendering-outlines/
How to prevent ring overlap with a 3D material?
- Edited
cybereality Correct me if I'm wrong but I took a proper look at this and it seems to be about creating outlines only? It doesn't seem to address the issue of how to prevent overlap. My goal when looking at this is to have two different circles representing a radius and then as the radius expands the circles expand with it merging together if they overlap.
Now I think about it and this kind of surprised me but the most modern example I can find of this technique is Civilization VI, in this screenshot I searched up you can see though that the border even follows the terrain. See the border on the bottom right of the screenshot going over the hill.
I admit I have something of an obsession with this sort of effect, me and two other programmers managed to get it just about working in Unity and it even took some ridiculous shader trickery by making the engine think one type of maths was another but it is possible to do. I'm actually kind of hoping because of Godot's more open source nature there's a better way to implement it, although maybe that's wishful thinking.
Learning about merging shaders together this way is something I'd find particularly useful for the kind of effects I'm looking at because when you nail this effect for influence borders and the like it looks fantastic and clean. Even better if you could get a more 3D look from it.
- Edited
I think that being able to grab vertices within a radius could be the main key to getting the sorts of influence border effects that I'm after. If I could get the vertices added to an array then I could potentially use a shader to hide those by simply lowering the alpha to 0 that are within the radius then preventing the overlap. That would be really nice to be able to do compared to the absolute craziness we were messing with in Unity although then again, I wonder if that would just be a more efficient solution overall since it's purely for a shader.
I don't know, it depends on what people here would suggest I guess?
Yes, what you want is essentially an outline, which is why I linked that article. The way I would do it is render all the shapes, the silhouette of the objects with the outline to a viewport. These would be the volume of the shape, for example, circles with a solid white color and no outline. Then you could render two, or more, of these to a render target texture (the viewport texture). Then use a shader to draw an outline in any color you want. This will give you the outline color and the shape, everything else will be white. Then you project this texture onto another surface with a multiply blend mode. The white will become invisible, and then the other color will mix with what is there. From the screenshot of Civilization, it appears they did something similar to this, but there are other ways.
cybereality Interesting, I really want to learn in detail about these sorts of methods, I guess the outline shader isn't actually going to be that big of a deal as it turns out so thank your for those resources. What's confusing me now is going to be the multiply blend mode because that's what is making the magic happen with stopping the overlap. I'll poke at the documentation some more, one thing I've also been doing is taking a look at how other pieces of software are handling these types of modes as there seems to be a lot of overlap ( Aha ) where you've got very similar modes but different ideas on how to approach them and what they're used for.
Wait, what? LOL okay, it looks like the thing I need to learn about most is what the blend modes actually do especially multiply, confirmed what you're writing about checking out this video, I didn't know about any of this, that's fascinating thanks.
I'll have an experiment with the colours and see what I can come up with, the reason it would be better if it was 3D is I could have the vertices drop down to the terrain individually without any of the crazy mesh generation nonsense. Plus I could also potentially fiddle around with some shader trickery and make some fancy animation effect for the outline.
- Edited
Maybe it will make more sense with an image.
- Edited
It's pretty simple actually. Multiply just uses multiplication on the components of a color (additive uses addition, etc.). So let use just take one color floating point number (such as a grayscale image). If the original color value of the red component is 0.67
and you multiply by 1.0
you get the original value of 0.67
(0.67 * 1.0 = 0.67
). This is why white is invisible, because the color white is (1.0, 1.0, 1.0) so any color you multiply will be the original color. Black is (0.0, 0.0, 0.0) so if you multiply by black, you get black (0.67 * 0.0 = 0.0
). Additive works the same way, but you add the two colors rather than multiply.
- Edited
Okay, that's definitely making way more sense now thanks, I guess my main question is now why is it that you can't use a standard material and have to draw the outline yourself? Is it just down to the way the materials work that it can't happen? I think this is what has been throwing me off all this time with trying to reproduce these kinds of influence border effects.
- Edited
Well a lot of stuff in game development you have to do yourself. The engine gives you the tools, just like an artist may get a paint brush and a blank canvas, it's up to you to draw something.
- Edited
cybereality Sorry, I should be clear, I guess I meant to ask what is is about how materials work in the backend that simply putting in a transparent material with an outline doesn't work? It looks like the same sort of method to me on the front end, but clearly it's not.
- Edited
In a 3D engine, each object is drawn one by one. If you have transparent objects, they are also drawn one by one (after the opaque objects in a particular order, such as back-to-front). Nothing is really ever combined (though by use of the z-buffer and other tricks, it appears to be one scene). Transparency is very difficult to do properly and is basically a hack and causes all sorts of weird issues if you do it wrong. So there is not a good way to handle this generically. But the concept is complex, and there are a few ways around it, like rendering to a separate buffer like I am suggesting.
cybereality So I cheated a little just to do some testing and grabbed an outline shader from the asset library, what seems to be happening is the outline is automatically registering the existence of the two top and bottom faces and rendering them. This obviously isn't the effect that I want any ideas why this might be? I suppose the good thing is that the two cylinders are blending correctly.
- Edited
Okay, now I think I have a clearer understanding of what's been confusing me. I did some blender trickery to make my mesh sort of work and I made two different materials for the top and bottom faces and sides of the cylinder. The top and bottom faces are white and set to multiply so they're all transparent this prevents the outline shader from just blindly creating an outline around the whole mesh.
However where I'm struggling is the last bit where you get a quad or decal? What does that involve? I think I might need a step by step on how to do that. I can't really wrap my head around the concept terribly well even though it makes total sense in theory.
Should I be declaring the blend mode in the shader itself in this instance? Or is this something I'm badly misunderstanding in how the method is supposed to work?
- Edited
Lethn I took the 3D pixel perfect outline shader from the asset library and did some editing to the shader but no dice on the blend mode? I feel like I'm badly misunderstanding the process. On the bright side I do seem to be able to edit and tweak shaders a bit at least, got rid of the culling so I could see everything.
shader_type spatial;
render_mode cull_disabled, unshaded, skip_vertex_transform;
render_mode blend_mul;
uniform vec4 albedo : hint_color;
uniform float outline_width = 1.0;
void vertex() {
mat4 matrix_m = WORLD_MATRIX;
mat4 matrix_vp = PROJECTION_MATRIX * INV_CAMERA_MATRIX;
vec4 clip_position = matrix_vp * (matrix_m * vec4(VERTEX, 1.0));
vec3 clip_normal = mat3(matrix_vp) * (mat3(matrix_m) * NORMAL);
vec2 screen_size = VIEWPORT_SIZE;
vec2 offset = normalize(clip_normal.xy) / screen_size.xy * outline_width * clip_position.w * 2.0;
clip_position.xy += offset;
VERTEX = (INV_PROJECTION_MATRIX * clip_position).xyz;
}
void fragment() {
ALBEDO = albedo.rgb;
}
Bumping because it would be nice to get this problem out of the way.
This is a tutorial for the method I'm suggesting, but in Unity. The code is a little complex (and much longer than needed in Godot) but you might better understand the general concept.
https://willweissman.wordpress.com/tutorials/shaders/unity-shaderlab-object-outlines/
- Edited
cybereality Thanks, I've got more to search for now that I've got new keywords, I just came across this tutorial which is exactly what I want but it's for 2D.
Almost there though, trying to keep digging, I would be very surprised if somebody hasn't done this before me, yes it's a complicated shader technique, but also it's a very specific one I've noticed. I think that's mainly the problem I have with searching up information and tutorials.
- Edited
Okay, I think after looking at this tutorial on viewports I've realised I barely know how to use viewports properly and that's why I'm struggling, this basic video helped.
Believe it or not I think the issue is less to do with the outline shader itself, I believe that's setup correctly unless I'm blatantly missing something and the pixel checks are what's needed to turn the overlapping transparent most of all. It's the viewport trickery I'm struggling with. Amazingly with this sort of code messing with the vertices positioning is actually the easy part, I already know how I'm going to deal with that now.
- Edited
I have so many questions about this method that I'm actually going to have to do a write up but I think I'm getting something of a breakthrough with it thanks to the video I found on the 2D version of it. At the moment though, I've got some errors on it and I don't know why. I also have no idea where I'm supposed to be placing the quad mesh with the viewport texture on it in order for the multiply blend to work on the shader.
I'll be coming back to this thread when I've had more time to think on the specifics.
E 0:00:01.245 get_path: Cannot get path of node as it is not in a scene tree.
<C++ Error> Condition "!is_inside_tree()" is true. Returned: NodePath()
<C++ Source> scene/main/node.cpp:1587 @ get_path()E 0:00:01.247 get_node: (Node not found: "ViewportContainer/Viewport" (relative to "").)
<C++ Error> Condition "!node" is true. Returned: nullptr
<C++ Source> scene/main/node.cpp:1325 @ get_node()E 0:00:01.248 setup_local_to_scene: ViewportTexture: Path to node is invalid.
<C++ Error> Condition "!vpn" is true.
<C++ Source> scene/main/viewport.cpp:69 @ setup_local_to_scene()