gl_FragCoord 的含义

gl_FragCoord 表示当前片元着色器处理的候选片元窗口相对坐标信息,是一个 vec4 类型的变量 (x, y, z, 1/w), 其中 x, y 是当前片元的窗口坐标,OpenGL 默认以窗口左下角为原点, 在 着色器中通过布局限定符可以重新设定原点,比如窗口左上角为原点 origin_upper_left,窗口大小由 glViewport() 函数指定。x, y 默认是像素中心 而非 整数, 原点 的窗口坐标值为 (0.5, 0.5), 小数部分恒为 0.5,  当viewport 范围 为(0,0,800,600)时, x, y 的取值范围为(0.5, 0.5, 799.5, 599.5), 当在着色器中布局限定符设置为 pixel_center_integer  时, x, y 取值为整数。

 

第三个分量  z 表示的是当前片元的深度信息,由 vertex shader 处理过后系统插值得到, gl_FragCoord.z 的产生过程:

假设 OpenGL 变换的各坐标系统定义如下: world.xyzw 表示 世界坐标系的坐标, eye.xyzw 表示眼坐标系(也叫观察坐标系,还有叫相机坐标系,呃。。。) 的坐标, clip.xyzw 表示 裁剪坐标系的坐标 , ndc.xyzw 表示 规范化设备坐标系坐标, win.xyzw 表示 窗口坐标系坐标, OpenGL 一个完整的空间变换流水线如下:

world coordinate system --> eye coordinate system --> clip coordinate system --> normalized device coordinate system --> window device coordinate system。

  gl_FragCoord.z 生成过程:

(1)世界坐标系内的坐标乘以观察矩阵变换到眼坐标空间  eye.xyzw = viewMatrix * world.xyzw;

(2)眼坐标系内的坐标通过乘上投影矩阵变换到裁剪空间 clip.xyzw = projectMatrix * eye.xyzw;

(3)裁剪坐标系内的坐标通过透视除法(也就是  w 为 1 化) 到 规范化设备坐标系 ndc.xyz = clip.xyz / clip.w;

(4)设备规范化坐标系到窗口坐标系 win.z = (dfar - dnear)/2 * ndc.z + (dfar+dnear)/2;

可以看出 gl_FragCoord.z 是 win.z 。dnear ,dfar 是由 glDepthRange(dnear, dfar) 给定的,按openGL 默认值 (0,1) , win.z = ndc.z/2 + 0.5

有时候我们需要在 shader 内反算 眼坐标系 或 世界坐标系 内的坐标, 这在后处理或延迟着色中很有用,不需要另外使用颜色缓存保留物体位置信息,减少带宽占用。反算窗口空间内的片元的空间坐标: 

ndc.xyzw =  ( gl_FragCoord.xy/viewport.wh * 2.0  - 1.0,  gl_FragCoord.z * 2.0  - 1.0, 1.0 );

这样我们只需向shader 中传入 矩阵信息 , 就可以获得该片元在指定空间内的坐标 ,例如

  • eye.xyzw    = projectionMatrixInverse * ndc.xyzw;
  • world.xyzw = modelViewProjectionMatrixInverse * ndc.xyzw

注意最终结果要除以 w 分量, eye.xyz = eye.xyz/eye.w;

第四个分量  gl_FragCoord.w

先看看透视投影矩阵的推导:

 

gl_FragCoord.w 是裁剪空间 clip.w 的倒数即 1/clip.w ,  由上面的透视投影矩阵的推导过程可以看出,为了凑透视除法, clip.w 值就是 眼坐标系 z 值的负数,也就是距离相机的距离。 取负数 是因为 眼坐标系 与 规范化设备坐标系 手向性不同, 眼坐标系是右手系, 规范化设备坐标系是左手系。这里暗示我们,对于透视投影, 由 gl_FragCoord.w  可以很方便的知道当前片元在眼坐标系中 距离相机的距离  : gl_FragCoord.w = - 1/Ze  ---------->   Ze = - 1/gl_FragCoord.w;

那么正交投影呢?  没门! 正交投影矩阵的 w 值 恒为 1, 还是乖乖的传矩阵做逆运算吧。当然 用逆矩阵反求 各坐标系的坐标 具有通用性。

总结:

gl_FragCoord 比较有用的是反求各坐标系的空间位置,减少带宽占用。 也可以方便的获取当前片元的窗口坐标和片元深度值。当深度测试开启时, 如果在片元着色器中没有定义 gl_FragDepth 的值, gl_FragCoord.z 即成为默认输出的深度值,并且gl_FragDepth 不会存在未定义输出的情况(因为不是用户定义的值就是系统给定的默认值)。

题外话, 给定一个矩阵,怎样判断投影矩阵 是 透视投影还是正交投影呢?

 

最后给两个函数,在基于延迟着色的fragment shader 中 反求片元在 眼坐标系 或 世界坐标系的位置:

vec3 decodeCameraSpacePositionFromDepthBuffer(in vec2 texCoord){
	vec4 clipSpaceLocation;	
	clipSpaceLocation.xy = texCoord*2.0-1.0;
	clipSpaceLocation.z  = texture(depthTexture, texCoord).r * 2.0-1.0;
	clipSpaceLocation.w  = 1.0;
	vec4 homogenousLocation = projectionMatrixInverse * clipSpaceLocation;
	return homogenousLocation.xyz/homogenousLocation.w;
}

vec3 decodeWorldSpacePositionFromDepthBuffer(in vec2 texCoord){
	vec4 clipSpaceLocation;	
	clipSpaceLocation.xy = texCoord*2.0-1.0;
	clipSpaceLocation.z  = texture(depthTexture, texCoord).r * 2.0-1.0;
	clipSpaceLocation.w  = 1.0;
	vec4 homogenousLocation = viewProjectionMatrixInverse * clipSpaceLocation;
	return homogenousLocation.xyz/homogenousLocation.w;
}

 

  • 24
    点赞
  • 52
    收藏
    觉得还不错? 一键收藏
  • 12
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值