OpenGL interoperatibility
There is no browser build of this example yet.
Is it the same as C?
- Desktop:verified The Zig version renders a final frame that is byte-identical to the C example.
- Browser:not checked yet The browser comparison does not cover this example yet.
//! raylib [others] example - OpenGL interoperatibility
//!
//! Zig-API translation of examples/others/raylib_opengl_interop.c (raylib 6.2).
//! Example contributed by Stephan Soller (@arkanis) and reviewed by Ramon Santamaria (@raysan5)
//! Example licensed under an unmodified zlib/libpng license.
//! Copyright (c) 2021-2025 Stephan Soller (@arkanis) and Ramon Santamaria (@raysan5)
//!
//! Mixes raylib and plain OpenGL code to draw a GL_POINTS based particle system. The
//! primary point is to demonstrate raylib and OpenGL interop
//!
//! rlgl batched draw operations internally so we have to flush the current batch before
//! doing our own OpenGL work (rl.gl.drawRenderBatchActive())
//!
//! The example also demonstrates how to get the current model view projection matrix of
//! raylib. That way raylib cameras and so on work as expected
const std = @import("std");
const rl = @import("raylib");
const glsl_version = 330;
const max_particles = 1000;
/// Particle type
const Particle = extern struct {
x: f32,
y: f32,
period: f32,
};
pub fn main() !void {
// Initialization
const screen_width = 800;
const screen_height = 450;
rl.initWindow(screen_width, screen_height, "raylib [others] example - OpenGL interoperatibility");
defer rl.closeWindow(); // Close window and OpenGL context
const shader = try rl.loadShader(
std.fmt.comptimePrint("resources/shaders/glsl{d}/point_particle.vs", .{glsl_version}),
std.fmt.comptimePrint("resources/shaders/glsl{d}/point_particle.fs", .{glsl_version}),
);
defer rl.unloadShader(shader);
const current_time_loc = rl.getShaderLocation(shader, "currentTime");
const color_loc = rl.getShaderLocation(shader, "color");
// Initialize the vertex buffer for the particles and assign each particle random values
var particles: [max_particles]Particle = undefined;
for (&particles) |*particle| {
const x: f32 = @floatFromInt(rl.getRandomValue(20, screen_width - 20));
const y: f32 = @floatFromInt(rl.getRandomValue(50, screen_height - 20));
// Give each particle a slightly different period. But don't spread it to much
// This way the particles line up every so often and you get a glimps of what is going on
const period: f32 = @floatFromInt(rl.getRandomValue(10, 30));
particle.* = .{ .x = x, .y = y, .period = period / 10.0 };
}
// Create a plain OpenGL vertex buffer with the data and an vertex array object
// that feeds the data from the buffer into the vertexPosition shader attribute
var vao: gl.Uint = 0;
var vbo: gl.Uint = 0;
gl.genVertexArrays(1, &vao);
gl.bindVertexArray(vao);
gl.genBuffers(1, &vbo);
gl.bindBuffer(gl.array_buffer, vbo);
gl.bufferData(gl.array_buffer, @sizeOf(@TypeOf(particles)), &particles, gl.static_draw);
// Note: loadShader() automatically fetches the attribute index of "vertexPosition" and saves it in shader.loc(.vertex_position)
gl.vertexAttribPointer(@intCast(shader.loc(.vertex_position).*), 3, gl.float, gl.false_, 0, null);
gl.enableVertexAttribArray(0);
gl.bindBuffer(gl.array_buffer, 0);
gl.bindVertexArray(0);
defer gl.deleteVertexArrays(1, &vao);
defer gl.deleteBuffers(1, &vbo);
// Allows the vertex shader to set the point size of each particle individually
gl.enable(gl.program_point_size);
rl.setTargetFPS(60);
// Main game loop
while (!rl.windowShouldClose()) { // Detect window close button or ESC key
// Draw
rl.beginDrawing();
defer rl.endDrawing();
rl.clearBackground(.white);
rl.drawRectangle(10, 10, 210, 30, .maroon);
rl.drawText(std.fmt.comptimePrint("{d} particles in one vertex buffer", .{max_particles}), 20, 20, 10, .ray_white);
rl.gl.drawRenderBatchActive(); // Draw iternal buffers data (previous draw calls)
// Switch to plain OpenGL
gl.useProgram(shader.id);
gl.uniform1f(current_time_loc, @floatCast(rl.getTime()));
const color = rl.colorNormalize(.{ .r = 255, .g = 0, .b = 0, .a = 128 });
gl.uniform4fv(color_loc, 1, &color.x);
// Get the current modelview and projection matrix so the particle system is displayed and transformed
const model_view_projection = rl.math.matrixMultiply(rl.gl.getMatrixModelview(), rl.gl.getMatrixProjection());
gl.uniformMatrix4fv(shader.loc(.matrix_mvp).*, 1, gl.false_, &rl.math.matrixToFloatV(model_view_projection).v);
gl.bindVertexArray(vao);
gl.drawArrays(gl.points, 0, max_particles);
gl.bindVertexArray(0);
gl.useProgram(0);
rl.drawFPS(screen_width - 100, 10);
}
}
/// The few plain OpenGL entry points this example calls. raylib loads OpenGL
/// through GLAD, which exports every entry point as a `glad_gl*` function
/// pointer (valid once the window, and so the GL context, exists).
const gl = struct {
const Uint = c_uint;
const Enum = c_uint;
const false_: u8 = 0;
const points: Enum = 0x0000;
const float: Enum = 0x1406;
const array_buffer: Enum = 0x8892;
const static_draw: Enum = 0x88E4;
const program_point_size: Enum = 0x8642;
fn Proc(comptime Fn: type) type {
return ?*const Fn;
}
extern var glad_glGenVertexArrays: Proc(fn (c_int, *Uint) callconv(.c) void);
extern var glad_glDeleteVertexArrays: Proc(fn (c_int, *const Uint) callconv(.c) void);
extern var glad_glBindVertexArray: Proc(fn (Uint) callconv(.c) void);
extern var glad_glGenBuffers: Proc(fn (c_int, *Uint) callconv(.c) void);
extern var glad_glDeleteBuffers: Proc(fn (c_int, *const Uint) callconv(.c) void);
extern var glad_glBindBuffer: Proc(fn (Enum, Uint) callconv(.c) void);
extern var glad_glBufferData: Proc(fn (Enum, isize, ?*const anyopaque, Enum) callconv(.c) void);
extern var glad_glVertexAttribPointer: Proc(fn (Uint, c_int, Enum, u8, c_int, ?*const anyopaque) callconv(.c) void);
extern var glad_glEnableVertexAttribArray: Proc(fn (Uint) callconv(.c) void);
extern var glad_glEnable: Proc(fn (Enum) callconv(.c) void);
extern var glad_glUseProgram: Proc(fn (Uint) callconv(.c) void);
extern var glad_glUniform1f: Proc(fn (c_int, f32) callconv(.c) void);
extern var glad_glUniform4fv: Proc(fn (c_int, c_int, *const f32) callconv(.c) void);
extern var glad_glUniformMatrix4fv: Proc(fn (c_int, c_int, u8, *const f32) callconv(.c) void);
extern var glad_glDrawArrays: Proc(fn (Enum, c_int, c_int) callconv(.c) void);
fn genVertexArrays(n: c_int, arrays: *Uint) void {
glad_glGenVertexArrays.?(n, arrays);
}
fn deleteVertexArrays(n: c_int, arrays: *const Uint) void {
glad_glDeleteVertexArrays.?(n, arrays);
}
fn bindVertexArray(array: Uint) void {
glad_glBindVertexArray.?(array);
}
fn genBuffers(n: c_int, buffers: *Uint) void {
glad_glGenBuffers.?(n, buffers);
}
fn deleteBuffers(n: c_int, buffers: *const Uint) void {
glad_glDeleteBuffers.?(n, buffers);
}
fn bindBuffer(target: Enum, buffer: Uint) void {
glad_glBindBuffer.?(target, buffer);
}
fn bufferData(target: Enum, size: isize, data: ?*const anyopaque, usage: Enum) void {
glad_glBufferData.?(target, size, data, usage);
}
fn vertexAttribPointer(index: Uint, size: c_int, @"type": Enum, normalized: u8, stride: c_int, pointer: ?*const anyopaque) void {
glad_glVertexAttribPointer.?(index, size, @"type", normalized, stride, pointer);
}
fn enableVertexAttribArray(index: Uint) void {
glad_glEnableVertexAttribArray.?(index);
}
fn enable(cap: Enum) void {
glad_glEnable.?(cap);
}
fn useProgram(program: Uint) void {
glad_glUseProgram.?(program);
}
fn uniform1f(location: c_int, v0: f32) void {
glad_glUniform1f.?(location, v0);
}
fn uniform4fv(location: c_int, count: c_int, value: *const f32) void {
glad_glUniform4fv.?(location, count, value);
}
fn uniformMatrix4fv(location: c_int, count: c_int, transpose: u8, value: *const [16]f32) void {
glad_glUniformMatrix4fv.?(location, count, transpose, &value[0]);
}
fn drawArrays(mode: Enum, first: c_int, count: c_int) void {
glad_glDrawArrays.?(mode, first, count);
}
};/*******************************************************************************************
*
* raylib [others] example - OpenGL interoperatibility
*
* Example complexity rating: [★★★★] 4/4
*
* Example originally created with raylib 3.8, last time updated with raylib 4.0
*
* Example contributed by Stephan Soller (@arkanis) and reviewed by Ramon Santamaria (@raysan5)
*
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
* BSD-like license that allows static linking with closed source software
*
* Copyright (c) 2021-2025 Stephan Soller (@arkanis) and Ramon Santamaria (@raysan5)
*
********************************************************************************************
*
* Mixes raylib and plain OpenGL code to draw a GL_POINTS based particle system. The
* primary point is to demonstrate raylib and OpenGL interop
*
* rlgl batched draw operations internally so we have to flush the current batch before
* doing our own OpenGL work (rlDrawRenderBatchActive())
*
* The example also demonstrates how to get the current model view projection matrix of
* raylib. That way raylib cameras and so on work as expected
*
********************************************************************************************/
#include "raylib.h"
#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_DESKTOP_SDL) || defined(PLATFORM_DESKTOP_RGFW)
#if defined(GRAPHICS_API_OPENGL_ES2)
#include "glad_gles2.h" // Required for: OpenGL functionality
#define glGenVertexArrays glGenVertexArraysOES
#define glBindVertexArray glBindVertexArrayOES
#define glDeleteVertexArrays glDeleteVertexArraysOES
#define GLSL_VERSION 100
#else
#if defined(__APPLE__)
#define GL_SILENCE_DEPRECATION // Silence Opengl API deprecation warnings
#include <OpenGL/gl3.h> // OpenGL 3 library for OSX
#include <OpenGL/gl3ext.h> // OpenGL 3 extensions library for OSX
#else
#include "glad.h" // Required for: OpenGL functionality
#endif
#define GLSL_VERSION 330
#endif
#else // PLATFORM_ANDROID, PLATFORM_WEB
#define GLSL_VERSION 100
#endif
#include "rlgl.h" // Required for: rlDrawRenderBatchActive(), rlGetMatrixModelview(), rlGetMatrixProjection()
#include "raymath.h" // Required for: MatrixMultiply(), MatrixToFloat()
#define MAX_PARTICLES 1000
//------------------------------------------------------------------------------------
// Module Functions Declaration
//------------------------------------------------------------------------------------
// Particle type
typedef struct Particle {
float x;
float y;
float period;
} Particle;
//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
int main(void)
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [others] example - OpenGL interoperatibility");
Shader shader = LoadShader(TextFormat("resources/shaders/glsl%i/point_particle.vs", GLSL_VERSION),
TextFormat("resources/shaders/glsl%i/point_particle.fs", GLSL_VERSION));
int currentTimeLoc = GetShaderLocation(shader, "currentTime");
int colorLoc = GetShaderLocation(shader, "color");
// Initialize the vertex buffer for the particles and assign each particle random values
Particle particles[MAX_PARTICLES] = { 0 };
for (int i = 0; i < MAX_PARTICLES; i++)
{
particles[i].x = (float)GetRandomValue(20, screenWidth - 20);
particles[i].y = (float)GetRandomValue(50, screenHeight - 20);
// Give each particle a slightly different period. But don't spread it to much
// This way the particles line up every so often and you get a glimps of what is going on
particles[i].period = (float)GetRandomValue(10, 30)/10.0f;
}
// Create a plain OpenGL vertex buffer with the data and an vertex array object
// that feeds the data from the buffer into the vertexPosition shader attribute
GLuint vao = 0;
GLuint vbo = 0;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, MAX_PARTICLES*sizeof(Particle), particles, GL_STATIC_DRAW);
// Note: LoadShader() automatically fetches the attribute index of "vertexPosition" and saves it in shader.locs[SHADER_LOC_VERTEX_POSITION]
glVertexAttribPointer(shader.locs[SHADER_LOC_VERTEX_POSITION], 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
// Allows the vertex shader to set the point size of each particle individually
#ifndef GRAPHICS_API_OPENGL_ES2
glEnable(GL_PROGRAM_POINT_SIZE);
#endif
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(WHITE);
DrawRectangle(10, 10, 210, 30, MAROON);
DrawText(TextFormat("%zu particles in one vertex buffer", MAX_PARTICLES), 20, 20, 10, RAYWHITE);
rlDrawRenderBatchActive(); // Draw iternal buffers data (previous draw calls)
// Switch to plain OpenGL
//------------------------------------------------------------------------------
glUseProgram(shader.id);
glUniform1f(currentTimeLoc, GetTime());
Vector4 color = ColorNormalize((Color){ 255, 0, 0, 128 });
glUniform4fv(colorLoc, 1, (float *)&color);
// Get the current modelview and projection matrix so the particle system is displayed and transformed
Matrix modelViewProjection = MatrixMultiply(rlGetMatrixModelview(), rlGetMatrixProjection());
glUniformMatrix4fv(shader.locs[SHADER_LOC_MATRIX_MVP], 1, false, MatrixToFloat(modelViewProjection));
glBindVertexArray(vao);
glDrawArrays(GL_POINTS, 0, MAX_PARTICLES);
glBindVertexArray(0);
glUseProgram(0);
//------------------------------------------------------------------------------
DrawFPS(screenWidth - 100, 10);
EndDrawing();
//----------------------------------------------------------------------------------
}
// De-Initialization
//--------------------------------------------------------------------------------------
glDeleteBuffers(1, &vbo);
glDeleteVertexArrays(1, &vao);
UnloadShader(shader); // Unload shader
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}From raylib 6.2. The original on GitHub.