OpenGL ES 2.0 中奇怪的精度行为
Weird precision behavior in OpenGL ES 2.0
为了实现 ,我编写了以下两个版本的顶点和片段着色器:
// Vertex:
precision highp int;
precision highp float;
uniform vec4 r_info;
attribute vec2 s_coords;
attribute vec2 r_coords;
varying vec2 t_coords;
void main (void) {
int w = int(r_info.w);
int x = int(r_coords.x) + int(r_coords.y) * int(r_info.y);
int y = x / w;
x = x - y * w;
y = y + int(r_info.x);
t_coords = vec2(x, y) * r_info.z;
gl_Position = vec4(s_coords, 0.0, 1.0);
}
// Fragment:
precision highp float;
uniform sampler2D sampler;
uniform vec4 color;
varying vec2 t_coords;
void main (void) {
gl_FragColor = vec4(color.rgb, color.a * texture2D(sampler, t_coords).a);
}
对比
// Vertex:
precision highp float;
attribute vec2 s_coords;
attribute vec2 r_coords;
varying vec2 t_coords;
void main (void) {
t_coords = r_coords;
gl_Position = vec4(s_coords, 0.0, 1.0);
}
// Fragment:
precision highp float;
precision highp int;
uniform vec4 r_info;
uniform sampler2D sampler;
uniform vec4 color;
varying vec2 t_coords;
void main (void) {
int w = int(r_info.w);
int x = int(t_coords.x) + int(t_coords.y) * int(r_info.y);
int y = x / w;
x = x - y * w;
y = y + int(r_info.x);
gl_FragColor = vec4(color.rgb, color.a * texture2D(sampler, vec2(x, y) * r_info.z).a);
}
它们之间的唯一区别(我希望如此)是纹理坐标转换的位置。在第一个版本中,数学运算发生在顶点着色器中,在第二个版本中,它发生在片段着色器中。
现在,官方 OpenGL ES SL 1.0 Specifications 声明“[t]he vertex 语言必须提供至少 16 位的整数精度,外加一个符号位”和“[t]he fragment 语言必须提供至少 10 位的整数精度,外加一个符号位”(第 4.5.1 章)。如果我理解正确,这意味着只给定一个最小的实现,我应该能够在顶点着色器中获得的精度应该比在片段着色器中更好,对吗?但是,出于某种原因,代码的 second 版本可以正常工作,而第一个版本会导致一堆舍入错误。我错过了什么吗???
事实证明我从根本上误解了事物的运作方式......也许我仍然这样,但让我根据我目前的理解回答我的问题:
我认为对于渲染的每个像素,首先执行顶点着色器,然后执行片段着色器。但是,如果我现在理解正确的话,顶点着色器只为三角形图元的每个顶点调用一次(考虑到它的名字,这也有点道理......)。
所以,我上面的代码的第一个版本只计算了我正在绘制的三角形的实际角点(顶点)的正确纹理坐标。对于三角形中的所有其他像素,纹理坐标只是这些角坐标之间的线性插值。当然,由于我的公式不是线性的(包括舍入和模运算),这会导致每个像素的纹理坐标错误。
不过,第二个版本将非线性变换应用于每个像素位置的纹理坐标,从而在所有位置提供正确的纹理坐标。
所以,广义学习(以及我没有删除问题的原因):
所有非线性纹理坐标变换都必须在片段着色器中完成。
为了实现
// Vertex:
precision highp int;
precision highp float;
uniform vec4 r_info;
attribute vec2 s_coords;
attribute vec2 r_coords;
varying vec2 t_coords;
void main (void) {
int w = int(r_info.w);
int x = int(r_coords.x) + int(r_coords.y) * int(r_info.y);
int y = x / w;
x = x - y * w;
y = y + int(r_info.x);
t_coords = vec2(x, y) * r_info.z;
gl_Position = vec4(s_coords, 0.0, 1.0);
}
// Fragment:
precision highp float;
uniform sampler2D sampler;
uniform vec4 color;
varying vec2 t_coords;
void main (void) {
gl_FragColor = vec4(color.rgb, color.a * texture2D(sampler, t_coords).a);
}
对比
// Vertex:
precision highp float;
attribute vec2 s_coords;
attribute vec2 r_coords;
varying vec2 t_coords;
void main (void) {
t_coords = r_coords;
gl_Position = vec4(s_coords, 0.0, 1.0);
}
// Fragment:
precision highp float;
precision highp int;
uniform vec4 r_info;
uniform sampler2D sampler;
uniform vec4 color;
varying vec2 t_coords;
void main (void) {
int w = int(r_info.w);
int x = int(t_coords.x) + int(t_coords.y) * int(r_info.y);
int y = x / w;
x = x - y * w;
y = y + int(r_info.x);
gl_FragColor = vec4(color.rgb, color.a * texture2D(sampler, vec2(x, y) * r_info.z).a);
}
它们之间的唯一区别(我希望如此)是纹理坐标转换的位置。在第一个版本中,数学运算发生在顶点着色器中,在第二个版本中,它发生在片段着色器中。
现在,官方 OpenGL ES SL 1.0 Specifications 声明“[t]he vertex 语言必须提供至少 16 位的整数精度,外加一个符号位”和“[t]he fragment 语言必须提供至少 10 位的整数精度,外加一个符号位”(第 4.5.1 章)。如果我理解正确,这意味着只给定一个最小的实现,我应该能够在顶点着色器中获得的精度应该比在片段着色器中更好,对吗?但是,出于某种原因,代码的 second 版本可以正常工作,而第一个版本会导致一堆舍入错误。我错过了什么吗???
事实证明我从根本上误解了事物的运作方式......也许我仍然这样,但让我根据我目前的理解回答我的问题:
我认为对于渲染的每个像素,首先执行顶点着色器,然后执行片段着色器。但是,如果我现在理解正确的话,顶点着色器只为三角形图元的每个顶点调用一次(考虑到它的名字,这也有点道理......)。
所以,我上面的代码的第一个版本只计算了我正在绘制的三角形的实际角点(顶点)的正确纹理坐标。对于三角形中的所有其他像素,纹理坐标只是这些角坐标之间的线性插值。当然,由于我的公式不是线性的(包括舍入和模运算),这会导致每个像素的纹理坐标错误。
不过,第二个版本将非线性变换应用于每个像素位置的纹理坐标,从而在所有位置提供正确的纹理坐标。
所以,广义学习(以及我没有删除问题的原因):
所有非线性纹理坐标变换都必须在片段着色器中完成。