tobbebex / GPipe-Core

Core library of new GPipe, encapsulating OpenGl and providing a type safe minimal library
158 stars 30 forks source link

Can't find a way to loop over uniforms #43

Closed raymoo closed 7 years ago

raymoo commented 7 years ago

Hi, I'm looking for a way to loop through a list of uniforms (for example, light sources) in the shader, but I can't find one. I can't use while to do the looping because getUniform operates in a Shader monad.

Is there some way that I can do this?

EDIT: To clarify, by "list of uniforms" I mean a bunch of stuff in a uniform buffer.

raymoo commented 7 years ago

It seems like this is implemented by GLSL compilers as loop unrolling: https://stackoverflow.com/questions/19868781/glsl-indexing-into-uniform-array-with-variable-length

So I guess it would make sense that this isn't possible. I will need to write instances for vector of 100 uniforms I guess.

tobbebex commented 7 years ago

Hi! No unfortunately that is not currently supported in GPipe.

The workaround is to use 1d textures instead. You would then have to represent your light data as primitive types, but you should be able to abstract away the sample functions and return a data type of your choice instead.

raymoo commented 7 years ago

@tobbebex: That sounds like a good workaround, I'll try it out.

EDIT: Actually I think I will just read in a static number of uniforms and use that (using ifThenElse and friends to ignore the ones that aren't filled). My uniform includes light space projection matrices and would require lots of samples per iteration. I think this will be similar to how vanilla GLSL does looping over a uniform buffer.

raymoo commented 7 years ago

I tried doing what I said I would do in the edit, and for large numbers I get an error from declaring too many uniform blocks. It looks like each access to the uniform buffer gets compiled to a separate uniform block rather than the uniform being declared as an array and accesses being made through that. Is that right? Is there something that is in the way of making it use arrays? (I don't know much GLSL)

Maybe this is why you suggested using textures.

tobbebex commented 7 years ago

I would suggest looking up deferred shading. It s a common solution to the complexity problem of multiple lights.

raymoo commented 7 years ago

I will consider it, but my project deadline is soon and am okay with the limitation of only a few lights if I can get more things implemented. I'm also worried about increasing the number of draw calls too much.

I'm guessing I would render the scene once for each light and add the colors together in a final pass by summing samples from an array texture?

tobbebex commented 7 years ago

In deferred shading you render geometry like normal, but instead of writing a lit color to the frame buffer you write positions, normals and material parameters (like surface color, roughness, etc) to a g-buffer. You then render every light using this g-buffer and adds the results. Just google it.

I guess you could write a UniformInput instance for lists, hardcoding it to read in 100 elements (serialize the length first, then "take 100" of the list, padded with zeroes if shorter than 100)...

raymoo commented 7 years ago

I didn't end up deferring lighting calculations, though I did have to write out some geometry info because I wanted to implement SSAO. Next time I need to do something like this I'll take the lists as uniform inputs approach. If I had more time on my project I would have just switched completely to deferred rendering.

What I didn't really get while I was having problems was that GPipe uniforms are associated with GLSL uniform blocks, rather than GLSL uniforms. I think it would help if the documentation indicated that uniform collections meant to all be available at once should be grouped into one element of a uniform buffer rather than spread across one individually.

tobbebex commented 7 years ago

Please make a pull request for that documentation change if you like. I think the types expresses it already: getUniform gets the index in the uniform buffer from the shader environment as an Int (and not from the actual shader as an S a Int)

raymoo commented 7 years ago

In OpenGL 3.3 you can only refer to uniform arrays with static indices, so if you want to index one in a loop you have to either unroll it or do n if statements for each possible value of the loop counter. I think that sometimes the GLSL compiler will unroll loops for you. I thought that the Int type was expressing this restriction (it is the same restriction, but uniform arrays have it too).