一个直截了当的想法是将粒子顶部和底部的一个点转换到屏幕空间并找到距离。这可以很好地取消,并且仅使用 y 坐标非常简单。
广告牌是屏幕对齐的,并且视图矩阵通常不会缩放,因此世界空间中的粒子大小与眼睛空间相同。这只是将投影留给 NDC,除以 w 并按视口大小缩放。
一个典型的投影矩阵,P,可能看起来像这样......
[ +1.2990 +0.0000 +0.0000 +0.0000 ]
[ +0.0000 +1.7321 +0.0000 +0.0000 ]
[ +0.0000 +0.0000 -1.0002 -0.0020 ]
[ +0.0000 +0.0000 -1.0000 +0.0000 ]
以y_eye开头,眼睛空间中的一个y坐标,得到图像空间坐标y_image,以像素为单位……
插入广告牌上方/下方的半径并减去取消以...
或者,在文本中,pixelSize = vpHeight * P[1][1] * radius / w_clip
对于透视投影,P[1][1] = 1 / tan(fov_y / 2)。 w_clip 是gl_Position.w,也是-z_eye(来自透视矩阵中的-1)。为确保您的点涵盖您想要的每个像素,这可能需要一个额外的小常数。
旁注:广告牌上的球体在屏幕中间看起来不错。如果您有大视野透视投影,则真正的球体在接近屏幕边缘时应该会变形。您可以为广告牌中的每个像素隐式投射虚拟球体以获得正确的结果,但广告牌边界需要相应调整。谷歌快速搜索结果:1234
[编辑]
好吧,既然我费心测试这个,我也会把我的着色器扔在这里......
顶点:
#version 150
in vec4 osVert;
uniform mat4 projectionMat;
uniform mat4 modelviewMat;
uniform vec2 vpSize;
flat out vec2 centre;
flat out float radiusPixels;
const float radius = 1.0;
void main()
{
gl_Position = projectionMat * modelviewMat * osVert;
centre = (0.5 * gl_Position.xy/gl_Position.w + 0.5) * vpSize;
gl_PointSize = vpSize.y * projectionMat[1][5] * radius / gl_Position.w;
radiusPixels = gl_PointSize / 2.0;
}
片段:
#version 150
flat in vec2 centre;
flat in float radiusPixels;
out vec4 fragColour;
void main()
{
vec2 coord = (gl_FragCoord.xy - centre) / radiusPixels;
float l = length(coord);
if (l > 1.0)
discard;
vec3 pos = vec3(coord, sqrt(1.0-l*l));
fragColour = vec4(vec3(pos.z), 1.0);
}
(请注意右下角的可见间隙如上所述是不正确的)