- 6,013
- 4,331
Описание:
Рендер линии в 3D пространстве без артефактов, когда одна из точек линии уходит за камеру.
Принцип работы:
1) Обе точки видны на экране: линия рисуется напрямую черезrenderDrawLine
2) Ключевой случай: одна точка впереди, другая сзади. Нарисовать такую линию нельзя: проекция точки за камерой даёт «вывернутые» координаты и линию будеткосоёбитьуводить в разные стороны. Поэтому функция используя бинарный поиск (12 итераций, точность ~1/4096) ищет вдоль отрезка ту точку, которая лежит прямо на границе видимости (z > 0) и использует её вместо настоящей
3) Обе точки за экраном: ничего не рисуем, тут всё логично
Код:
Lua:local function clipToNearPlane(fx, fy, fz, bx, by, bz) local sx, sy local left, right = 0.0, 1.0 for _ = 1, 12 do local mid = (left + right) * 0.5 local _, cx, cy, cz = convert3DCoordsToScreenEx( fx + (bx - fx) * mid, fy + (by - fy) * mid, fz + (bz - fz) * mid ) if cz > 0 then left, sx, sy = mid, cx, cy else right = mid end end return sx, sy end function renderDrawLine3D(x1, y1, z1, x2, y2, z2, thickness, color) local _, sx1, sy1, sz1 = convert3DCoordsToScreenEx(x1, y1, z1) local _, sx2, sy2, sz2 = convert3DCoordsToScreenEx(x2, y2, z2) local v1, v2 = sz1 > 0, sz2 > 0 if not v1 and not v2 then return false end if not v2 then sx2, sy2 = clipToNearPlane(x1, y1, z1, x2, y2, z2) elseif not v1 then sx1, sy1 = clipToNearPlane(x2, y2, z2, x1, y1, z1) end if not sx1 or not sx2 then return false end renderDrawLine(sx1, sy1, sx2, sy2, thickness, color) return true, sx1, sy1, sx2, sy2 end
Пример:
Lua:function onD3DPresent() local x1, y1, z1 = 1806.59, -2547.78, 14 local x2, y2, z2 = 1806.59, -2567.78, 14 -- Улучшенный рендер renderDrawLine3D(x1, y1, z1, x2, y2, z2, 5, 0xFF00FF00) x1 = x1 + 1 x2 = x2 + 1 -- Обычный рендер local _, sx1, sy1, sz1 = convert3DCoordsToScreenEx(x1, y1, z1) local _, sx2, sy2, sz2 = convert3DCoordsToScreenEx(x2, y2, z2) if sz1 > 0 and sz2 > 0 then renderDrawLine(sx1, sy1, sx2, sy2, 5, 0xFFFF0000) end end
Демонстрация:
Красная линия - обычный рендер
Зелёная линия - улучшенный рендер
Можно заметить, как зелёная линия стабильно отрисовывается под разными углами камеры, в то время как красная нет
Lua:
local NEAR_PLANE_EPSILON = 1e-3
local function clipToNearPlane(fx, fy, fz, wf, bx, by, bz, wb)
local dw = wf - wb
if dw == 0 then return nil, nil end
local t = math.max(0.0, math.min(1.0, (wf - NEAR_PLANE_EPSILON) / dw))
local _, sx, sy = convert3DCoordsToScreenEx(
fx + (bx - fx) * t,
fy + (by - fy) * t,
fz + (bz - fz) * t
)
return sx, sy
end
local function renderDrawLine3D(x1, y1, z1, x2, y2, z2, thickness, color)
local _, sx1, sy1, sz1 = convert3DCoordsToScreenEx(x1, y1, z1)
local _, sx2, sy2, sz2 = convert3DCoordsToScreenEx(x2, y2, z2)
if sz1 <= 0 and sz2 <= 0 then return false end
if sz2 <= 0 then
sx2, sy2 = clipToNearPlane(x1, y1, z1, sz1, x2, y2, z2, sz2)
elseif sz1 <= 0 then
sx1, sy1 = clipToNearPlane(x2, y2, z2, sz2, x1, y1, z1, sz1)
end
if not sx1 or not sx2 then return false end
renderDrawLine(sx1, sy1, sx2, sy2, thickness, color)
return true, sx1, sy1, sx2, sy2
end