Shader Problems - GLSL sampler3d() - 5.1.9

Before I spend any more time bashing my head against a wall...

I'm trying to bind a texture to a sampler3D object and I'm assuming that I can't use al_set_shader_sampler() because that would bind a 2D sampler. (Or am I missing something?)

So I'm trying to bind it manually but having trouble sorting out how to get the program ID out of the ALLEGRO_SHADER so I can glGetUniformLocation() and manually bind.

Other than setting up the shader manually myself (which I can do, just wanted to try using allegros built-in stuff as much as possible this time around)

Whats the most graceful way to get the program ID of a shader? (I know this will be openGL specific, and I'll have to write a separate routine for DX but I'll get to that later.)

ALLEGRO_SHADER_GLSL_S does not appear to be exposed, so I can't really cast to that and get the program id out there. (Trying to define ALLEGRO_SHADER_GLSL_S and all its nested internal types seems really messy...)


Only reason I'm able to use 5.1.9 is because of Reynoldo sharing the binaries - I have only successfully built Allegro ONCE, adn it was on OSX and ... frankly... I don't have it in me to build Allegro from scratch just to add an al_set_shader_sampler3d() for myself... if thats whats necessary, then fine. But yeah... I'm lazy. I've got so much past trauma trying to build Allegro (and other libraries) that I'm paralyzed there ;)

RPG Hacker

No simple way, as far as I can tell. You could try the following, though:

1. Include the header "allegro5\internal\aintern_shader.h" so that you get the definition of ALLEGRO_SHADER.

2. Get your shader pointer and cast it to an unsigned char pointer, so that you can do simple byte-length pointer arithmetic.

3. Add sizeof(ALLEGRO_SHADER) to this pointer.

4. Add ((sizeof(GLuint) * 2) to this pointer

5. Cast this pointer to a GLuint pointer

If everything worked as expected, this GLuint pointer should now point to the memory holding the program ID.

Note that this might not work at all. Like, if ALLEGRO_SHADER_GLSL_S happens not to be a tightly packed struct, this method will fail. Even if it doesn't fail, note that this is a highly platform-specific solution, becaues different compilers might pack structs differently (even the same compiler could pack structs differently depending on whether you're doing a 32- or a 64-bit build). If you go with this method, make sure to thoroughly test the outcome on all platforms you're interested in. The foolproof version would be to either build a GL shader yourself or build the Allegro library, though.


This seems relevant: GLuint al_get_opengl_program_object(ALLEGRO_SHADER *shader)

I dont see any documentation, but it seems straight forward. It would be nice if the documentation mentioned it - it would have saved me a full day of head banging.

Also, the documentation isnt clear to me that when using al_use_shader(NULL), how that might affect subsequent calls. It seems that as soon as I al_use_shader(NULL), no subsequent calls to al_use_shader() seem to work? The program continues to use the default shader from that point on, despite being toggled between the shader I created and the default?

EDIT: aactually when I used al_use_shader() on any two shaders (even if I manually load a default shader) it doesn't work after the second call. Wtf am I doing wrong? I can see the screen blink with the correct shader for one draw call, but after I toggle to another shader it stops working (and seems to be using default only.)>:(>:(

EDIT #2: does al_set_target_bitmap() change the shader being used?!?!?!?!?!?

Mark Oates

Hey thebicnic,

Good news is you can do it, I've used essentially the exact same process you describe to add a GL_TEXTURE_CUBE_MAP / sampler_cube and extending it to sampler3d should be very similar. There are a few critical things:

1) You should upgrade to the latest version of Allegro 5.1.13. It has al_get_opengl_program_object() which was literally just added in this version. Here's the change log. Building and/or getting Allegro since 5.1.9 has been made quite a bit easier than Previous versions, across all platforms. Shader API has also improved.

2) Always reference the latest documentation from the official Allegro 5 repo. It's the most up to date. The manuals on are out of date - don't use them.

3) To add anything OpenGL specific to Allegro 5 you just need to #include <allegro5/allegro_opengl.h> (and link the opengl libraries to build).

4) Here is how I implemented adding GL_TEXTURE_CUBE_MAP and sampler_cube:
- cubemap.h
- cubemap.cpp
- setting the cube map uniform in the shader
- here's a vertex shader that uses the cubemap
- here's the corresponding fragment shader that uses the cubemap

thebignic said:

Also, the documentation isnt clear to me that when using al_use_shader(NULL), how that might affect subsequent calls.


EDIT #2: does al_set_target_bitmap() change the shader being used?!?!?!?!?!?

Shaders are relatively-ish new. Their behavior is a little undocumented and you will find some peculiarities, for example with al_set_target_bitmap() which stores/flushes/resets some state, including projection. That is, for example, each bitmap has its own projection transform. Also, when using al_draw_bitmap(), al_draw_prim(), and friends, Allegro binds the first texture TEXTURE0 to the ALLEGRO_BITMAP that you pass into those functions (or does nothing if you pass NULL).

Allegro could really benefit from a great shader tutorial since the exact bounds of these behaviors is still yet to be outlined in the docs. They're completely usable, however. :)

RPG Hacker

which was literally just added in this version

Welp, no wonder I didn't find it while scanning the headers of Allegro on my PC. That was actually my first idea, I thought "maybe the allegro_opengl.h header already has a getter for the program index", but I think I have version 5.1.10 on my system, so naturally, I didn't find anything like that. Feel stupid for my last, rather hacky, post now.


Ok, so I'm on 5.1.13 and I can get the program ID now.

Running into issues with texture3d() / glTexImage3D() now.

Does anyone have a working code snippet of generating/binding a 3D texture buffer? I can't imagine its all that different from 2D and the (very few) examples I've seen of glTexImage3D() appear to be very similar (to each other, and to what I have.)

Marks cube map is interesting and I don't see anything that is being done there that I'm not doing (albeit for texture3d) in my own code.

It seems like no matter what my data is (all bytes = 0 or all bytes = 255) I cant read anything sensible in the shader using texture3d() (ie: from vec(1,1,1) or vec(0,0,0) or vec (0.5,0.5,0.5) texture3d() is still returning color values like (0.1,0.9,0.1) when I've uploaded a buffer full of zeros?

I'm still grabbing glGetError after every single GL call and nothing is throwing an error with the shader compile/link or attaching the texture to a uniform at draw time.

I'm totally stumped. Day #2 .... ???

EDIT: Well.. moments after I posted this, of course, I discovered that I was passing a reference of a pointer to glTexImage3D() so.. yeah... don't do that.

... I still blame OpenGL for my basic C++ snafu :D

Mark Oates

What is a texture3d? Like, what would you use it for? Serious question.

Another thing to keep in mind that could be a bit strange is that Allegro texture coordinates are [0-bitmap_width], [0-bitmap_height]. This is different from the usual [0-1], [0-1] texture coordinates that you get with UV textures. Because of that, in the shader you might use al_use_tex_matrix and multiply the incoming texture coordinates by al_tex_matrix.

That may have an affect on the appearance of your texture3d. You could be getting textures that are only drawing in the top left pixel (0,0) :P I've done that before ;)

You can also create a custom vertex which may make more sense for 3D and textures. My vertex definition uses [0-1],[0-1] for texture coordinates and also includes a vertex normal. Here's what it looks like.

But that may or may not be related to your actual problem. :)

Mark said:

What is a texture3d? Like, what would you use it for? Serious question.

Color correction look-up table (LUT)

Other than that, it appears to be used for volumetric rendering (clouds, water?) and maybe voxel rendering but thats all way above my head!

Mark Oates
thebignic said:

Color correction look-up table (LUT)

I have no idea how that works. :P

It sounds cool, though. I'd be totally into hearing about your implementation. :)


Here's a brief overview of my current color correction lookup table solution. I just learned about the possibility of doing this last week, so I am by no means an expert.

I just thought I'd share cause there doesn't seem to be any reasonably complete examples anywhere, let alone any that use Allegro. While this may have a few pieces missing, I think all of the heavy lifting is documented... If I left something important out, let me know.

I'm no artist, but see attachments for examples of what a LUT can do to transform an image in real time, with no other processing. ("lut-natural" is the way the game draws without any color correction)

Also see attachments for a neutral LUT (RGBTable16x1.png) that you can use to build your own.

How it works...

To make a LUT, just draw your game as you normally would, screenshot, take it into Photoshop and paste a neutral LUT into a corner somewhere. Then modify the colours, saturation, hue, contrast, etc until the image looks how you want.
Then just take the LUT portion and save it as a new PNG which we'll load into the shader later.

This "look up table" basically contains every colour that you can draw, in a 3D cube, and corrected using the rules we used to modify the colours in Photoshop. When you draw a pixel with a shader, all the colour correction shader will do is use the source colour as a position in the LUT cube and see what the new colour should be based on the LUT image we loaded up. 16x16x16 colours is enough to do a decent linear interpolation for my needs but you may want to go to 32x32x32 if you're into that sort of thing.

The Code...

At program start, call loadShaders() to load the pixel/vertex shaders and build any LUTs that are specified in LUTList / LUTStrings[].

buildLut() will take an image (ie: the PNG we created above) and then load it into a GL_TEXTURE_3D array so that we can use Sampler3D in our shader.

We can use Allegro to build the shaders as long as we can get the OpenGL ID later with al_get_opengl_program_object() so it'll require the latest version of Allegro to do it that way.

We can't use Allegro to bind the 3D texture to the shader though, so EnableLUTDrawing()
allows us to do that.

To draw using a lookup table, just enable the LUT shader with a specified table ID
for example:

1 2al_set_target_bitmap(tempBitmap); 3 4[... draw your game using default shader ... ] 5 6al_set_target_bitmap(al_get_backbuffer(displayManager.display)); 7 8shaderManager.EnableLUTDrawing( LUT_ARENA01_DUSK ); 9 10al_draw_bitmap(tempBitmap, 0,0,0); 11 12al_flip_display();

PIXEL SHADER (Colour Correction)

1#ifdef GL_ES 2precision mediump float; 3#endif 4uniform sampler2D al_tex; 5uniform sampler3D ColorGradingLUT; 6 7uniform bool al_use_tex; 8varying vec4 varying_color; 9varying vec2 varying_texcoord; 10 11 12const float lutSize = 16.0; 13const vec3 scale = vec3((lutSize - 1.0) / lutSize); 14const vec3 offset = vec3(1.0 / (2.0 * lutSize)); 15 16void main() 17{ 18 if (al_use_tex) { 19 20 vec4 rawColor = varying_color * texture2D(al_tex, varying_texcoord); 21 vec4 lutSample = texture3D(ColorGradingLUT, scale * texture2D(al_tex, varying_texcoord).rgb + offset); 22 23 gl_FragColor.a = texture2D(al_tex, varying_texcoord).a; 24 if (gl_FragColor.a < 0.001) { 25 gl_FragColor.g=0; 26 gl_FragColor.r=0; 27 gl_FragColor.b=0; 28 gl_FragColor.a=0; 29 } 30 31 } else { 32 gl_FragColor = varying_color; 33 } 34}


1attribute vec4 al_pos; 2attribute vec4 al_color; 3attribute vec2 al_texcoord; 4uniform mat4 al_projview_matrix; 5uniform bool al_use_tex_matrix; 6uniform mat4 al_tex_matrix; 7varying vec4 varying_color; 8varying vec2 varying_texcoord; 9void main() 10{ 11 varying_color = al_color; 12 if (al_use_tex_matrix) { 13 vec4 uv = al_tex_matrix * vec4(al_texcoord, 0, 1); 14 varying_texcoord = vec2(uv.x, uv.y); 15 } 16 else 17 varying_texcoord = al_texcoord; 18 gl_Position = al_projview_matrix * al_pos; 19}

Shader Manager (partial)

1 2static const int MAX_LUTS = 3; 3enum LUTList { 4 LUT_01_DAY 5 ,LUT_01_DUSK 6 ,LUT_01_NIGHT 7}; 8 9///point to a single LUT image 10static const std::string LUTStrings[] = { 11 "LUT_01_day.png" 12 ,"LUT_01_dusk.png" 13 ,"LUT_01_evening.png" 14};

1 2 3 ALLEGRO_BITMAP *bmpLUT[MAX_LUTS]; 4 GLuint bmpLUT_GL[MAX_LUTS]; 5 6 ALLEGRO_SHADER *default_shader; 7 ALLEGRO_SHADER *LUT_shader; 8 GLuint LUT_shader_GL; 9 GLuint default_shader_GL; 10 11 12void ShaderManager::EnableLUTDrawing(int LUTID) { 13 14 al_use_shader(LUT_shader); 15 16 GLenum err; 17 18 glActiveTexture(GL_TEXTURE1); 19 err=glGetError(); if ( err != GL_NO_ERROR) std::cout << " glActiveTexture(); ERROR: " << err << "\n"; 20 21 GLint handle = glGetUniformLocation(LUT_shader_GL, "ColorGradingLUT"); 22 if (handle < 0) std::cout << " No uniform variable found -- handle: " << handle << "\n"; 23 err=glGetError(); if ( err != GL_NO_ERROR) std::cout << " glGetUniformLocation(); ERROR: " << err << "\n"; 24 25 GLuint texture = bmpLUT_GL[LUTID]; 26 27 glBindTexture(GL_TEXTURE_3D, texture); 28 err=glGetError(); if ( err != GL_NO_ERROR) std::cout << " glBindTexture(); ERROR: " << err << "\n"; 29 30 glUniform1i(handle, 1); 31 err=glGetError(); if ( err != GL_NO_ERROR) std::cout << " glUniform1i(); ERROR: " << err << "\n"; 32 33} 34 35///given a bitmap, build a GL_TEXTURE_3D object and bind it 36GLuint ShaderManager::buildLut(ALLEGRO_BITMAP * lutBMP) { 37 38 if (!lutBMP) return -1; 39 40 std::cout << " generating 3d lut image from bitmap...\n"; 41 42 ALLEGRO_LOCKED_REGION *bmpLock= al_lock_bitmap(lutBMP, ALLEGRO_PIXEL_FORMAT_ANY, ALLEGRO_LOCK_READONLY ); 43 44 GLuint glID; 45 46 GLenum err; 47 48 GLubyte * pixels; 49 pixels = new GLubyte [ 16 * 16 * 16 * 3 ]; ///LUT size is 16x16x16 and only contains RGB elements (no alpha) 50 long i=0; 51 for (int z = 0; z <16; z++) { 52 for (int y = 0; y < 16; y++) { 53 for (int x = 0; x < 16; x++) { 54 ///neutral 55 //pixels[i + 0] = util.unlerp(0,15,z) * 255; // blue 56 //pixels[i + 1] = util.unlerp(0,15,y) * 255; // green 57 //pixels[i + 2] = util.unlerp(0,15,x) * 255; // red 58 59 ///sample bitmap 60 int x2d = x + (z * 16); 61 int y2d = y; 62 ALLEGRO_COLOR c = al_get_pixel(lutBMP, x2d, y2d); 63 pixels[i + 2] = c.b * 255; // blue 64 pixels[i + 1] = c.g * 255; // green 65 pixels[i + 0] = c.r * 255; // red 66 67 i=i+3; 68 } 69 } 70 } 71 al_unlock_bitmap(lutBMP); 72 73 glEnable(GL_TEXTURE_3D); 74 err=glGetError(); if ( err != GL_NO_ERROR) std::cout << " glEnable(); ERROR: " << err<< "\n"; 75 76 glActiveTexture(GL_TEXTURE1); 77 err=glGetError(); if ( err != GL_NO_ERROR) std::cout << " glActiveTexture(); ERROR: " << err << "\n"; 78 79 glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 80 err=glGetError(); if ( err != GL_NO_ERROR) std::cout << " glPixelStorei(); ERROR: " << err << "\n"; 81 82 glGenTextures(1, &glID); 83 err=glGetError(); if ( err != GL_NO_ERROR) std::cout << " glGenTextures(); ERROR: " << err << "\n"; 84 85 glBindTexture(GL_TEXTURE_3D, glID); 86 err=glGetError(); if ( err != GL_NO_ERROR) std::cout << " glBindTexture(); ERROR: " << err << "\n"; 87 88 glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); ///required for trilinear filtering which we NEED 89 glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 90 //glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_REPEAT); 91 //glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_REPEAT); 92 //glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_REPEAT); 93 94 err=glGetError(); if ( err != GL_NO_ERROR) std::cout << " glTexParameteri(); ERROR: " << err << "\n"; 95 96 glTexImage3D(GL_TEXTURE_3D, 0, GL_RGB8, 16, 16, 16, 0, GL_RGB,GL_UNSIGNED_BYTE, pixels); 97 err=glGetError(); if ( err != GL_NO_ERROR) std::cout << " glTexImage3D(); ERROR: " << err << "\n"; 98 99 glDisable(GL_TEXTURE_3D); 100 err=glGetError(); if ( err != GL_NO_ERROR) std::cout << " glDisable(); ERROR: " << err << "\n"; 101 102 glActiveTexture(GL_TEXTURE0); 103 err=glGetError(); if ( err != GL_NO_ERROR) std::cout << " glActiveTexture(); ERROR: " << err << "\n"; 104 105 106 107 delete [] pixels; 108 pixels = NULL; 109 110 std::cout << "glID=" << glID << "\n"; 111 112 return glID; 113 114} 115 116void ShaderManager::loadShaders() 117{ 118 119 std::cout << "\n\n\nloadShaders()\n"; 120 121 default_shader = al_loadShaderFromFile("PIXEL.txt", "VERTEX.txt"); 122 default_shader_GL = al_get_opengl_program_object(default_shader); 123 124 LUT_shader = al_loadShaderFromFile("PIXEL_COLOUR_CORRECT.txt", "VERTEX.txt"); 125 LUT_shader_GL = al_get_opengl_program_object(LUT_shader); 126 127 128 std::cout << " loading / building LUTS...\n"; 129 130 for (int i=0; i<MAX_LUTS; i++) { 131 bmpLUT[i] = al_load_bitmap( LUTStrings[i].c_str() ); 132 if(!bmpLUT[i]) 133 { 134 std::cout << "image file open error: " << LUTStrings[i] << "\n"; 135 return; 136 } 137 138 bmpLUT_GL[i] = buildLut( bmpLUT[i]); 139 if (bmpLUT_GL[i] <1) std::cout << "ERROR: " << bmpLUT[i] << " handle returned=" << bmpLUT_GL[i] << "\n"; 140 141 } 142 143 144 145 146 147} 148 149 150 151ALLEGRO_SHADER * ShaderManager::al_loadShaderFromFile(std::string fragment, std::string vertex){ 152 153 ALLEGRO_SHADER * shadey; 154 155 shadey = al_create_shader(ALLEGRO_SHADER_GLSL); 156 if (!shadey) { 157 std::cout << "Could not create shader.\n"; 158 } 159 160 if (!al_attach_shader_source_file(shadey, ALLEGRO_VERTEX_SHADER, vertex.c_str())) { 161 std::cout << "al_attach_shader_source_file failed:" << al_get_shader_log(shadey) << "\n"; 162 } 163 if (!al_attach_shader_source_file(shadey, ALLEGRO_PIXEL_SHADER, fragment.c_str())) { 164 std::cout << "al_attach_shader_source_file failed: " << al_get_shader_log(shadey) << "\n"; 165 } 166 167 if (!al_build_shader(shadey)) { 168 std::cout << "al_build_shader failed:" << al_get_shader_log(shadey) << "\n"; 169 } else { 170 std::cout << "shader build succeeded: " << al_get_shader_log(shadey) << "\n"; 171 } 172 173 std::cout << "gl program: " << al_get_opengl_program_object(shadey) << "\n"; 174 175 return shadey; 176 177}

Mark Oates

That's cool.

So it's like a giant lookup table that's values are interpolated, similar to how 2D texture's pixel values are interpolated.

The only thing I can relate "3D texture" to is like, procedurally generated textures like wood that have different values based on XYZ.


Thread #616043. Printed from