I have extracted the code (and added some comments) to try to understand it more and make the API clearer…
local function screen_to_world(camera, screen_x, screen_y, input_z)
local function is_within_viewport()
return
camera.viewport_pixel_x <= screen_x and
screen_x <= camera.viewport_pixel_x + camera.viewport_pixel_width and
camera.viewport_pixel_y <= screen_y and
screen_y <= camera.viewport_pixel_y + camera.viewport_pixel_height
end
if not is_within_viewport() then return end
local inverse_frustum = vmath.inv(camera.frustum)
-- convert to clip space
local clip_x = (screen_x - camera.viewport_pixel_x) / camera.viewport_pixel_width * 2 - 1
local clip_y = (screen_y - camera.viewport_pixel_y) / camera.viewport_pixel_height * 2 - 1
-- make both a near and a far position by projecting back to world space with different z's
local near_world_position = vmath.vector4(inverse_frustum * vmath.vector4(clip_x, clip_y, -1, 1))
local far_world_position = vmath.vector4(inverse_frustum * vmath.vector4(clip_x, clip_y, 1, 1))
-- scale it back so w == 1
near_world_position = near_world_position / near_world_position.w
far_world_position = far_world_position / far_world_position.w
-- this is a ratio for lerping
local frustum_z = (input_z - camera.z_min) / (camera.z_max - camera.z_min)
-- when we lerp why don't we get back to the input z?
local world_position = vmath.lerp(frustum_z, near_world_position, far_world_position)
return vmath.vector3(world_position.x, world_position.y, world_position.z)
end
The question remains. What is the input_z doing? Why doesn’t the lerp function make the output_z the same? Was that the intention?