/******************************************************************************************* * * raylib [shaders] example - Hybrid Rendering * * 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-2023 Buğra Alptekin Sarı (@BugraAlptekinSari) * ********************************************************************************************/ using System; using System.Numerics; using static Raylib_cs.Raylib; namespace Examples.Shaders; public class HybridRender { struct RayLocs { public int CamPos; public int CamDir; public int ScreenCenter; } const int GLSL_VERSION = 330; public static int Main() { // Initialization //-------------------------------------------------------------------------------------- const int screenWidth = 800; const int screenHeight = 450; InitWindow(screenWidth, screenHeight, "raylib [shaders] example - hybrid render"); // This shader calculates pixel depth and color using raymarch Shader shdrRaymarch = LoadShader(null, $"resources/shaders/glsl{GLSL_VERSION}/hybrid_raymarch.fs"); // 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 Shader shdrRaster = LoadShader(null, $"resources/shaders/glsl{GLSL_VERSION}/hybrid_raster.fs"); // Declare struct used to store camera locs RayLocs marchLocs = new(); // Fill the struct with shader locs. marchLocs.CamPos = GetShaderLocation(shdrRaymarch, "camPos"); marchLocs.CamDir = GetShaderLocation(shdrRaymarch, "camDir"); marchLocs.ScreenCenter = GetShaderLocation(shdrRaymarch, "screenCenter"); // Transfer screenCenter position to shader. Which is used to calculate ray direction. Vector2 screenCenter = new(screenWidth / 2, screenHeight / 2); SetShaderValue( shdrRaymarch, marchLocs.ScreenCenter, screenCenter, ShaderUniformDataType.SHADER_UNIFORM_VEC2 ); // Use customized function to create writable depth texture buffer RenderTexture2D target = LoadRenderTextureDepthTex(screenWidth, screenHeight); // Define the camera to look into our 3d world Camera3D camera; camera.Position = new Vector3(0.5f, 1.0f, 1.5f); camera.Target = new Vector3(0.0f, 0.5f, 0.0f); camera.Up = new Vector3(0.0f, 1.0f, 0.0f); camera.FovY = 45.0f; camera.Projection = CameraProjection.CAMERA_PERSPECTIVE; // Camera FOV is pre-calculated in the camera Distance. float camDist = 1.0f / (MathF.Tan(camera.FovY * 0.5f * Raylib.DEG2RAD)); SetTargetFPS(60); //-------------------------------------------------------------------------------------- // Main game loop while (!WindowShouldClose()) { // Update //---------------------------------------------------------------------------------- UpdateCamera(ref camera, CameraMode.CAMERA_ORBITAL); // Update Camera Postion in the ray march shader. SetShaderValue( shdrRaymarch, marchLocs.CamPos, camera.Position, ShaderUniformDataType.SHADER_UNIFORM_VEC3 ); // Update Camera Looking Vector. Vector length determines FOV. Vector3 camDir = Vector3.Normalize(camera.Target - camera.Position) * camDist; SetShaderValue(shdrRaymarch, marchLocs.CamDir, camDir, ShaderUniformDataType.SHADER_UNIFORM_VEC3); //---------------------------------------------------------------------------------- // Draw //---------------------------------------------------------------------------------- // Draw into our custom render texture (framebuffer) BeginTextureMode(target); ClearBackground(Color.WHITE); // Raymarch Scene // Manually enable Depth Test to handle multiple rendering methods. Rlgl.EnableDepthTest(); BeginShaderMode(shdrRaymarch); DrawRectangleRec(new Rectangle(0, 0, screenWidth, screenHeight), Color.WHITE); EndShaderMode(); // Rasterize Scene BeginMode3D(camera); BeginShaderMode(shdrRaster); DrawCubeWiresV(new Vector3(0.0f, 0.5f, 1.0f), new Vector3(1.0f, 1.0f, 1.0f), Color.RED); DrawCubeV(new Vector3(0.0f, 0.5f, 1.0f), new Vector3(1.0f, 1.0f, 1.0f), Color.PURPLE); DrawCubeWiresV(new Vector3(0.0f, 0.5f, -1.0f), new Vector3(1.0f, 1.0f, 1.0f), Color.DARKGREEN); DrawCubeV(new Vector3(0.0f, 0.5f, -1.0f), new Vector3(1.0f, 1.0f, 1.0f), Color.YELLOW); DrawGrid(10, 1.0f); EndShaderMode(); EndMode3D(); EndTextureMode(); // Draw custom render texture BeginDrawing(); ClearBackground(Color.RAYWHITE); DrawTextureRec( target.Texture, new Rectangle(0, 0, screenWidth, -screenHeight), Vector2.Zero, Color.WHITE ); DrawFPS(10, 10); EndDrawing(); //---------------------------------------------------------------------------------- } // De-Initialization //-------------------------------------------------------------------------------------- UnloadRenderTextureDepthTex(target); UnloadShader(shdrRaymarch); UnloadShader(shdrRaster); CloseWindow(); //-------------------------------------------------------------------------------------- return 0; } // Load custom render texture, create a writable depth texture buffer private static unsafe RenderTexture2D LoadRenderTextureDepthTex(int width, int height) { RenderTexture2D target = new(); // Load an empty framebuffer target.Id = Rlgl.LoadFramebuffer(width, height); if (target.Id > 0) { Rlgl.EnableFramebuffer(target.Id); // Create color texture (default to RGBA) target.Texture.Id = Rlgl.LoadTexture( null, width, height, PixelFormat.PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, 1 ); target.Texture.Width = width; target.Texture.Height = height; target.Texture.Format = PixelFormat.PIXELFORMAT_UNCOMPRESSED_R8G8B8A8; target.Texture.Mipmaps = 1; // Create depth texture buffer (instead of raylib default renderbuffer) target.Depth.Id = Rlgl.LoadTextureDepth(width, height, false); target.Depth.Width = width; target.Depth.Height = height; target.Depth.Format = PixelFormat.PIXELFORMAT_COMPRESSED_PVRT_RGBA; target.Depth.Mipmaps = 1; // Attach color texture and depth texture to FBO Rlgl.FramebufferAttach( target.Id, target.Texture.Id, FramebufferAttachType.RL_ATTACHMENT_COLOR_CHANNEL0, FramebufferAttachTextureType.RL_ATTACHMENT_TEXTURE2D, 0 ); Rlgl.FramebufferAttach( target.Id, target.Depth.Id, FramebufferAttachType.RL_ATTACHMENT_DEPTH, FramebufferAttachTextureType.RL_ATTACHMENT_TEXTURE2D, 0 ); // Check if fbo is complete with attachments (valid) if (Rlgl.FramebufferComplete(target.Id)) { TraceLog(TraceLogLevel.LOG_INFO, $"FBO: [ID {target.Id}] Framebuffer object created successfully"); } Rlgl.DisableFramebuffer(); } else { TraceLog(TraceLogLevel.LOG_WARNING, "FBO: Framebuffer object can not be created"); } return target; } // Unload render texture from GPU memory (VRAM) private static void UnloadRenderTextureDepthTex(RenderTexture2D target) { if (target.Id > 0) { // Color texture attached to FBO is deleted Rlgl.UnloadTexture(target.Texture.Id); Rlgl.UnloadTexture(target.Depth.Id); // NOTE: Depth texture is automatically // queried and deleted before deleting framebuffer Rlgl.UnloadFramebuffer(target.Id); } } }