g3n / engine

Go 3D Game Engine (http://g3n.rocks)
https://discord.gg/NfaeVr8zDg
BSD 2-Clause "Simplified" License
2.8k stars 295 forks source link

[question] Custom shaders with many uniforms #66

Closed amyadzuki closed 6 years ago

amyadzuki commented 6 years ago

I feel like I'm missing something obvious here, but how do I make a material for a custom shader that needs lots of uniforms? I'm implementing my human skin shader, and it needs uniforms for the bones to implement skeletal animation, as well as some values specific to my use case, such as skin color in a vec3, etc. Sorry if I missed something obvious.

Thanks, Amy

P.S. Exposing a ModelMatrix uniform would grant access to the position in world space of the current fragment inside fragment shaders, which might be helpful for things like texture tiling or fixed-location objects, etc. I don't need it because in my one use case it just happens that the model matrix was constant, but I thought I should mention it.

amyadzuki commented 6 years ago

I may have found it. It looks like the magic happens with the call to gs.Uniform3fvUP inside RenderSetup. If I create my own material type which implements IMaterial, then I can buffer as many uniforms as I like by using repeated calls to gs.Uniform3fvUP inside my type's RenderSetup method. Right?

danaugrs commented 6 years ago

Right - you should create your own material type, and uniforms should be transferred inside a RenderSetup function. You can see some internal examples of uniform transfer calls in gui/panel.go, texture/texture2D.go and material/standard.go.

There are several gs.Uniform<*> functions, one for each data type and quantity - see here for the full supported list. You can use repeated calls inside RenderSetup and that would be a good way to start and probably fine to keep. However, cgo calls are very slow relative to normal Go functions and that slowness can quickly add up when those calls happen every frame. To make g3n as fast as possible, we try to minimize cgo calls per frame like uniform transfer calls. Therefore we often pack all uniforms into a single udata object and send it to the GPU with only a single transfer call gs.Uniform3fvUP or gs.Uniform4fvUP depending on which is more convenient to unpack inside the shader. You can see an example of unpacking inside a shader here: https://github.com/g3n/engine/blob/235d48ba1ac33f2992dcf66ba87e6c36858ae83f/renderer/shaders/physical_fragment.glsl#L47-L53 When transferring a udata Go object we need to use an unsafe.Pointer (that's why the "UP" in the function name) because we are assuming that the internal memory structure of udata is unchanged when going from Go to C.

amyadzuki commented 6 years ago

Thank you so so much! That helped a lot and I ended up getting it to work ^_^