Skip to contents

Shaders are small programs that run on the GPU and control how things are drawn. A vertex shader determines where vertices appear on screen, and a fragment shader determines the color of each pixel. raylibr lets you load custom GLSL shaders to create visual effects.

Loading shaders

Load shaders from files or from strings in memory:

# From files
shader <- load_shader("vertex.glsl", "fragment.glsl")

# Pass NULL for either to use Raylib's default
shader <- load_shader(NULL, "my_effect.glsl")

# From strings
fs_code <- "
#version 330
in vec2 fragTexCoord;
in vec4 fragColor;
out vec4 finalColor;
uniform sampler2D texture0;
void main() {
    vec4 color = texture(texture0, fragTexCoord) * fragColor;
    float gray = dot(color.rgb, vec3(0.299, 0.587, 0.114));
    finalColor = vec4(gray, gray, gray, color.a);
}
"
shader <- load_shader_from_memory(NULL, fs_code)

Using shaders

Wrap your drawing calls in begin_shader_mode() / end_shader_mode():

begin_shader_mode(shader)
draw_texture(my_texture, 0L, 0L, "white")
end_shader_mode()

Everything drawn between these calls uses the custom shader instead of the default rendering.

Uniforms

Pass data from R to the shader via uniforms — named variables in the GLSL code:

loc <- get_shader_location(shader, "intensity")
set_shader_value(shader, loc, 0.5)           # float
set_shader_value(shader, loc, c(1.0, 0.5))   # vec2
set_shader_value(shader, loc, c(1, 0, 0))    # vec3
set_shader_value(shader, loc, c(1, 0, 0, 1)) # vec4

set_shader_value() automatically picks the right GLSL type based on the length of the R vector.

For matrices, use set_shader_value_matrix(). For textures, use set_shader_value_texture().

Post-processing pattern

The most common shader use case is post-processing: render the scene normally to an off-screen texture, then draw that texture to the screen through a shader.

# Setup
target <- load_render_texture(400L, 300L)
shader <- load_shader(NULL, "my_effect.glsl")
source_rec <- rectangle(0, 0, 400, -300)  # flipped Y for render texture

# In the game loop:
begin_texture_mode(target)
  clear_background("black")
  # ... draw your scene normally ...
end_texture_mode()

begin_drawing()
  begin_shader_mode(shader)
  draw_texture_rec(target$texture, source_rec, c(0, 0), "white")
  end_shader_mode()
end_drawing()

Note the negative height in the source rectangle — render textures are vertically flipped in OpenGL.

Example

Without a shader, a scene with colored circles looks like this:

raylibr_screenshot(function() {
  draw_circle(100L, 150L, 80.0, "red")
  draw_circle(200L, 150L, 80.0, "green")
  draw_circle(300L, 150L, 80.0, "blue")
  draw_text("No shader", 10L, 10L, 20L, "white")
})
raylibr image
raylibr image

A grayscale fragment shader would convert this to shades of gray by computing a weighted luminance from the RGB channels. The GLSL code uses the standard luminance weights (0.299, 0.587, 0.114) to match human perception.

Further reading

See these demos for more advanced shader usage:

  • Shader Stars — animated GLSL fragment shader with uniform updates each frame
  • Julia Sets — fractal rendering with mouse-driven zoom via shader uniforms