LearnOpenGL学习(高级OpenGL -> 高级GLSL,几何着色器,实例化)

高级GLSL

内建变量

顶点着色器

gl_PointSoze : float 输出变量,用于控制渲染 GL_POINTS 型图元时,点的大小。可用于粒子系统。将其设置为 gl_Position.z 时,可以使点的距离越远,大小越大。创建出类似近视眼看远处灯光的效果

glEnable(GL_PROGRAM_POINT_SIZE); //也是需要开启

cpp 复制代码
void main()
{
    gl_Position = projection * view * model * vec4(aPos, 1.0);    
    gl_PointSize = gl_Position.z;    
}

gl_vertexID: int 型输入变量(只读),存储了正在绘制顶点的ID(当(使用glDrawElements)进行索引渲染的时候,这个变量会存储正在绘制顶点的当前索引。当(使用glDrawArrays)不使用索引进行绘制的时候,这个变量会储存从渲染调用开始的已处理顶点数量。)

片段着色器

gl_FragCoord: vec4型输出变量。存储了屏幕空间坐标(x,y ,以窗口左下角为原点)和图元深度值(z,0 - 1).常用来获取深度值。

我们已经使用glViewport设定了一个800x600的窗口了,所以片段窗口空间坐标的x分量将在0到800之间,y分量在0到600之间。

cpp 复制代码
void main()
{             
    if(gl_FragCoord.x < 400)
        FragColor = vec4(1.0, 0.0, 0.0, 1.0);
    else
        FragColor = vec4(0.0, 1.0, 0.0, 1.0);        
}

gl_FrontFacing: bool 型输入变量。标记了当前图元是否为正面

cpp 复制代码
#version 330 core
out vec4 FragColor;

in vec2 TexCoords;

uniform sampler2D frontTexture;
uniform sampler2D backTexture;

void main()
{             
    if(gl_FrontFacing)
        FragColor = texture(frontTexture, TexCoords);
    else
        FragColor = texture(backTexture, TexCoords);
}

给立方体里外使用不同的纹理:

gl_FragDepth :float 型输出变量。用于手动设置片段的深度值。在片段着色器中出现后,Early-Z 将被禁用

cpp 复制代码
gl_FragDepth = 0.0; // 这个片段现在的深度值为 0.0
//如果着色器没有写入值到gl_FragDepth,它会自动取用gl_FragCoord.z的值。

从OpenGL 4.2起,我们仍可以对两者进行一定的调和,在片段着色器的顶部使用深度条件**(Depth Condition)**重新声明gl_FragDepth变量:

layout (depth_<condition>) out float gl_FragDepth;

这样子的话,当深度值比片段的深度值要小的时候,OpenGL仍是能够进行提前深度测试的。

接口块

但当程序变得更大时,你希望发送的可能就不只是几个变量了,它还可能包括数组和结构体。

接口块的声明和struct的声明有点相像,不同的是,现在根据它是一个输入还是输出块(Block) ,使用in 或out关键字来定义的。

cpp 复制代码
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoords;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

out VS_OUT
{
    vec2 TexCoords;
} vs_out;

void main()
{
    gl_Position = projection * view * model * vec4(aPos, 1.0);    
    vs_out.TexCoords = aTexCoords;
}

这次我们声明了一个叫做vs_out的接口块,它打包了我们希望发送到下一个着色器中的所有输出变量

之后,我们还需要在下一个着色器,即片段着色器,中定义一个输入接口块。

cpp 复制代码
#version 330 core
out vec4 FragColor;

in VS_OUT
{
    vec2 TexCoords;
} fs_in;

uniform sampler2D texture;

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

只要两个接口块的名字一样,它们对应的输入和输出将会匹配起来。这是帮助你管理代码的又一个有用特性,它在几何着色器这样穿插特定着色器阶段的场景下会很有用。

Uniform 缓冲对象

OpenGL为我们提供了一个叫做Uniform缓冲对象(Uniform Buffer Object)的工具,它允许我们定义一系列在多个着色器程序中相同的全局 Uniform变量。当使用Uniform缓冲对象的时候,我们只需要设置相关的uniform一次。

因为Uniform缓冲对象仍是一个缓冲,我们可以使用glGenBuffers 来创建它,将它绑定到GL_UNIFORM_BUFFER缓冲目标,并将所有相关的uniform数据存入缓冲。在Uniform缓冲对象中储存数据是有一些规则的

cpp 复制代码
#version 330 core
layout (location = 0) in vec3 aPos;

layout (std140) uniform Matrices
{
    mat4 projection;
    mat4 view;
};

uniform mat4 model;

void main()
{
    gl_Position = projection * view * model * vec4(aPos, 1.0);
}

这里,我们声明了一个叫做Matrices的Uniform块,它储存了两个4x4矩阵。Uniform块中的变量可以直接访问,不需要加块名作为前缀。

接下来,我们在OpenGL代码中将这些矩阵值存入缓冲中,每个声明了这个Uniform块的着色器都能够访问这些矩阵。

其中,layout(std140) 指定了Uniform块布局。默认情况下,Uniform块布局是Shared型,这类布局的各变量偏移量会随设备和系统的不同而变化。但我们希望Uniform块中各变量的偏移量能被手工计算出,以便让块内各变量能与UBO中各变量相对应。std140布局便是我们需要的。

在std140布局中,每个变量都有一个基准对齐量(Base Alignment) ,它是一个变量在Uniform块中占据的空间。每个变量还有一个对齐偏移量(Aligned Offset),它是一个变量从块起始位置的偏移量,它必须是Base Alignment的倍数。简而言之,前者是size,后者是offset。

Uniform 块布局

Uniform块的内容是储存在一个缓冲对象中的,它实际上只是一块预留内存。因为这块内存并不会保存它具体保存的是什么类型的数据,我们还需要告诉OpenGL内存的哪一部分对应着着色器中的哪一个uniform变量。

假设着色器中有以下的这个Uniform块:

cpp 复制代码
layout (std140) uniform ExampleBlock
{
    float value;
    vec3  vector;
    mat4  matrix;
    float values[3];
    bool  boolean;
    int   integer;
};

4 字节是 1 N

cpp 复制代码
layout (std140) uniform ExampleBlock
{
                     // 基准对齐量       // 对齐偏移量
    float value;     // 4               // 0 
    vec3 vector;     // 16              // 16  (必须是16的倍数,所以 4->16)
    mat4 matrix;     // 16              // 32  (列 0)
                     // 16              // 48  (列 1)
                     // 16              // 64  (列 2)
                     // 16              // 80  (列 3)
    float values[3]; // 16              // 96  (values[0])
                     // 16              // 112 (values[1])
                     // 16              // 128 (values[2])
    bool boolean;    // 4               // 144
    int integer;     // 4               // 148
}; 

使用 Uniform 缓冲

我们已经讨论了如何在着色器中定义Uniform块,并设定它们的内存布局了,但我们还没有讨论该如何使用它们。

首先,我们需要调用glGenBuffers,创建一个Uniform缓冲对象。一旦我们有了一个缓冲对象,我们需要将它绑定到GL_UNIFORM_BUFFER目标,并调用glBufferData,分配足够的内存。

cpp 复制代码
unsigned int uboExampleBlock;
glGenBuffers(1, &uboExampleBlock);
glBindBuffer(GL_UNIFORM_BUFFER, uboExampleBlock);
glBufferData(GL_UNIFORM_BUFFER, 152, NULL, GL_STATIC_DRAW); // 分配152字节的内存
glBindBuffer(GL_UNIFORM_BUFFER, 0);

参考:高级GLSL - LearnOpenGL CN

LearnOpenGL学习笔记(十) - 高级GLSL、几何着色器、实例化与抗锯齿 - Yoi's Home

相关推荐
扶尔魔ocy9 天前
【qt openGL入门笔记】OSG实现飞机模型在网格图上飞行并进行态势标绘(四)
qt·opengl·osg
:mnong11 天前
Web 三维可视化技术对比:ParaView Web vs 本项目 wasm + Three.js
前端·webgl·opengl·opencl·科学可视化
扶尔魔ocy12 天前
【qt openGL入门笔记】OSG实现显示飞机模型飞行的点迹(二)
qt·opengl·osg
扶尔魔ocy12 天前
【qt openGL入门笔记】OSG实现飞机模型在网格图上飞行(三)
opengl·osg
扶尔魔ocy13 天前
【qt openGL入门笔记】OSG实现飞机模型在网格图上飞行
qt·opengl·osg
扶尔魔ocy17 天前
【qt openGL入门笔记】FPS相机
qt·opengl
扶尔魔ocy17 天前
qt openGL入门笔记】FPS相机和轨道相机
qt·opengl
扶尔魔ocy19 天前
【qt openGL入门笔记】基础光照
qt·opengl
扶尔魔ocy19 天前
【qt openGL入门笔记】坐标系统和摄像机
qt·opengl
茉莉玫瑰花茶22 天前
OpenGL [ 纹理 ]
开发语言·c++·opengl