透视投影下的像素级文本渲染

3

我正在试图以透视投影的方式将文本渲染为纹理四边形(不想使用正交投影),但在像素对齐方面遇到了困难。

设置很简单,我有一个带有三维对齐锚点的文本,我将其模型变换转换为广告牌变换,并计算比例(三角相似性),使得文本始终保持相同的大小。由于文本四边形的几何结构是使用与像素对应的世界单位构建的,所以无论相机方向或锚点偏移如何,最终的文本看起来确实是相同的大小。

Vector3d dist = camera.getPosition();
dist.sub(translation);

double pxFov = camera.getFOV() / camera.getScreenWidth();
double scale = Math.sin(pxFov) / Math.sin((Math.PI / 2) - pxFov) 
            * dist.length() * camera.getAspectRatio();

V.getRotationScale(R);
R.transpose();

M.setIdentity();
M.setRotation(R);
M.setScale(scale);
M.setTranslation(translation);

其中V是4x4相机视图矩阵,R是临时的3x3矩阵,M是用于MVP计算的4x4模型变换矩阵。

我找到了所谓的解决方案,但它只是稍微改变了渲染文本的行为,而没有解决问题。

当使用顶点着色器时

void main () {
    vec2 view = vec2(1280, 720);

    vec4 cpos = MVP * vec4 (position, 1.0f);
    vec2 p = floor(cpos.xy * view*0.5/cpos.w);
    p += 0.5; // does not influence result
    cpos.xy = p * (2.0/view*cpos.w);

    gl_Position = cpos;
}

有些地方文本呈现清晰,而有些地方模糊不清

text blurred in waves

在简单的顶点着色器情况下

void main () {
    gl_Position = MVP * vec4 (position, 1.0f);
}

文本是完全模糊还是完全清晰的?

sort of completely sharp

completely blurred

将顶点位置转换为视口空间,进行四舍五入并移回原位似乎是合乎逻辑的,但似乎还缺少一些东西。

编辑:我如何计算比例因子的解释。

Scale factor explanation

在这里,您可以看到我正在以红、绿和蓝线的形式得到直角三角形。通过知道红线(相机-文本锚点距离)的长度、红线和蓝线之间的角度((fov/2)/(屏幕宽度/2)),以及红线和绿线之间的角度为直角,我可以使用正弦定理来计算绿线的长度,这也是一个像素在当前投影中具有相同大小的比例尺。

文本的比例似乎无论相机/文本方向/位置如何都是正确的(期望为8个像素)。可能比例只是稍微偏差了一点,导致了模糊效果,但我不知道原因。


我不明白scale计算应该如何工作。透视除法并不取决于点与相机之间的距离,而是取决于点与包含相机且平行于图像平面(或者换句话说,是在投影的主轴上正交投影距离)的平面之间的距离。对于任何不恰好在该主轴上的点,您的比例因子都应该是错误的。 - derhass
@derhass为比例计算添加了更多细节。 - Vojtěch Kaiser
你的解释没有解决我的问题。在图像中,您的文本可以位于任何高度,并且您计算 length(pos - camPos),因此您用作“相机-文本距离”的内容并不是您在图中绘制的水平距离。此外,您现在引入了一个 pxFOV,将角度与屏幕大小相关联,这根本毫无意义。 - derhass
它很可能毫无意义,事实上只有亚像素误差纯属偶然。但正确的解决方案是什么呢? - Vojtěch Kaiser
1个回答

0

我认为由于数字问题,您正在遭受z缓冲区战斗的影响。

也许您可以为文本z坐标添加一个小值,使其与背景分开一点。

另一种解决方案是避免文本缩放-反缩放计算。
您可以通过对每个文本进行CPU端透视数学计算来计算像素锚定。然后使用正交投影显示它。


z值/深度测试与描述问题的问题有什么关系? - derhass
我刚刚看了图片并描述了我所看到的。 - Ripi2
不存在Z fighting。字体的轮廓和面在同一纹理中,纹理过滤设置为最近邻,所有透明片段都被丢弃。由于字母之间没有重叠,因此绘制的任意两个片段都没有相同的坐标。 - Vojtěch Kaiser

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接