基于Qt的OpenGL编程(3.x以上GLSL可编程管线版)---(二十四)高级GLSL技巧

Vries的教程是我看过的最好的可编程管线OpenGL教程,没有之一其原地址如下,https://learnopengl-cn.github.io/04%20Advanced%20OpenGL/08%20Advanced%20GLSL/关于高级GLSL技巧的详细知识了解请看原教程,本篇旨在对Vires基于visual studio平台的编程思想与c++代码做纯Qt平台的移植,代码移植顺序基本按照原教程顺序,并附加一些学习心得,重在记录自身学习之用

Tip1Joey De Vries的教程写的真是非常好呢,他本人可能想不到,他的中文版教程在中国的CSDN上有N多的“备份”,为什么有的人通篇转载他的教程时不能加上“转载”两个字呢。

Tip2: 博客最后的“Uniform缓冲对象”特别有用,特别推荐看Vries原教程的解析,强推!!!

Tip3: 这节的Qt代码移植没有值得注意的地方,可直接将Vries的代码直接复制进来使用。

 

程序源代码链接:https://pan.baidu.com/s/1stvBQWxAI6Pu_GznF4Jc7Q 提取码:juh9 

编译环境:Qt5.9.4

编译器:Desktop Qt5.9.4 MSVC2017 64bit

IDE:QtCreator

 

一. 这篇博客要讲什么

    这节的内容在讲,关于着色器shader本身,一些有趣且有用的内建变量,比如gl_Position就是着色器的一个内建变量,负责在顶点着色器中,向其他着色器传递空间中点的坐标。因为这些内建变量都是着色器本身就具有的,所以在移植到Qt时,不需要结合Qt本身平台做一些函数的特殊化处理,直接粗暴复制代码即可。

 

. 顶点着色器变量

  2.1 gl_PointSize

  当以GL_POINTS格式进行绘制点时,默认的点非常小,如左下图所示。

  core->glDrawArrays(GL_POINTS, 0, 36);

而顶点着色器中的内建变量gl_PointSize就可以改变绘制的点的大小。

基于Qt的OpenGL编程(3.x以上GLSL可编程管线版)---(二十四)高级GLSL技巧_第1张图片基于Qt的OpenGL编程(3.x以上GLSL可编程管线版)---(二十四)高级GLSL技巧_第2张图片

让我们简单粗暴一些,通过下属代码开启修改点大小的功能:

  core->glEnable(GL_PROGRAM_POINT_SIZE);

pointsize.vert


#version 330 core
layout (location = 0) in vec3 aPos;

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

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

. 片段着色器变量

  3.1 gl_FragCoord

    片段着色器中的这个变量,gl_FragCoord.z代表片段的深度,gl_FragCoord.x与gl_FragCoord.y代表窗口的坐标。怎么理解它的xy呢?举Vries的例子:

  fragcoord.frag:

#version 330 core
out vec4 FragColor;
in vec2 TexCoords;

uniform sampler2D ambientMap;
uniform float halfWindowWidth; //窗口宽度的一半 ,比如生成一个600*600的窗口,那我就传进来一个300

void main(){
  if(gl_FragCoord.x < halfWindowWidth)
    FragColor = texture2D(ambientMap, TexCoords);
  else
    FragColor = vec4(1.0f, 0.0f, 0.0f, 1.0f);
}

  渲染结果就是,我在窗口的左半边用纹理渲染,右半边用纯红色渲染。这个方法在比较两种不同的光照效果时,真的很有用也很方便,真的。

基于Qt的OpenGL编程(3.x以上GLSL可编程管线版)---(二十四)高级GLSL技巧_第3张图片

3.2 gl_FrontFacing

  这个变量与面剔除那节的知识有些联系,识别片段所在的面是正向面还是反向面这里要注意的是立方体的顶点绘制顺序,必须按制定顺序绘制三角形,才能正确被GL识别。

还是举个简单例子:

  frontfacing.frag


#version 330 core
out vec4 FragColor;
in vec2 TexCoords;

uniform sampler2D ambientMap1;
uniform sampler2D ambientMap2;


void main(){
  if(gl_FrontFacing)
    FragColor = texture2D(ambientMap1, TexCoords);
  else
    FragColor = texture2D(ambientMap2, TexCoords);
}

  正向面用一个纹理,反向面用另一个纹理,当然,不要开启面剔除,反向面都让你剔了,还怎么映射纹理。

基于Qt的OpenGL编程(3.x以上GLSL可编程管线版)---(二十四)高级GLSL技巧_第4张图片

3.3 gl_FragDepth

   知道这个变量可以在片段着色器中,指定片段的深度就可以了。不同于gl_FragCoord.z,因为他只能读深度,而gl_FragDepth可以修改深度。

. 接口块

   接口块的概念和c++的struct有些像,在用时,注意几个细节:

  • out与in作为 接口块 的关键字识别块的输入输出。
  • 输入输出的块名,比如下面的“VS_OUT”应该一致,但实例名,如“vs_out”与“fs_in”可以随意,表示的都是同一个块

 xxx.vert

#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;
}  

xxx.frag

#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缓冲对象

    你是否因为每生成一个着色器,都要重复设定他们的projection,view矩阵而烦恼?如下图所示,现在学习了Uniform Buffer Object,一切问题迎刃而解。只需要设置一次projection矩阵的Uniform变量,以后只要稍微修改一下着色器中的绑定点,就不用再管这些矩阵啦。

                   ..................
  ResourceManager::getShader("cube").use().setMatrix4f("projection", projection);
  ResourceManager::getShader("cube").use().setMatrix4f("view", view);

  ResourceManager::getShader("cube_nonLinear").use().setMatrix4f("projection", projection);
  ResourceManager::getShader("cube_nonLinear").use().setMatrix4f("view", view);

  ResourceManager::getShader("cube_linear").use().setMatrix4f("projection", projection);
  ResourceManager::getShader("cube_linear").use().setMatrix4f("view", view);

  ResourceManager::getShader("plane").use().setMatrix4f("projection", projection);
  ResourceManager::getShader("plane").use().setMatrix4f("view", view);

  ResourceManager::getShader("plane_nonLinear").use().setMatrix4f("projection", projection);
  ResourceManager::getShader("plane_nonLinear").use().setMatrix4f("view", view);

  ResourceManager::getShader("plane_linear").use().setMatrix4f("projection", projection);
  ResourceManager::getShader("plane_linear").use().setMatrix4f("view", view);

  ResourceManager::getShader("coordinate").use().setMatrix4f("projection", projection);
  ResourceManager::getShader("coordinate").use().setMatrix4f("view", view);
                     .....................

这里简单说说怎么用,具体知识请转进Vries的教程,链接在本文第一行,真不想打这么多字了。

     基于Qt的OpenGL编程(3.x以上GLSL可编程管线版)---(二十四)高级GLSL技巧_第5张图片

  1. 将projection与view矩阵的数据保存至uniform buffer objects中。
  2. 将这个ubo绑定至BindingPoints中的一个点,比如0点
  3. 再顶点着色器中,指定projection与view矩阵的数据由BindPoints的0点处获得

基于Qt的OpenGL编程(3.x以上GLSL可编程管线版)---(二十四)高级GLSL技巧_第6张图片

 

使用std140布局,布局规则如下,GLSL中的变量int,float,bool都被定义为4字节,而每4个字节将会用一个N来表示。

基于Qt的OpenGL编程(3.x以上GLSL可编程管线版)---(二十四)高级GLSL技巧_第7张图片

Vries举了一个例子

基于Qt的OpenGL编程(3.x以上GLSL可编程管线版)---(二十四)高级GLSL技巧_第8张图片

当然,我们实际应用的情况没有这么复杂,针对projection与view矩阵,我们将他绑定至一个BindingPoint “0”上。

  GLSL_BLUE.vert

#version 420 core
layout (location = 0) in vec3 aPos;

layout (std140, binding = 0) uniform Matrices{
  mat4 projection;
  mat4 view;
};

uniform mat4 model;

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

这里亲测在 420 core的着色器中,直接指定BindingPoint即可。不要那么麻烦的用

unsigned int lights_index = glGetUniformBlockIndex(shaderA.ID, "Lights");   
glUniformBlockBinding(shaderA.ID, lights_index, 2);

这俩函数了。

着色器搞定了,说说管理代码怎么写:

 
GLuint uboMatrices;

void initializeGL(){  //在QOpenGLWidget的初始化函数中         
.............
  core->glGenBuffers(1, &uboMatrices); //提前开辟ubo的内存空间
  core->glBindBuffer(GL_UNIFORM_BUFFER, uboMatrices);
/*
 * 因为我们要绑定的是两个简单的4x4矩阵,所以声明8个4维向量的大小即可。
 * 为什么不用sizeof(QMatrix4x4)呢,因为
 * sizeof(QMatrix4x4) = 68; 多了4个字节应该是文件头之类的玩意吧,惊喜吧
 * sizeof(QVector4D) = 16;
 */
  core->glBufferData(GL_UNIFORM_BUFFER, 2 * 4 * sizeof(QVector4D), NULL, GL_STATIC_DRAW); 
  core->glBindBuffer(GL_UNIFORM_BUFFER, 0);

  core->glBindBufferRange(GL_UNIFORM_BUFFER, 0, uboMatrices, 0, 2 * 4 * sizeof(QVector4D));//将ubo绑定至BindingPoint上
}

void updateGL(){ //这个一个不断循环的更新函数

  /***********  处理Uniform Buffer相关参数 **************/
  core->glBindBuffer(GL_UNIFORM_BUFFER, uboMatrices); //向ubo缓冲中传递数据
  core->glBufferSubData(GL_UNIFORM_BUFFER, 0, 4 * sizeof(QVector4D), &projection);
  core->glBufferSubData(GL_UNIFORM_BUFFER, 4 * sizeof(QVector4D), 4 * sizeof(QVector4D), &view);
  core->glBindBuffer(GL_UNIFORM_BUFFER, 0);
}

然后,用四个不同颜色的着色器画四个cube立方体,Vries的例子就完成啦。

基于Qt的OpenGL编程(3.x以上GLSL可编程管线版)---(二十四)高级GLSL技巧_第9张图片

 

越写越放飞自我。。。。

基于Qt的OpenGL编程(3.x以上GLSL可编程管线版)---(二十四)高级GLSL技巧_第10张图片

 

 

 

你可能感兴趣的:(现代OpenGL学习教程)