mirror of
https://github.com/raylib-zig/raylib-zig.git
synced 2025-12-06 06:13:08 +00:00
198 lines
8.1 KiB
Zig
198 lines
8.1 KiB
Zig
// raylib [shaders] example - Hybrid Rendering
|
||
//
|
||
// Example complexity rating: [★★★★] 4/4
|
||
//
|
||
// Example originally created with raylib 4.2, last time updated with raylib 4.2
|
||
//
|
||
// Example contributed by Buğra Alptekin Sarı (@BugraAlptekinSari) 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) 2022-2025 Buğra Alptekin Sarı (@BugraAlptekinSari)
|
||
|
||
const rl = @import("raylib");
|
||
const pi = @import("std").math.pi;
|
||
|
||
//------------------------------------------------------------------------------------
|
||
// Declare custom Structs
|
||
//------------------------------------------------------------------------------------
|
||
|
||
const RayLocs = struct {
|
||
cam_pos: i32,
|
||
cam_dir: i32,
|
||
screen_center: i32,
|
||
};
|
||
|
||
//------------------------------------------------------------------------------------
|
||
// Program main entry point
|
||
//------------------------------------------------------------------------------------
|
||
pub fn main() anyerror!void {
|
||
// Initialization
|
||
//--------------------------------------------------------------------------------------
|
||
const screen_width = 800;
|
||
const screen_height = 450;
|
||
|
||
rl.initWindow(screen_width, screen_height, "raylib [shaders] example - write depth buffer");
|
||
defer rl.closeWindow(); // Close window and OpenGL context
|
||
|
||
|
||
// This Shader calculates pixel depth and color using raymarch
|
||
const shdr_raymarch: rl.Shader = try rl.loadShader(null, "resources/shaders/glsl330/hybrid_raymarch.fs");
|
||
defer rl.unloadShader(shdr_raymarch);
|
||
|
||
// This Shader is a standard rasterization fragment shader with the addition of depth writing
|
||
// You are required to write depth for all shaders if one shader does it
|
||
const shdr_raster: rl.Shader = try rl.loadShader(null, "resources/shaders/glsl330/hybrid_raster.fs");
|
||
defer rl.unloadShader(shdr_raster);
|
||
|
||
// Declare Struct used to store camera locs.
|
||
const march_locs: RayLocs = .{
|
||
.cam_pos = rl.getShaderLocation(shdr_raymarch, "camPos"),
|
||
.cam_dir = rl.getShaderLocation(shdr_raymarch, "camDir"),
|
||
.screen_center = rl.getShaderLocation(shdr_raymarch, "screenCenter"),
|
||
};
|
||
|
||
// Transfer screenCenter position to shader. Which is used to calculate ray direction.
|
||
const screen_center: rl.Vector2 = .init(screen_width / 2.0, screen_height / 2.0);
|
||
rl.setShaderValue(shdr_raymarch, march_locs.screen_center , &screen_center , .vec2);
|
||
|
||
// Use Customized function to create writable depth texture buffer
|
||
const target: rl.RenderTexture2D = try loadRenderTextureDepthTex(screen_width, screen_height);
|
||
defer unloadRenderTextureDepthTex(target);
|
||
|
||
// Define the camera to look into our 3d world
|
||
var camera: rl.Camera = .{
|
||
.position = .init(0.5, 1, 1.5), // Camera position
|
||
.target = .init(0, 0.5, 0), // Camera looking at point
|
||
.up = .init(0, 1, 0), // Camera up vector (rotation towards target)
|
||
.fovy = 45, // Camera field-of-view Y
|
||
.projection = .perspective, // Camera projection type
|
||
};
|
||
|
||
// Camera FOV is pre-calculated in the camera Distance.
|
||
const cam_dist: f32 = 1.0 / @tan(camera.fovy * 0.5 * (pi / 180.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
|
||
//----------------------------------------------------------------------------------
|
||
camera.update(.orbital);
|
||
|
||
// Update Camera Postion in the ray march shader.
|
||
rl.setShaderValue(shdr_raymarch, march_locs.cam_pos, &camera.position, .vec3);
|
||
|
||
// Update Camera Looking Vector. Vector length determines FOV.
|
||
const cam_dir: rl.Vector3 = .scale(.normalize(.subtract(camera.target, camera.position)), cam_dist);
|
||
rl.setShaderValue(shdr_raymarch, march_locs.cam_dir, &cam_dir, .vec3);
|
||
//----------------------------------------------------------------------------------
|
||
|
||
// Draw
|
||
//----------------------------------------------------------------------------------
|
||
// Draw into our custom render texture (framebuffer)
|
||
{
|
||
target.begin();
|
||
defer target.end();
|
||
|
||
rl.clearBackground(.white);
|
||
|
||
// Raymarch Scene
|
||
rl.gl.rlEnableDepthTest(); //Manually enable Depth Test to handle multiple rendering methods.
|
||
{
|
||
shdr_raymarch.activate();
|
||
defer shdr_raymarch.deactivate();
|
||
rl.drawRectangleRec(.init(0, 0, screen_width, screen_height), .white);
|
||
}
|
||
|
||
// Rasterize Scene
|
||
{
|
||
rl.beginMode3D(camera);
|
||
defer rl.endMode3D();
|
||
|
||
shdr_raster.activate();
|
||
defer shdr_raster.deactivate();
|
||
|
||
rl.drawCubeWiresV(.init(0, 0.5, 1), .init(1, 1, 1), .red);
|
||
rl.drawCubeV(.init(0, 0.5, 1), .init(1, 1, 1), .purple);
|
||
rl.drawCubeWiresV(.init(0, 0.5, -1), .init(1, 1, 1), .dark_green);
|
||
rl.drawCubeV(.init(0, 0.5, -1), .init(1, 1, 1), .yellow);
|
||
rl.drawGrid(10, 1);
|
||
}
|
||
}
|
||
|
||
// Draw into screen our custom render texture
|
||
rl.beginDrawing();
|
||
defer rl.endDrawing();
|
||
|
||
rl.clearBackground(.ray_white);
|
||
|
||
target.texture.drawRec(.init(0, 0, screen_width, -screen_height), .init(0, 0), .white);
|
||
rl.drawFPS(10, 10);
|
||
}
|
||
}
|
||
|
||
//------------------------------------------------------------------------------------
|
||
// Define custom functions required for the example
|
||
//------------------------------------------------------------------------------------
|
||
// Load custom render texture, create a writable depth texture buffer
|
||
fn loadRenderTextureDepthTex(width: i32, height: i32) !rl.RenderTexture2D {
|
||
const id = rl.gl.rlLoadFramebuffer(); // Load an empty framebuffer
|
||
if (id <= 0) {
|
||
return error.LoadFrameBufferFail;
|
||
}
|
||
|
||
rl.gl.rlEnableFramebuffer(id);
|
||
defer rl.gl.rlDisableFramebuffer();
|
||
|
||
const pix_format: i32 = @intFromEnum(rl.gl.rlPixelFormat.rl_pixelformat_uncompressed_r8g8b8a8);
|
||
|
||
const target: rl.RenderTexture2D = .{
|
||
.id = id,
|
||
// Create color texture (default to RGBA)
|
||
.texture = .{
|
||
.id = rl.gl.rlLoadTexture(null, width, height, pix_format, 1),
|
||
.width = width,
|
||
.height = height,
|
||
.format = .uncompressed_r8g8b8a8,
|
||
.mipmaps = 1,
|
||
},
|
||
// Create depth texture buffer (instead of raylib default renderbuffer)
|
||
.depth = .{
|
||
.id = rl.gl.rlLoadTextureDepth(width, height, false),
|
||
.width = width,
|
||
.height = height,
|
||
.format = .compressed_etc2_rgb, //DEPTH_COMPONENT_24BIT?
|
||
.mipmaps = 1,
|
||
}
|
||
};
|
||
|
||
// Attach color texture and depth texture to FBO
|
||
const channel0: i32 = @intFromEnum(rl.gl.rlFramebufferAttachType.rl_attachment_color_channel0);
|
||
const depth: i32 = @intFromEnum(rl.gl.rlFramebufferAttachType.rl_attachment_depth);
|
||
const texture2d: i32 = @intFromEnum(rl.gl.rlFramebufferAttachTextureType.rl_attachment_texture2d);
|
||
rl.gl.rlFramebufferAttach(target.id, target.texture.id, channel0, texture2d, 0);
|
||
rl.gl.rlFramebufferAttach(target.id, target.depth.id, depth, texture2d, 0);
|
||
|
||
// Check if fbo is complete with attachments (valid)
|
||
if (rl.gl.rlFramebufferComplete(target.id)) {
|
||
rl.traceLog(.info, "FBO: [ID %i] Framebuffer object created successfully", .{ target.id });
|
||
}
|
||
|
||
return target;
|
||
}
|
||
|
||
// Unload render texture from GPU memory (VRAM)
|
||
fn unloadRenderTextureDepthTex(target: rl.RenderTexture2D) void {
|
||
// Color texture attached to FBO is deleted
|
||
rl.gl.rlUnloadTexture(target.texture.id);
|
||
rl.gl.rlUnloadTexture(target.depth.id);
|
||
|
||
// NOTE: Depth texture is automatically
|
||
// queried and deleted before deleting framebuffer
|
||
rl.gl.rlUnloadFramebuffer(target.id);
|
||
}
|