[examples] Add core_monitor_change and shaders_ascii_rendering (#293)

* Add monitor_change example

* Adding `core_monitor_change` and `shaders_ascii_rendering` examples
This commit is contained in:
Maicon Santana 2025-10-26 15:00:10 +00:00 committed by GitHub
parent d9933008f5
commit 6ecc0455eb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 498 additions and 0 deletions

View File

@ -108,6 +108,11 @@ pub fn build(b: *std.Build) !void {
.path = "examples/core/basic_window.zig", .path = "examples/core/basic_window.zig",
.desc = "Creates a basic window with text", .desc = "Creates a basic window with text",
}, },
.{
.name = "core_monitor_change",
.path = "examples/core/core_monitor_change.zig",
.desc = "Simple Monitor Manager",
},
.{ .{
.name = "basic_window_web", .name = "basic_window_web",
.path = "examples/core/basic_window_web.zig", .path = "examples/core/basic_window_web.zig",
@ -183,6 +188,11 @@ pub fn build(b: *std.Build) !void {
.path = "examples/shaders/raymarching.zig", .path = "examples/shaders/raymarching.zig",
.desc = "Uses a raymarching in a shader to render shapes", .desc = "Uses a raymarching in a shader to render shapes",
}, },
.{
.name = "shaders_ascii_rendering",
.path = "examples/shaders/shaders_ascii_rendering.zig",
.desc = "Post-processing to render in ASCII",
},
.{ .{
.name = "shaders_basic_pbr", .name = "shaders_basic_pbr",
.path = "examples/shaders/shaders_basic_pbr.zig", .path = "examples/shaders/shaders_basic_pbr.zig",

View File

@ -0,0 +1,153 @@
// raylib-zig (c) 2025 Maicon Santana (@maiconpintoabreu)
const std = @import("std");
const rl = @import("raylib");
const MAX_MONITORS = 10;
// Monitor Details
const Monitor = struct {
position: rl.Vector2,
name: [*c]const u8,
width: i32,
height: i32,
physicalWidth: i32,
physicalHeight: i32,
refreshRate: i32,
};
pub fn main() anyerror!void {
// Initialization
//--------------------------------------------------------------------------------------
const screenWidth: i32 = 800;
const screenHeight: i32 = 450;
var monitors: [MAX_MONITORS]Monitor = undefined;
rl.initWindow(screenWidth, screenHeight, "raylib-zig [core] example - monitor change");
defer rl.closeWindow(); // Close window and OpenGL context
var currentMonitorIndex: i32 = rl.getCurrentMonitor();
var monitorCount: usize = 0;
rl.setTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
// Main game loop
while (!rl.windowShouldClose()) { // Detect window close button or ESC key
// Update
//----------------------------------------------------------------------------------
// Variables to find the max x and Y to calculate the scale
var maxWidth: i32 = 1;
var maxHeight: i32 = 1;
// Monitor offset is to fix when monitor position x is negative
var monitorOffsetX: f32 = 0;
// Rebuild monitors array every frame
monitorCount = @intCast(rl.getMonitorCount());
for (0..monitorCount) |i| {
const intI: i32 = @intCast(i);
monitors[i] = .{
.position = rl.getMonitorPosition(intI),
.name = rl.getMonitorName(intI),
.width = rl.getMonitorWidth(intI),
.height = rl.getMonitorHeight(intI),
.physicalWidth = rl.getMonitorPhysicalWidth(intI),
.physicalHeight = rl.getMonitorPhysicalHeight(intI),
.refreshRate = rl.getMonitorRefreshRate(intI),
};
if (monitors[i].position.x < monitorOffsetX) {
monitorOffsetX = monitors[i].position.x * -1.0;
}
const width: i32 = @as(i32, @intFromFloat(monitors[i].position.x)) + monitors[i].width;
const height: i32 = @as(i32, @intFromFloat(monitors[i].position.y)) + monitors[i].height;
if (maxWidth < width) maxWidth = width;
if (maxHeight < height) maxHeight = height;
}
if (rl.isKeyPressed(.enter) and monitorCount > 1) {
currentMonitorIndex += 1;
// Set index to 0 if the last one
if (currentMonitorIndex == monitorCount) currentMonitorIndex = 0;
rl.setWindowMonitor(currentMonitorIndex); // Move window to currentMonitorIndex
} else {
// Get currentMonitorIndex if manually moved
currentMonitorIndex = rl.getCurrentMonitor();
}
var monitorScale: f32 = 0.6;
const intMonitorOffsetX: i32 = @intFromFloat(monitorOffsetX);
if (maxHeight > maxWidth + intMonitorOffsetX) {
monitorScale *= @as(f32, @floatFromInt(screenHeight)) / @as(f32, @floatFromInt(maxHeight));
} else {
monitorScale *= @as(f32, @floatFromInt(screenWidth)) / @as(f32, @floatFromInt((maxWidth + intMonitorOffsetX)));
}
// Draw
//----------------------------------------------------------------------------------
rl.beginDrawing();
defer rl.endDrawing();
rl.clearBackground(.ray_white);
rl.drawText("Press [Enter] to move window to next monitor available", 20, 20, 20, .dark_gray);
rl.drawRectangleLines(20, 60, screenWidth - 40, screenHeight - 100, .dark_gray);
// Draw Monitor Rectangles with information inside
for (0..monitorCount) |i| {
// Calculate retangle position and size using monitorScale
const rec: rl.Rectangle = .{
.x = (monitors[i].position.x + monitorOffsetX) * monitorScale + 140,
.y = monitors[i].position.y * monitorScale + 80,
.width = @as(f32, @floatFromInt(monitors[i].width)) * monitorScale,
.height = @as(f32, @floatFromInt(monitors[i].height)) * monitorScale,
};
// Draw monitor name and information inside the rectangle
rl.drawText(
rl.textFormat("[%i] %s", .{ i, monitors[i].name }),
@intFromFloat(rec.x + 10.0),
@intFromFloat(rec.y + (100.0 * monitorScale)),
@as(i32, @intFromFloat(120.0 * monitorScale)),
.blue,
);
rl.drawText(rl.textFormat("Resolution: [%ipx x %ipx]\nRefreshRate: [%ihz]\nPhysical Size: [%imm x %imm]\nPosition: %3.0f x %3.0f", .{
monitors[i].width,
monitors[i].height,
monitors[i].refreshRate,
monitors[i].physicalWidth,
monitors[i].physicalHeight,
monitors[i].position.x,
monitors[i].position.y,
}), @intFromFloat(rec.x + 10), @intFromFloat(rec.y + (200 * monitorScale)), @as(i32, @intFromFloat(120.0 * monitorScale)), .dark_gray);
// Highlight current monitor
if (i == currentMonitorIndex) {
rl.drawRectangleLinesEx(rec, 5, .red);
const windowPosition: rl.Vector2 = .{
.x = (rl.getWindowPosition().x + monitorOffsetX) * monitorScale + 140,
.y = rl.getWindowPosition().y * monitorScale + 80,
};
// Draw window position based on monitors
rl.drawRectangleV(
windowPosition,
.{ .x = @as(f32, @floatFromInt(screenWidth)) * monitorScale, .y = @as(f32, @floatFromInt(screenHeight)) * monitorScale },
rl.fade(.green, 0.5),
);
} else {
rl.drawRectangleLinesEx(rec, 5, .gray);
}
}
//----------------------------------------------------------------------------------
}
}

View File

@ -0,0 +1,4 @@
| resource | author | licence | notes |
| :----------------- | :-----------: | :------ | :---- |
| fudesumi.png | [Eiden Marsal](https://www.artstation.com/marshall_z) | [CC-BY-NC](https://creativecommons.org/licenses/by-nc/4.0/) | - |
| raysan.png | [@raysan5](https://github.com/raysan5) | [CC0](https://creativecommons.org/publicdomain/zero/1.0/) | - |

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

@ -0,0 +1,80 @@
#version 100
precision mediump float;
// Input from the vertex shader
varying vec2 fragTexCoord;
// Output color for the screen
varying vec4 finalColor;
uniform sampler2D texture0;
uniform vec2 resolution;
// Fontsize less then 9 may be not complete
uniform float fontSize;
float GreyScale(in vec3 col)
{
return dot(col, vec3(0.2126, 0.7152, 0.0722));
}
float GetCharacter(float n, vec2 p)
{
p = floor(p*vec2(-4.0, 4.0) + 2.5);
// Check if the calculated coordinate is inside the 5x5 grid (from 0.0 to 4.0)
if (clamp(p.x, 0.0, 4.0) == p.x && clamp(p.y, 0.0, 4.0) == p.y)
{
float a = floor(p.x + 0.5) + 5.0*floor(p.y + 0.5);
// This checked if the 'a'-th bit of 'n' was set
float shiftedN = floor(n/pow(2.0, a));
if (mod(shiftedN, 2.0) == 1.0)
{
return 1.0; // The bit is on
}
}
return 0.0; // The bit is off, or we are outside the grid
}
// -----------------------------------------------------------------------------
// Main shader logic
// -----------------------------------------------------------------------------
void main()
{
vec2 charPixelSize = vec2(fontSize, fontSize);
vec2 uvCellSize = charPixelSize/resolution;
// The cell size is based on the fontSize set by application
vec2 cellUV = floor(fragTexCoord/uvCellSize)*uvCellSize;
vec3 cellColor = texture2D(texture0, cellUV).rgb;
// Gray is used to define what character will be selected to draw
float gray = GreyScale(cellColor);
float n = 4096.0;
// Character set from https://www.shadertoy.com/view/lssGDj
// Create new bitmaps https://thrill-project.com/archiv/coding/bitmap/
if (gray > 0.2) n = 65600.0; // :
if (gray > 0.3) n = 18725316.0; // v
if (gray > 0.4) n = 15255086.0; // o
if (gray > 0.5) n = 13121101.0; // &
if (gray > 0.6) n = 15252014.0; // 8
if (gray > 0.7) n = 13195790.0; // @
if (gray > 0.8) n = 11512810.0; // #
vec2 localUV = (fragTexCoord - cellUV)/uvCellSize; // Range [0.0, 1.0]
vec2 p = localUV*2.0 - 1.0; // Range [-1.0, 1.0]
// cellColor and charShape will define the color of the char
vec3 color = cellColor*GetCharacter(n, p);
gl_FragColor = vec4(color, 1.0);
}

View File

@ -0,0 +1,78 @@
#version 120
// Input from the vertex shader
varying vec2 fragTexCoord;
// Output color for the screen
varying vec4 finalColor;
uniform sampler2D texture0;
uniform vec2 resolution;
// Fontsize less then 9 may be not complete
uniform float fontSize;
float GreyScale(in vec3 col)
{
return dot(col, vec3(0.2126, 0.7152, 0.0722));
}
float GetCharacter(float n, vec2 p)
{
p = floor(p*vec2(-4.0, 4.0) + 2.5);
// Check if the calculated coordinate is inside the 5x5 grid (from 0.0 to 4.0)
if (clamp(p.x, 0.0, 4.0) == p.x && clamp(p.y, 0.0, 4.0) == p.y)
{
float a = floor(p.x + 0.5) + 5.0*floor(p.y + 0.5);
// This checked if the 'a'-th bit of 'n' was set
float shiftedN = floor(n/pow(2.0, a));
if (mod(shiftedN, 2.0) == 1.0)
{
return 1.0; // The bit is on
}
}
return 0.0; // The bit is off, or we are outside the grid
}
// -----------------------------------------------------------------------------
// Main shader logic
// -----------------------------------------------------------------------------
void main()
{
vec2 charPixelSize = vec2(fontSize, fontSize);
vec2 uvCellSize = charPixelSize / resolution;
// The cell size is based on the fontSize set by application
vec2 cellUV = floor(fragTexCoord / uvCellSize)*uvCellSize;
vec3 cellColor = texture2D(texture0, cellUV).rgb;
// Gray is used to define what character will be selected to draw
float gray = GreyScale(cellColor);
float n = 4096.0;
// Character set from https://www.shadertoy.com/view/lssGDj
// Create new bitmaps https://thrill-project.com/archiv/coding/bitmap/
if (gray > 0.2) n = 65600.0; // :
if (gray > 0.3) n = 18725316.0; // v
if (gray > 0.4) n = 15255086.0; // o
if (gray > 0.5) n = 13121101.0; // &
if (gray > 0.6) n = 15252014.0; // 8
if (gray > 0.7) n = 13195790.0; // @
if (gray > 0.8) n = 11512810.0; // #
vec2 localUV = (fragTexCoord - cellUV)/uvCellSize; // Range [0.0, 1.0]
vec2 p = localUV*2.0 - 1.0; // Range [-1.0, 1.0]
// cellColor and charShape will define the color of the char
vec3 color = cellColor*GetCharacter(n, p);
gl_FragColor = vec4(color, 1.0);
}

View File

@ -0,0 +1,73 @@
#version 330
// Input from the vertex shader
in vec2 fragTexCoord;
// Output color for the screen
out vec4 finalColor;
uniform sampler2D texture0;
uniform vec2 resolution;
// Fontsize less then 9 may be not complete
uniform float fontSize;
float GreyScale(in vec3 col)
{
return dot(col, vec3(0.2126, 0.7152, 0.0722));
}
float GetCharacter(int n, vec2 p)
{
p = floor(p*vec2(-4.0, 4.0) + 2.5);
// Check if the coordinate is inside the 5x5 grid (0 to 4)
if (clamp(p.x, 0.0, 4.0) == p.x && clamp(p.y, 0.0, 4.0) == p.y)
{
int a = int(round(p.x) + 5.0*round(p.y));
if (((n >> a) & 1) == 1)
{
return 1.0;
}
}
return 0.0; // The bit is off, or we are outside the grid
}
// -----------------------------------------------------------------------------
// Main shader logic
// -----------------------------------------------------------------------------
void main()
{
vec2 charPixelSize = vec2(fontSize, fontSize);
vec2 uvCellSize = charPixelSize/resolution;
// The cell size is based on the fontSize set by application
vec2 cellUV = floor(fragTexCoord/uvCellSize)*uvCellSize;
vec3 cellColor = texture(texture0, cellUV).rgb;
// Gray is used to define what character will be selected to draw
float gray = GreyScale(cellColor);
int n = 4096;
// Character set from https://www.shadertoy.com/view/lssGDj
// Create new bitmaps https://thrill-project.com/archiv/coding/bitmap/
if (gray > 0.2) n = 65600; // :
if (gray > 0.3) n = 18725316; // v
if (gray > 0.4) n = 15255086; // o
if (gray > 0.5) n = 13121101; // &
if (gray > 0.6) n = 15252014; // 8
if (gray > 0.7) n = 13195790; // @
if (gray > 0.8) n = 11512810; // #
vec2 localUV = (fragTexCoord - cellUV)/uvCellSize; // Range [0.0, 1.0]
vec2 p = localUV*2.0 - 1.0; // Range [-1.0, 1.0]
vec3 color = cellColor*GetCharacter(n, p);
finalColor = vec4(color, 1.0);
}

View File

@ -0,0 +1,100 @@
// raylib-zig (c) 2025 Maicon Santana (@maiconpintoabreu)
const builtin = @import("builtin");
const rl = @import("raylib");
const GLSL_VERSION: i16 = if (builtin.cpu.arch.isWasm()) 100 else 330;
pub fn main() anyerror!void {
// Initialization
//--------------------------------------------------------------------------------------
const screenWidth = 800;
const screenHeight = 450;
rl.initWindow(screenWidth, screenHeight, "raylib-zig [core] example - monitor change");
// De-Initialization
//--------------------------------------------------------------------------------------
defer rl.closeWindow(); // Close window and OpenGL context
// Texture to test static drawing
const fudesumi: rl.Texture2D = try rl.loadTexture("examples/shaders/resources/fudesumi.png");
defer fudesumi.unload();
// Texture to test moving drawing
const raysan: rl.Texture2D = try rl.loadTexture("examples/shaders/resources/raysan.png");
defer raysan.unload();
// Load shader to be used on postprocessing
const shader: rl.Shader = try rl.loadShader(
null,
rl.textFormat("examples/shaders/resources/shaders/glsl%i/ascii.fs", .{GLSL_VERSION}),
);
// These locations are used to send data to the GPU
const resolutionLoc: i32 = rl.getShaderLocation(shader, "resolution");
const fontSizeLoc: i32 = rl.getShaderLocation(shader, "fontSize");
// Set the character size for the ASCII effect
// Fontsize should be 9 or more
var fontSize: f32 = 9.0;
// Send the updated values to the shader
const resolution: rl.Vector2 = .{ .x = @floatFromInt(screenWidth), .y = @floatFromInt(screenHeight) };
rl.setShaderValue(shader, resolutionLoc, &resolution, .vec2);
var circlePos: rl.Vector2 = .{ .x = 40.0, .y = @as(f32, @floatFromInt(screenHeight)) * 0.5 };
var circleSpeed: f32 = 1.0;
// RenderTexture to apply the postprocessing later
const target: rl.RenderTexture2D = try rl.loadRenderTexture(screenWidth, screenHeight);
rl.setTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
// Main game loop
while (!rl.windowShouldClose()) { // Detect window close button or ESC key
// Update
//----------------------------------------------------------------------------------
circlePos.x += circleSpeed;
if ((circlePos.x > 200.0) or (circlePos.x < 40.0)) circleSpeed *= -1; // Revert speed
if (rl.isKeyPressed(.left) and (fontSize > 9.0)) fontSize -= 1.0; // Reduce fontSize
if (rl.isKeyPressed(.right) and (fontSize < 15.0)) fontSize += 1.0; // Increase fontSize
// Set fontsize for the shader
rl.setShaderValue(shader, fontSizeLoc, &fontSize, .float);
// Draw
//----------------------------------------------------------------------------------
{
rl.beginTextureMode(target);
defer rl.endTextureMode();
rl.clearBackground(.white);
// Draw scene in our render texture
rl.drawTexture(fudesumi, 500, -30, .white);
rl.drawTextureV(raysan, circlePos, .white);
}
rl.beginDrawing();
defer rl.endDrawing();
rl.clearBackground(.ray_white);
{
rl.beginShaderMode(shader);
defer rl.endShaderMode();
// Draw the scene texture (that we rendered earlier) to the screen
// The shader will process every pixel of this texture
rl.drawTextureRec(target.texture, .{
.x = 0,
.y = 0,
.width = @floatFromInt(target.texture.width),
.height = -@as(f32, @floatFromInt(target.texture.height)),
}, .zero(), .white);
}
rl.drawRectangle(0, 0, screenWidth, 40, .black);
rl.drawText(rl.textFormat("Ascii effect - FontSize:%2.0f - [Left] -1 [Right] +1 ", .{fontSize}), 120, 10, 20, .light_gray);
rl.drawFPS(10, 10);
//----------------------------------------------------------------------------------
}
}