refactor(render3d): render targets and passes behind gpu.ludic
Framebuffers and their attachments, renderbuffers (multisampled too), draw and read buffers, completeness checks, blits, viewports, clears, the screen framebuffer, the multisample enable, the wireframe switch and GL error checks now go through gpu_fb_* / gpu_rb_* / gpu_viewport / gpu_clear / gpu_blit / gpu_check, and no other file names them. Each call is the one GL call it replaces, in the same order: the fixed viewpoints render bit-identically and the game's self-tests report exactly what they did. What is attached to each framebuffer - colour slots, a depth texture or one layer of an array, renderbuffers and their samples - is recorded as it is attached, for a backend that builds render passes and image views. gpu_read_screen is the frame read-back a photograph takes. R3D_GLCHECK=1 checks, around every draw, clear and blit, that the bound framebuffer is complete and that no error is left behind, naming the framebuffer. It has already narrowed the old "gl error 1286 at terrain shadow bake": the error is pending before a draw after the map self- test, so it comes from a call that is not a draw, clear or blit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
4d3479d431
commit
78c1f4f9ca
10 changed files with 294 additions and 150 deletions
|
|
@ -13,6 +13,7 @@
|
|||
# - uniforms: looked up by program and name (gpu_uniform), set by the u_* setters
|
||||
# - vertex data: meshes, their attribute layouts, instance and stream buffers, draws
|
||||
# - textures: creation, upload, sampler state, mipmaps, units, read-back, freeing
|
||||
# - render targets and passes: framebuffers, attachments, blits, viewports, clears, the screen
|
||||
#
|
||||
# Render state is cached. A pipeline API bakes this state into an object picked by
|
||||
# key; OpenGL gets the same effect by only telling the driver what changed. The
|
||||
|
|
@ -430,17 +431,25 @@ function gpu_buffer_free(buf: int) -> void {
|
|||
function gpu_mesh_bind(m: Mesh) -> void { gl_bind_vertex_array(m.vao) }
|
||||
function gpu_mesh_unbind() -> void { gl_bind_vertex_array(0) }
|
||||
function gpu_draw_mesh(m: Mesh) -> void {
|
||||
gpu_glcheck_before("a draw")
|
||||
gl_bind_vertex_array(m.vao)
|
||||
if m.ebo != 0 { gl_draw_elements(m.mode, m.count, m.itype, null) }
|
||||
else { gl_draw_arrays(m.mode, 0, m.count) }
|
||||
gpu_glcheck_after("a draw")
|
||||
}
|
||||
function gpu_draw_mesh_instanced(m: Mesh, n: int) -> void {
|
||||
gpu_glcheck_before("an instanced draw")
|
||||
gl_bind_vertex_array(m.vao)
|
||||
if m.ebo != 0 { gl_draw_elements_instanced(m.mode, m.count, m.itype, null, n) }
|
||||
else { gl_draw_arrays_instanced(m.mode, 0, m.count, n) }
|
||||
gpu_glcheck_after("an instanced draw")
|
||||
}
|
||||
# the bound mesh's indices again (a patch mesh drawn once per terrain node)
|
||||
function gpu_draw_bound_elements(m: Mesh) -> void { gl_draw_elements(m.mode, m.count, m.itype, null) }
|
||||
function gpu_draw_bound_elements(m: Mesh) -> void {
|
||||
gpu_glcheck_before("a terrain patch")
|
||||
gl_draw_elements(m.mode, m.count, m.itype, null)
|
||||
gpu_glcheck_after("a terrain patch")
|
||||
}
|
||||
# vertices [first, first + count) of the bound mesh, as triangles (the overlay's ranges)
|
||||
function gpu_draw_range(m: Mesh, first: int, count: int) -> void { gl_draw_arrays(GL_TRIANGLES, first, count) }
|
||||
|
||||
|
|
@ -547,3 +556,166 @@ function gpu_bind_sampler(prog: int, name: string, unit: int, kind: int, tex: in
|
|||
gpu_tex_bind(kind, tex)
|
||||
u_i(gpu_uniform(prog, name), unit)
|
||||
}
|
||||
|
||||
# ---- render targets and passes -------------------------------------------------------------
|
||||
# A framebuffer is a handle (OpenGL's name). What is attached to it - colour textures in slots,
|
||||
# a depth texture or one layer of an array, renderbuffers and their sample count - is recorded as
|
||||
# it is attached, which is what a backend with render passes and image views builds from. Each
|
||||
# call is the GL call it replaces, in the renderer's order.
|
||||
#
|
||||
# gpu_check(tag) reports a pending error under a name, as gl_check did. R3D_GLCHECK=1 adds a
|
||||
# completeness check of the bound framebuffer whenever a viewport is set, naming the handle, so
|
||||
# a pass drawing into an incomplete target says which one; unset, it calls nothing.
|
||||
const GPU_FB_W: int = 8 # per handle: colour 0, colour 1, depth texture, depth layer + 1, colour rb, depth rb, samples, colour layer + 1
|
||||
var gpu_fb: words = null
|
||||
var gpu_fb_cap: int = 0
|
||||
var gpu_fb_cur: int = 0
|
||||
var gpu_glcheck: int = -1
|
||||
var gpu_rb_samples: int = 0
|
||||
var gpu_drawbufs: words = null
|
||||
|
||||
function gpu_fb_at(fb: int) -> int {
|
||||
if fb <= 0 { return -1 }
|
||||
if fb >= gpu_fb_cap {
|
||||
var cap = gpu_fb_cap * 2
|
||||
if cap < 64 { cap = 64 }
|
||||
while cap <= fb { cap = cap * 2 }
|
||||
let t = words(cap * GPU_FB_W)
|
||||
for i in 0 .. cap * GPU_FB_W { t[i] = 0 }
|
||||
if gpu_fb != null { mem_copy(t, gpu_fb, gpu_fb_cap * GPU_FB_W * 4); free(gpu_fb) }
|
||||
gpu_fb = t
|
||||
gpu_fb_cap = cap
|
||||
}
|
||||
return fb * GPU_FB_W
|
||||
}
|
||||
function gpu_glcheck_on() -> bool {
|
||||
if gpu_glcheck < 0 { gpu_glcheck = 0; if Os.has_env("R3D_GLCHECK") { gpu_glcheck = 1 } }
|
||||
return gpu_glcheck == 1
|
||||
}
|
||||
function gpu_check(tag: string) -> int { return gl_check(tag) }
|
||||
# a named checkpoint that costs nothing unless R3D_GLCHECK is set
|
||||
function gpu_debug_check(tag: string) -> void { if gpu_glcheck_on() { gl_check(tag) } }
|
||||
|
||||
function gpu_fb_new() -> int { return gl_framebuffer() }
|
||||
function gpu_fb_bind(fb: int) -> void {
|
||||
gl_bind_framebuffer(GL_FRAMEBUFFER, fb)
|
||||
gpu_fb_cur = fb
|
||||
}
|
||||
function gpu_fb_bind_read(fb: int) -> void { gl_bind_framebuffer(GL_READ_FRAMEBUFFER, fb) }
|
||||
function gpu_fb_bind_draw(fb: int) -> void { gl_bind_framebuffer(GL_DRAW_FRAMEBUFFER, fb) }
|
||||
function gpu_fb_color(slot: int, tex: int) -> void {
|
||||
gl_framebuffer_texture2d(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + slot, GL_TEXTURE_2D, tex, 0)
|
||||
let o = gpu_fb_at(gpu_fb_cur)
|
||||
if o >= 0 and slot < 2 { gpu_fb[o + slot] = tex; if slot == 0 { gpu_fb[o + 7] = 0 } }
|
||||
}
|
||||
function gpu_fb_color_layer(slot: int, tex: int, layer: int) -> void {
|
||||
gl_framebuffer_texture_layer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + slot, tex, 0, layer)
|
||||
let o = gpu_fb_at(gpu_fb_cur)
|
||||
if o >= 0 and slot < 2 { gpu_fb[o + slot] = tex; if slot == 0 { gpu_fb[o + 7] = layer + 1 } }
|
||||
}
|
||||
function gpu_fb_depth(tex: int) -> void {
|
||||
gl_framebuffer_texture2d(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, tex, 0)
|
||||
let o = gpu_fb_at(gpu_fb_cur)
|
||||
if o >= 0 { gpu_fb[o + 2] = tex; gpu_fb[o + 3] = 0 }
|
||||
}
|
||||
function gpu_fb_depth_layer(tex: int, layer: int) -> void {
|
||||
gl_framebuffer_texture_layer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, tex, 0, layer)
|
||||
let o = gpu_fb_at(gpu_fb_cur)
|
||||
if o >= 0 { gpu_fb[o + 2] = tex; gpu_fb[o + 3] = layer + 1 }
|
||||
}
|
||||
function gpu_rb_new() -> int {
|
||||
let ids = gpu_tmp()
|
||||
gl_gen_renderbuffers(1, ids)
|
||||
return ids[0]
|
||||
}
|
||||
# storage for a renderbuffer: samples > 0 makes it multisampled
|
||||
function gpu_rb_storage(rb: int, ifmt: int, w: int, h: int, samples: int) -> void {
|
||||
gl_bind_renderbuffer(GL_RENDERBUFFER, rb)
|
||||
if samples > 0 { gl_renderbuffer_storage_multisample(GL_RENDERBUFFER, samples, ifmt, w, h) }
|
||||
else { gl_renderbuffer_storage(GL_RENDERBUFFER, ifmt, w, h) }
|
||||
gpu_rb_samples = samples
|
||||
}
|
||||
function gpu_fb_color_rb(slot: int, rb: int) -> void {
|
||||
gl_framebuffer_renderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + slot, GL_RENDERBUFFER, rb)
|
||||
let o = gpu_fb_at(gpu_fb_cur)
|
||||
if o >= 0 { gpu_fb[o + 4] = rb; gpu_fb[o + 6] = gpu_rb_samples }
|
||||
}
|
||||
function gpu_fb_depth_rb(rb: int) -> void {
|
||||
gl_framebuffer_renderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rb)
|
||||
let o = gpu_fb_at(gpu_fb_cur)
|
||||
if o >= 0 { gpu_fb[o + 5] = rb; gpu_fb[o + 6] = gpu_rb_samples }
|
||||
}
|
||||
# colour slots 0 .. n-1 are drawn into (several: an MRT bake)
|
||||
function gpu_fb_draw_buffers(n: int) -> void {
|
||||
if gpu_drawbufs == null { gpu_drawbufs = words(8) }
|
||||
for i in 0 .. n { gpu_drawbufs[i] = GL_COLOR_ATTACHMENT0 + i }
|
||||
gl_draw_buffers(n, gpu_drawbufs)
|
||||
}
|
||||
# a depth-only target: no colour is drawn or read
|
||||
function gpu_fb_no_color() -> void {
|
||||
gl_draw_buffer(GL_NONE)
|
||||
gl_read_buffer(GL_NONE)
|
||||
}
|
||||
function gpu_fb_status() -> int { return gl_check_framebuffer_status(GL_FRAMEBUFFER) }
|
||||
function gpu_fb_free(fb: int) -> void {
|
||||
if fb == 0 { return }
|
||||
let ids = gpu_tmp()
|
||||
ids[0] = fb
|
||||
gl_delete_framebuffers(1, ids)
|
||||
let o = gpu_fb_at(fb)
|
||||
if o >= 0 { for i in 0 .. GPU_FB_W { gpu_fb[o + i] = 0 } }
|
||||
}
|
||||
function gpu_rb_free(rb: int) -> void {
|
||||
if rb == 0 { return }
|
||||
let ids = gpu_tmp()
|
||||
ids[0] = rb
|
||||
gl_delete_renderbuffers(1, ids)
|
||||
}
|
||||
function gpu_viewport(x: int, y: int, w: int, h: int) -> void { gl_viewport(x, y, w, h) }
|
||||
|
||||
# R3D_GLCHECK: before a draw or a clear, the bound framebuffer must be complete; after it, no
|
||||
# error may be pending. Each report names the framebuffer and what was being done, once per
|
||||
# distinct message, so the first bad pass is found without a flood. (Checking when a viewport
|
||||
# is set reported the shadow pass, which sets its viewport before attaching a cascade.)
|
||||
var gpu_glcheck_seen: []string = null
|
||||
function gpu_glcheck_say(msg: string) -> void {
|
||||
if gpu_glcheck_seen == null { gpu_glcheck_seen = new []string }
|
||||
for i in 0 .. len(gpu_glcheck_seen) { if gpu_glcheck_seen[i] == msg { return } }
|
||||
push(gpu_glcheck_seen, msg)
|
||||
print(msg)
|
||||
}
|
||||
function gpu_glcheck_before(what: string) -> void {
|
||||
if not gpu_glcheck_on() { return }
|
||||
let pending = gl_get_error()
|
||||
if pending != 0 { gpu_glcheck_say(`gpu: error {pending} pending before {what} into framebuffer {gpu_fb_cur}`) }
|
||||
let st = gl_check_framebuffer_status(GL_FRAMEBUFFER)
|
||||
if st != GL_FRAMEBUFFER_COMPLETE { gpu_glcheck_say(`gpu: framebuffer {gpu_fb_cur} incomplete ({st}) at {what}`) }
|
||||
}
|
||||
function gpu_glcheck_after(what: string) -> void {
|
||||
if not gpu_glcheck_on() { return }
|
||||
let e = gl_get_error()
|
||||
if e != 0 { gpu_glcheck_say(`gpu: error {e} from {what} into framebuffer {gpu_fb_cur}`) }
|
||||
}
|
||||
function gpu_clear_color(r: fixed, g: fixed, b: fixed, a: fixed) -> void { gl_clear_color(r, g, b, a) }
|
||||
function gpu_clear(mask: int) -> void {
|
||||
gpu_glcheck_before("a clear")
|
||||
gl_clear(mask)
|
||||
gpu_glcheck_after("a clear")
|
||||
}
|
||||
# the bound read framebuffer's [0, w) x [0, h) into the bound draw framebuffer's, unscaled
|
||||
function gpu_blit(w: int, h: int, mask: int) -> void {
|
||||
gl_blit_framebuffer(0, 0, w, h, 0, 0, w, h, mask, GL_NEAREST)
|
||||
gpu_glcheck_after("a blit")
|
||||
}
|
||||
# the framebuffer the finished frame is presented from (an offscreen one, headless)
|
||||
function gpu_screen_fb() -> int { return gl_screen }
|
||||
function gpu_multisample(on: bool) -> void { gpu_gl_cap(GL_MULTISAMPLE, gpu_b(on)) }
|
||||
function gpu_wireframe(on: bool) -> void {
|
||||
if on { gl_polygon_mode(GL_FRONT_AND_BACK, GL_LINE) } else { gl_polygon_mode(GL_FRONT_AND_BACK, GL_FILL) }
|
||||
}
|
||||
# the presented frame as RGB8, bottom row first (a photograph)
|
||||
function gpu_read_screen(w: int, h: int, out: pointer) -> void {
|
||||
gl_bind_framebuffer(GL_READ_FRAMEBUFFER, gl_screen)
|
||||
gl_pixel_storei(GL_PACK_ALIGNMENT, 1)
|
||||
gl_read_pixels(0, 0, w, h, GL_RGB, GL_UNSIGNED_BYTE, out)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -155,8 +155,8 @@ function ov_glyph(cp: int) -> int {
|
|||
# start drawing onto the screen: blending on, depth off
|
||||
function ov_begin() -> void {
|
||||
if not ov_ready { return }
|
||||
gl_bind_framebuffer(GL_FRAMEBUFFER, gl_screen_fbo())
|
||||
gl_viewport(0, 0, gl_w, gl_h)
|
||||
gpu_fb_bind(gpu_screen_fb())
|
||||
gpu_viewport(0, 0, gl_w, gl_h)
|
||||
gpu_depth_test(false)
|
||||
gpu_cull(false)
|
||||
gpu_blend(true)
|
||||
|
|
|
|||
|
|
@ -59,20 +59,17 @@ function post_init(w: int, h: int) -> void {
|
|||
post_w = w; post_h = h
|
||||
post_hdr = target_new(w, h, GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT, true, GL_LINEAR)
|
||||
if post_ms_samples > 1 {
|
||||
post_ms_fbo = gl_framebuffer()
|
||||
gl_bind_framebuffer(GL_FRAMEBUFFER, post_ms_fbo)
|
||||
let ids = gl_scratch()
|
||||
gl_gen_renderbuffers(1, ids)
|
||||
gl_bind_renderbuffer(GL_RENDERBUFFER, ids[0])
|
||||
gl_renderbuffer_storage_multisample(GL_RENDERBUFFER, post_ms_samples, GL_RGBA16F, w, h)
|
||||
gl_framebuffer_renderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, ids[0])
|
||||
gl_gen_renderbuffers(1, ids)
|
||||
gl_bind_renderbuffer(GL_RENDERBUFFER, ids[0])
|
||||
gl_renderbuffer_storage_multisample(GL_RENDERBUFFER, post_ms_samples, GL_DEPTH_COMPONENT32F, w, h)
|
||||
gl_framebuffer_renderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, ids[0])
|
||||
let st = gl_check_framebuffer_status(GL_FRAMEBUFFER)
|
||||
post_ms_fbo = gpu_fb_new()
|
||||
gpu_fb_bind(post_ms_fbo)
|
||||
let rbc = gpu_rb_new()
|
||||
gpu_rb_storage(rbc, GL_RGBA16F, w, h, post_ms_samples)
|
||||
gpu_fb_color_rb(0, rbc)
|
||||
let rbd = gpu_rb_new()
|
||||
gpu_rb_storage(rbd, GL_DEPTH_COMPONENT32F, w, h, post_ms_samples)
|
||||
gpu_fb_depth_rb(rbd)
|
||||
let st = gpu_fb_status()
|
||||
if st != GL_FRAMEBUFFER_COMPLETE { print(`r3d: msaa framebuffer incomplete {st}`); post_ms_fbo = 0 }
|
||||
gl_bind_framebuffer(GL_FRAMEBUFFER, 0)
|
||||
gpu_fb_bind(0)
|
||||
}
|
||||
post_bloom = new []Target
|
||||
var bw = w / 2; var bh = h / 2
|
||||
|
|
@ -163,28 +160,28 @@ function post_measure() -> void {
|
|||
|
||||
function post_begin_scene() -> void {
|
||||
target_bind(post_hdr)
|
||||
if post_ms_fbo != 0 { gl_bind_framebuffer(GL_FRAMEBUFFER, post_ms_fbo); gl_enable(GL_MULTISAMPLE) }
|
||||
if post_ms_fbo != 0 { gpu_fb_bind(post_ms_fbo); gpu_multisample(true) }
|
||||
gpu_depth_test(true)
|
||||
gpu_depth_func(GL_LESS)
|
||||
gpu_depth_write(true)
|
||||
gpu_cull(true)
|
||||
gpu_cull_face(GL_BACK)
|
||||
gl_clear_color(0.0, 0.0, 0.0, 1.0)
|
||||
gl_clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
|
||||
gpu_clear_color(0.0, 0.0, 0.0, 1.0)
|
||||
gpu_clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
|
||||
}
|
||||
|
||||
# resolve the multisampled scene into the plain HDR target (colour + depth)
|
||||
function post_resolve() -> void {
|
||||
if post_ms_fbo != 0 {
|
||||
gl_bind_framebuffer(GL_READ_FRAMEBUFFER, post_ms_fbo)
|
||||
gl_bind_framebuffer(GL_DRAW_FRAMEBUFFER, post_hdr.fbo)
|
||||
gl_blit_framebuffer(0, 0, post_w, post_h, 0, 0, post_w, post_h, GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, GL_NEAREST)
|
||||
gpu_fb_bind_read(post_ms_fbo)
|
||||
gpu_fb_bind_draw(post_hdr.fbo)
|
||||
gpu_blit(post_w, post_h, GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
|
||||
}
|
||||
# the depth copy every pass after this may read while the frame is still being drawn into
|
||||
gl_bind_framebuffer(GL_READ_FRAMEBUFFER, post_hdr.fbo)
|
||||
gl_bind_framebuffer(GL_DRAW_FRAMEBUFFER, post_depth_copy.fbo)
|
||||
gl_blit_framebuffer(0, 0, post_w, post_h, 0, 0, post_w, post_h, GL_DEPTH_BUFFER_BIT, GL_NEAREST)
|
||||
gl_bind_framebuffer(GL_FRAMEBUFFER, 0)
|
||||
gpu_fb_bind_read(post_hdr.fbo)
|
||||
gpu_fb_bind_draw(post_depth_copy.fbo)
|
||||
gpu_blit(post_w, post_h, GL_DEPTH_BUFFER_BIT)
|
||||
gpu_fb_bind(0)
|
||||
}
|
||||
|
||||
# There is no temporal anti-aliasing. It was reprojecting every pixel through the
|
||||
|
|
@ -200,19 +197,19 @@ function post_resolve() -> void {
|
|||
# sheet laid over the ground: the bottom is seen THROUGH the water, tinted and dimmed by
|
||||
# how far the light travelled, instead of being the dry terrain showing through an alpha.
|
||||
function post_capture_scene() -> void {
|
||||
gl_bind_framebuffer(GL_READ_FRAMEBUFFER, post_hdr.fbo)
|
||||
gl_bind_framebuffer(GL_DRAW_FRAMEBUFFER, post_scene.fbo)
|
||||
gl_blit_framebuffer(0, 0, post_w, post_h, 0, 0, post_w, post_h, GL_COLOR_BUFFER_BIT, GL_NEAREST)
|
||||
gl_bind_framebuffer(GL_FRAMEBUFFER, post_hdr.fbo)
|
||||
gl_viewport(0, 0, post_w, post_h)
|
||||
gpu_fb_bind_read(post_hdr.fbo)
|
||||
gpu_fb_bind_draw(post_scene.fbo)
|
||||
gpu_blit(post_w, post_h, GL_COLOR_BUFFER_BIT)
|
||||
gpu_fb_bind(post_hdr.fbo)
|
||||
gpu_viewport(0, 0, post_w, post_h)
|
||||
}
|
||||
|
||||
# Keep a copy of the finished scene colour: the SSGI bounce reads last frame's colour.
|
||||
function post_capture_prev() -> void {
|
||||
gl_bind_framebuffer(GL_READ_FRAMEBUFFER, post_hdr.fbo)
|
||||
gl_bind_framebuffer(GL_DRAW_FRAMEBUFFER, post_prev.fbo)
|
||||
gl_blit_framebuffer(0, 0, post_w, post_h, 0, 0, post_w, post_h, GL_COLOR_BUFFER_BIT, GL_NEAREST)
|
||||
gl_bind_framebuffer(GL_FRAMEBUFFER, 0)
|
||||
gpu_fb_bind_read(post_hdr.fbo)
|
||||
gpu_fb_bind_draw(post_prev.fbo)
|
||||
gpu_blit(post_w, post_h, GL_COLOR_BUFFER_BIT)
|
||||
gpu_fb_bind(0)
|
||||
post_frame += 1
|
||||
}
|
||||
|
||||
|
|
@ -295,8 +292,8 @@ function post_tonemap(color_tex: int) -> void {
|
|||
u_f3(gpu_uniform(post_p_tone, "u_gain"), fl(0.99), fl(0.995), fl(1.0))
|
||||
mesh_draw(post_fs)
|
||||
# sharpen + grain onto the screen
|
||||
gl_bind_framebuffer(GL_FRAMEBUFFER, gl_screen)
|
||||
gl_viewport(0, 0, gl_w, gl_h)
|
||||
gpu_fb_bind(gpu_screen_fb())
|
||||
gpu_viewport(0, 0, gl_w, gl_h)
|
||||
gl_use_program(post_p_sharp)
|
||||
r3d_bind_2d(post_p_sharp, "u_src", 0, post_ldr.color)
|
||||
u_f2(gpu_uniform(post_p_sharp, "u_texel"), fr(1, post_w), fr(1, post_h))
|
||||
|
|
|
|||
|
|
@ -101,27 +101,26 @@ property Target {
|
|||
function target_new(w: int, h: int, ifmt: int, fmt: int, ty: int, with_depth: bool, filter: int) -> Target {
|
||||
let t = new Target
|
||||
t.w = w; t.h = h
|
||||
t.fbo = gl_framebuffer()
|
||||
gl_bind_framebuffer(GL_FRAMEBUFFER, t.fbo)
|
||||
t.fbo = gpu_fb_new()
|
||||
gpu_fb_bind(t.fbo)
|
||||
t.color = tex_target(w, h, ifmt, fmt, ty, filter)
|
||||
gl_framebuffer_texture2d(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, t.color, 0)
|
||||
gpu_fb_color(0, t.color)
|
||||
if with_depth {
|
||||
t.depth = tex_target(w, h, GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT, GL_NEAREST)
|
||||
gl_framebuffer_texture2d(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, t.depth, 0)
|
||||
gpu_fb_depth(t.depth)
|
||||
}
|
||||
let st = gl_check_framebuffer_status(GL_FRAMEBUFFER)
|
||||
let st = gpu_fb_status()
|
||||
if st != GL_FRAMEBUFFER_COMPLETE { print(`r3d: framebuffer incomplete {st} ({w}x{h})`) }
|
||||
gl_bind_framebuffer(GL_FRAMEBUFFER, 0)
|
||||
gpu_fb_bind(0)
|
||||
return t
|
||||
}
|
||||
function target_free(t: Target) -> void {
|
||||
if t == null { return }
|
||||
let ids = gl_scratch()
|
||||
ids[0] = t.fbo; gl_delete_framebuffers(1, ids)
|
||||
gpu_fb_free(t.fbo)
|
||||
gpu_tex_free(t.color)
|
||||
if t.depth != 0 { gpu_tex_free(t.depth) }
|
||||
}
|
||||
function target_bind(t: Target) -> void {
|
||||
gl_bind_framebuffer(GL_FRAMEBUFFER, t.fbo)
|
||||
gl_viewport(0, 0, t.w, t.h)
|
||||
gpu_fb_bind(t.fbo)
|
||||
gpu_viewport(0, 0, t.w, t.h)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ function r3d_init(w: int, h: int, title: string) -> bool {
|
|||
r3d_fog_density = fl(0.00014)
|
||||
r3d_fog_falloff = fl(0.002)
|
||||
r3d_ready = true
|
||||
gl_check("r3d init")
|
||||
gpu_check("r3d init")
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
@ -226,5 +226,5 @@ function r3d_frame(time: int) -> void {
|
|||
post_capture_prev()
|
||||
prof_end()
|
||||
prof_collect()
|
||||
gl_check("frame")
|
||||
gpu_check("frame")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -421,22 +421,17 @@ function impostor_bake(model: Model, tiles: int, tw: int, th: int) -> Impostor {
|
|||
let aw = tiles * tw
|
||||
im.albedo = tex_target(aw, th, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, GL_LINEAR)
|
||||
im.normal = tex_target(aw, th, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, GL_LINEAR)
|
||||
let fbo = gl_framebuffer()
|
||||
gl_bind_framebuffer(GL_FRAMEBUFFER, fbo)
|
||||
gl_framebuffer_texture2d(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, im.albedo, 0)
|
||||
gl_framebuffer_texture2d(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, im.normal, 0)
|
||||
let ids = gl_scratch()
|
||||
gl_gen_renderbuffers(1, ids)
|
||||
let rb = ids[0]
|
||||
gl_bind_renderbuffer(GL_RENDERBUFFER, rb)
|
||||
gl_renderbuffer_storage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, aw, th)
|
||||
gl_framebuffer_renderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rb)
|
||||
let bufs = words(2)
|
||||
bufs[0] = GL_COLOR_ATTACHMENT0; bufs[1] = GL_COLOR_ATTACHMENT1
|
||||
gl_draw_buffers(2, bufs)
|
||||
gl_viewport(0, 0, aw, th)
|
||||
gl_clear_color(0.0, 0.0, 0.0, 0.0)
|
||||
gl_clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
|
||||
let fbo = gpu_fb_new()
|
||||
gpu_fb_bind(fbo)
|
||||
gpu_fb_color(0, im.albedo)
|
||||
gpu_fb_color(1, im.normal)
|
||||
let rb = gpu_rb_new()
|
||||
gpu_rb_storage(rb, GL_DEPTH_COMPONENT24, aw, th, 0)
|
||||
gpu_fb_depth_rb(rb)
|
||||
gpu_fb_draw_buffers(2)
|
||||
gpu_viewport(0, 0, aw, th)
|
||||
gpu_clear_color(0.0, 0.0, 0.0, 0.0)
|
||||
gpu_clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
|
||||
gpu_depth_test(true)
|
||||
gpu_depth_func(GL_LESS)
|
||||
gpu_cull(false)
|
||||
|
|
@ -461,7 +456,7 @@ function impostor_bake(model: Model, tiles: int, tw: int, th: int) -> Impostor {
|
|||
u_mat4(gpu_uniform(bake, "u_view"), view)
|
||||
u_mat4(gpu_uniform(bake, "u_proj"), proj)
|
||||
u_f(gpu_uniform(bake, "u_wind"), F_ZERO)
|
||||
gl_viewport(t * tw, 0, tw, th)
|
||||
gpu_viewport(t * tw, 0, tw, th)
|
||||
for i in 0 .. len(model.prims) {
|
||||
let pr = model.prims[i]
|
||||
r3d_bind_2d(bake, "u_diff", 0, pr.diff)
|
||||
|
|
@ -469,19 +464,17 @@ function impostor_bake(model: Model, tiles: int, tw: int, th: int) -> Impostor {
|
|||
mesh_draw_instanced(pr.mesh, 1)
|
||||
}
|
||||
}
|
||||
free(view); free(proj); free(eye); free(at); free(up); free(bufs)
|
||||
gl_bind_framebuffer(GL_FRAMEBUFFER, 0)
|
||||
ids[0] = fbo
|
||||
gl_delete_framebuffers(1, ids)
|
||||
ids[0] = rb
|
||||
gl_delete_renderbuffers(1, ids)
|
||||
free(view); free(proj); free(eye); free(at); free(up)
|
||||
gpu_fb_bind(0)
|
||||
gpu_fb_free(fbo)
|
||||
gpu_rb_free(rb)
|
||||
gpu_tex_bind(GPU_TEX2D, im.albedo)
|
||||
gpu_tex_param(GPU_TEX2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR)
|
||||
gpu_tex_mips(GPU_TEX2D)
|
||||
gpu_tex_bind(GPU_TEX2D, im.normal)
|
||||
gpu_tex_param(GPU_TEX2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR)
|
||||
gpu_tex_mips(GPU_TEX2D)
|
||||
gl_check("impostor bake")
|
||||
gpu_check("impostor bake")
|
||||
if sc_debug_dump { tex_dump_alpha = true; tex_dump(im.albedo, aw, th, "build/atlas_alpha.ppm"); tex_dump_alpha = false }
|
||||
return im
|
||||
}
|
||||
|
|
@ -990,21 +983,16 @@ function cb_rnd() -> int {
|
|||
}
|
||||
function carpet_bake(layers: []Layer, count: int, tile: int, res: int) -> int {
|
||||
let tex = tex_target(res, res, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, GL_LINEAR)
|
||||
let fbo = gl_framebuffer()
|
||||
gl_bind_framebuffer(GL_FRAMEBUFFER, fbo)
|
||||
gl_framebuffer_texture2d(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0)
|
||||
let ids = gl_scratch()
|
||||
gl_gen_renderbuffers(1, ids)
|
||||
let rb = ids[0]
|
||||
gl_bind_renderbuffer(GL_RENDERBUFFER, rb)
|
||||
gl_renderbuffer_storage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, res, res)
|
||||
gl_framebuffer_renderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rb)
|
||||
let bufs = words(1)
|
||||
bufs[0] = GL_COLOR_ATTACHMENT0
|
||||
gl_draw_buffers(1, bufs)
|
||||
gl_viewport(0, 0, res, res)
|
||||
gl_clear_color(0.0, 0.0, 0.0, 0.0)
|
||||
gl_clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
|
||||
let fbo = gpu_fb_new()
|
||||
gpu_fb_bind(fbo)
|
||||
gpu_fb_color(0, tex)
|
||||
let rb = gpu_rb_new()
|
||||
gpu_rb_storage(rb, GL_DEPTH_COMPONENT24, res, res, 0)
|
||||
gpu_fb_depth_rb(rb)
|
||||
gpu_fb_draw_buffers(1)
|
||||
gpu_viewport(0, 0, res, res)
|
||||
gpu_clear_color(0.0, 0.0, 0.0, 0.0)
|
||||
gpu_clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
|
||||
gpu_depth_test(true)
|
||||
gpu_depth_func(GL_LESS)
|
||||
gpu_cull(false)
|
||||
|
|
@ -1057,12 +1045,10 @@ function carpet_bake(layers: []Layer, count: int, tile: int, res: int) -> int {
|
|||
mesh_draw_instanced(pr.mesh, n9)
|
||||
}
|
||||
}
|
||||
free(view); free(proj); free(eye); free(at); free(up); free(bufs)
|
||||
gl_bind_framebuffer(GL_FRAMEBUFFER, 0)
|
||||
ids[0] = fbo
|
||||
gl_delete_framebuffers(1, ids)
|
||||
ids[0] = rb
|
||||
gl_delete_renderbuffers(1, ids)
|
||||
free(view); free(proj); free(eye); free(at); free(up)
|
||||
gpu_fb_bind(0)
|
||||
gpu_fb_free(fbo)
|
||||
gpu_rb_free(rb)
|
||||
gpu_buffer_free(buf)
|
||||
gpu_tex_bind(GPU_TEX2D, tex)
|
||||
gpu_tex_param(GPU_TEX2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
|
||||
|
|
@ -1070,7 +1056,7 @@ function carpet_bake(layers: []Layer, count: int, tile: int, res: int) -> int {
|
|||
gpu_tex_param(GPU_TEX2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR)
|
||||
gpu_tex_paramf(GPU_TEX2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, tex_anisotropy)
|
||||
gpu_tex_mips(GPU_TEX2D)
|
||||
gl_check("carpet bake")
|
||||
gpu_check("carpet bake")
|
||||
return tex
|
||||
}
|
||||
|
||||
|
|
@ -1082,7 +1068,6 @@ function carpet_bake(layers: []Layer, count: int, tile: int, res: int) -> int {
|
|||
function scatter_clear_all() -> void {
|
||||
stream_clear_all()
|
||||
if sc_layers == null { return }
|
||||
let ids = gl_scratch()
|
||||
for i in 0 .. len(sc_layers) {
|
||||
let l = sc_layers[i]
|
||||
if l.buf != 0 { gpu_buffer_free(l.buf) }
|
||||
|
|
|
|||
|
|
@ -34,11 +34,10 @@ function shadow_init() -> void {
|
|||
gl_put(border, 0, 1.0); gl_put(border, 1, 1.0); gl_put(border, 2, 1.0); gl_put(border, 3, 1.0)
|
||||
gpu_tex_border(GPU_TEX2D_ARRAY, border)
|
||||
free(border)
|
||||
sh_fbo = gl_framebuffer()
|
||||
gl_bind_framebuffer(GL_FRAMEBUFFER, sh_fbo)
|
||||
gl_draw_buffer(GL_NONE)
|
||||
gl_read_buffer(GL_NONE)
|
||||
gl_bind_framebuffer(GL_FRAMEBUFFER, 0)
|
||||
sh_fbo = gpu_fb_new()
|
||||
gpu_fb_bind(sh_fbo)
|
||||
gpu_fb_no_color()
|
||||
gpu_fb_bind(0)
|
||||
sh_vp = words(16 * SHADOW_CASCADES)
|
||||
sh_split = words(SHADOW_CASCADES)
|
||||
sh_range = words(SHADOW_CASCADES)
|
||||
|
|
@ -114,8 +113,8 @@ function shadow_cascade_vp(c: int) -> words { return mem_off(sh_vp, c * 64) }
|
|||
# render every cascade; `draw` happens through terrain_draw_shadow + the scene's casters
|
||||
function shadow_pass() -> void {
|
||||
var near = cam_near
|
||||
gl_bind_framebuffer(GL_FRAMEBUFFER, sh_fbo)
|
||||
gl_viewport(0, 0, SHADOW_RES, SHADOW_RES)
|
||||
gpu_fb_bind(sh_fbo)
|
||||
gpu_viewport(0, 0, SHADOW_RES, SHADOW_RES)
|
||||
gpu_depth_test(true)
|
||||
gpu_depth_func(GL_LESS)
|
||||
gpu_depth_bias(2.0, 4.0)
|
||||
|
|
@ -124,9 +123,9 @@ function shadow_pass() -> void {
|
|||
sh_cascade = c
|
||||
shadow_fit(c, near, sh_split[c])
|
||||
near = sh_split[c]
|
||||
gl_framebuffer_texture_layer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, sh_tex, 0, c)
|
||||
if r3d_debug and c == 0 { let st = gl_check_framebuffer_status(GL_FRAMEBUFFER); print(`shadow fbo status {st}`) }
|
||||
gl_clear(GL_DEPTH_BUFFER_BIT)
|
||||
gpu_fb_depth_layer(sh_tex, c)
|
||||
if r3d_debug and c == 0 { let st = gpu_fb_status(); print(`shadow fbo status {st}`) }
|
||||
gpu_clear(GL_DEPTH_BUFFER_BIT)
|
||||
let vp = shadow_cascade_vp(c)
|
||||
# shadows off (a video setting): the cascades stay cleared, so everything reads lit
|
||||
if sh_enabled {
|
||||
|
|
@ -135,7 +134,7 @@ function shadow_pass() -> void {
|
|||
}
|
||||
}
|
||||
gpu_depth_bias(0.0, 0.0)
|
||||
gl_bind_framebuffer(GL_FRAMEBUFFER, 0)
|
||||
gpu_fb_bind(0)
|
||||
if r3d_debug_shadow { shadow_dump() }
|
||||
if r3d_debug_shadow and not sh_printed2 {
|
||||
sh_printed2 = true
|
||||
|
|
|
|||
|
|
@ -69,11 +69,11 @@ function sky_load(path: string) -> bool {
|
|||
}
|
||||
|
||||
function sky_convolve(prog: int, target_tex: int, layer: int, w: int, h: int, rough: int) -> void {
|
||||
let fbo = gl_framebuffer()
|
||||
gl_bind_framebuffer(GL_FRAMEBUFFER, fbo)
|
||||
if layer < 0 { gl_framebuffer_texture2d(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, target_tex, 0) }
|
||||
else { gl_framebuffer_texture_layer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target_tex, 0, layer) }
|
||||
gl_viewport(0, 0, w, h)
|
||||
let fbo = gpu_fb_new()
|
||||
gpu_fb_bind(fbo)
|
||||
if layer < 0 { gpu_fb_color(0, target_tex) }
|
||||
else { gpu_fb_color_layer(0, target_tex, layer) }
|
||||
gpu_viewport(0, 0, w, h)
|
||||
gl_use_program(prog)
|
||||
r3d_bind_2d(prog, "u_sky", 0, sky_tex)
|
||||
sky_bind_rot(prog)
|
||||
|
|
@ -81,16 +81,13 @@ function sky_convolve(prog: int, target_tex: int, layer: int, w: int, h: int, ro
|
|||
u_f(gpu_uniform(prog, "u_rough"), rough)
|
||||
u_f(gpu_uniform(prog, "u_sky_w"), fi(sky_w))
|
||||
mesh_draw(sky_fullscreen)
|
||||
gl_bind_framebuffer(GL_FRAMEBUFFER, 0)
|
||||
let ids = gl_scratch()
|
||||
ids[0] = fbo
|
||||
gl_delete_framebuffers(1, ids)
|
||||
gpu_fb_bind(0)
|
||||
gpu_fb_free(fbo)
|
||||
}
|
||||
|
||||
function sky_precompute() -> void {
|
||||
gpu_depth_test(false)
|
||||
if sky_irradiance != 0 {
|
||||
let ids = gl_scratch()
|
||||
gpu_tex_free(sky_irradiance)
|
||||
gpu_tex_free(sky_prefilter)
|
||||
gpu_tex_free(sky_brdf)
|
||||
|
|
@ -118,7 +115,7 @@ function sky_precompute() -> void {
|
|||
let p_brdf = sky_p_brdf
|
||||
sky_brdf = tex_target(256, 256, GL_RG16F, GL_RG, GL_FLOAT, GL_LINEAR)
|
||||
sky_convolve(p_brdf, sky_brdf, -1, 256, 256, F_ZERO)
|
||||
gl_check("sky precompute")
|
||||
gpu_check("sky precompute")
|
||||
}
|
||||
|
||||
# bind the IBL set + sun for a lit program (units 12..14)
|
||||
|
|
|
|||
|
|
@ -222,10 +222,10 @@ function terrain_generate() -> void {
|
|||
if ter_smooth { defs = "#define SMOOTH\n" }
|
||||
let p = r3d_program("fullscreen.vert", "heightgen.frag", defs)
|
||||
ter_height_tex = tex_target(TERRAIN_RES, TERRAIN_RES, GL_R32F, GL_RED, GL_FLOAT, GL_LINEAR)
|
||||
let fbo = gl_framebuffer()
|
||||
gl_bind_framebuffer(GL_FRAMEBUFFER, fbo)
|
||||
gl_framebuffer_texture2d(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, ter_height_tex, 0)
|
||||
gl_viewport(0, 0, TERRAIN_RES, TERRAIN_RES)
|
||||
let fbo = gpu_fb_new()
|
||||
gpu_fb_bind(fbo)
|
||||
gpu_fb_color(0, ter_height_tex)
|
||||
gpu_viewport(0, 0, TERRAIN_RES, TERRAIN_RES)
|
||||
gpu_depth_test(false)
|
||||
gl_use_program(p)
|
||||
u_f(gpu_uniform(p, "u_half"), fi(TERRAIN_HALF))
|
||||
|
|
@ -246,7 +246,7 @@ function terrain_generate() -> void {
|
|||
# second pass: R = height, GBA = the smooth surface normal, baked once (ternormal.frag)
|
||||
let raw = ter_height_tex
|
||||
ter_height_tex = tex_target(TERRAIN_RES, TERRAIN_RES, GL_RGBA32F, GL_RGBA, GL_FLOAT, GL_LINEAR)
|
||||
gl_framebuffer_texture2d(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, ter_height_tex, 0)
|
||||
gpu_fb_color(0, ter_height_tex)
|
||||
let pn = r3d_program("fullscreen.vert", "ternormal.frag", "")
|
||||
gl_use_program(pn)
|
||||
r3d_bind_2d(pn, "u_src", 0, raw)
|
||||
|
|
@ -259,12 +259,10 @@ function terrain_generate() -> void {
|
|||
gpu_tex_bind(GPU_TEX2D, ter_height_tex)
|
||||
gpu_pixel_store(GL_PACK_ALIGNMENT, 4)
|
||||
gpu_tex_read(GPU_TEX2D, GL_RED, GL_FLOAT, ter_heights)
|
||||
gl_bind_framebuffer(GL_FRAMEBUFFER, 0)
|
||||
let ids = gl_scratch()
|
||||
ids[0] = fbo
|
||||
gl_delete_framebuffers(1, ids)
|
||||
gpu_fb_bind(0)
|
||||
gpu_fb_free(fbo)
|
||||
gl_delete_program(p)
|
||||
gl_check("terrain generate")
|
||||
gpu_check("terrain generate")
|
||||
}
|
||||
|
||||
# The height field for shaders that place things on the ground (model.vert's u_ground)
|
||||
|
|
@ -281,10 +279,10 @@ function terrain_bake_shadow() -> void {
|
|||
if ter_shadow_tex == 0 { ter_shadow_tex = tex_target(TERRAIN_SHADOW_RES, TERRAIN_SHADOW_RES, GL_RGBA32F, GL_RGBA, GL_FLOAT, GL_LINEAR) }
|
||||
if ter_shadow_prog == 0 { ter_shadow_prog = r3d_program("fullscreen.vert", "tershadow.frag", "#define NOISE_ONLY\n") }
|
||||
let p = ter_shadow_prog
|
||||
let fbo = gl_framebuffer()
|
||||
gl_bind_framebuffer(GL_FRAMEBUFFER, fbo)
|
||||
gl_framebuffer_texture2d(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, ter_shadow_tex, 0)
|
||||
gl_viewport(0, 0, TERRAIN_SHADOW_RES, TERRAIN_SHADOW_RES)
|
||||
let fbo = gpu_fb_new()
|
||||
gpu_fb_bind(fbo)
|
||||
gpu_fb_color(0, ter_shadow_tex)
|
||||
gpu_viewport(0, 0, TERRAIN_SHADOW_RES, TERRAIN_SHADOW_RES)
|
||||
gpu_depth_test(false)
|
||||
gpu_blend(false)
|
||||
gl_use_program(p)
|
||||
|
|
@ -292,12 +290,10 @@ function terrain_bake_shadow() -> void {
|
|||
u_f(gpu_uniform(p, "u_half"), fi(TERRAIN_HALF))
|
||||
u_v3(gpu_uniform(p, "u_sun"), sun_dir)
|
||||
mesh_draw(sky_fullscreen)
|
||||
gl_bind_framebuffer(GL_FRAMEBUFFER, 0)
|
||||
let ids = gl_scratch()
|
||||
ids[0] = fbo
|
||||
gl_delete_framebuffers(1, ids)
|
||||
gpu_fb_bind(0)
|
||||
gpu_fb_free(fbo)
|
||||
ter_shadow_yaw = sky_yaw
|
||||
gl_check("terrain shadow bake")
|
||||
gpu_check("terrain shadow bake")
|
||||
}
|
||||
|
||||
# height at world (x, z) — float bits, bilinear over the CPU copy
|
||||
|
|
@ -424,7 +420,7 @@ function terrain_init() -> void {
|
|||
ter_far_band = fi(60)
|
||||
if Os.has_env("R3D_TBAND") { ter_far_band = fi(Text.to_int(Os.env("R3D_TBAND"))) }
|
||||
ter_snow_line = fi(880)
|
||||
gl_check("terrain init")
|
||||
gpu_check("terrain init")
|
||||
}
|
||||
|
||||
# draw into the current cascade with the given light view-projection
|
||||
|
|
@ -516,18 +512,18 @@ function terrain_sun_pass(w: int, h: int, depth: int) -> Target {
|
|||
# an error per draw and a pass that silently does nothing. One call a pass is cheaper
|
||||
# than any scheme for noticing.
|
||||
target_bind(t)
|
||||
gl_framebuffer_texture2d(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depth, 0)
|
||||
gpu_fb_depth(depth)
|
||||
if not ter_sun_checked {
|
||||
ter_sun_checked = true
|
||||
let st = gl_check_framebuffer_status(GL_FRAMEBUFFER)
|
||||
let st = gpu_fb_status()
|
||||
if st != GL_FRAMEBUFFER_COMPLETE { print(`r3d: sun-visibility framebuffer incomplete {st}`) }
|
||||
}
|
||||
gpu_depth_test(true)
|
||||
gpu_depth_func(GL_LESS)
|
||||
gpu_depth_write(true)
|
||||
# the depth is the frame's own and was cleared with it; only the visibility is cleared
|
||||
gl_clear_color(1.0, 1.0, 1.0, 1.0) # unshadowed where nothing is drawn
|
||||
gl_clear(GL_COLOR_BUFFER_BIT)
|
||||
gpu_clear_color(1.0, 1.0, 1.0, 1.0) # unshadowed where nothing is drawn
|
||||
gpu_clear(GL_COLOR_BUFFER_BIT)
|
||||
let p = ter_sun_prog
|
||||
gl_use_program(p)
|
||||
r3d_bind_2d(p, "u_height", 0, ter_height_tex)
|
||||
|
|
@ -572,7 +568,7 @@ function terrain_draw() -> void {
|
|||
if ter_reflect { target_bind(water_refl) }
|
||||
else {
|
||||
target_bind(post_hdr)
|
||||
if post_ms_fbo != 0 { gl_bind_framebuffer(GL_FRAMEBUFFER, post_ms_fbo) }
|
||||
if post_ms_fbo != 0 { gpu_fb_bind(post_ms_fbo) }
|
||||
}
|
||||
gpu_depth_test(true)
|
||||
# the prepass already laid this geometry's depth down: only the frontmost fragment of
|
||||
|
|
@ -587,9 +583,9 @@ function terrain_draw() -> void {
|
|||
cd_far_draws = 0
|
||||
cd_near_draws = 0
|
||||
gpu_mesh_bind(cd_mesh)
|
||||
if ter_wire { gl_polygon_mode(GL_FRONT_AND_BACK, GL_LINE) }
|
||||
if ter_wire { gpu_wireframe(true) }
|
||||
cdlod_select(CD_LEVELS - 1, 0, 0)
|
||||
if ter_wire { gl_polygon_mode(GL_FRONT_AND_BACK, GL_FILL) }
|
||||
if ter_wire { gpu_wireframe(false) }
|
||||
gpu_depth_func(GL_LESS)
|
||||
if r3d_debug and not ter_printed { ter_printed = true; print(`cdlod patches drawn: {cd_draws} (far {cd_far_draws}, near {cd_near_draws}, band {cd_draws - cd_far_draws - cd_near_draws})`) }
|
||||
}
|
||||
|
|
@ -763,7 +759,6 @@ function cdlod_select(level: int, ix: int, iz: int) -> bool {
|
|||
# belongs to the process stays: the patch mesh, the material textures, the programs, the
|
||||
# frame-sized targets and the shadow texture, whose size does not depend on the map.
|
||||
function terrain_unload() -> void {
|
||||
let ids = gl_scratch()
|
||||
if ter_height_tex != 0 { gpu_tex_free(ter_height_tex); ter_height_tex = 0 }
|
||||
if ter_dem_tex != 0 { gpu_tex_free(ter_dem_tex); ter_dem_tex = 0 }
|
||||
if ter_ortho_tex != 0 { gpu_tex_free(ter_ortho_tex); ter_ortho_tex = 0 }
|
||||
|
|
@ -792,5 +787,5 @@ function terrain_reload(dem: string, emin: int, emax: int, base: int, ox: int, o
|
|||
terrain_generate()
|
||||
terrain_bake_shadow()
|
||||
cdlod_bounds()
|
||||
gl_check("terrain reload")
|
||||
gpu_check("terrain reload")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,8 +72,8 @@ function water_reflection_pass() -> void {
|
|||
gpu_depth_write(true)
|
||||
gpu_cull(true)
|
||||
gpu_cull_face(GL_FRONT) # the mirror flips the winding
|
||||
gl_clear_color(0.0, 0.0, 0.0, 1.0)
|
||||
gl_clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
|
||||
gpu_clear_color(0.0, 0.0, 0.0, 1.0)
|
||||
gpu_clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
|
||||
sc_freeze = true
|
||||
let sb = sc_skip_blade
|
||||
sc_skip_blade = true # blades are invisible at this scale in a reflection
|
||||
|
|
@ -93,7 +93,7 @@ function water_reflection_pass() -> void {
|
|||
m4_copy(cam_inv_vp, mem_off(water_saved, 128))
|
||||
v3_set(cam_pos, sx, sy, sz)
|
||||
free(eye); free(fwd); free(up); free(at)
|
||||
gl_bind_framebuffer(GL_FRAMEBUFFER, 0)
|
||||
gpu_fb_bind(0)
|
||||
}
|
||||
|
||||
# the program and the plane are the process's, built once; the bodies are the map's
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue