0%

实例化

在使用 glDrawArraysglDrawElements 函数绘制顶点数据时,OpenGL 需要告诉 OpenGL 从哪里读取数据以及从哪里找顶点属性,这会非常消耗性能(因为这些操作都是在相对缓慢的 CPUGPU 总线( CPU to GPU Bus )上进行的)。

这就导致一个问题,在绘制一个包含非常多(相同)模型的场景时,代码大致如下:

1
2
3
4
5
for(GLuint i = 0; i < amount_of_models_to_draw; i++)
{
DoSomePreparations(); // 绑定VAO,绑定纹理,设置uniform等
glDrawArrays(GL_TRIANGLES, 0, amount_of_vertices);
}

这样,即使渲染(如,绘制简单的三角形)是瞬间完成的,但是在如此浩瀚的模型渲染时,更多的性能都消耗在了 CPUGPU 总线上了。实例化就可以很好的解决此类问题。

实例化(Instancing):将数据一次性发送给 GPU,然后使用一个绘制函数(glDrawArraysInstancedglDrawElementsInstanced)让 OpenGL 利用这些数据绘制多个相同的物体。

gl_InstanceID:每个实例都有唯一的 ID,在使用实例化渲染调用时,gl_InstanceID 会从 0 开始,在每个实例被渲染时递增 1

例子,绘制100个2D四边形

  1. 顶点着色器:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    #version 330 core       /* 指定 GLSL 版本3.3,匹配 OpenGL 版本 */

    layout (location = 0) in vec2 aPos; /* 顶点位置变量的属性位置值为:0 */
    layout (location = 1) in vec3 aColor; /* 顶点颜色变量的属性位置值为:1 */

    out vec3 fColor; // 输出,颜色

    uniform vec2 offsets[100]; // 偏移

    void main()
    {
    /* 顶点着色器内建变量:‘gl_InstanceID’
    在使用实例化渲染调用(glDrawArraysInstanced 和 glDrawElementsInstanced)时,
    gl_InstanceID 会从 0 开始,在每个实例被渲染时递增 1 */
    vec2 offset = offsets[gl_InstanceID];
    gl_Position = vec4(aPos + offset, 0.0, 1.0);
    fColor = aColor;
    }
  2. 片段着色器:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #version 330 core       /* 指定GLSL版本3.3,匹配 OpenGL 版本 */

    in vec3 fColor; /* 输入,颜色值 */

    out vec4 FragColor; /* 输出,指定片段颜色 */

    void main()
    {
    FragColor = vec4(fColor, 1.0);
    }
  3. 偏移量计算并给 uniform 赋值:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    // 计算 100 个偏移量
    glm::vec2 translations[100];
    GLint index = 0;
    GLfloat offset = 0.1f;
    for(GLint y = -10; y < 10; y += 2)
    {
    for(GLint x = -10; x < 10; x += 2)
    {
    glm::vec2 translation;
    translation.x = (GLfloat)x / 10.0f + offset;
    translation.y = (GLfloat)y / 10.0f + offset;
    translations[index++] = translation;
    }
    }
    // 100 个 uniform 偏移量赋值
    for(GLuint i = 0; i < 100; i++)
    {
    std::stringstream ss;
    std::string index;
    ss << i;
    index = ss.str();
    quadShader.setUniformVec2(("offsets[" + index + "]").c_str(), translations[i]);
    }
  4. 绘制:

    1
    2
    3
    quadShader.use();
    glBindVertexArray(quadVAO);
    glDrawArraysInstanced(GL_TRIANGLES, 0, 6, 100);

实例化数组

阅读全文 »

几何图元

glDrawArrays(GLenum mode, GLint first, GLsizei count);glDrawElements(GLenum mode, GLsizei count, GLenum type, const void *indices); 是用于会渲染绘制图形的。

其参数 mode 表示几何图元的描述类型,常见的类型如下:

  • GL_POINTS单个顶点集;为 n 个顶点的每一个都绘制一个点;示意图如下:

  • GL_LINES多组双顶点线段;两个顶点解释为一条直线,直线之间并不连接(如果奇数个顶点,最后一个将忽略);示意图如下:

  • GL_LINE_LOOP闭合折线;从 v1vN 一系列的直线且构成环;示意图如下

  • GL_LINE_STRIP不闭合折线; 从 v1vN 一系列的直线;示意图如下:

  • GL_TRAINGLES多组独立填充三角形;一系列的三角形(3的倍数个顶点,多余的将忽略);示意图如下:

  • GL_TRAINGLE_STRIP线型连续填充三角形串; (v1,v2,v3)(v2,v3,v4),依次类推(所有的三角形是按相同方向绘制);示意图如下:

  • GL_TRAINGLE_FAN扇形连续填充三角形串;(v1,v2,v3)(v1,v3,v4),以此类推(一直是以v1开始);示意图如下:

几何着色器

几何着色器(Geometry Shader):位于顶点和片段着色器之间的一个 (可选的) 着色器,其输入是一个图元(如点或三角形)的一组顶点;可以在顶点发送到下一着色器阶段之前对它们随意变换。

一个简单的几何着色器例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#version 330 core       /* 指定 GLSL 版本3.3,匹配 OpenGL 版本 */

layout (points) in; /* 指定几何着色器输入的图元类型 */

layout (line_strip, max_vertices = 2) out; /* 指定几何着色器输出的图元类型和最大能够输出的顶点数量 */

/* GLSL 内建变量:'gl_in' 大致结构如下
in gl_Vertex
{
vec4 gl_Position;
float gl_PointSize;
float gl_ClipDistance[];
} gl_in[];
*/

void main()
{
/* 修改顶点位置向量 */
gl_Position = gl_in[0].gl_Position + vec4(-0.1, 0.0, 0.0, 0.0);
/* 几何着色器函数,将 gl_Position 中的(位置)向量添加到图元中,
即:发射出新顶点 */
EmitVertex();

/* 修改顶点位置向量 */
gl_Position = gl_in[0].gl_Position + vec4( 0.1, 0.0, 0.0, 0.0);
/* 发射新顶点 */
EmitVertex();

/* 几何着色器函数,将发射出的(Emitted)顶点合成为指定的输出渲染图元 */
EndPrimitive();
}

几何着色器“输入”的图元类型:

  • points绘制 GL_POINTS 图元时(最小顶点数:1)。
  • lines绘制 GL_LINESGL_LINE_STRIP时(最小顶点数:2)。
  • lines_adjacency绘制 GL_LINES_ADJACENCYGL_LINE_STRIP_ADJACENCY 时(最小顶点数:4)。
  • triangles绘制 GL_TRIANGLESGL_TRIANGLE_STRIPGL_TRIANGLE_FAN 时(最小顶点数:3)。
  • triangles_adjacency绘制 GL_TRIANGLES_ADJACENCYGL_TRIANGLE_STRIP_ADJACENCY 时(最小顶点数:6)。
阅读全文 »

GLSL的内建变量

顶点着色器变量

  • gl_Position:顶点着色器 输出 变量,类型:vec4,作用:设置裁剪空间输出位置向量。
  • gl_PointSize:顶点着色器 输出 变量,类型:float,作用:设置点的宽高(像素)。(该功能默认禁止,需要启用 GL_PROGRAM_POINT_SIZE
  • gl_VertexID:顶点着色器 输入 变量,类型:int,作用:当使用 glDrawElements 进行索引渲染的时候,该变量会存储当前正在绘制顶点的 索引;而当使用 glDrawArrays 不使用索引进行绘制的时候,该变量会储存从渲染调用开始的已处理 顶点数量
  • gl_InstanceID:顶点着色器 输入 变量,类型:unsigned int,作用:获取当前实例化渲染调用( glDrawArraysInstancedglDrawElementsInstanced)时正在渲染的是第几个实例(在使用实例化渲染时 gl_InstanceID 会从 0 开始,在每个实例被渲染时递增 1)。

片段着色器变量

  • gl_FragCoord:片段着色器 输入 变量,类型:vec3,作用:获取片段的位置向量。(其中,xy 分量是片段的窗口空间(Window-space)坐标,其原点为窗口的左下角;z 分量片段的深度值。)
  • gl_FrontFacing:片段着色器 输入 变量,类型:bool,作用:标识当前片段是属于正面的一部分还是背面的一部分(前提是没有启用 面剔除 GL_FACE_CULL)。
  • gl_FragDepth:片段着色器 输出 变量,类型:float,作用:设置片段的深度值。(注意:如果着色器没有写入值到 gl_FragDepth,它会自动取用 gl_FragCoord.z的值;但是,只要在片段着色器中对 gl_FragDepth 进行写入,OpenGL 就会禁用所有的 *提前深度测试(Early Depth Testing)*,其原因是,OpenGL 无法在片段着色器运行之前得知片段将拥有的深度值,因为片段着色器可能会完全修改这个深度值。)

接口块

GLSL提供了一个叫做 接口块(Interface Block) 的数据类型,来方便组合输入/输出变量;根据是一个输入还是输出块(Block),使用 inout 关键字来定义的。

  • 顶点着色器大致如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    #version 330 core   /* 指定GLSL版本3.3,匹配 OpenGL 版本 */

    layout (location = 0) in vec3 aPos; /* 顶点位置变量的属性位置值为:0 */
    layout (location = 1) in vec2 aTexCoords; /* 顶点纹理坐标变量的属性位置值为:1 */

    uniform mat4 modelMat; /* 模型矩阵 */
    uniform mat4 viewMat; /* 观察矩阵 */
    uniform mat4 projectionMat; /* 投影矩阵 */

    /* out 定义输出接口块,‘VS_OUT’:块名,在下一个
    着色器(片段着色器)中的输入接口块必须一致;
    ‘vs_out’:实例名,任意起 */
    out VS_OUT
    {
    vec2 TexCoords;
    } vs_out;

    void main()
    {
    gl_Position = projectionMat * viewMat * modelMat * vec4(aPos, 1.0);
    vs_out.TexCoords = aTexCoords; // 接口块赋值
    }
  • 片段着色器大致如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    #version 330 core   /* 指定GLSL版本3.3,匹配 OpenGL 版本 */

    out vec4 FragColor; /* 输出,指定片段颜色 */

    /* in 定义输入接口块,‘VS_OUT’:接口块类型名,与
    上一个着色器(顶点着色器)中的输出接口块一致;
    ‘fs_in’:实例名,随意起 */
    in VS_OUT
    {
    vec2 TexCoords;
    } fs_in;

    uniform sampler2D texture1; /* 纹理采样 1 */

    void main()
    {
    FragColor = texture(texture1, fs_in.TexCoords);
    }

Uniform缓冲对象

Uniform缓冲对象(Uniform Buffer Object):定义一系列在多个着色器中相同的全局 Uniform变量,当使用 Uniform缓冲对象的时候,只需要设置相关的 uniform 一次。

阅读全文 »

高级数据

OpenGL 中的 缓冲 只是一个管理特定内存块的对象,当将其绑定到一个 Buffer Target 时,就赋予了其意义(如,当绑定一个缓冲到 GL_ARRAY_BUFFER 时,它就是一个顶点数组缓冲。)OpenGL 内部会为每个 缓冲目标 储存一个缓冲,并且会根据目标的不同,以不同的方式处理缓冲。

  • glBufferData 函数来填充缓冲对象所管理的内存;该函数会分配一块内存,并将数据添加到这块内存中(如果将 data 参数设置为 NULL,则该函数将只会分配内存,但不进行数据填充)。

  • glBufferSubData 填充缓冲的特定区域;该函数不同的地方在于,可以提供一个偏移量,指定从何处开始填充这个缓冲,使其能够插入或者更新缓冲内存的某一部分。(注意,缓冲需要有足够的已分配内存,所以对一个缓冲调用 glBufferSubData 之前必须要先调用 glBufferData。)

  • glMapBuffer 函数用于请求缓冲内存的指针,直接将数据复制到缓冲当中:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    // 数据
    GLfloat data[] =
    {
    0.5f, 1.0f, -0.35f
    };
    // 绑定缓冲到指定的目标
    glBindBuffer(GL_ARRAY_BUFFER, buffer);
    // 获取缓冲内存的指针(以只写方式)
    void *ptr = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
    // 复制数据到内存
    memcpy(ptr, data, sizeof(data));
    // 释放指针(当不再需要使用的时候,必需释放)
    GLboolean isSuccess = glUnmapBuffer(GL_ARRAY_BUFFER);
    if (GL_TRUE == isSuccess)
    {
    // 映射(填充)数据成功
    }
    else
    {
    // 映射(填充)数据失败
    }

分批顶点属性

glVertexAttribPointer 函数用于指定顶点数组缓冲内容的属性布局:

  • 交错(Interleave)布局:123123123123

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // 顶点数据
    GLfloat vertices[] =
    {
    // 位置 法线 纹理坐标
    ... ... ..
    };
    // 填充缓冲
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), &vertices, GL_STATIC_DRAW);
    // 设置缓冲内容属性(位置)布局
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (void*)0);
    // 设置缓冲内容属性(法线)布局
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (void*)(3 * sizeof(GLfloat)));
    // 设置缓冲内容属性(纹理坐标)布局
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (void*)(6 * sizeof(GLfloat)));
  • 分批(Batch)布局: 111122223333

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    // 顶点位置数据
    GLfloat positions[] = { ... };
    // 顶点法线数据
    GLfloat normals[] = { ... };
    // 顶点纹理坐标数据
    GLfloat tex[] = { ... };
    // 填充(位置)缓冲
    glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(positions), &positions);
    // 填充(法线)缓冲
    glBufferSubData(GL_ARRAY_BUFFER, sizeof(positions), sizeof(normals), &normals);
    // 填充(纹理坐标)缓冲
    glBufferSubData(GL_ARRAY_BUFFER, sizeof(positions) + sizeof(normals), sizeof(tex), &tex);
    // 设置缓冲内容属性(位置)布局
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), 0);
    // 设置缓冲内容属性(法线)布局
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (void*)(sizeof(positions)));
    // 设置缓冲内容属性(纹理坐标)布局
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), (void*)(sizeof(positions) + sizeof(normals)));

复制缓冲

glCopyBufferSubData 函数可以从一个缓冲中复制数据到另一个缓冲中,原型如下:

1
void glCopyBufferSubData(GLenum readtarget, GLenum writetarget, GLintptr readoffset, GLintptr writeoffset, GLsizeiptr size);
  • readtarget:复制 缓冲目标(如,GL_COPY_READ_BUFFER,需提前将缓冲绑定到该缓冲目标上)
  • writetarget:复制 目标 缓冲目标(如,GL_COPY_WRITE_BUFFER,需提前将缓冲绑定到该缓冲目标上)
  • readoffset:读取缓冲起始位置(偏移量)
  • writeoffset:写入缓冲起始位置(偏移量)
  • size:读取数据的大小
阅读全文 »

立方体贴图

立方体贴图(Cube Map):将多个纹理组合起来映射到一张纹理上的一种纹理(简单来说,就是一个包含了 62D 纹理的纹理,每个 2D 纹理都组成了立方体的一个面:一个有纹理的立方体。)。

图片来源于:learnopengl.com

创建立方体贴图

1
2
3
GLuint textureID;
glGenTextures(1, &textureID); // 创建纹理
glBindTexture(GL_TEXTURE_CUBE_MAP, textureID); // 绑定纹理(绑定到 GL_TEXTURE_CUBE_MAP)

生成立方体贴图:

1
2
3
4
5
6
7
GLint width, height, nrChannels;
GLchar* data;
for(GLuint i = 0; i < 6; i++) // 立方体有 6 个面
{
data = stbi_load(texturePath[i], &width, &height, &nrChannels, 0); // 加载纹理图像
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data); // 生成纹理
}

由于立方体有 6 个面,OpenGL提供了 6 个特殊的纹理目标,专门对应立方体贴图的一个面。

纹理目标 方位
GL_TEXTURE_CUBE_MAP_POSITIVE_X
GL_TEXTURE_CUBE_MAP_NEGATIVE_X
GL_TEXTURE_CUBE_MAP_POSITIVE_Y
GL_TEXTURE_CUBE_MAP_NEGATIVE_Y
GL_TEXTURE_CUBE_MAP_POSITIVE_Z
GL_TEXTURE_CUBE_MAP_NEGATIVE_Z

设置环绕和过滤方式:

阅读全文 »

前言

OpenGL 的渲染管线中,用于存储颜色值和测试结果的二维数组的几何被称为 帧缓冲区(frame buffer)。这些二维数组按用途划分,可分为 颜色缓冲区(color buffer)深度缓冲区(depth buffer)模版缓冲区(stencil buffer)

当创建了一个可供 OpenGL 绘制用的窗体后,窗体系统会为自动生成一个默认的帧缓冲区,这个帧缓冲区完全是由窗体系统来管理的,且仅用于 将渲染后的图像输出到窗口的显示区域

窗体系统创建的帧缓冲区也有对应的帧缓冲区对象(Framebuffer-Object:FBO)的实例。但是,这个 FBO 与额外创建的 FBO 相比有许多不同:

  • 通过额外创建的 FBO 不能用于将渲染结果直接显示到窗口输出区,只能用于离屏渲染(Off-screen Rendering);
  • 窗体系统生成的 FBO 在创建的时候就拥有颜色缓冲区,深度缓冲区,模版缓冲区,且创建后就为这些缓冲区分配了空间;而额外创建的FBO需要手动为其添加各个缓冲区,并为其申请空间。
  • 窗体系统创建的 FBO 中的各个缓冲区对象不能与手动创建的 FBO 混用。

帧缓冲

把用于写入颜色值的 颜色缓冲,用于写入深度信息的 深度缓冲 和根据一些条件丢弃特定片段的 模板缓冲 结合起来叫做 **帧缓冲(Framebuffer)**。

一个完整的帧缓冲需要满足以下的条件:

  • 附加至少一个缓冲(颜色、深度或模板缓冲)。
  • 至少有一个颜色附件(Attachment)。
  • 所有的附件都必须是完整的(保留了内存)。
  • 每个缓冲都应该有相同的样本数。

附件 是一个内存位置,它能够作为帧缓冲的一个缓冲(可以将它想象为一个图像)。当创建一个附件的时候有两个选项:纹理附件渲染缓冲对象(Renderbuffer Object)附件

通过使用**GLenum glCheckFramebufferStatus( GLenum target);** 以 GL_FRAMEBUFFER 作为参数检测当前绑定的帧缓冲,根据返回值来判断是否完整(如果是 GL_FRAMEBUFFER_COMPLETE,则完整,更多返回值参看 这里)。

阅读全文 »

环绕顺序

当定义一组三角形顶点时,通常会以特定的环绕顺序(顺时针(Clockwise)或逆时针(Counter-clockwise)的)来定义;这样每组组成三角形图元的三个顶点就包含了一个环绕顺序OpenGL 在渲染图元的时候将使用这个信息来决定一个三角形是一个正向三角形还是背向三角形。(默认情况下,逆时针 顶点所定义的三角形将会被处理为 正向 三角形。)实际的环绕顺序是在光栅化阶段进行的,也就是顶点着色器运行之后。

观察者所面向的所有三角形顶点就是所指定的正确环绕顺序了,而立方体另一面的三角形顶点则是以相反的环绕顺序所渲染的。所以,所面向的三角形将会是正向三角形,而背面的三角形则是背向三角形,如图:

图片来源于:learnopengl.com

上图中,两个三角都是以形逆时针顺序定义的;但是,在渲染阶段,从观察者的方向来看,背面的三角形将会是以顺时针顺序渲染的,这正是将要剔除(丢弃)的不可见的面!

面剔除

面剔除(Face Culling):OpenGL 能够检查所有面向(Front Facing)观察者的面,并渲染它们,而丢弃那些背向(Back Facing)的面,从而节省很多的片段着色器调用(开销很大);而可以通过分析顶点数据的 环绕顺序(Winding Order) 来区分那些是正向面,那些是背向面。

OpenGL能够丢弃那些渲染为背向三角形的三角形图元。

启用面剔除

要想启用面剔除,需要启用 OpenGLGL_CULL_FACE选项:

阅读全文 »

概述

混合(Blending):通常是 OpenGL 实现物体透明度(通过颜色的 aplha 值,即颜色向量的第四个分量来决定的。)的一种技术。

透明(Transparency):指的是一个物体(或者其中的一部分)不是纯色(Solid Color)的,其颜色是物体本身的颜色和它背后其它物体的颜色的不同强度结合。

图片来源于:learnopengl.com

丢弃片段

有些图片纹理并不需要半透明,只需要根据纹理颜色值,显示一部分(完全不透明,alpha 值为 1.0),或者不显示一部分(完全透明,alpha 值为 0.0),没有中间情况;对这样的图片纹理将要 丢弃(Discard) 纹理中透明部分的片段,不将这些片段存储到颜色缓冲中。如图:

图片来源于:learnopengl.com

使用 GLSLdiscard 命令,保证片段不会被进一步处理,即不会进入颜色缓冲。所以在片段着色器中检测一个片段的 alpha 值是否低于某个阈值,如果是的话,则丢弃这个片段。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#version 330 core

in vec2 TexCoords;

out vec4 FragColor;

uniform sampler2D texture1;

void main()
{
vec4 texColor = texture(texture1, TexCoords);
if(texColor.a < 0.1)
{
discard;
}
FragColor = texColor;
}

注意:丢弃片段的方式,不能很好处理半透明的纹理。

阅读全文 »

模板测试

当片段着色器处理完一个片段之后,模板测试(Stencil Test) 会开始执行(可能会丢弃片段);被保留的片段会进入 深度测试(可能会丢弃更多的片段)。

一个模板缓冲中,(通常)每个模板值(Stencil Value)是 8 位的;所以每个像素/片段一共能有 256 种不同的模板值。

模板缓冲使用步骤:

  • 启用模板缓冲的写入(glEnable(GL_STENCIL_TEST);)。
  • 渲染物体,更新模板缓冲的内容(glStencilFuncglStencilOp)。
  • 禁用模板缓冲的写入(glStencilMask(GL_FALSE)glStencilMask(0x00); )。
  • 渲染(其它)物体,这次根据模板缓冲的内容丢弃特定的片段。

模板函数

有两个函数能够用来配置模板测试:glStencilFuncglStencilOp

  1. **glStencilFunc(GLenum func, GLint ref, GLuint mask);**:(描述了 OpenGL 应该对模板缓冲内容做什么)

    • func:设置模板测试函数(Stencil Test Function);这个测试函数将会应用到已储存的模板值上和 glStencilFunc 函数的 ref 值上。可用的选项有:
函数 描述
GL_ALWAYS 永远通过深度测试
GL_NEVER 永远不通过深度测试
GL_LESS 在片段模板值小于( )缓冲的模板值时通过测试
GL_EQUAL 在片段模板值等于( )缓冲的模板值时通过测试
GL_LEQUAL 在片段模板值小于等于( )缓冲的模板值时通过测试
GL_GREATER 在片段模板值大于( )缓冲的模板值时通过测试
GL_GEQUAL 在片段模板值大于等于( )缓冲的模板值时通过测试
GL_NOTEQUAL 在片段模板值不等于( )缓冲的模板值时通过测试
  • ref:设置了模板测试的参考值(Reference Value);模板缓冲的内容将会与这个值进行比较。
  • mask:设置一个掩码;它将会与参考值和储存的模板值在测试比较它们之前进行与(AND)运算(初始情况下所有位都为 1)。
阅读全文 »

深度测试

深度缓冲

深度缓冲 或 z 缓冲(z-buffer):就像颜色缓冲(Color Buffer)(储存所有的片段颜色:视觉输出)一样,在每个片段中储存了信息,并且(通常)和颜色缓冲有着一样的宽度和高度。深度缓冲是由窗口系统自动创建的,它会以 162432float 的形式储存它的深度值。在大部分的系统中,深度缓冲的精度都是24位的。

当深度测试(Depth Testing)被启用的时候,OpenGL 会执行一个深度测试,如果这个测试通过了的话,就会绘制该片段并将深度缓冲将会更新为新的深度值(前提是深度缓冲允许写入)。如果深度测试失败了,片段将会被丢弃不更新缓冲

屏幕空间坐标可以直接使用 GLSL 内建变量 gl_FragCoord 从片段着色器中直接访问;其 xy 分量代表了片段的屏幕空间坐标(其中 (0, 0) 位于左下角);z 分量,代表了片段真正的深度值(就是需要与深度缓冲内容所对比的那个值)。

注意:如果启用了深度缓冲,必需在每个渲染迭代之前使用 GL_DEPTH_BUFFER_BIT 来清除深度缓冲,否则会仍在使用上一次渲染迭代中的写入的深度值:glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

深度测试函数

可以调用 glDepthFunc 函数来设置比较运算符(或者说**深度函数(Depth Function)**):

1
glDepthFunc(GLenum func);

其中参数 func 可以使用的 比较运算符

阅读全文 »