2025-04-26 10:51:41 +03:00

221 lines
7.8 KiB
Plaintext

//------------------------------------------------------------------------------
// offscreen/main.jai
//
// Render to an offscreen rendertarget texture, and use this texture
// for rendering to the display.
//------------------------------------------------------------------------------
#import "Basic";
#import,dir "../../sokol/log"(USE_GL=USE_GL);
#import,dir "../../sokol/gfx"(USE_GL=USE_GL);
#import,dir "../../sokol/app"(USE_GL=USE_GL);
#import,dir "../../sokol/glue"(USE_GL=USE_GL);
#import,dir "../../sokol/shape"(USE_GL=USE_GL);
#load "../math.jai";
#load "./shader.jai";
OFFSCREEN_SAMPLE_COUNT :: 1;
state: struct {
offscreen: struct {
pass_action: sg_pass_action;
attachments: sg_attachments;
pip: sg_pipeline;
bind: sg_bindings;
};
default: struct {
pass_action: sg_pass_action;
pip: sg_pipeline;
bind: sg_bindings;
};
donut: sshape_element_range_t;
sphere: sshape_element_range_t;
rx, ry: float;
vertices: [4000]sshape_vertex_t;
indices: [24000]u16;
}
init :: () #c_call {
push_context,defer_pop;
sg_setup(*(sg_desc.{
environment = xx,force sglue_environment(),
logger = .{ func = slog_func },
}));
// default pass action: clear to blue-ish
state.default.pass_action = .{
colors[0] = .{ load_action = .CLEAR, clear_value = .{ 0.25, 0.45, 0.65, 1.0 } },
};
// offscreen pass action: clear to grey
state.offscreen.pass_action = .{
colors[0] = .{ load_action = .CLEAR, clear_value = .{ 0.25, 0.25, 0.25, 1.0 } },
};
// a render pass with one color- and one depth-attachment image
img_desc := sg_image_desc.{
render_target = true,
width = 256,
height = 256,
pixel_format = .RGBA8,
sample_count = OFFSCREEN_SAMPLE_COUNT,
};
color_img := sg_make_image(*img_desc);
img_desc.pixel_format = .DEPTH;
depth_img := sg_make_image(*img_desc);
state.offscreen.attachments = sg_make_attachments(*(sg_attachments_desc.{
colors[0] = .{ image = color_img },
depth_stencil = .{
image = depth_img,
},
}));
// a donut shape which is rendered into the offscreen render target, and
// a sphere shape which is rendered into the default framebuffer
buf := sshape_buffer_t.{
vertices = .{ buffer = .{ ptr = *state.vertices, size = size_of(type_of(state.vertices)) } },
indices = .{ buffer = .{ ptr = *state.indices, size = size_of(type_of(state.indices)) } },
};
buf = sshape_build_torus(*buf, *(sshape_torus_t.{
radius = 0.5,
ring_radius = 0.3,
sides = 20,
rings = 30,
}));
state.donut = sshape_element_range(*buf);
buf = sshape_build_sphere(*buf, *(sshape_sphere_t.{
radius = 0.5,
slices = 72,
stacks = 40,
}));
state.sphere = sshape_element_range(*buf);
vbuf := sg_make_buffer(xx,force *sshape_vertex_buffer_desc(*buf));
ibuf := sg_make_buffer(xx,force *sshape_index_buffer_desc(*buf));
// pipeline-state-object for offscreen-rendered donut, don't need texture coord here
offscreen_pip_desc := sg_pipeline_desc.{
shader = sg_make_shader(*offscreen_shader_desc(sg_query_backend())),
index_type = .UINT16,
cull_mode = .BACK,
sample_count = OFFSCREEN_SAMPLE_COUNT,
depth = .{
pixel_format = .DEPTH,
compare = .LESS_EQUAL,
write_enabled = true,
},
colors[0] = .{ pixel_format = .RGBA8 },
};
offscreen_pip_desc.layout.buffers[0] = xx,force sshape_vertex_buffer_layout_state();
offscreen_pip_desc.layout.attrs[ATTR_offscreen_position] = xx,force sshape_position_vertex_attr_state();
offscreen_pip_desc.layout.attrs[ATTR_offscreen_normal] = xx,force sshape_normal_vertex_attr_state();
state.offscreen.pip = sg_make_pipeline(*offscreen_pip_desc);
// and another pipeline-state-object for the default pass
default_pip_desc := sg_pipeline_desc.{
shader = sg_make_shader(*default_shader_desc(sg_query_backend())),
index_type = .UINT16,
cull_mode = .BACK,
depth = .{
compare = .LESS_EQUAL,
write_enabled = true,
},
};
default_pip_desc.layout.buffers[0] = xx,force sshape_vertex_buffer_layout_state();
default_pip_desc.layout.attrs[ATTR_default_position] = xx,force sshape_position_vertex_attr_state();
default_pip_desc.layout.attrs[ATTR_default_normal] = xx,force sshape_normal_vertex_attr_state();
default_pip_desc.layout.attrs[ATTR_default_texcoord0] = xx,force sshape_texcoord_vertex_attr_state();
state.default.pip = sg_make_pipeline(*default_pip_desc);
// a sampler object for sampling the render target as texture
smp := sg_make_sampler(*(sg_sampler_desc.{
min_filter = .LINEAR,
mag_filter = .LINEAR,
wrap_u = .REPEAT,
wrap_v = .REPEAT,
}));
// the resource bindings for rendering a non-textured cube into offscreen render target
state.offscreen.bind = .{
vertex_buffers[0] = vbuf,
index_buffer = ibuf,
};
// resource bindings to render a textured shape, using the offscreen render target as texture
state.default.bind = .{
vertex_buffers[0] = vbuf,
index_buffer = ibuf,
};
state.default.bind.images[IMG_tex] = color_img;
state.default.bind.samplers[SMP_smp] = smp;
}
frame :: () #c_call {
push_context,defer_pop;
t := cast(float) (sapp_frame_duration() * 60.0);
state.rx += 1.0 * t;
state.ry += 2.0 * t;
// the offscreen pass, rendering an rotating, untextured donut into a render target image
vs_params := Vs_Params.{
mvp = compute_mvp(rx = state.rx, ry = state.ry, aspect = 1.0, eye_dist = 2.5),
};
sg_begin_pass(*(sg_pass.{ action = state.offscreen.pass_action, attachments = state.offscreen.attachments }));
sg_apply_pipeline(state.offscreen.pip);
sg_apply_bindings(*state.offscreen.bind);
sg_apply_uniforms(UB_vs_params, *(sg_range.{ ptr = *vs_params, size = size_of(type_of(vs_params)) }));
sg_draw(state.donut.base_element, state.donut.num_elements, 1);
sg_end_pass();
// and the default-pass, rendering a rotating textured sphere which uses the
// previously rendered offscreen render-target as texture
vs_params = Vs_Params.{
mvp = compute_mvp(
rx = -state.rx * 0.25,
ry = state.ry * 0.25,
aspect = sapp_widthf() / sapp_heightf(),
eye_dist = 2.0,
),
};
sg_begin_pass(*(sg_pass.{ action = state.default.pass_action, swapchain = xx,force sglue_swapchain() }));
sg_apply_pipeline(state.default.pip);
sg_apply_bindings(*state.default.bind);
sg_apply_uniforms(UB_vs_params, *(sg_range.{ ptr = *vs_params, size = size_of(type_of(vs_params)) }));
sg_draw(state.sphere.base_element, state.sphere.num_elements, 1);
sg_end_pass();
sg_commit();
}
compute_mvp :: (rx: float, ry: float, aspect: float, eye_dist: float) -> Matrix4 {
proj := persp_mat4(fov = 45.0, aspect = aspect, near = 0.01, far = 10.0);
view := lookat_mat4(eye = .{ 0.0, 0.0, eye_dist }, center = .{}, up = .{ 0.0, 1.0, 0.0 });
view_proj := multiply_mat4(proj, view);
rxm := rotate_mat4(rx, .{ 1.0, 0.0, 0.0 });
rym := rotate_mat4(ry, .{ 0.0, 1.0, 0.0 });
model := multiply_mat4(rym, rxm);
mvp := multiply_mat4(view_proj, model);
return mvp;
}
cleanup :: () #c_call {
sg_shutdown();
}
main :: () {
sapp_run(*(sapp_desc.{
init_cb = init,
frame_cb = frame,
cleanup_cb = cleanup,
width = 800,
height = 600,
sample_count = 4,
window_title = "offscreen",
icon = .{ sokol_default = true },
logger = .{ func = slog_func },
}));
}