Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for shader templates to override built-in shader code #8366

Open
clayjohn opened this issue Nov 7, 2023 · 12 comments
Open

Add support for shader templates to override built-in shader code #8366

clayjohn opened this issue Nov 7, 2023 · 12 comments

Comments

@clayjohn
Copy link
Member

clayjohn commented Nov 7, 2023

Describe the project you are working on

Godot

Describe the problem or limitation you are having in your project

Users want more control over their shaders including:

  1. Drastically changing how lighting works
  2. Appending code before or after all GDshaders
  3. Modifying colors after lighting happens
  4. Hand-optimizing shaders when only basic functions are needed

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

Our proposed solution is shader templates. Shader templates are a resource that replace the Godot shader code that surrounds GDShaders with a custom shader. That way, users can sidestep our built in shader code entirely

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

The technical proposal was originally prepared by Juan. The below is a copy-paste from his technical proposal.

Currently, the python file that parses the glsl shaders bundles #include preprocessor directives into the main shader text. A change would need to be done so, for the main file, includes are still kept separate within ShaderRD and are only includes when a custom template is created.

The proposed API would be something like this:

in ShaderRD:

	RID custom_template_create();
	void custom_template_set_code(RID p_template, ...);
	void custom_template_set_compute_code(RID p_template, ...);
	void custom_template_free(RID p_custom_template);

// Then, these need to change (note the added last argument in bold):

	RID version_create();

	void version_set_code(RID p_version, const HashMap<String, String> &p_code, const String &p_uniforms, const String &p_vertex_globals, const String &p_fragment_globals, const Vector<String> &p_custom_defines, RID p_custom_template = RID());
	void version_set_compute_code(RID p_version, const HashMap<String, String> &p_code, const String &p_uniforms, const String &p_compute_globals, const Vector<String> &p_custom_defines, RID p_custom_template = RID());



// Finally on the higher level engine code, we will need the following resources:

class ShaderTemplate : public RefCounted

// Lets you add a shader template. Could be imported directly from a .glsl_template file. A tool to dump the current (built-in) shader template to a .glsl_template file would be quite useful in the filesystem dock.
// Then, we add to Shader class:

void set_template(const Ref<ShaderTemplate>& p_template);
Ref<ShaderTemplate> get_template() const;

// and respective property.

For the sake of usability, though, we should probably also add this to BaseMaterial3D, so users can set templates to their “fixed-like” materials.

Custom templates should be able to have custom includes too, so something like:

#include “res://path/to/someother_shader.glsl” 

Works. Keep in mind we already have .glsl supported for other use cases (import directly for SPIRV), so we may need to use a different extension for these, such as .template_glsl or something.

Additionally the main shaders we have should be moving most of their functionality to includes and only present a relatively simple template shader that users can edit to create custom template shaders.

This would need to be supported in the GLES3 renderer too.

Additional clarifications

To clarify, the idea is that we would move all functionality in the main shaders into includes that users can include in their shader templates. That way users can do anything in their templates that we can do in core and they don't have to rewrite all the built in rendering logic. Similarly all resources will be exposed to the user (textures, buffers, uniforms) through includes.

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

It can't be worked around without editing the source

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

It can't be an addon

Production edit: relates to godotengine/godot-roadmap#15

@reduz
Copy link
Member

reduz commented Nov 7, 2023

One thing here from my point of view, together with this, we should really reorganize a bit the base shaders, so the main shader code is much smaller than it is now and most work is moved to functions included from include files.

This way, it is less risky to change the base shader and break compatibility with future changes.

@BastiaanOlij
Copy link

One of the things I ran into when doing some preliminary work on this, is the ability to register the build in include files so you can reuse those, and to have "virtual" include files, i.e. include files generated by code.

A good example of this is around RenderSceneDataRD which right now is mirrored by servers/rendering/renderer_rd/shaders/scene_data_inc.glsl.
That include file should be embedded and made available, but even better is if the code for this include file can be generated by the RenderSceneDataRD class. As an example, this would make it possible to make MAX_VIEWS configurable with a project settings instead of hardcoded. But there are other things that can be improved here where there should be differences between mobile and forward+ renderer and possibly future renderers.

@Calinou Calinou changed the title Shader Templates Add support for shader templates to override built-in shader code Nov 7, 2023
@DxUr
Copy link

DxUr commented Nov 8, 2023

@reduz @Calinou @fire @clayjohn @BastiaanOlij
I'm working on this: #8355

@clayjohn
Copy link
Member Author

clayjohn commented Nov 8, 2023

@reduz @Calinou @fire @clayjohn @BastiaanOlij I'm working on this: #8355

That doesn't seem related to this proposal.

@DxUr
Copy link

DxUr commented Nov 8, 2023

@reduz @Calinou @fire @clayjohn @BastiaanOlij I'm working on this: #8355

That doesn't seem related to this proposal.

In fact it is, I think I didn't explain my idea very well.

@ODtian
Copy link

ODtian commented May 3, 2024

Will this function imply being able to modify the core shader in default render pipeline?

@clayjohn
Copy link
Member Author

clayjohn commented May 3, 2024

@ODtian Yes, that's exactly what this is

@clayjohn
Copy link
Member Author

Thinking about this more. I am starting to think that we should instead just expose more to GDShaders. In other words, start by doing the shader reorganization we would need to do for this, and then expose functions to do the things we need.

I.e. things like get_sky_specular(), get_sky_diffuse(), get_diffuse_gi() (reads the diffuse GI buffer), process_omni_lights(), process_direct_lights()

Then a shader could look like:

shader_type spatial;
render_mode unshaded;

void fragment() {
ALBEDO = ...
vec3 specular = get_sky_specular();
vec3 diffuse = get_sky_diffuse();

//Process all directional lights
process_directional_lights(ALBEDO, specular, diffuse, ...);

//Process all omni lights
process_omni_lights(ALBEDO, specular, diffuse, ...);

// Do some other processing, like a custom toon shader ramp or something

ALBEDO = specular + diffuse;
}

void light() {
 // Some custom lighting
}

This would give most of the benefit of shader templates, but it would be portable across backends and would continue to allow users to avoid boilerplate.

However, the downside of this approach is that it replaces SpatialMaterials instead of wrapping around them. Shader templates have the benefit that you can easily change all shaders in the game by specifying a new template. This would keep all your materials the same, while allowing you to control the final shader

@hayahane
Copy link

hayahane commented Jun 4, 2024

Will this proposal enable users to do custom shadow passes or we need to use something like the new compositor api to achieve that?

@mrjustaguy
Copy link

wouldn't just exposing more to GDShaders also have the drawback of not being able to override existing shaders vs shader templates originally proposed? (like changing the soft shadow algorithm, or scrapping half of Godot's shaders and just keep it ultra basic for performance)

@bertodelrio256
Copy link

bertodelrio256 commented Nov 14, 2024

Thinking about this more. I am starting to think that we should instead just expose more to GDShaders. In other words, start by doing the shader reorganization we would need to do for this, and then expose functions to do the things we need.

I.e. things like get_sky_specular(), get_sky_diffuse(), get_diffuse_gi() (reads the diffuse GI buffer), process_omni_lights(), process_direct_lights()

Then a shader could look like:

shader_type spatial;
render_mode unshaded;

void fragment() {
ALBEDO = ...
vec3 specular = get_sky_specular();
vec3 diffuse = get_sky_diffuse();

//Process all directional lights
process_directional_lights(ALBEDO, specular, diffuse, ...);

//Process all omni lights
process_omni_lights(ALBEDO, specular, diffuse, ...);

// Do some other processing, like a custom toon shader ramp or something

ALBEDO = specular + diffuse;
}

void light() {
 // Some custom lighting
}

This would give most of the benefit of shader templates, but it would be portable across backends and would continue to allow users to avoid boilerplate.

However, the downside of this approach is that it replaces SpatialMaterials instead of wrapping around them. Shader templates have the benefit that you can easily change all shaders in the game by specifying a new template. This would keep all your materials the same, while allowing you to control the final shader

the thing is.. we would need to be able to override things like "get_gi" "get reflections". for my project, I want to be able to change how reflections are applied to my shader, id want to reduce the saturation and increase the contrast. id want to give different processing for metals versus dielectric. also things that would most efficiently be done at the time of sampling the reflection probe, but allowing us to process the reflection color in the shader before being applied would also work.

@Steinbeuge
Copy link

Would having some kind of renderer component system be feasible?

Instead of fighting against the built in renderer, adding more and more things in, or exposing more things, the built in shaders are components that can be added (or removed) to the rendering server? (Not sure if it should be via code or project manager)

So a project stripped back to "clean" with nothing in it has, 1 specular component, 1 diffuse component, 1 lighting component. No shadows, no SSR/AO, glow, GI etc, no clearcoat, subsurface and so on. Only the rendering components added to the project would appear in an environment list and the materials list. Same for nodes in the visual graph editor, those components might be things like the depthtoworldspace and the like.

For example @bertodelrio256 could duplicate the default "reflections" component and edit the code, replacing the default reflections with their own modified version.

Users could create render components for things like Tonemaps, GI, reflections, DOF, diffuse and such. Once added to the project they appear in the lists, components register their inputs and outputs to the various shader passes and add setting in project (or a specific render component editor)

The idea would be, you can kind of have your cake and eat it too. Only include the things you need, to reduce bloat. Removing everything but the essentials leaves you with a simple single forward pass pipeline, perfect for XR (which could be enabled by adding a "stereo render" component). Users can choose to have a single shader single pass system for maximum performance, or use an "ubershader-like" system like we have now for flexibility. Unused features simply don't exist in the compiled project, making the shaders more lightweight

Most of all, this may help people who aren't as experienced in rendering management and allow plugin developers to make modular render components

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

9 participants