在着色器中设置向量,它是如何工作的? OpenGL GLSL
Setting vectors in shaders, How does it work? OpenGL GLSL
我正在尝试了解顶点和片段着色器在 OpenGL ES 2.0 中的工作原理。
我的着色器看起来像这样:
顶点着色器:
// source code for the vertex shader
attribute vec4 vPosition
attribute vec2 a_texCoord;
varying vec2 v_texCoord;
void main() {
gl_Position = vPosition;
v_texCoord = a_texCoord;
}
片段着色器:
// source code for the fragment shader
precision mediump float;
uniform sampler2D s_texture;
varying vec2 v_texCoord;
void main() {
gl_FragColor = texture2D(s_texture, v_texCoord);
}
使用这些着色器的程序绘制一个带有纹理(图像)的简单二维矩形。首先,它将矩形的顶点和纹理坐标传递给具有属性的顶点着色器。然后顶点着色器将带有变化的纹理坐标传递给片段着色器。
令我困惑的是向量vPosition,a_texCoord和v_texCoord只能保存1个Point,却需要保存4个不同的Point。我在某处读到顶点着色器将为每个顶点调用。这是否意味着我要绘制的每个图像的顶点和片段着色器将被处理 4 次?当我定义属性(glVertexAttribPointer)时,我正在使用一个有 4 个点的数组,着色器如何在一个只能存储 1 个点的变量中保存 4 个点?
我想使用制服而不是属性,我读得更好。我也不需要 varying 变量。我想要这样:
顶点着色器:
// source code for the vertex shader
uniform vec4 vPosition;
void main() {
gl_Position = vPosition;
}
片段着色器:
// source code for the fragment shader
precision mediump float;
uniform sampler2D s_texture;
uniform vec2 v_texCoord;
void main() {
gl_FragColor = texture2D(s_texture, v_texCoord);
}
这可能吗?我仍然可以将我需要的所有点传递给着色器吗?这是个好主意吗?
不,您不能将每个顶点的数据存储到制服中。 uniform 设计为每个绘制调用,顶点属性设计为每个顶点。您可以将顶点位置与制服一起使用的唯一方法是绘制一个点,因为它仅使用 1 个坐标。
关于顶点和处理:是的,如果您使用 4 个顶点进行绘制,将至少调用 4 次顶点着色器来处理这些顶点。您无需为此担心,假设使用 4 个顶点绘制 100x100 图像,将调用 4 次顶点着色器和 10000(100x100) 次片段着色器调用。这就是 GPU 的设计目的,它的效率非常高。
您可以查看 openGL 管道的一些完整图表以更好地理解所有内容,但只是为您提供有关调用 openGL 进行绘制时发生的情况的线索:
- 根据您正在绘制的形状(例如三角形),将在顶点着色器中获取和处理一些点,这包括所有属性,例如位置、纹理坐标、法线...
- 接下来将进行光栅化,此时将确定要绘制缓冲区上的所有像素,并为每个像素分配一个片段着色器。
- 然后片段着色器发生,它将接收所有插值和它需要的固定制服,并刷新您当前使用的每个附加缓冲区中的单个特定像素(颜色、深度...)。
与我描述的相比,这条管道现在又多了很多,但这些对于您提出的问题来说是最有趣的。
试着理解着色器只是一个长的固定管道的一部分,现在可以被覆盖,你不应该问自己它们被调用了多少次至少不是为了像你正在做的那样简单的操作.如果您需要优化这些调用的数量,您很可能需要在 CPU 上进行优化。例如,如果您正在绘制数千个彼此重叠的大矩形,您可以考虑使用一种算法来减小绘制一个矩形所需的大小,该矩形大部分被其他矩形重叠,或者如果它完全重叠则根本不绘制。
所以要了解您发布的着色器发生了什么:如果您正在绘制带纹理的矩形,您将调用 4 或 6 次顶点着色器(6 次使用两个三角形)。顶点着色器将接收所有启用的属性,在您的情况下是位置和纹理坐标。这两个值什么也没做,但在这一点上向前传递。然后这个位置是自动的(你不需要另一个可变参数来传递它)但是纹理坐标被分配给一个你称为 v_texCoord
的可变 属性。这意味着将为要绘制的每个像素插值纹理坐标,然后在片段着色器中接收这些值。这就是为什么您实际上可以看到绘制的纹理的原因,因为每个像素都有不同的颜色,取自具有片段着色器接收的不同插值坐标的纹理。所以用 uniform 替换属性会破坏这一切,不会发生插值,所有像素看起来都完全一样。
我正在尝试了解顶点和片段着色器在 OpenGL ES 2.0 中的工作原理。
我的着色器看起来像这样:
顶点着色器:
// source code for the vertex shader
attribute vec4 vPosition
attribute vec2 a_texCoord;
varying vec2 v_texCoord;
void main() {
gl_Position = vPosition;
v_texCoord = a_texCoord;
}
片段着色器:
// source code for the fragment shader
precision mediump float;
uniform sampler2D s_texture;
varying vec2 v_texCoord;
void main() {
gl_FragColor = texture2D(s_texture, v_texCoord);
}
使用这些着色器的程序绘制一个带有纹理(图像)的简单二维矩形。首先,它将矩形的顶点和纹理坐标传递给具有属性的顶点着色器。然后顶点着色器将带有变化的纹理坐标传递给片段着色器。
令我困惑的是向量vPosition,a_texCoord和v_texCoord只能保存1个Point,却需要保存4个不同的Point。我在某处读到顶点着色器将为每个顶点调用。这是否意味着我要绘制的每个图像的顶点和片段着色器将被处理 4 次?当我定义属性(glVertexAttribPointer)时,我正在使用一个有 4 个点的数组,着色器如何在一个只能存储 1 个点的变量中保存 4 个点?
我想使用制服而不是属性,我读得更好。我也不需要 varying 变量。我想要这样:
顶点着色器:
// source code for the vertex shader
uniform vec4 vPosition;
void main() {
gl_Position = vPosition;
}
片段着色器:
// source code for the fragment shader
precision mediump float;
uniform sampler2D s_texture;
uniform vec2 v_texCoord;
void main() {
gl_FragColor = texture2D(s_texture, v_texCoord);
}
这可能吗?我仍然可以将我需要的所有点传递给着色器吗?这是个好主意吗?
不,您不能将每个顶点的数据存储到制服中。 uniform 设计为每个绘制调用,顶点属性设计为每个顶点。您可以将顶点位置与制服一起使用的唯一方法是绘制一个点,因为它仅使用 1 个坐标。
关于顶点和处理:是的,如果您使用 4 个顶点进行绘制,将至少调用 4 次顶点着色器来处理这些顶点。您无需为此担心,假设使用 4 个顶点绘制 100x100 图像,将调用 4 次顶点着色器和 10000(100x100) 次片段着色器调用。这就是 GPU 的设计目的,它的效率非常高。
您可以查看 openGL 管道的一些完整图表以更好地理解所有内容,但只是为您提供有关调用 openGL 进行绘制时发生的情况的线索:
- 根据您正在绘制的形状(例如三角形),将在顶点着色器中获取和处理一些点,这包括所有属性,例如位置、纹理坐标、法线...
- 接下来将进行光栅化,此时将确定要绘制缓冲区上的所有像素,并为每个像素分配一个片段着色器。
- 然后片段着色器发生,它将接收所有插值和它需要的固定制服,并刷新您当前使用的每个附加缓冲区中的单个特定像素(颜色、深度...)。
与我描述的相比,这条管道现在又多了很多,但这些对于您提出的问题来说是最有趣的。
试着理解着色器只是一个长的固定管道的一部分,现在可以被覆盖,你不应该问自己它们被调用了多少次至少不是为了像你正在做的那样简单的操作.如果您需要优化这些调用的数量,您很可能需要在 CPU 上进行优化。例如,如果您正在绘制数千个彼此重叠的大矩形,您可以考虑使用一种算法来减小绘制一个矩形所需的大小,该矩形大部分被其他矩形重叠,或者如果它完全重叠则根本不绘制。
所以要了解您发布的着色器发生了什么:如果您正在绘制带纹理的矩形,您将调用 4 或 6 次顶点着色器(6 次使用两个三角形)。顶点着色器将接收所有启用的属性,在您的情况下是位置和纹理坐标。这两个值什么也没做,但在这一点上向前传递。然后这个位置是自动的(你不需要另一个可变参数来传递它)但是纹理坐标被分配给一个你称为 v_texCoord
的可变 属性。这意味着将为要绘制的每个像素插值纹理坐标,然后在片段着色器中接收这些值。这就是为什么您实际上可以看到绘制的纹理的原因,因为每个像素都有不同的颜色,取自具有片段着色器接收的不同插值坐标的纹理。所以用 uniform 替换属性会破坏这一切,不会发生插值,所有像素看起来都完全一样。