diff options
| author | Patrick | 2026-10-05 17:38:38 +0200 |
|---|---|---|
| committer | Patrick | 2026-10-05 17:38:38 +0200 |
| commit | 9a7d2d44dc17603ff705be5d00416f01704459ef (patch) | |
| tree | 7014ec0089e76e513249dfdba4e9a0f55163b313 /src | |
| parent | c511ae90162f506f34ca0b98732577dc688ebcca (diff) | |
| download | sg3-9a7d2d44dc17603ff705be5d00416f01704459ef.tar.gz sg3-9a7d2d44dc17603ff705be5d00416f01704459ef.zip | |
update
Diffstat (limited to 'src')
| -rw-r--r-- | src/main.zig | 229 | ||||
| -rw-r--r-- | src/shaders/main.glsl | 97 |
2 files changed, 291 insertions, 35 deletions
diff --git a/src/main.zig b/src/main.zig index 9f68540..d714e3f 100644 --- a/src/main.zig +++ b/src/main.zig @@ -4,6 +4,8 @@ // Render to an offscreen rendertarget texture, and use this texture // for rendering to the display. //------------------------------------------------------------------------------ +const std = @import("std"); +const stbi = @import("stbi"); const sokol = @import("sokol"); const slog = sokol.log; const sg = sokol.gfx; @@ -17,12 +19,32 @@ const mat4 = @import("math.zig").Mat4; const shd = @import("shaders/main.glsl.zig"); const state = struct { + var alloc: std.mem.Allocator = undefined; + var io: std.Io = undefined; + const Width = 800; + const Height = 800; + const offscreen = struct { + var pass: sg.Pass = .{}; + var pip: sg.Pipeline = .{}; + var bind: sg.Bindings = .{}; + var vs: shd.VsParams = .{ + .position = .{ 0, 0 }, + }; + var fs: shd.FsParams = .{ + .resolution = .{ Width, Height }, + .rayCount = 16, + .maxSteps = 4, + .size = 1.0, + }; + + var pixels: [state.Width * state.Height * 4]u8 = @splat(255); + var pixels_updated: bool = true; + var pixels_img: sg.Image = undefined; + }; const display = struct { var pass_action: sg.PassAction = .{}; var pip: sg.Pipeline = .{}; var bind: sg.Bindings = .{}; - var vs: shd.VsParams = .{.position = .{0,0}}; - var fs: shd.FsParams = .{.color = .{1,0,0,1}}; }; }; @@ -32,25 +54,74 @@ export fn init() void { .logger = .{ .func = slog.func }, }); - simgui.setup(.{}); + simgui.setup(.{ + .logger = .{ .func = slog.func }, + }); sappimgui.setup(); ig.igGetIO().*.ConfigFlags |= ig.ImGuiConfigFlags_DockingEnable; - // default pass action: clear to blue-ish + state.offscreen.pass.action.colors[0] = .{ + .load_action = .LOAD, + .store_action = .STORE, + }; + state.offscreen.pass.action.depth = .{ + .load_action = .LOAD, + .store_action = .STORE, + }; + state.display.pass_action.colors[0] = .{ .load_action = .CLEAR, - .clear_value = .{ .r = 1, .g = 0, .b = 0, .a = 1.0 }, }; - const vbuf = sg.makeBuffer(.{.data = sg.asRange(&[6]f32 { + const offscreen_width = state.Width; + const offscreen_height = state.Height; + const offscreen_sample_count = 1; + const color_img = sg.makeImage(.{ + .usage = .{ .color_attachment = true }, + .width = offscreen_width, + .height = offscreen_height, + .sample_count = offscreen_sample_count, + .pixel_format = .RGBA8, + }); + state.offscreen.pixels_img = sg.makeImage(.{ + .usage = .{ .dynamic_update = true }, + .width = offscreen_width, + .height = offscreen_height, + .sample_count = offscreen_sample_count, + .pixel_format = .RGBA8, + }); + const depth_img = sg.makeImage(.{ + .usage = .{ .depth_stencil_attachment = true }, + .width = offscreen_width, + .height = offscreen_height, + .sample_count = offscreen_sample_count, + .pixel_format = .DEPTH, + }); + + state.offscreen.pass.attachments.colors[0] = sg.makeView(.{ + .color_attachment = .{ .image = color_img }, + }); + state.offscreen.pass.attachments.depth_stencil = sg.makeView(.{ + .depth_stencil_attachment = .{ .image = depth_img }, + }); + + state.offscreen.bind.views[shd.VIEW_off_tex] = sg.makeView(.{ + .texture = .{ .image = state.offscreen.pixels_img }, + }); + state.display.bind.views[shd.VIEW_scr_tex] = sg.makeView(.{ + .texture = .{ .image = color_img }, + }); + + const vbuf = sg.makeBuffer(.{ .data = sg.asRange(&[6]f32{ -1.0, -1.0, - -1.0, 3.0, - 3.0, -1.0, - })}); + -1.0, 3.0, + 3.0, -1.0, + }) }); + state.offscreen.bind.vertex_buffers[0] = vbuf; state.display.bind.vertex_buffers[0] = vbuf; // shader and pipeline object for the default render pass - state.display.pip = sg.makePipeline(.{ + state.offscreen.pip = sg.makePipeline(.{ .shader = sg.makeShader(shd.offscreenShaderDesc(sg.queryBackend())), .layout = init: { var l = sg.VertexLayoutState{}; @@ -58,44 +129,143 @@ export fn init() void { break :init l; }, .cull_mode = .BACK, + .sample_count = offscreen_sample_count, + .depth = .{ + .pixel_format = .DEPTH, + .compare = .LESS_EQUAL, + .write_enabled = true, + }, + .colors = init: { + var c: [sg.max_color_attachments]sg.ColorTargetState = @splat(.{}); + c[0].pixel_format = .RGBA8; + break :init c; + }, + }); + + state.display.pip = sg.makePipeline(.{ + .shader = sg.makeShader(shd.defaultShaderDesc(sg.queryBackend())), + .layout = init: { + var l = sg.VertexLayoutState{}; + l.attrs[shd.ATTR_default_vpos] = .{ .format = .FLOAT2 }; + break :init l; + }, + .cull_mode = .BACK, .depth = .{ .compare = .LESS_EQUAL, .write_enabled = true, }, }); + + // a sampler object for sampling the render target texture + state.offscreen.bind.samplers[shd.SMP_off_smp] = sg.makeSampler(.{ + .min_filter = .LINEAR, + .mag_filter = .LINEAR, + .wrap_u = .REPEAT, + .wrap_v = .REPEAT, + }); + state.display.bind.samplers[shd.SMP_scr_smp] = sg.makeSampler(.{ + .min_filter = .LINEAR, + .mag_filter = .LINEAR, + .wrap_u = .REPEAT, + .wrap_v = .REPEAT, + }); } export fn event(ev: [*c]const sapp.Event) void { sappimgui.trackEvent(ev.*); - // forward input events to sokol-imgui _ = simgui.handleEvent(ev.*); + + switch (ev.*.type) { + .KEY_DOWN => { + if (ev.*.key_code == .ESCAPE) { + sapp.requestQuit(); + } + }, + else => {}, + } } +// fn load_bmp(path: []const u8, buffer: []u8) !void { +// var file = try std.Io.Dir.cwd().openFile(state.io, path, .{}); +// var read_buffer: [1024]u8 = undefined; +// var reader = file.reader(state.io, &read_buffer).interface; +// var bfType = try reader.takeInt(u16, .little); +// var bfSize = try reader.takeInt(u32, .little); +// var bfReserved = try reader.takeInt(u32, .little); +// var bfOffBits = try reader.takeInt(u32, .little); +// var biSize = try reader.takeInt(u32, .little); +// var biWidth = try reader.takeInt(u32, .little); +// var biHeight = try reader.takeInt(u32, .little); +// var biPlanes = try reader.takeInt(u16, .little); +// var biBitCount = try reader.takeInt(u16, .little); +// var biCompression = try reader.takeInt(u32, .little); +// var biSizeImage = try reader.takeInt(u32, .little); +// var biXPelsPerMeter = try reader.takeInt(u32, .little); +// var biYPelsPerMeter = try reader.takeInt(u32, .little); +// var biClrUsed = try reader.takeInt(u32, .little); +// var biClrImportant = try reader.takeInt(u32, .little); +// +// if (buffer.len < biWidth * biHeight * biBitCount / 8) { +// return error.BufferTooSmall; +// } +// +// +// } + export fn frame() void { // call simgui.newFrame() before any ImGui calls + sappimgui.trackFrame(); simgui.newFrame(.{ .width = sapp.width(), .height = sapp.height(), .delta_time = sapp.frameDuration(), .dpi_scale = sapp.dpiScale(), }); - sappimgui.trackFrame(); // const dt: f32 = @floatCast(sapp.frameDuration() * 60); // const aspect = sapp.widthf() / sapp.heightf(); - - if (ig.igBegin("abc", null, 0)) - { - _ = ig.igDragFloat2("position", &state.display.vs.position); - _ = ig.igColorEdit4("color", &state.display.fs.color, 0); - ig.igEnd(); + state.offscreen.fs.resolution[0] = sapp.widthf(); + state.offscreen.fs.resolution[1] = sapp.heightf(); + + if (ig.igBegin("abc", null, 0)) { + // vs + _ = ig.igDragFloat2("position", &state.offscreen.vs.position); + + // fs + _ = ig.igInputInt("rayCount", &state.offscreen.fs.rayCount); + _ = ig.igInputInt("maxSteps", &state.offscreen.fs.maxSteps); + _ = ig.igDragFloatEx("size", &state.offscreen.fs.size, 0.1, 0, 100, "%f", 0); + + if (ig.igButton("load png")) { + var x: c_int = 0; + var y: c_int = 0; + var n: c_int = 0; + const img = stbi.stbi_load("test.png", &x, &y, &n, 0); + @memcpy(&state.offscreen.pixels, img); + stbi.stbi_image_free(img); + state.offscreen.pixels_updated = true; + } + } + ig.igEnd(); + + if (state.offscreen.pixels_updated) { + state.offscreen.pixels_updated = false; + var imageData: sg.ImageData = .{}; + imageData.mip_levels[0] = sg.asRange(&state.offscreen.pixels); + sg.updateImage(state.offscreen.pixels_img, imageData); } + sg.beginPass(state.offscreen.pass); + sg.applyPipeline(state.offscreen.pip); + sg.applyBindings(state.offscreen.bind); + sg.applyUniforms(shd.UB_vs_params, sg.asRange(&state.offscreen.vs)); + sg.applyUniforms(shd.UB_fs_params, sg.asRange(&state.offscreen.fs)); + sg.draw(0, 3, 1); + sg.endPass(); + sg.beginPass(.{ .action = state.display.pass_action, .swapchain = sglue.swapchain() }); sg.applyPipeline(state.display.pip); sg.applyBindings(state.display.bind); - sg.applyUniforms(shd.UB_vs_params, sg.asRange(&state.display.vs)); - sg.applyUniforms(shd.UB_fs_params, sg.asRange(&state.display.fs)); sg.draw(0, 3, 1); simgui.render(); sg.endPass(); @@ -109,27 +279,20 @@ export fn cleanup() void { sg.shutdown(); } -pub fn main() void { +pub fn main(_init: std.process.Init) void { + state.alloc = _init.arena.allocator(); + state.io = _init.io; + sapp.run(.{ .init_cb = init, .frame_cb = frame, .event_cb = event, .cleanup_cb = cleanup, .sample_count = 4, - .width = 800, - .height = 600, + .width = state.Width, + .height = state.Height, .icon = .{ .sokol_default = true }, .window_title = "offscreen.zig", .logger = .{ .func = slog.func }, }); } - -fn computeVsParams(rx: f32, ry: f32, aspect: f32, eye_dist: f32) shd.VsParams { - const proj = mat4.persp(45, aspect, 0.01, 10); - const view = mat4.lookat(.{ .x = 0, .y = 0, .z = eye_dist }, vec3.zero(), vec3.up()); - const view_proj = mat4.mul(proj, view); - const rxm = mat4.rotate(rx, .{ .x = 1, .y = 0, .z = 0 }); - const rym = mat4.rotate(ry, .{ .x = 0, .y = 1, .z = 0 }); - const model = mat4.mul(rxm, rym); - return shd.VsParams{ .mvp = mat4.mul(view_proj, model) }; -} diff --git a/src/shaders/main.glsl b/src/shaders/main.glsl index 4e2d4ea..784176f 100644 --- a/src/shaders/main.glsl +++ b/src/shaders/main.glsl @@ -11,22 +11,115 @@ layout(binding=0) uniform vs_params { }; in vec2 vpos; +out vec2 fpos; void main() { gl_Position = vec4(vpos + position, 0, 1); + fpos = (vpos + 1) * 0.5; } @end @fs fs_offscreen +layout(binding=1) uniform texture2D off_tex; +layout(binding=1) uniform sampler off_smp; layout(binding=1) uniform fs_params { - vec4 color; + vec2 resolution; + int rayCount; + int maxSteps; + float size; }; +in vec2 fpos; out vec4 frag_color; +bool outOfBounds(vec2 uv) { + return uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0; +} + +#define TAU 6.28318530718 +// #define RAYCOUNT 16 +// #define MAXSTEPS 8 + +float rand(vec2 seed) +{ + return fract(sin(dot(seed.xy, vec2(12.9898, 78.233))) * 43758.5453); +} + +vec4 raymarch() { + vec2 scale = resolution.xy / resolution.xy; + + // sample texture at pixel + vec4 light = textureLod(sampler2D(off_tex, off_smp), fpos * scale, 0); + // if it is a light source, color that pixel in this color + if (light.a > 0.1) + { + return light; + } + + float oneOverRayCount = 1.0 / float(rayCount); + float tauOverRayCount = TAU * oneOverRayCount; + + float noise = rand(fpos); + + vec4 radiance = vec4(0.0); + + for (int i = 0; i < rayCount; i++) + { + float angle = tauOverRayCount * (float(i) + noise); + vec2 rayDirectionUv = vec2(cos(angle), -sin(angle)) / size; + + vec2 sampleUv = fpos + rayDirectionUv; + + for (int step = 0; step < maxSteps; step++) + { + if (outOfBounds(sampleUv)) + break; + + vec4 sampleLight = textureLod(sampler2D(off_tex, off_smp), sampleUv, 0); + if (sampleLight.a > 0.1) + { + radiance += sampleLight; + break; + } + + sampleUv += rayDirectionUv; + } + } + + return radiance * oneOverRayCount; +} + void main() { - frag_color = color; + frag_color = raymarch(); } @end @program offscreen vs_offscreen fs_offscreen + + +// default-pass shaders +@vs vs_default + +in vec2 vpos; +out vec2 fpos; + +void main() { + gl_Position = vec4(vpos, 0, 1); + fpos = (vpos + 1) * 0.5; +} +@end + +@fs fs_default +layout(binding=0) uniform texture2D scr_tex; +layout(binding=0) uniform sampler scr_smp; + +in vec2 fpos; + +out vec4 frag_color; + +void main() { + frag_color = texture(sampler2D(scr_tex, scr_smp), fpos); +} +@end + +@program default vs_default fs_default |
