纹理倒置
背景
在 render-gl
接入 frame buffer object
实现 off-screen
渲染后,发现得到的渲染图发生了180°的倒置.
查阅了有关资料后,在 eglspec.1.5
中的 2.2.2.1 Native Surface Coordinate Systems
找到了答案:
The coordinate system for native windows and pixmaps in most platforms is in-
verted relative to the OpenGL, OpenGL ES, and OpenVG client API coordinate
systems. In such systems, native windows and pixmaps have (0, 0) in the upper
left of the pixmap, while the client APIs have (0, 0) in the lower left. To accomo-
date this, client API rendering to window and pixmap surfaces must invert their
own y coordinate when accessing the color buffer in the underlying native win-
dow or pixmap, so that the resulting images appear as intended by the application
when the final image is displayed by eglSwapBuffers or copied from a pixmap to
a visible window using native rendering APIs.
原生 render-gl
依赖于 native window system
实现 on-screen
渲染,其 frame buffer
的起始坐标为左下角; 而使用 frame buffer object
实现的 off-screen
渲染其坐标与 texture
保持一致,起始坐标为左上角,如下图所示:
weston
原生只支持基于 native window system
的渲染方式,故其已经做过 texture
顶点左上角至 default framebuffer
顶点左下角的适配了;而在引入 frame buffer object
之后, frmae buffer object
的顶点坐标重新变为左上角,而其逻辑却与之前保持一致,结果就是图像发生了180°倒置.
解决方案
解决这个问题的方案有很多种,因为归根结底是因为顶点坐标的 y 在最终渲染前没有进行一次正确的翻转;所以无论怎么样,只要能够完成一次正确的翻转,就能够解决问题; 大体上分为两大种解决方案:
- 软件上进行适配
- Vertex Shader 中修改顶点坐标
实际上第一次解决纹理倒置时使用的第一种方案,但是软件上的顶点倒置并没有那么简单,需要正确处理所有与之相关的业务逻辑;而有关与此的上下文又散落在各种零碎的代码片段,正确处理起来的难度实际上相当之大.
在 Vertex Shader
中修改坐标顶点实际上讲的就是修改 gl_Position
, render-gl
的 Vertex Shader
如下所示:
uniform mat4 proj;
attribute vec2 position;
attribute vec2 texcoord;
varying vec2 v_texcoord;
void main()
{
gl_Position = proj * vec4(position, 0.0, 1.0);
v_texcoord = texcoord;
}
要想正确地处理顶点坐标,就需要正确地处理 gl_Position
, 有关于 gl_Postion
的描述可以参见 GLSLangSpec.4.60
:
The variable gl_Position is intended for writing the homogeneous vertex position. It can be written
at any time during shader execution. This value will be used by primitive assembly, clipping,
culling, and other fixed functionality operations, if present, that operate on primitives after vertex
processing has occurred. Its value is undefined after the vertex processing stage if the vertex
shader executable does not write gl_Position.
简单地将, gl_Position
是一个四元组,分别为 x、 y、 z 、w, 取值范围为 [-1, 1]; 对于上层用户而言,坐标应当是整数,比如我的屏幕尺寸是1080x1920,那么右下角的坐标就应当是 (1920, 1080),对应到 Vertex Shader
中即是 attribute position
, 所以从 position
到 gl_Position
的过程中需要做一个归一化处理.
OpenGL
使用的坐标系并不是笛卡尔坐标系,即不是由 x, y, z 组成; 而是使用齐次坐标系,即由 x, y, z, w 组成; 当 w 恒为 1 时, 齐次坐标系等价于笛卡尔坐标系, 这里就是这种情况.
在 Vertex Shader
中, gL_Position
的结果由 proj
和由 position
组成的四元组相乘得到, 这里的相乘实际上一个矩阵运算,更具体一点是由 proj
这个 4x4 的矩阵与由 position
组成的 4x1 的矩阵得到 gl_Position
这个 4x1 且范围在 [-1, 1] 之间的矩阵.
proj
的全称为 projection
, 在这里翻译成投影应该是比较合适的,它主要做了两件事:
- 坐标映射 (Clamp)
- 坐标旋转 (Rotate)
这两步可以通过 proj
来一起完成,用图示来表示如下:
所以实际上的解决办法就是修改 proj
这个矩阵的值,将其 (2,1) 位置的值由负修改为正,那么最终得到的顶点齐次坐标系的参考点就为左上角,或者也可以说是垂直翻转180°.