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

Implement #extends "path" #9622

Closed
RadiantUwU opened this issue Apr 28, 2024 · 5 comments
Closed

Implement #extends "path" #9622

RadiantUwU opened this issue Apr 28, 2024 · 5 comments

Comments

@RadiantUwU
Copy link

Describe the project you are working on

Sci-fi game

Describe the problem or limitation you are having in your project

Trying to overlay multiple shaders on top of one another without having to re-draw the same object.

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

This would work by creating a new preprocessor instruction named #extends "shader.gdshader".

This would allow overlaying multiple shaders on top of one another.

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

To overlay, we just copy the code from the old shader, add on top of everything defined by it (unless overridden or referenced by the extended shader), and do the same with the vertex, fragment and light functions.

Overlaying them is not a single task, this feature proposal says:

  • #extend works a bit similar with #include, but allows overriding definitions, creating a very extensible shader system.
  • discard in the base shader causes a discard in the extended shader.
  • The extended shader in vertex, fragment and light run after the ones inside the base shader.
  • Anything not overriden/referenced by the extended shader (constants, variables, structures, functions) will be renamed.
  • Check multi-inheritance addenum for more details about this
  • Overrides must be able to substitute in-line the definition of what is being overridden.
  • You can use OVERRIDE_BEFORE(what_you_override, whole_code_segment) to place something before the thing that's being overriden.
  • If overriding fails instead of just giving a warning it will full out give an error.

Under the hood during preprocessor when #extend is ran it will run the preprocessor on the base file with the same definitions from the extended file (similar to #7870 ), then get all of the definitions inside the base shader file. Once that criteria is met, it verifies for references or overrides. If it finds a reference, it will not rename it, if it finds an override, it verifies if the new definition can substitute the old one, and if so, it will remove the definition in the base file and introduce the new definition in the same place where the old definition should have been. If it cannot substitute, it gives a warning about having the same name and how it failed to substitute, and thinks that those 2 are not related. This will just rename the base references and definition and continue with no error.

Example:
base.gdshader

vec3 gimme_color() {
  return vec3(1.0,0.0,0.0);
}
float gimme_alpha() {
#ifdef ALPHA_GIVEN
  return 0.5;
#else
  return 0.0;
#endif
}

void vertex() {
  VERTEX+=NORMAL;
}

void fragment() {
  ALBEDO=gimme_color();
  ALPHA=gimme_alpha();
}

extended.gdshader

#define ALPHA_GIVEN

#extend "base.gdshader"

OVERRIDE_BEFORE(gimme_color, bool some_bool_function() {
  return true;
})
vec3 gimme_color() {
  if (some_bool_function()) {
    return vec3(0.0,1.0,0.0);
  }
  return vec3(0.0,0.0,1.0);
}

void fragment() {
  ALPHA=0.3;
}

The output from this:

#define ALPHA_GIVEN

bool some_bool_function() {
  return true;
}

vec3 gimme_color() {
  if (some_bool_function()) {
    return vec3(0.0,1.0,0.0);
  }
  return vec3(0.0,0.0,1.0);
}

float gimme_alpha() {
#ifdef ALPHA_GIVEN
  return 0.5;
#else
  return 0.0;
#endif
}

void vertex() {
  VERTEX+=NORMAL;
}

void fragment() {
  ALBEDO=gimme_color();
  ALPHA=gimme_alpha();
  ALPHA=0.3;
}

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

This could be worked around by having both the base and the extended shader use a shader include.

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

N/A

@AThousandShips
Copy link
Member

Shaders aren't classes, IMO this doesn't make sense as shader functions aren't inheriting anything, this would be extremely unusual and AFAIK isn't available in any other shading languages, do you have any practical examples of something else doing this? To me this seems like something extremely unorthodox and unintuitive

It also isn't inheritance, not properly, you don't call the parent method anywhere, so this means you can't control the order of composition, unlike GDScript for example with super.foo()

@RadiantUwU
Copy link
Author

Shaders aren't classes, IMO this doesn't make sense as shader functions aren't inheriting anything, this would be extremely unusual and AFAIK isn't available in any other shading languages, do you have any practical examples of something else doing this? To me this seems like something extremely unorthodox and unintuitive

It also isn't inheritance, not properly, you don't call the parent method anywhere, so this means you can't control the order of composition, unlike GDScript for example with super.foo()

Do you have any idea of a better way to do overlaying?

@AThousandShips
Copy link
Member

AThousandShips commented Apr 28, 2024

Do you have any idea of a better way to do overlaying?

That isn't really constructive or relevant, right? I'm pointing out things that I find are unusual and potentially confusing and hard to use in your suggestion, not claiming to have a better solution.

@RadiantUwU
Copy link
Author

To be honest, this instead implements an extendable system and does treat shaders as classes. I am also thinking of an alternative solution to overlay.

@Calinou
Copy link
Member

Calinou commented Apr 28, 2024

One of the goals of Godot's shader language is to be as close as possible to standard GLSL (with common preprocessor functions such as #include and #if). This is done so that you can port GLSL shaders you can find online to Godot's shader language quickly (and vice versa - if you need to reuse the shader outside Godot, there's less of a lock-in effect).

#extend doesn't exist in any preprocessor I know of, so this feature would be contrary to that goal.

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

4 participants