关于多视口渲染的两个拓展

 
  在游戏中经常有需要用到多视口渲染的地方,比如级联阴影技术(CSM)、立方体贴图(Cube Map)、虚拟现实(VR)等等。
  实质上,在 OpenGL4.1 之后,几何着色器(GS)已经开始支持 gl_ViewportIndex,也就具有了多视口渲染的能力。在进行多视口渲染时,GS 往往会成为性能瓶颈,因为所有的顶点数据都是通过 GS 分发到不同的视口中。所以 GPU 厂商都提供了一些语法糖来加速多视口渲染的数据传输速度,期待它们会加入到标准库中。
  NV_geometry_shader_passthrough / GL_NV_viewport_array2,Nvidia 公司提出的拓展,在 GS 中使用,不需要 Emit 语句和重复的输出将数据输出到多个 Viewport/Layer 中。
  GL_AMD_vertex_shader_layer / GL_AMD_vertex_shader_viewport_index,AMD 公司提出的拓展,在 VS 中使用,在 VS 阶段就可以将数据输出到多个 Viewport/Layer 中。
 

NV_geometry_shader_passthrough

  GS 可以用来做许多不同的操作,包括细分图元、改变图元类型等等。但是这些操作都有一个共同点,GS 可以看做一个大号的数据传递通道(passthrough)。但是在标准的 GS 中,需要写许多代码来将每个图元的顶点同时复制到输出图元中。而且,有的时候需要在 GS 中计算一些其他的数据,比如指定输出的 gl_Layer,而这些数据也要反复复制到输出图元的所有顶点数据。NV_geometry_shader_passthrough 扩展提供了语言抽象来避免显式的属性拷贝。
  下面是一个没有使用 NV_geometry_shader_passthrough 的例子。在这个 shader 中,gl_Position、Inputs.texcoord、Inputs.baseColor 都只是简单地从输入顶点复制到输出顶点。

layout(triangles) in;
layout(triangle_strip) out;
layout(max_vertices=3) out;

in Inputs {
    vec2 texcoord;
    vec4 baseColor;
} v_in[];
out Outputs {
    vec2 texcoord;
    vec4 baseColor;
};

void main()
{
    int layer = compute_layer();
    for (int i = 0; i < 3; i++) {
        gl_Position = gl_in[i].gl_Position;
        texcoord = v_in[i].texcoord;
        baseColor = v_in[i].baseColor;
        gl_Layer = layer;
        EmitVertex();
    }
}

  下面是等价的使用了扩展的 GS 代码。

#extension GL_NV_geometry_shader_passthrough : require

layout(triangles) in;
layout(passthrough) in gl_PerVertex {
    vec4 gl_Position;
} gl_in[];

layout(passthrough) in Inputs {
    vec2 texcoord;
    vec4 baseColor;
} v_in[];

void main()
{
    gl_Layer = compute_layer();
}

  这个扩展里的 passthrough 修饰符表示拷贝输入顶点的数据到输出顶点中。而 GS 代码 main 函数中可以做的就是计算需要赋值给单个图元的所有顶点数据。之所以提出了这个扩展,是因为 GS 已经对这个函数提供了硬件支持。
  当 GS 与 FS 进行交互时,逐面元操作会被广播到输出面元的所有顶点。这里,强烈推荐在 FS 中使用 flat 关键字。flat 表示 GS 的输出不做任何操作。比如,一个三角形的透视校正如下所示。
f = {(a * {f_a \over w_a} + b * {f_b \over w_b} + c * {f_c \over w_c}) \over ({a \over w_a} + {b \over w_b} + {c\over w_c})}  在这里,abc 表示插值权重,相加为 1。f_af_bf_c 是每个顶点的变量,w_aw_bw_c 表示每个顶点的裁剪 w 分量。如果是某个属性是针对面元的,有 f_a=f_b=f_c=f_p,所以有,
f=f_p * {({a\over w_a} + {b\over w_b} + {c\over w_c}) \over ({a\over w_a} + {b\over w_b} + {c\over w_c})}  如果浮点数有无限的精度,多做运算问题不大。可是除法会影响浮点数低位 bit 的数值,造成一定的误差,而且 flat 也节省了计算。
 

GL_NV_viewport_array2

  GL_NV_viewport_array2 提供了一个新的 shader 输出:gl_ViewportMask[],增加了新的广播机制,允许单个图元同时输出到多个视口中。原有的 GS 多视口渲染没有广播机制,比较低效。NV_viewport_array2 扩展便是为了支持比 GS Loop 更高效的多视口渲染。
  gl_ViewportMask[] 输出在 Vertex shader/Control Shader/Evaluation Shade/Geometry Shader 中都是有效的,与此同时,还附带了 gl_ViewportIndex 和 gl_Layer 在上述 shader 中生效。从这个意义上来说,GL_NV_viewport_array2 拓展是 GL_AMD_vertex_shader_layer 和 GL_AMD_vertex_shader_viewport_index 的超集。
 
  GL_NV_viewport_array2 和 NV_geometry_shader_passthrough 配合使用将会非常强大,Nvidia 在 Maxwell 显卡中组合使用这两个扩展来加速级联阴影映射,详情请见:CascadedShadowMapping
  不仅如此,GL_NV_viewport_array2 在 Maxwell 中还可以和另一个拓展 NV_viewport_swizzle 配合,在一个 pass 中将图元渲染到 Cubemap 的六个面上,详情请见:NVIDIA OpenGL in 2016CubemapRendering
  关于 GL_NV_viewport_array2 具体的使用方式不再赘述,可从上述链接中找到示例。
 
 

About the Author

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注