Apart from conditional branching with if-else
or switch
, you may also want to have a look at the GLSL subroutines, this allows you to control the shader code branching on the C++ side, without touching the shader. Theoretically, this approach should be more efficient than using if-else
, but the downside is that subroutines are not supported in SPIR-V, which is the future of shaders.
A subroutine is very much like a function pointer in C, you define multiple functions of the same "subroutine" type, and then use a subroutine uniform to control which function should be called at runtime. Here's an example of debug drawing a framebuffer using subroutines.
#version 460
// this shader is used by: `FBO::DebugDraw()`
////////////////////////////////////////////////////////////////////////////////
#ifdef vertex_shader
layout(location = 0) in vec3 position;
layout(location = 1) in vec3 normal;
layout(location = 2) in vec2 uv;
layout(location = 0) out vec2 _uv;
void main() {
_uv = uv;
gl_Position = vec4(position.xy, 0.0, 1.0);
}
#endif
////////////////////////////////////////////////////////////////////////////////
#ifdef fragment_shader
layout(location = 0) in vec2 _uv;
layout(location = 0) out vec4 color;
layout(binding = 0) uniform sampler2D color_depth_texture;
layout(binding = 1) uniform usampler2D stencil_texture;
const float near = 0.1;
const float far = 100.0;
subroutine vec4 draw_buffer(void); // typedef the subroutine (like a function pointer)
layout(location = 0) subroutine uniform draw_buffer buffer_switch;
layout(index = 0)
subroutine(draw_buffer)
vec4 DrawColorBuffer() {
return texture(color_depth_texture, _uv);
}
layout(index = 1)
subroutine(draw_buffer)
vec4 DrawDepthBuffer() {
// sampling the depth texture format should return a float
float depth = texture(color_depth_texture, _uv).r;
// depth in screen space is non-linear, the precision is high for small z-values
// and low for large z-values, we need to linearize depth values before drawing
float ndc_depth = depth * 2.0 - 1.0;
float z = (2.0 * near * far) / (far + near - ndc_depth * (far - near));
float linear_depth = z / far;
return vec4(vec3(linear_depth), 1.0);
}
layout(index = 2)
subroutine(draw_buffer)
vec4 DrawStencilBuffer() {
uint stencil = texture(stencil_texture, _uv).r;
return vec4(vec3(stencil), 1.0);
}
void main() {
color = buffer_switch();
}
#endif