Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GLSL sampler2D in struct

Tags:

opengl

glsl

In GLSL there seems to be linking error of shaders when I try to pass a uniform struct with a sampler2D attribute to a function which is forward declared. The code works if I remove forward declaration and move the function above main. Is this illegal code?

#version 330 core

in vec2 texcoords;
out vec4 color;

struct Material{
    sampler2D tex; // Sampler inside a struct
}; 

uniform Material material;

// Forward Declaration
vec4 add(Material m);

void main() {
    color = add(material);
}

// Function Definition
vec4 add(Material m) {
    return vec4(texture(m.tex, texcoords));
}

// C++
glUniform1f(glGetUniformLocation(shader, "material.tex"), 0);
glBindTexture(GL_TEXTURE_2D, texture);

EDIT: So after a bit of searching it appears to be a bug in AMD's driver. I personally use ATI Mobility Radeon HD 4670 which is pretty old, but it still runs OpenGL 3.3. On AMD's forums I found a similar post, and so it would be interesting to know how big this is on AMD's graphic cards. Because if you're developing on Intel or NVidia, then how are you to know your shaders won't compile on some AMD's graphic cards? Should we stay safe and not use prototypes on structs with samplers, or even go as far as not putting samplers in struct completely?... It's also worth noting that WebGL doesn't even allow for samplers inside structs.

Error Message:

Vertex shader(s) failed to link, fragment shader(s) failed to link.
unexpected error.
unexpected error.
like image 293
Iggy Avatar asked Aug 24 '14 17:08

Iggy


1 Answers

This actually is not supposed to work because you cannot instantiate a struct that contains an opaque data type (sampler, image, atomic counter). It is acceptable to have a struct with an opaque type as a uniform, but you cannot implement the function add (...) by passing an instance of Material.

Data Type (GLSL) - Opaque Types

Variables of opaque types can only be declared in one of two ways. They can be declared at global scope, as a uniform​ variables. Such variables can be arrays of the opaque type. They can be declared as members of a struct, but if so, then the struct can only be used to declare a uniform​ variable (or to declare a member of a struct/array that itself a uniform variable). They cannot be part of a buffer-backed interface block or an input/output variable, either directly or indirectly.


This change to your code should work across all compliant implementations of OpenGL:

// Must be in, because you cannot assign values to an opaque type.
vec4 add (in sampler2D tex) {
  return vec4(texture(tex, texcoords));
}

This would also work, since it uses the sampler in the material.tex uniform:

vec4 add (void) {
  return vec4(texture(material.tex, texcoords));
}
like image 135
Andon M. Coleman Avatar answered Sep 29 '22 19:09

Andon M. Coleman