我在片段着色器中进行光线投射。为了这个目的,我可以想出几种方法来绘制全屏四边形。在投影矩阵设置为单位矩阵的情况下在剪辑空间中绘制四边形,或使用几何着色器将点转换为三角形条带。前者使用立即模式,在OpenGL 3.2中弃用。后者我使用了新颖性,但它仍然使用立即模式来描绘一点。
答案 0 :(得分:17)
您可以发送两个创建四边形的三角形,其顶点属性分别设置为-1/1。
您无需将它们与顶点/片段着色器中的任何矩阵相乘。
以下是一些代码示例,简单如下:)
顶点着色器:
const vec2 madd=vec2(0.5,0.5);
attribute vec2 vertexIn;
varying vec2 textureCoord;
void main() {
textureCoord = vertexIn.xy*madd+madd; // scale vertex attribute to [0-1] range
gl_Position = vec4(vertexIn.xy,0.0,1.0);
}
Fragment Shader:
varying vec2 textureCoord;
void main() {
vec4 color1 = texture2D(t,textureCoord);
gl_FragColor = color1;
}
答案 1 :(得分:12)
要输出全屏四边形几何着色器,可以使用:
#version 330 core
layout(points) in;
layout(triangle_strip, max_vertices = 4) out;
out vec2 texcoord;
void main()
{
gl_Position = vec4( 1.0, 1.0, 0.5, 1.0 );
texcoord = vec2( 1.0, 1.0 );
EmitVertex();
gl_Position = vec4(-1.0, 1.0, 0.5, 1.0 );
texcoord = vec2( 0.0, 1.0 );
EmitVertex();
gl_Position = vec4( 1.0,-1.0, 0.5, 1.0 );
texcoord = vec2( 1.0, 0.0 );
EmitVertex();
gl_Position = vec4(-1.0,-1.0, 0.5, 1.0 );
texcoord = vec2( 0.0, 0.0 );
EmitVertex();
EndPrimitive();
}
顶点着色器只是空的:
#version 330 core
void main()
{
}
要使用此着色器,您可以使用带有空VBO的虚拟绘图命令:
glDrawArrays(GL_POINTS, 0, 1);
答案 2 :(得分:9)
我将争辩说,最有效的方法将是绘制一个单个“全屏”三角形。要使三角形覆盖全屏,它必须大于实际的视口。在NDC中(如果设置了w=1
,则在裁剪空间中),视口将始终是[-1,1]
正方形。为了使三角形完全覆盖该区域,我们需要使两个侧面的长度为视口矩形的两倍,以便使第三侧与视口的边缘相交,因此我们可以例如使用以下坐标(在逆时针顺序):(-1,-1)
,(3,-1)
,(-1,3)
。
我们也不必担心texcoords。要在可见视口上获得通常的归一化[0,1]
范围,我们只需要使顶点的相应texcoords变大,重心插值将对任何视口像素产生与使用四边形时完全相同的结果
当然可以按照demanze's answer中的建议将这种方法与无属性渲染结合使用:
out vec2 texcoords; // texcoords are in the normalized [0,1] range for the viewport-filling quad part of the triangle
void main() {
vec2 vertices[3]=vec2[3](vec2(-1,-1), vec2(3,-1), vec2(-1, 3));
gl_Position = vec4(vertices[gl_VertexID],0,1);
textcoords = 0.5 * gl_Position.xy + vec2(0.5);
}
为什么单个三角形会更有效?
关于一个保存的顶点着色器调用,以及另一个要在前端处理的少三角形,这不是不是。使用单个trianlge的最显着效果是更少的片段shder调用)
一旦图元的单个像素落入此类块,实际GPU就会为2x2像素大小的块调用片段着色器。这对于计算window-space derivative functions是必需的(纹理采样也隐式需要这些,请参见this question)。
如果图元未覆盖该块中的所有4个像素,则其余片段着色器调用将无济于事(除了为导数计算提供数据外),这就是所谓的 helper调用 em>(甚至可以通过gl_HelperInvocation
GLSL function进行查询。有关更多详细信息,另请参见Fabian "ryg" Giesen's blog article。
如果渲染具有两个三角形的四边形,则两个都将有一条沿对角线穿过视口的边缘,并且在这两个三角形上,您将在该边缘上生成很多无用的辅助调用。对于完美的正方形视口(纵横比1),效果将最差。如果您绘制单个三角形,则不会有这样的对角线边缘(它位于视口之外并且完全不涉及栅格化器),因此将没有其他辅助程序调用。
请稍等一下,如果三角形在视口边界上延伸,它是否会被裁剪并实际上将更多工作在GPU上?
如果您阅读有关图形管线(甚至是GL规范)的教科书材料,则可能会给人以印象。但是现实世界中的GPU使用了一些不同的方法,例如保护带限幅。我在这里不做详细介绍(这本身就是一个主题,有关详细信息,请查看Fabian "ryg" Giesen's fine blog article),但是一般的想法是光栅化器将仅为视口内的像素生成片段(或无论它是否完全位于其内部,因此只要满足以下两个条件,我们就可以向其抛出较大的三角形:
a)三角形仅延伸2D顶部/底部/左侧/右侧剪切平面(与z维度的近/远剪切平面相反)
b)实际顶点坐标(以及光栅化器可能会对它们进行的所有中间计算结果)可以用GPU硬件光栅化器使用的内部数据格式表示。光栅化器将使用实现特定宽度的定点数据类型,而顶点坐标为32Bit单精度浮点数。 (这基本上是定义保护带大小的原因)
我们的tiranlge仅比视口大3倍,因此我们可以确定根本不需要修剪它。
但这值得吗?
好吧,片段着色器调用的节省是真实的(尤其是当您使用复杂的片段着色器时),但是在实际情况下,总体效果可能几乎无法测量。另一方面,该方法并不比使用全屏四边形更为复杂,并且使用的是较少的数据,因此即使可能不会产生很大的变化,也不会造成伤害,所以为什么< em>不使用吗?
可以将这种方法用于各种轴对齐的矩形,而不仅仅是全屏矩形吗?
从理论上讲,您可以将其与剪刀测试结合使用以绘制一些仲裁轴对齐的矩形(剪刀测试将非常有效,因为它仅限制了首先生成的片段,这不是硬件中的实际“测试”,会丢弃片段)。但是,这要求您更改要绘制的每个矩形的剪刀参数,这意味着要进行很多状态更改,并且每次绘制调用都将您限制为一个矩形,因此在大多数情况下,这样做不是一个好主意。
答案 3 :(得分:6)
Chistophe Riccio的提示:
由于我在视频中说明的原因,大三角形效率更高:
答案 4 :(得分:3)
根本不需要使用几何体着色器,VBO或任何内存。
顶点着色器可以生成四边形。
layout(location = 0) out vec2 uv;
void main()
{
float x = float(((uint(gl_VertexID) + 2u) / 3u)%2u);
float y = float(((uint(gl_VertexID) + 1u) / 3u)%2u);
gl_Position = vec4(-1.0f + x*2.0f, -1.0f+y*2.0f, 0.0f, 1.0f);
uv = vec2(x, y);
}
绑定一个空的VAO。发送6个顶点的绘图调用。
答案 5 :(得分:2)
以下来自类的绘制函数,它将fbo纹理绘制到屏幕对齐的四边形。
Gl.glUseProgram(shad);
Gl.glBindBuffer(Gl.GL_ARRAY_BUFFER, vbo);
Gl.glEnableVertexAttribArray(0);
Gl.glEnableVertexAttribArray(1);
Gl.glVertexAttribPointer(0, 3, Gl.GL_FLOAT, Gl.GL_FALSE, 0, voff);
Gl.glVertexAttribPointer(1, 2, Gl.GL_FLOAT, Gl.GL_FALSE, 0, coff);
Gl.glActiveTexture(Gl.GL_TEXTURE0);
Gl.glBindTexture(Gl.GL_TEXTURE_2D, fboc);
Gl.glUniform1i(tileLoc, 0);
Gl.glDrawArrays(Gl.GL_QUADS, 0, 4);
Gl.glBindTexture(Gl.GL_TEXTURE_2D, 0);
Gl.glBindBuffer(Gl.GL_ARRAY_BUFFER, 0);
Gl.glUseProgram(0);
实际四边形本身和坐标来自:
private float[] v=new float[]{ -1.0f, -1.0f, 0.0f,
1.0f, -1.0f, 0.0f,
1.0f, 1.0f, 0.0f,
-1.0f, 1.0f, 0.0f,
0.0f, 0.0f,
1.0f, 0.0f,
1.0f, 1.0f,
0.0f, 1.0f
};
vbo的绑定和设置我留给你。
顶点着色器:
#version 330
layout(location = 0) in vec3 pos;
layout(location = 1) in vec2 coord;
out vec2 coords;
void main() {
coords=coord.st;
gl_Position=vec4(pos, 1.0);
}
因为位置是原始的,也就是说,没有乘以任何矩阵,-1,-1 :: 1,1的四边形适合视口。寻找Alfonse的教程链接他在openGL.org上的任何帖子。
答案 6 :(得分:1)
这类似于demanze的答案,但我认为它更容易理解。另外,使用TRIANGLE_STRIP只能绘制4个顶点。
#version 300 es
out vec2 textureCoords;
void main() {
const vec2 positions[4] = vec2[](
vec2(-1, -1),
vec2(+1, -1),
vec2(-1, +1),
vec2(+1, +1)
);
const vec2 coords[4] = vec2[](
vec2(0, 0),
vec2(1, 0),
vec2(0, 1),
vec2(1, 1)
);
textureCoords = coords[gl_VertexID];
gl_Position = vec4(positions[gl_VertexID], 0.0, 1.0);
}