天天看点

曲面细分着色器---细分二维四边形

openGL系列文章目录

文章目录

  • ​​openGL系列文章目录​​
  • ​​前言​​
  • ​​一、曲面细分​​
  • ​​二、细分二维四边形​​
  • ​​参考​​

前言

术语Tessellation(镶嵌)是指一大类设计活动,通常是指在平坦的表面上,用各种几何形状的瓷砖相邻排列以形成图案。它的目的可以是艺术性的或实用性的,很多例子可以追溯到几千年前在3D 图形学中,Tessellation 指的是有点不同的东西(曲面细分),但显然是由它的经典对应物(镶嵌)启发而成的。在这里,曲面细分指的是生成并且操控大量三角形以渲染复杂的形状和表面,尤其是使用硬件进行渲染。曲面细分是OpenGL 核心近期才增加的新功能,在2010 年的4.0 版本中出现。

一、曲面细分

OpenGL 对硬件曲面细分的支持,通过3 个管线阶段提供:

(1)曲面细分控制着色器;

(2)曲面细分器;

(3)曲面细分评估着色器。

第(1)和第(3)阶段是可编程的;而中间的第(2)阶段不是。为了使用曲面细分,

程序员通常会提供控制着色器和评估着色器。

曲面细分器(其全名是曲面细分图元生成器,或TPG)是硬件支持的引擎,可以生成固

定的三角形网格。②控制着色器允许我们配置曲面细分器要构建什么样的三角形网格。然后,

评估着色器允许我们以各种方式操控网格。然后,被操控过的三角形网格,会作为通过管

线前进的顶点的源数据。回想一下图2.2,在管线上,曲面细分着色器位于顶点着色器和几

何着色器阶段之间。

让我们从一个简单的应用程序开始,该应用程序只使用曲面细分器创建顶点的三角形网

格,然后在不进行任何操作的情况下显示它。为此,我们需要以下模块。

(1)C++/OpenGL 应用程序:

创建一个摄像机和相关的MVP 矩阵,视图(v)和投影(p)矩阵确定摄像机朝向,模

型(m)矩阵可用于修改网格的位置和方向。

(2)顶点着色器:

在这个例子中基本上什么都不做,顶点将在曲面细分器中生成。

(3)曲面细分控制着色器:

指定曲面细分器要构建的网格。

(4)曲面细分评估着色器:

将MVP 矩阵应用于网格中的顶点。

(5)片段着色器:

只需为每个像素输出固定颜色。

程序12.1 显示了整个应用程序的代码。即使像这样的简单示例也相当复杂,因此许多代

码元素都需要解释。请注意,这是我们第一次使用除顶点和片段着色器之外的组件构建

GLSL 渲染程序。因此,我们实现了createShaderProgram()的4 参数重载版本。

二、细分二维四边形

理解OpenGL的硬件细分操作最好的方法就是细分一个二维四边形,而后可视化细分的结果。使用线性插值后,产生的三角形和细分坐标(u,v)相关。通过使用不同的输入和输出细分级别产生四边形对于学习很有价值。

曲面细分图元生成器(TPG)根据6个参数来划分(u,v)空间。它们是内部细分级别(0和1两个)和外部细分级别(0-3共6个)。下面是对它们的描述:

外部细分级别0(OL0):沿v方向,u坐标为0的边进行划分的个数。

外部细分级别1(OL1):沿u方向,v坐标为0的边进行划分的个数。

外部细分级别2(OL2):沿v方向,u坐标为1的边进行划分的个数。

外部细分级别3(OL3):沿u方向,v坐标为1的边进行划分的个数。

内部细分级别0(IL0):沿u方向进行内部划分的个数。

内部细分级别1(IL1):沿v方向进行内部划分的个数。

下图显示了划分级别对应的空间划分。外部划分级别定义了沿边划分的个数,内部划分级别定义了

曲面细分着色器---细分二维四边形

这6个细分级别可以通过数组g_TessLevelOuter和gl_TessLevelInner设置。比如,gl_TessLevelInner[0]对应IL0,gl_TessLevelOuter[2]对应OL2。

我们绘制一个细分四边形来帮助理解OpenGL的四边形细分,如下图所示。

曲面细分着色器---细分二维四边形

我们使用线性插值进行细分,图中的三角形代表了四边形的划分。x轴对应u坐标,y轴对应v坐标。三角形的顶点由TPG产生。细分的三角形数量,可以直接从图中看出。比如,何止外部细分级别为2,内部细分级别为8时,我们可以看到外部的边被划分为两部分,在内部,(u,v)被划分为8部分。在进行代码实现之前,我们先讨论以下线性插值。对于下图的四边形,它内部的任一点可以通过对四边形四角的顶点插值得到。

曲面细分着色器---细分二维四边形

我们通过TPG生成u,v坐标,然后通过上图的线性插值确定它在四边形中的位置。

准备

我们通过Uniform变量Inner和Outer在OpenGL程序中设置细分级别。我们使用之前提到的几何着色器来处理这些三角形。

然后,设置OpenGL程序渲染包含4个顶点的Patch图元。

实现

我们采取下面的步骤来进行四边形细分:

  1. 使用下面的代码作为顶点着色器:
  2. ​#version 400 layout (location = 0 ) in vec2 VertexPosition; void main() { gl_Position = vec4(VertexPosition, 0.0, 1.0); }​

  3. 使用下面的代码作为曲面细分控制着色器:
  4. ​#version 400 layout( vertices=4 ) out; uniform int Outer; uniform int Inner; void main() { // Pass along the vertex position unmodified gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position; gl_TessLevelOuter[0] = float(Outer); gl_TessLevelOuter[1] = float(Outer); gl_TessLevelOuter[2] = float(Outer); gl_TessLevelOuter[3] = float(Outer); gl_TessLevelInner[0] = float(Inner); gl_TessLevelInner[1] = float(Inner); }​

  5. 使用下面代码作为曲面细分计算着色器:
  6. ​#version 400 layout( quads, equal_spacing, ccw ) in; uniform mat4 MVP; void main() { float u = gl_TessCoord.x; float v = gl_TessCoord.y; vec4 p0 = gl_in[0].gl_Position; vec4 p1 = gl_in[1].gl_Position; vec4 p2 = gl_in[2].gl_Position; vec4 p3 = gl_in[3].gl_Position; // Linear interpolation gl_Position = p0 * (1-u) * (1-v) + p1 * u * (1-v) + p3 * v * (1-u) + p2 * u * v; // Transform to clip coordinates gl_Position = MVP * gl_Position; }​

  7. 使用专栏文章在着色后的网格上绘制线框中的几何着色器。
  8. 使用下面的代码作为片段着色器:

#version 400

uniform float LineWidth;

uniform vec4 LineColor;

uniform vec4 QuadColor;

noperspective in vec3 EdgeDistance;

// From geometry shader

layout ( location = 0 ) out vec4 FragColor;

float edgeMix()

{

//insert code here to determine how much of the edge

//color to include (see recipe “Drawing a wireframe on

//top of a shaded mesh”). **

}

void main()

{

float mixVal = edgeMix();

FragColor=mix( QuadColor, LineColor, mixVal);

}

6. 在OpenGL程序中设置Patch图元顶点个数:

glPatchParameteri(GL_PATCH_VERTICES, 4);

7. 渲染Patch图元。

原理

我们的顶点着色器在这里只用来传递参数。

我们在TCS中定义Patch图元图元的顶点数目:

layout (vertices=4) out

在main函数,我们没有对顶点进行修改,只是进行了细分级别的设置。所有4个外部细分级别被设置为变量Outer的值,所有内部细分级别被设置为变量Inner的值。

在曲面细分计算着色器,我们设置了一些其它的细分参数:

layout ( quads, equal_spacing, ccw ) in;

参数quads表示TPG进行的是四边形细分。参数equal_spacing表示所有细分大小相同,最后一个参数ccw表示产生的顶点的顺序是逆时针的。

在TES的main函数,我们通过数组gl_TessCoord获取参数坐标。然后读取数组gl_in中存储的Patch图元的4个顶点信息。最后将这些信息存储在临时变量中进行插值计算。

插值计算的结果赋值给变量gl_Position。最后,使用模型视图投影矩阵转换顶点坐标到剪切空间。

在片段着色器,我们通过混合函数来渲染三角形的边和非边部分。

参阅

参考

继续阅读