0
\$\begingroup\$

I need to be able to output individual bits from a fragment shader.

I need a lot of bits per fragment too, otherwise I will face a tremendous performance drag.

Let's say that we have a whole bunch of single-direction lights, let's say 128 of them.

Let's also say that we have already rendered their depth buffers and have a huge FBO

Well, from the player-camera's perspective, All we really need to know from the depth buffer is whether or not an individual screen pixel is in a shadow or not.

I want to do a mid-level pass in my algorithm (Trust me- I have a reason) for testing whether or not a screen pixel is in the shadow of a particular light

As in, all the pixels have boolean values for each light stored in them

The thing is, I don't want Numlights of these buffers.

I would like to pack as many lights into one of these "Boolean Buffers" as I call them as possible. I want to be memory efficient.

(NOTE: I've devised a way to mix this with the shadow map generation too, i'm really trying to reduce the amount of gpu memory my algorithm uses)

Booleans are either 1 or 0. Conveniently, computers store data as ones and zeros.

This includes the output of a fragment, which is four GLfloats.

I'm wondering if it would be possible to manipulate individual bits of a GLfloat, to the maximum bitdepth of a float.

This would allow me to run through all my depth buffers (32 buffers at a time, even) and calculate the cansee/cannotsee of all my lights, 32 at a time (OpenGL allows for 32 texture units at once)

My depth buffers are stored as textures.

My algorithm theoretically works, however ANY evaluation of the floats' values when they are being read from or written to GPU memory will cause my algorithm to fail (Although it wouldn't crash...).

E.g. if Not-a-Number floats get evaluated as all zeros in bits (rather than directly copying the bits regardless of evaluation) then my boolean buffer will fail!

My algorithm also fails if there is any sort of clamping or estimation applied to the floats.

My questions are then as such:

  1. Are there any float values (in bits) that will be evaluated and copied incorrectly to/from GPU memory when a fragment is rendered or the texture2d call in glsl reads it?
  2. Is there any clamping or evaluation or estimation done on the output floats of a fragment shader? What are they?
  3. Is there a better way of outputting as many bits as possible from a fragment shader, that wouldn't have any weirdnesses? (E.g. changing the rendering mode to doubles or ints or something)

but... I would also like to tell you...

    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    In case you're curious what I'm using it for...
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

I'm developing a brand-new low-memory-footprint and relatively computationally efficient light and shadow rendering technique for rendering thousands of lights (my current plan is 128 per middle-pass FBO, as the bitdepth of a GLfloat is 32 and there are 4 floats output by the fragment shader... RGBA) which uses shadow mapping but I need to use middle-pass buffers filled with boolean data for whether or not a shadow can "see" a light. Using this along with of course all the light locations in the scene I can generate the lighting buffer (Stores the net color change of fragments due to lighting) using relatively few passes (For unidirectional lights, the number of passes to generate the lighting color buffer would be equivalent to 1 + Numlights(1 per light)(NumShadowmaps) + Numlights/128/32 (rounded up, next highest int)(Number of Middlepass BooleanBuffers) + Numlights/128/32/31 (rounded up, next highest int)(Number of Cumulative light color change buffer passes, 31 b/c we need to pass in the old cumulative buffer alongside all those boolean buffers)

if I was able to find someway to store more than 128 bits per fragment, say 256, then I could store even more lights! 1+Numlights + Numlights/256/32 + Numlights/256/32/31! SO MANY LIGHTS, SO FEW PASSES, SO LITTLE MEMORY! :D

The pass to generate the lighting color buffer of course gets a couple vector operations more complicated with every new light, but since the shader for my lighting color buffer automatically checks all its uniform variables to see if they are valid and only calculates what it needs to, and since I seperated out the shadowmapping process, it is not a huge impact. An additional shadowmap is usually the only additional complexity, with only shadowmaps that increase the number of middlepasses (multiples of 32) or cumulative color change passes (multiples of 128 times 31). Cumulative color change passes are additive, so there is no recursive effect or anything. We're not getting an infinite series of 1+1/32+1/(128*31)+...

if I ever manage to make it a reality, i'll see what I can do about uploading an open-source demo. I've been studying shadow algorithms for a couple weeks and very few algorithms seem to solve this apparent light-number-limit and computational complexity problem as efficiently as I do.

My solution becomes even more efficient with more unit textures allowed.

If I bothered to write much larger textures made up of a lot of smaller textures (I've heard this called "texels" but I hate the name), it could be even more efficient but I'm somewhat new to OpenGL and I don't feel like coding a huge complicated texel mess right now. it would actually decrease performance for a small number of lights too, and make the overhead for rendering the final pass larger.

\$\endgroup\$
  • 1
    \$\begingroup\$ "Texels" are individual pixels in a texture. Large textures that are made up of smaller textures are "texture atlases". \$\endgroup\$ – Trevor Powell Jan 13 '18 at 0:56
  • \$\begingroup\$ I fail to understand your algorithm. You want to determine whether a screen pixel is visible to a light. But how do you intend to make that determination? How do you intend to populate this texture? If you're doing shadow mapping, then for each light, you have to render a shadow map, right? And then you have to sample from each light's shadow map, for each pixel, to determine if that pixel is within that light's shadow. That's a lot of shadow maps that have to be preserved, and that's a lot of texture sampling to convert it into a bit-map. \$\endgroup\$ – Nicol Bolas Jan 13 '18 at 1:21
  • \$\begingroup\$ I think the idea here is that if you have a mostly static scene, you don't need to recalculate the shadow maps and sample from them all the time; you could just calculate (shadowed/not-shadowed) once and store the result into a temporary, intermediate texture. So further rendering could then be done without updating the shadow maps or sampling from them at all; just sample from the buffer of shadowmap lookup results you pre-calculated. You could even throw away the shadow maps. Of course, if anything moves (including the camera), you kind of have to recalculate everything again. \$\endgroup\$ – Trevor Powell Jan 13 '18 at 1:48
  • \$\begingroup\$ Also, (if I've understood the intent correctly), this approach is not going to support transparencies at all. \$\endgroup\$ – Trevor Powell Jan 13 '18 at 1:50
1
\$\begingroup\$

3) Is there a better way of outputting as many bits as possible from a fragment shader, that wouldn't have any weirdnesses? (E.g. changing the rendering mode to doubles or ints or something)

If you're using at least OpenGL 3.0, you can just use integer buffers and bypass the complexity of floats entirely. Just bind a buffer of type GL_RGBA32UI to GL_COLOR_ATTACHMENT0, and do something like this in your shader:

#version 300
layout(location = 0) out uvec4  frag_color_uint_output;

void main(void)
{
    uint myBits = whatever; // 32 bits of data.
    uint myBits2 = whatever; // 32 bits of data.
    uint myBits3 = whatever; // 32 bits of data.
    uint myBits4 = whatever; // 32 bits of data.
    frag_color_uint_output = uvec4( myBits, myBits2, myBits3, myBits4 );
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.