Texture splatting more than 5 textures - opengl

I'm looking for a solution to store more than 5 diffrent texture in one chunk of a terrain and I've found part of this solution only. I have to pack all my textures (512x512) into one big texture (8192x8192) then in shader I have to calculate texture id using r,g,b values from splat texture. But how to calculate this id from r,g,b values. What is a formula?

So you got map (left texture/image) where ID's of surface type is stored per terrain segment. Than you have texture atlas (right texture) with 16x16=256 textures and want to produce combined texel for terrain coloring (bottom image).
map texture
You did not specify its format and encoding but I suspect that is what you ask for. So you got 256 textures meaning you need 8bit per each ID. If you use 32bit RGBA integer format you can store up to 4 ID's.
encoding/decoding
If you need more then that use either more map textures or lover the number of textures in atlas. For example for 5 IDs you have floor(32/5)=6 bits/ID so you would need to have max 64 textures in your atlas (but still got 2 spare bits left so one ID could still use 256 or two ID's could use 128 textures.)
Of coarse for all this you need integer sampling for map texture so just in case see:
Precise control over texture bits in GLSL
instead of floating r,g,b values. The math equation on integers for 5 IDs GPU decoding from r8g8b8a8 format could look like this (untested just illustrative):
uniform usampler2D tex;
uvec4 a=texture(tex, ...); // fetch 4x8 bits
uint b=0; // construct 32 bit int
b|=uint(a.r);
b|=uint(a.g)<<8;
b|=uint(a.b)<<16;
b|=uint(a.a)<<32;
uint ID1=(b )&0x3F; // decode ID's
uint ID2=(b>> 6)&0x3F;
uint ID3=(b>>12)&0x3F;
uint ID4=(b>>18)&0x3F;
uint ID5=(b>>24)&0x3F;
And similarly CPU encoding:
uint a=0; // construct 32 bit int
a|=(ID1&0x3F);
a|=(ID2&0x3F)<< 6;
a|=(ID3&0x3F)<<12;
a|=(ID4&0x3F)<<18;
a|=(ID5&0x3F)<<24;
map_texture_data[...]=a;
The codes are not tested so there may by reverse RGBA order or wrongly assumed uint bit-widths (they should match the rem-ed values.

Related

"Interleaved rendering" in fragment shader

P.S. Yes, I posted this question on Computer Graphics Stack Exchange. But posting there also in hope more people will see
Intro
I'm trying to render multi-channel images (more than 4 channels, for the purposes of feeding it to a Neural Network). Since OpenGL doesn't support it natively, I have multiple 4-channel render buffers, into which I render a corresponding portion of channels.
For example, I need multi-channel image of size 512 x 512 x 16, in OpenGL I have 4 render buffers of size 512 x 512 x 4. Now the problem is that the Neural Network expects the data with strides 512 x 512 x 16, i.e. 16 values of channels of one pixel are followed by 16 values of channels from the next pixel. However currently I can efficiently read my 4 render buffers via 4 calls to glReadPixels, basically making the data having strides 4 x 512 x 512 x 4. Manual reordering of data on the client side will not suffice me as it's too slow.
Main question
I've got an idea to render to a single 4-channel render buffer of size 512*4 x 512 x 4, because stride-wise it's equivalent to 512 x 512 x 16, we just treat a combination of 4 pixels in a row as a single pixel of 16-channel output image. Let's call it an "interleaved rendering"
But this requires me to magically adjust my fragment shader, so that every group of consequent 4 fragments would have exactly the same interpolation of vertex attributes. Is there any way to do that?
This bad illustration with 1 render buffer of 1024 x 512 4-channel image, is an example of how it should be rendered. With that I can in 1 call glReadPixels extract the data with stride 512 x 512 x 8
EDIT: better pictures
What I have now (4 render buffers)
What I want to do natively in OpenGL (this image is done in Python offline)
But this requires me to magically adjust my fragment shader, so that every group of consequent 4 fragments would have exactly the same interpolation of vertex attributes.
No, it would require a bit more than that. You have to fundamentally change how rasterization works.
Rendering at 4x the width is rendering at 4x the width. That means stretching the resulting primitives, relative to a square area. But that's not the effect you want. You need the rasterizer to rasterize at the original resolution, then replicate the rasterization products.
That's not possible.
From the comments:
It just got to me, that I can try to get a 512 x 512 x 2 image of texture coordinates from vertex+fragment shaders, then stitch it with itself to make 4 times wider (thus we'll get the same interpolation) and from that form the final image
This is a good idea. You'll need to render whatever interpolated values you need to the original size texture, similar to how deferred rendering works. So it may be more than just 2 values. You could just store the gl_FragCoord.xy values, and then use them to compute whatever you need, but it's probably easier to store the interpolated values directly.
I would suggest doing a texelFetch when reading the texture, as you can specify exact integer texel coordinates. The integer coordinates you need can be computed from gl_FragCoord as follows:
ivec2 texCoords = ivec2(int(gl_FragCoord.x * 0.25f), int(gl_FragCoord.y));

OpenGL: How can I render a triangle mesh at 10bit (or 12bit or 16bit) channel depth color?

For a vision research experiment, I have a monitor that supports 10bit/channel (=30bit color). I want to render a triangle mesh in a simple scene that uses the full bit depth, and I want to save this rendering as a .png file. The rendering is just for single, static images, and doesn't need to happen lightning fast.
For the triangle mesh, I have:
List of vertices in xyz coordinates
List of triangles containing the indices of the vertices
List of the vertex normals
List of the triangle/face normals
My hardware includes (possibly irrelevant)
Dell UP3218K monitor - 8k and 10bits/channel
GeForce RTX 2080 Super (but can get a better one if needed)
I tried using the pyrender library, but it outputs the rendered scene as uint8 (limiting it to 8bit).
I can't find any code examples of OpenGL or PyOpenGL rendering meshes at 10bits or higher. With the increasing popularity of >8bit monitors, surely this is possible?
What can I use to render a mesh at 10 bit/channel depth?
Edit with more specific question:
I have a triangle mesh (points, vertices, normals). How can I render it (display unnecessary at this step) in a scene and save this rendering as a 10-bit depth .png file? Later, I would like to display this .png on a 10-bit monitor.
When you create a framebuffer object (FBO) you get to decide what kind of buffer you're rendering to. Most applications would use an GL_RGBA8 texture as the colour buffer, but you don't have to...
Here are a list of all the formats which your graphics driver is required to support. It may also support other ones which aren't on this list, but on this list are some formats that may be interesting to you:
GL_RGB10_A2 - 10 bits each for R/G/B, 2 bits for A - 32 bits per pixel
GL_RGBA16 - 16 bits each for R/G/B/A - 64 bits per pixel
GL_RGBA16F - 16 bits each for R/G/B/A - 64 bits per pixel - but they're floating-point numbers with with 1-bit sign, 5-bit exponent and 10-bit mantissa.

How to render super size texture in OpenGL

I use the API glGetIntegerv to gain the maximum size of viewport and texture. Both of them return 16K x 16K pixels.
Here is the codes:
GLint maxTexture,maxViewport;
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTexture);
glGetIntegerv(GL_MAX_VIEWPORT_DIMS, &maxViewport);
However I need a larger size of texture to show a series of images of high resolution. I tried to set the viewport size to 32K x 32K,and the program runs successfully. It seems that the maximum of viewport size I got from the API glGetIntegerv is not quite right.
But I can't set the texture size to a value larger than 16K x 16K.
Maybe I should try to create more than one texture unit and each of them has the size of 16K x 16K.
Someone presents a application called Manual Whole Slide Imaging.
here is the hyperlink:
http://www.microvisioneer.com/
It seems that the super size texture has realized in this application.
So is OpenGL the right tool to resolve my problem or is there any other solution?
UPDATE:Just now I found that when I set the viewport to 32K x 32K and no error occurred,but its true size is still 16K x 16K.
Consider having a RGBA8 type. In C it looks like this:
uint8_t rgba[4] = {0x00, 0x44, 0x66, 0xff};
But you could represent it also with:
uint32_t rgba = 0x004466ff;
Similarly for type RGBA32:
uint32_t rgba[4] = {0x00000000, 0x44000000, 0x66000000, 0xff000000};
// or
uint128_t _rgba = 0x000000004400000066000000ff000000; // assuming there is a uint128_t...
HOWEVER you are allowed to treat those bits any way you want. You could:
uint128_t rgba[4] = {tex1rgba, tex2rgba, tex3rgba, tex4rgba};
In above example you have put tex1 rgba8 data in the red channel of the
opengl internal rgba32 texture, tex2 in the green channel and so on. Assuming the textures are edge-by-edge in real life in a 2x2 manner a texcoord (0.1,0.1) would map the rgba8 value in the red channel at texcoord 2*(0.1, 0.1). Texcoord (0.6, 0.7) map to the rgba8 in the alpha channel at 2*(0.6-0.5, 0.7-0.5), assuming the texture in the alpha channel is located diagonally to the texture in the red channel.
I´ve used this technique myself in the opposite direction for making my application support 16-bit luminance (L16 or R16) on platforms only supporting 8-bit channels. Essentially I loaded a 16-bit single channel texture into a 8-bit dual channel texture, and split it in the shader as high/low-word.
Notice you can´t use any texture filtering such as GL_LINEAR while doing this. You have to filter yourself. In my experience this is not a very big problem in practice.

OpenGL color index in frag shader?

I have a large sprite library and I'd like to cut GPU memory requirements. Can I store textures on the gpu with only 1 byte per pixel and use that for an RGB color look up in a fragment shader? I see conflicting reports on the use of GL_R8.
I'd say this really depends on whether your hardware supports that texture format or not. How about skipping the whole issue by using a A8R8G8B8 texture instead? It would just be compressed, i.e. using a bit mask (or r/g/b/a members in glsl) to read "sub pixel" values. Like the first pixel is stored in alpha channel, second pixel in red channel, third pixel in green channel, etc.
You could even use this to store up to 4 layers in a single image (cutting max texture width/height); picking just one shouldn't be an issue.

What exactly is a floating point texture?

I tried reading the OpenGL ARB_texture_float spec, but I still cannot get it in my head..
And how is floating point data related to just normal 8-bit per channel RGBA or RGB data from an image that I am loading into a texture?
Here is a read a little bit here about it.
Basically floating point texture is a texture in which data is of floating point type :)
That is it is not clamped. So if you have 3.14f in your texture you will read the same value in the shader.
You may create them with different numbers of channels. Also you may crate 16 or 32 bit textures depending on the format. e.g.
// create 32bit 4 component texture, each component has type float
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, 16, 16, 0, GL_RGBA, GL_FLOAT, data);
where data could be like this:
float data[16][16];
for(int i=0;i<16*16;++i) data[i] = sin(i*M_PI/180.0f); // whatever
then in shader you can get exactly same (if you use FLOAT32 texture) value.
e.g.
uniform sampler2D myFloatTex;
float value = texture2D(myFloatTex, texcoord.xy);
If you were using 16bit format, say GL_RGBA16F, then whenever you read in shader you will have a convertion. So, to avoid this you may use half4 type:
half4 value = texture2D(my16BitTex, texcoord.xy);
So, basically, difference between the normalized 8bit and floating point texture is that in the first case your values will be brought to [0..1] range and clamped, whereas in latter you will receive your values as is ( except for 16<->32 conversion, see my example above).
Not that you'd probably want to use them with FBO as a render target, in this case you need to know that not all of the formats may be attached as a render target. E.g. you cannot attach Luminance and intensity formats.
Also not all hardware supports filtering of floating point textures, so you need to check it first for your case if you need it.
Hope this helps.
FP textures have a special designated range of internal formats (RGBA_16F,RGBA_32F,etc).
Regular textures store fixed-point data, so reading from them gives you [0,1] range values. Contrary, FP textures give you [-inf,+inf] range as a result (not necessarily with a higher precision).
In many cases (like HDR rendering) you can easily proceed without FP textures, just by transforming the values to fit in [0,1] range. But there are cases like deferred rendering when you may want to store, for example, world-space coordinate without caring about their range.