GLSL顶点着色器中的眼空间像素宽度

3

我希望在GLSL顶点着色器中计算当前顶点位置处一个像素的投影金字塔在眼空间的宽度,但是我无法得到正确的数学计算公式。以下是一个明显不正确的示例:

// GLSL VERTEX SHADER
#version 410 compatibility

uniform vec4 viewport; // same as glViewport​
in vec4 gl_Vertex;

void main ()
{
    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
    float pixelWidth = gl_Position.z / viewport.z;
<snip>

但这并不能解释视野或剪裁平面的问题。
2个回答

2

我仔细计算了一下,已经弄明白了。 :) 正如我所希望的那样,不需要额外的矩阵变换,只需要一个除法操作:

// GLSL VERTEX SHADER
#version 410 compatibility

uniform vec4 viewport; // same as glViewport​
in vec4 gl_Vertex;

float pixelWidthRatio = 2. / (viewport.z * gl_ProjectionMatrix[0][0]);

void main ()
{
    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
    float pixelWidth = gl_Position.w * pixelWidthRatio;
    <snip>

或者,另一种选择是:
<snip>
float pixelHeightRatio = 2. / (viewport.w * gl_ProjectionMatrix[1][1]);

void main ()
{
    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
    float pixelHeight = gl_Position.w * pixelHeightRatio;
<snip>

如果像素是正方形,那么像素宽度pixelWidth和像素高度pixelHeight应该相同。


1
在顶点着色器中,像素的世界空间宽度并不存在。只有在给定的Z距离处,才会将“宽度”投影到像素中。通常情况下,你会得到一个金字塔投影到像素中。
请看这里:
  1. 将两个屏幕点转换为NDC点:

        vec2 Screen = vec2( ScreenWidth, ScreenHeight );
        vec3 Point1 = vec3( ScreenPt1 / Screen * 2.0 - vec2( 1.0 ), 1.0 );
        vec3 Point2 = vec3( ScreenPt2 / Screen * 2.0 - vec2( 1.0 ), 1.0 );
    
  2. 将NDC点反投影到世界空间位置:

        vec4 R1 = vec4( Point1, 1.0 );
        vec4 R2 = vec4( Point2, 1.0 );
    
        R1 = Projection.GetInversed() * R1;
        R1 = ModelView.GetInversed() * R1;
        R1 /= R1.W;
    
        R2 = Projection.GetInversed() * R2;
        R2 = ModelView.GetInversed() * R2;
        R2 /= R2.W;
    
  3. 找到R1和R2之间的距离。

在片段着色器中,您可以使用局部导数。请看这里:

http://www.opengl.org/sdk/docs/manglsl/xhtml/dFdx.xml

在这里:

http://www.opengl.org/sdk/docs/manglsl/xhtml/fwidth.xml

dFdx和dFdy仅在片段着色器中可用,分别返回表达式p在x和y方向的偏导数。偏导数是使用局部差分计算的。暗示高阶导数(例如dFdx(dFdx(n))和混合阶导数(例如dFdx(dFdy(n)))的表达式结果未定义。假定表达式p是连续的,因此通过非均匀控制流求值的表达式可能是未定义的。


1
这些函数仅在片段着色器中可用,而我询问的是顶点着色器,所以这对我没有帮助。 - atb
好的,根据您的语义,我需要在顶点着色器中获取当前顶点深度处“像素金字塔”的世界空间宽度。您的回答仍然没有帮助到我,但感谢提供信息。 - atb
但是我只有一个观点,你的示例代码没有考虑视口分辨率... - atb
那很糟糕。已更新。您有2个点:(X,Y,Z)和(X,Y + 1,Z)。这里的+1表示一个像素。 - Sergey K.
我明白你的意思,谢谢。不过,我认为应该可以直接使用gl_Position.zw和glViewport.xz来计算宽度,而无需实际转换任何点。你的例子可能对我来说太昂贵了... - atb
显示剩余2条评论

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