Type: concept
Confidence: 0.95
Created: 2026-04-16
Updated: 2026-04-20
Tags: 游戏引擎正交投影相机数学UrhoX陷阱游戏开发

正交缩放补偿orthoSize半高度因子

概述

UrhoX 引擎中 camera.orthoSize 代表视野全高度,但投影矩阵内部使用 orthoSize * 0.5 作为半高度。手动计算屏幕到世界坐标转换时,若忽略此 0.5 因子会导致缩放补偿漂移。

关键内容

问题现象

实现"鼠标滚轮缩放时,鼠标指向的世界坐标保持固定"功能时,尽管数学公式看起来正确,每次缩放后锚点发生漂移,且漂移量与 orthoSize 变化量成正比(线性误差 = 存在常数因子错误的信号)。

根本原因:引擎源码中的 0.5 因子

Camera.cppUpdateProjection() 函数(约第 960 行):

float h = (1.0f / (orthoSize_ * 0.5f)) * zoom_;

orthoSize_ * 0.5f 就是关键的 2x 因子——引擎将 orthoSize 视为全高度,内部取半高度参与投影矩阵计算。

屏幕到视图空间的正确转换

-- 1. 屏幕坐标 → NDC (-1 到 1)
local ndcX = (screenX / screenWidth) * 2 - 1
local ndcY = 1 - (screenY / screenHeight) * 2  -- Y 轴翻转

-- 2. NDC → 视图空间偏移(必须乘以 0.5)
local viewX = ndcX * aspect * orthoSize * 0.5  -- ✅ 正确
local viewY = ndcY * orthoSize * 0.5            -- ✅ 正确

-- ❌ 错误(忘记 0.5):
-- local viewX = ndcX * aspect * orthoSize
-- local viewY = ndcY * orthoSize

缩放补偿完整实现(等距视角)

local function CalcZoomCompensation(screenX, screenY, oldOrthoSize, newOrthoSize)
    local ndcX = (screenX / screenWidth) * 2 - 1
    local ndcY = 1 - (screenY / screenHeight) * 2
    local deltaOrtho = oldOrthoSize - newOrthoSize

    -- 关键:乘以 0.5,与引擎投影矩阵对齐
    local viewDeltaX = ndcX * aspect * deltaOrtho * 0.5
    local viewDeltaY = ndcY * deltaOrtho * 0.5

    -- 转换到世界空间(沿相机 right/up 方向)
    local offsetX = camRight.x * viewDeltaX + camUp.x * viewDeltaY
    local offsetY = camRight.y * viewDeltaX + camUp.y * viewDeltaY
    local offsetZ = camRight.z * viewDeltaX + camUp.z * viewDeltaY

    -- 与 y=0 平面相交补偿
    if math.abs(camForward.y) > 0.001 then
        local t = -offsetY / camForward.y
        offsetX = offsetX + camForward.x * t
        offsetZ = offsetZ + camForward.z * t
    end

    return offsetX, offsetZ
end

调试策略

经验规律

参数 含义 内部使用
orthoSize 视野全高度 orthoSize * 0.5(半高度)参与投影矩阵
投影缩放因子 h 1 / (orthoSize * 0.5) * zoom 直接用于矩阵构造

来源

相关