raylib-zig/examples/shaders/shaders_hybrid_render.zig

198 lines
8.1 KiB
Zig
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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);
}