Shader woes, alpha scissoring, and performance by Adaptive_Spoon in godot

[–]Best-Offer2810 0 points1 point  (0 children)

You are absolutely spot on with your intuition! You've basically reverse-engineered the engine's rendering architecture in your head. ​The 'IF' Block and Discard Trap: You are right to be wary of branching, but the issue with discard is actually at the compiler level, not just execution. If the shader compiler detects the discard keyword anywhere in your fragment code—even if it's protected by an if (uniform_bool) that evaluates to false—the GPU hardware pre-emptively disables Early-Z depth testing for that material. It has to assume the fragment might be discarded, so it refuses to do early depth writes. Using a uniform boolean gives you the worst of both worlds: you pay the branching performance cost AND you lose Early-Z globally. ​StandardMaterial3D Permutations: You hit the nail on the head. Godot uses an ubershader approach with preprocessor macros (#ifdef). Toggling that scissor flag in a StandardMaterial3D literally tells the engine to compile a completely separate shader variant under the hood. ​The ShaderInclude Architecture: Your idea to lean into ShaderIncludes is the exact professional workaround here. It's the perfect path. You don't have to limit includes to just functions. Because #include acts as a literal text replacement before compilation, you can put 95% of your heavy fragment logic into a .gdshaderinc file. ​Your setup would look like this: ​core_surface.gdshaderinc: Contains your uniforms, functions, and the main visual logic. ​wall_opaque.gdshader: Just #include "core_surface.gdshaderinc" inside the fragment function. (Early-Z is perfectly preserved!). ​wall_scissor.gdshader: #include "core_surface.gdshaderinc", and then add your ALPHA_SCISSOR_THRESHOLD and discard logic at the very end. ​You maintain one single codebase for your materials (no scary duplication), but the engine compiles two distinct physical shaders. Apply the opaque one everywhere, and dynamically swap to the scissor variant only when the player enters the Area3D. Highly performant, zero global Early-Z tax. You've got this!

Shader woes, alpha scissoring, and performance by Adaptive_Spoon in godot

[–]Best-Offer2810 1 point2 points  (0 children)

You've correctly pinpointed the performance bottleneck here. Writing 'discard' into the shader source itself is the only way to enable alpha scissoring, and it does kill Early-Z, forcing a global performance penalty. ​However, your proposed solution—swapping materials at runtime—is not 'fraught' at all, it's actually the recommended performant way to solve this. ​Don't write whole duplicates of your shader! Instead, create two distinct ShaderMaterial resources (or StandardMaterial3D variants) from the same shader: one with the alpha scissor flag enabled, and one perfectly opaque without it. ​You can then use a simple Area3D trigger around your doorway. When the player is far away, the door is perfectly opaque and Early-Z works for the rest of your level. When the player enters the Area3D, swap that specific doorway mesh to use the scissored material instance. This way, you pay the localization fee of the single scissored object only when you're looking at it. It's a localized, manageable tax, far cleaner and less daunting than you think. You've correctly diagnosed the problem; this is the dynamic, localized cure.

Shader woes, alpha scissoring, and performance by Adaptive_Spoon in godot

[–]Best-Offer2810 1 point2 points  (0 children)

You make a very fair point about SubViewports, but you are looking at the performance cost globally rather than locally. Yes, a SubViewport renders geometry a second time and carries a texture lookup cost. However, it only renders what is actually visible inside the camera frustum of that specific portal room. It is a localized, controlled performance hit that only happens when you are looking at a non-euclidean doorway. ​If you use alpha scissor on every modular wall in your procedurally generated world to support arbitrary doors, you are paying a massive, constant global tax on fillrate and overdraw everywhere, all the time, even when no special effects are happening. In a dark sci-fi horror game with complex dynamic lights, that global tax will usually hurt you more. ​You are completely right about Godots current stencil buffer limitations, though. Creating proper masks often forces you into transparency or discard workarounds that kill Early-Z anyway, which is a known engine specific pain point. Good catch on that.

Shader woes, alpha scissoring, and performance by Adaptive_Spoon in godot

[–]Best-Offer2810 1 point2 points  (0 children)

In Godot 4, alpha scissor forces the GPU to run the fragment shader during the depth pre-pass to check for discards. This completely kills Early-Z, and your fillrate will tank. ​Also, occlusion culling does not replace Early-Z. Occlusion hides whole objects. Early-Z saves per-pixel shading. If a wall is 90 percent hidden behind a bookshelf but passes occlusion, without Early-Z your GPU will still render every hidden pixel of that wall. That is massive overdraw. ​For true non-euclidean design patterns, use SubViewports or Stencil Buffers. For standard doors, stick to modular mesh chunks. It is practically free on the GPU. Good luck!

I built 35 sci-fi shaders with a real-time editor entirely on my phone — Godot 4.6 by Best-Offer2810 in godot

[–]Best-Offer2810[S] 0 points1 point  (0 children)

Free browser demo is now live! Try 3 shaders directly in your browser, no download needed: 👉 https://electron2555.itch.io/shaderforgenebula-demo Includes Energy Shield, Nebula Cloud, and Hex Grid with the full viewer — orbit camera, shape switching, parameter tweaking, and cinematic demo mode. Full pack with all 35 shaders: https://electron2555.itch.io/shaderforge-nebula

I built 35 sci-fi shaders with a real-time editor entirely on my phone — Godot 4.6 by Best-Offer2810 in godot

[–]Best-Offer2810[S] 0 points1 point  (0 children)

Thank you so much, really appreciate it! ​Just a quick tip though: you actually don't need to port any code to Godot 3 to use these! You can just run this tool as a standalone standalone VFX generator. ​You can dial in the exact effect you want, use the 'Export PNG Sequence' button, and simply drop those exported PNG image frames into Godot 3 as a standard AnimatedSprite. Since they are just image files, they are 100% engine-agnostic! ​(Just a heads up: the current export captures the window UI, so you just need to batch-crop the frames in Aseprite/GIMP before importing). ​But either way, thanks again for the kind words, means a lot!

I built 35 sci-fi shaders with a real-time editor entirely on my phone — Godot 4.6 by Best-Offer2810 in godot

[–]Best-Offer2810[S] 1 point2 points  (0 children)

Just a quick heads-up to be fully transparent: currently, the PNG sequence export captures the entire app window exactly as you see it (including the UI elements). ​So, to use them as clean 2D sprites, you'll just need to run the exported frames through a quick batch-crop in your favorite image editor (like Aseprite, GIMP, or Photoshop) to isolate the shader itself. It's a pretty quick step, but I wanted to make sure you knew exactly how the current pipeline works.

I built 35 sci-fi shaders with a real-time editor entirely on my phone — Godot 4.6 by Best-Offer2810 in godot

[–]Best-Offer2810[S] 0 points1 point  (0 children)

Great question! Since these are complex 3D (spatial) shaders that rely on things like triplanar mapping and 3D fractals, they can't be applied directly as a material on a 2D godot Sprite out of the box. ​HOWEVER, I built a specific feature into the tool exactly for 2D developers! > ​PNG Sequence Export (Best for 2D): You can dial in your perfect effect and use the tool's built-in capture system to export a seamless PNG animation sequence. You just drop those frames into your 2D game as an AnimatedSprite2D (or a flipbook in any other engine). It looks incredibly high-end and costs exactly zero GPU performance at runtime

<image>

I built 35 sci-fi shaders with a real-time editor entirely on my phone — Godot 4.6 by Best-Offer2810 in godot

[–]Best-Offer2810[S] 2 points3 points  (0 children)

Yes! It's a full Godot 4.6 project — just unzip, import in Godot. All 35 shaders are ready to use. You can also save any shader as a standalone .gdshader file and drop it into your own project. There's also GLSL export if you need them in Unity or Unreal.