较真一下OpenGL Clip Space和NDC

在OpenGL ES 2.0 Programming Guide里面,有一段:

uniform mat4   u_mvpMatrix; // matrix to convert P from model
                                                   // space to normalized device space.

这个地方的注释将mvp矩阵解释为是将顶点变换到NDC的矩阵。看到这儿我困惑了一下,查了查以前写的博客,我是这样认为的:经过投影矩阵,顶点被变换到投影空间,即clip space,然后再经过透视除法,顶点被变换到NDC。
这个地方其实应该只是一处笔误(或者说作者意思nds是clip space而不是ndc),因为书后面讲图元装配的章节是说的很清楚的,无论如何,我还是把clip space和NDC再理一下,正好结合一下gles 2.0。

1) 顶点通过vertex shader,乘以mvp矩阵,被变换到clip space, 变换后的值写入gl_Position。
clip space中,顶点是4d齐次坐标(x,y,z,w),w=-Ze, 即eye空间z坐标的负值。这儿会有个错误的理解,以为x,y,z的范围是[-1,1],其实这是由于很多地方把clip space和NDC混为一谈了,NDC的范围是一个[-1,1]的立方体(OpenGL中,DX还不太一样),而clip sapce并不是NDC。clip space中的点经过透视除法,除以w之后,范围在[-1,1]的点进入NDC,其他的点被裁剪,因此才叫clip space。

参考:http://stackoverflow.com/questions/21841598/clip-space-and-screen-space

No, clip space and NDC space are not the same thing.

Clip space is actually 1 step away from NDC, all coordinates are divided by Clip.W to produce NDC. Anything outside of the range [-1,1] in resulting NDC space corresponds to a point that is outside of the clipping volume. There is a reason the coordinate space before NDC is called clip space ;)

Strictly speaking, however, NDC space is not necessarily cubic. True NDC space is a cube in OpenGL, but in Direct3D it is not. In D3D the Z coordinate in NDC space ranges from0.0 to 1.0, whereas it ranges from -1.0 to1.0 in GL. X and Y behave the same in GL's NDC as D3D's NDC (that is, they range from-1.0 to 1.0). NDC is a standard coordinate space, but it has different representation in different APIs.

Lastly, NDC space to screen space (AKA window space) occurs during rasterization and is defined by your viewport and depth range. Fragment locations really would not make sense in any other coordinate space, and this is what rasterization produces:fragments.



2)vs之后是图元装备阶段,图元装备包含3个步骤:clipping, 透视除法,viewport变换。

首先会进行的是剪裁(clipping)。clip会根据图元的类型进行处理,对于三角形如果图元部分处于clip space外面则三角形会被切割并生成新顶点形成一个三角扇。对于线如果部分剪裁就会被截断,也会生成新顶点。对于point sprite有点点特殊,点的位置只是用来检测是否在near,far plane之间,而quard会被用来检测是否在clip space之外,并且只要point sprite不被丢弃就直接保留原样(不会做部分剪裁),只是依靠scissor测试来做2d剪裁。
(clip volume的范围是[-w,w])

clipping之后就是透视除法了,通过clip的点(以及新生成的点)经过透视除法从clip齐次坐标变成了3d的NDC坐标。透视除法其实就是x/w,y/w,z/w。由于w=-Ze,因此这个除法就对x,y坐标产生了透视效果(近大远小),因此称为透视除法。显然NDC坐标(x,y,z)的范围是[-1,1],他是3d坐标,没有w。

再之后就是viewport变换,将3d的NDC坐标变换为3d的窗口坐标。x,y进过线性映射,变换到窗口坐标系x,y,而z会映射到depth range,经过z test后写入z buffer。viewport是由glViewport(x,y,w,h)定义的。depth range由glDepthRange(n,f)定义。

以上是自己的一些认识,也可能有错误,留待后看。
### Android 坐标系与 OpenGL 坐标系的区别 #### 区别一:坐标轴方向不同 在 Android 的视图系统中,坐标系的原点位于屏幕的左上角 (0, 0),x 轴正方向是从左向右,y 轴正方向则是从上向下。而在 OpenGL 中,世界坐标系是以屏幕中心为原点 (0, 0, 0),且是始终不变的。具体来说: - **X轴**:两者相同,都是从左至右。 - **Y轴**:Android 是从上往下,而 OpenGL 则是从下往上[^4]。 此外,在 OpenGL 中还存在 z 轴用于表示深度信息,其正方向指向观察者(即从屏幕内部向外)。 #### 区别二:单位差异 对于 Android 来说,默认情况下使用的通常是像素作为基本度量单位;然而,在 OpenGL ES 环境下,通常采用标准化设备坐标(NDC),其中整个可见区域被映射到了 [-1, 1] 这样一个范围内。 ### 转换方法 为了实现两个坐标系统的相互转换,主要涉及到以下几个方面的工作: #### 平移变换 由于两者的 y 轴方向相反以及原点位置的不同,因此需要通过平移操作来进行调整。假设当前有宽度 w 高度 h 的 Android 视窗,则可以通过如下方式完成转换: ```java // 将 Android 屏幕坐标转成 NDC (-1 to 1 range) float ndcX = ((androidX / screenWidth) * 2.0f) - 1; float ndcY = (((screenHeight - androidY) / screenHeight) * 2.0f) - 1; // 反之亦然 int screenX = Math.round(((ndcX + 1) / 2.0f) * screenWidth); int screenY = screenHeight - Math.round((((ndcY + 1) / 2.0f)) * screenHeight); ``` #### 正交投影设置 当使用 `Matrix` 类中的 `orthoM()` 函数创建一个正交投影矩阵时,可以根据实际需求指定近裁剪面、远裁剪面以及其他参数来适配不同的显示比例尺寸[^3]。这有助于确保绘制的内容能够在各种分辨率屏幕上正确呈现而不失真。 #### 使用 GLSurfaceView 实现自动处理 如果开发者利用了 Android 提供的 `GLSurfaceView` 组件及其渲染器接口 (`Renderer`) ,那么很多关于视角变换的任务都可以交给框架本身去完成。只需要关注如何构建合适的场景几何体并应用相应的材质属性即可。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

n5

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值