FyroxEngine / Fyrox

3D and 2D game engine written in Rust
https://fyrox.rs
MIT License
7.61k stars 340 forks source link

RectangleNode should allow custom fragment shaders. #426

Closed seivan closed 1 year ago

seivan commented 1 year ago

https://fyrox-book.github.io/fyrox/scene/rectangle.html#limitations

There's no reason for having that limitation. Hear me out... With some pseudo code...

Assume all Rectangles have these values in their fragment shaders

sampler2D u_texture
float u_time Uniform
vec2 v_tex_coord Uniform
vec4 v_color_mix; Varying
vec4 DefaultShading() | Function

And I want to add a custom fragment shader to my rectangle, but I don't want to reimplement the entire default fragment shader.

I should be able to just do this...

void main()
{
   gl_FragColor = DefaultShading();
}

To make it extra trippy, you could interpolate the default values into the custom shader at runtime, so the file doesn't even need to contain any of the aforementioned fields.

And it will behave as a default Rectangle for now.

Now why would you want that, if you're making your own fragment shader? Well, you wanna add something on top of default implementation without having to reimplement it.

Take this for instance

let spriteSize = vector_float2(Float(sprite.frame.size.width),
                               Float(sprite.frame.size.height))

let uniform = Uniform(fileNamed: "u_sprite_size", vectorFloat2: spriteSize)
let customShader = Shader(name: "nice.fsh", uniforms:[uniform])
rectangle.shader = customShader

That won't be batched, but anyone else having a reference to that custom Shader could potentially be batched together if they use the shader.

The engine could also interpolate vec2 u_sprite_size to the fragment shader as well, so you don't even need to define it again.

For per-rectangle, you could have something like


let attributeBasedShader = Shader(fileNamed: "UsingAttributes.fsh")
let attribute = ShaderAttribute{name: "a_sprite_size", type: .vectorFloat2}
attributeBasedShader.attributes = [
    attribute
]

rectangle.shader = attributeBasedShader

let spriteSize = vector_float2(Float(sprite.frame.size.width),
                               Float(sprite.frame.size.height))
rectangle.setShaderValue(value: spriteSize forAttribute: attribute)

This is the bases for Apples engine, and it actually allows for quicker feedback loop without abandoning the crutches.

Another advantage by having Shader, Uniform and Attribute instances, is that you could create them earlier, because they could potentially be heavy on WebGPU/Spirv/Shaderc to compile. By keeping them as separate objects, they can be preloaded.

seivan commented 1 year ago

Nevermind, I noticed you had a better thing implemented.