17cupsofcoffee / tetra

🎮 A simple 2D game framework written in Rust
MIT License
910 stars 63 forks source link

Custom uniform sampler2D #189

Closed tatref closed 3 years ago

tatref commented 4 years ago

Summary: UniformValue is not implemented for texture-like values (2D maps).

The shaders are provided with a default u_texture sampler2D, but we can't add our own textures.

This is here, but the value 0 is hardcoded: https://github.com/17cupsofcoffee/tetra/blob/8d59084e3754bf757aaa59dc841a321f53c6f6b0/src/platform/device_gl.rs#L281

Can I bind my own textures? Why does this work with value 0?

Why is this needed? This could be used for normal lights (see https://github.com/mattdesl/lwjgl-basics/wiki/ShaderLesson6)

17cupsofcoffee commented 4 years ago

Some context on how sampler variables work (apologies if this is stuff you already know, just want to be clear!) - in OpenGL, you have several slots you can bind textures to, called 'texture units'. Setting a sampler2D uniform to a number is how you tell the shader which texture unit each variable corresponds to.

Tetra currently binds all textures to texture unit 0, so we always set u_texture to 0 as well. There'd be nothing to stop you adding another sampler2D to your texture and setting it to a different number, but there's no way in Tetra's API currently to actually bind a texture to the corresponding texture unit - that's the bit that's missing.

This is definitely something I'd like to add at some point, as there's lots of cool uses for binding multiple textures - I just need to figure out what the API would look like. Not sure how high/low level an interface Tetra should be exposing.

tatref commented 4 years ago

I don't know much about OpenGL, so thanks for the explanation ;-)

So if we want to add that that functionality, we'd need to change bind_textureto something like

fn bind_texture(&mut self, texture: Option<&RawTexture>, texture_unit: u32)

However, RawTexture is not public, and maybe texture unit 0 should be reserved?

I'll try to play a bit with this.

17cupsofcoffee commented 4 years ago

Yeah, that sounds about right. The state caching for texture bindings would also have to be rewritten, so that we don't end up rebinding more times than we need to (avoiding redundant GL calls is really important for performance).

My main concern is that I don't want any of this complexity to 'bleed out' into the rest of the framework. If someone's game doesn't make use of multiple texture bindings, they shouldn't have to care about the existence of texture units, etc. It's an advanced feature, so it should be opt-in, in my opinion.

I think I need to go and have a look at how other frameworks handle this kind of thing 😄

17cupsofcoffee commented 3 years ago

Finally did a bit more looking at how Love and Raylib handle this, and it's pretty much what I expected - texture unit 0 is reserved for the default texture, and setting a uniform to a texture actually just binds a texture unit behind the scenes. So I think this is doable, it'd just require a bit of a rework of some of the GL state caching.

17cupsofcoffee commented 3 years ago

I did a bit of playing around with making shaders 'magically' rebind textures, but it feels like a lot of added complexity for something not many people are going to use. I actually kinda think just adding a bind_texture function to tetra::graphics is probably all we need for now. I'll probably reserve 0 for use by Tetra, as I think it could get confusing if people set that and then it immediately unsets itself.

17cupsofcoffee commented 3 years ago

Never mind, found a nicer way of doing it - this functionality is now available on the main branch, and will be in the next version of Tetra 🎊