godotengine / godot-proposals

Godot Improvement Proposals (GIPs)
MIT License
1.15k stars 97 forks source link

Add inherited ShaderScript modifier materials #2575

Open KnightNine opened 3 years ago

KnightNine commented 3 years ago

Describe the project you are working on

I am working on a dynamic shader based roof cutting system for my levels that exposes the interiors of buildings,caves,etc... This is an old gif showing how it works: test

Describe the problem or limitation you are having in your project

In order for it to work i need to have all my mesh materials converted to ShaderMaterials that feature this script (in it's simplified form) and its corresponding variables:

//in fragment()
if (world_vertex.y > height_uniform){
        ALPHA =0.0;
        }

This is pretty tedious to implement across all my level's materials and within every level i decide to create. I would have to create a script that dynamically converts all materials to shadermaterials i.e. the script equivalent of this which i am not sure exists (let me know if it does, since i'll probably end up needing to automate this later anyways): convert so that i can save myself the time of going through every material to select that option after importing my map and preserve the utility of having the SpatialMaterial settings available. Then after such i would need to create a script that edits shaderscript to integrate the code above into all of my shaderscript resources.

Describe the feature / enhancement and how it helps to overcome the problem or limitation

An inherited "shaderscript modifier material" that edits the materials/shaders of child nodes without overriding them completely.

Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams

This "shaderscript modifier material" would only override what is defined within the shaderscript but otherwise use what is already defined within the shaderscript of the child material.

in this shaderscript mod, if i were to define:

//world_vertex_coords would be appended to the render_mode of the materials of all child nodes
//(or if it was something like depth_draw_alpha_prepass, it would override conflicting depth_draw parameters)
render_mode world_vertex_coords;
//this variable would be added to the materials of all child nodes 
varying float world_vertex;

void vertex() {
       //this line would be appended to the vertex() functions of the materials of all child nodes
    world_vertex = VERTEX;
}

you get the picture.

There should also be a next_pass entry for this shaderscript modifier material (following the same UI structure as a material) if there needs to be a distinct modifier script used on the next_pass materials of child nodes (next-next_pass and so on).

If this enhancement will not be used often, can it be worked around with a few lines of script?

this enhancement would be very useful since you could dynamically apply an effect to any material (or group of surface materials) without that material(s) needing to inherently have the effect already integrated within its script. This would greatly reduce the need for repeating shaderscript code due to this being more modular.

Is there a reason why this should be core and not an add-on in the asset library?

seems like it would be a general purpose feature in service of convenience.

Calinou commented 1 year ago

Unfortunately, implementing a system like this could easily lead to an explosion of shader combinations that need to be compiled and tracked. This will lead to lower performance without people knowing why it happens.

KnightNine commented 1 year ago

Unfortunately, implementing a system like this could easily lead to an explosion of shader combinations that need to be compiled and tracked. This will lead to lower performance without people knowing why it happens.

The compilation/modification of a single shaderscript containing node would only happen when a node containing a shaderscript enters or leaves the parent with the modifier, it doesn't sound that intensive to modify some script dynamically. though looking through the node directory every time a node is added or removed might be considered intensive if that's the source of your concern.

there's two ways of how to do this that come to mind: One would be for godot to store every node containing a modifier within a list, so that when a new node is added, all parent nodes within the newly added node's directory are checked against this list to find the relevant modifiers. this would become more intensive as more modifier nodes are added across the entire project.

Another would be to store the references to the relevant modifier parents within all nodes so all the newly added node needs to check is its immediate parent. this would cost some, probably negligible, amount of ram, and it would be the least process intensive option. i.e. if node_a has a modifier, any node added to it will add node_a to its "modifier list", if a node is added to a node with a modifier list, it will duplicate that list, and so on... so that all nodes contain references to their relevant modifiers. (this has the downside of being more process intensive if you're constantly adding and removing modifiers, since you would have to alter the "modifier list" data contained within all children nodes).

Calinou commented 1 year ago

it doesn't sound that intensive to modify some script dynamically

The problem isn't that the shader source has to be modified. The problem is that every shader variant has to be compiled on the CPU by the graphics driver's shader compiler, which can cause stuttering if not cached. Even if cached, you will still have more active shader variants (leading to a higher number of "shader changes" as the scene is rendered). This has a GPU overhead as the GPU needs to keep track of more shaders, both in overall GPU utilization and memory usage.

KnightNine commented 1 year ago

Even if cached, you will still have more active shader variants (leading to a higher number of "shader changes" as the scene is rendered).

Assuming you do employ caching, Can't the unmodified shaders be disposed of when not in use within the scene? If so, I am unsure of how would that be different from/more intensive than having two or three shaders with the difference of modifications between them that are applied and removed via code when needed?

Though at the very least, say if this wasn't dynamic at all and changes would only take effect within the editor, where all shaderscripts have a "merge_with" property that links to the shader modifier, that would cut down drastically on repeat-code for multiple different shaders that require the same effect.

Zireael07 commented 1 year ago

How do you imagine the editor would know which shaders are needed in the scene?

KnightNine commented 1 year ago

How do you imagine the editor would know which shaders are needed in the scene?

By tracking the shaders of nodes that enter and leave the scene?

Though in the case of dynamic modifiers, the modifier would need to "filter" the shader before adding it to the scene, as to not also load the unused unmodified shader I guess.