OpenGL-GLSL语言入门教程(1)

现代OpenGL新手入门 专栏收录该内容
19 篇文章 3 订阅

目录

GLSL简介

GLSL的内建变量

顶点着色器变量

gl_PointSize

gl_VertexID

片段着色器变量

gl_FragCoord

gl_FragDepth


参考网站:LearnOpenGL

参考书籍:OpenGL编程指南第九版

GLSL简介

OpenGL着色语言(OpenGL Shading Language)是用来在OpenGL中着色编程的语言,也即开发人员写的短小的自定义程序,他们是在图形卡的GPU (Graphic Processor Unit图形处理单元)上执行的,代替了固定的渲染管线的一部分,使渲染管线中不同层次具有可编程性。比如:视图转换、投影转换等。GLSL(GL Shading Language)的着色器代码分成2个部分:Vertex Shader(顶点着色器)和Fragment(片段着色器),有时还会有Geometry Shader(几何着色器)。负责运行顶点着色的是顶点着色器。它可以得到当前OpenGL 中的状态,GLSL内置变量进行传递。GLSL其使用C语言作为基础高阶着色语言,避免了使用汇编语言或硬件规格语言的复杂性。

GLSL的内建变量

我们已经学会使用顶点属性、uniform和采样器来完成这一任务了。然而,除此之外,GLSL还定义了另外几个以gl_为前缀的变量,它们能提供给我们更多的方式来读取/写入数据。我们已经在前面教程中接触过其中的两个了:顶点着色器的输出向量gl_Position,和片段着色器的gl_FragCoord。

我们将会讨论几个有趣的GLSL内建输入和输出变量,并会解释它们能够怎样帮助你。注意,我们将不会讨论GLSL中存在的所有内建变量,如果你想知道所有的内建变量的话,请查看OpenGL的wiki

顶点着色器变量

我们已经见过gl_Position了,它是顶点着色器的裁剪空间输出位置向量。如果你想在屏幕上显示任何东西,在顶点着色器中设置gl_Position是必须的步骤。这已经是它的全部功能了。

gl_PointSize

我们能够选用的其中一个图元是GL_POINTS,如果使用它的话,每一个顶点都是一个图元,都会被渲染为一个点。我们可以通过OpenGL的glPointSize函数来设置渲染出来的点的大小,但我们也可以在顶点着色器中修改这个值。

GLSL定义了一个叫做gl_PointSize输出变量,它是一个float变量,你可以使用它来设置点的宽高(像素)。在顶点着色器中修改点的大小的话,你就能对每个顶点设置不同的值了。

在顶点着色器中修改点大小的功能默认是禁用的,如果你需要启用它的话,你需要启用OpenGL的GL_PROGRAM_POINT_SIZE:

glEnable(GL_PROGRAM_POINT_SIZE);

一个简单的例子就是将点的大小设置为裁剪空间位置的z值,也就是顶点距观察者的距离。点的大小会随着观察者距顶点距离变远而增大。

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

结果就是,当我们远离这些点的时候,它们会变得更大:

main中使用0号(接下来不再说明,如果用了多个,从小到大

glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glBindVertexArray(0);

shader.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.0);
	gl_PointSize = gl_Position.z;
}

shader.frag

#version 330 core

out vec4 FragColor;

void main() {
    FragColor = vec4(1.0, 1.0, 0.0, 1.0);
}

运行结果

gl_PointSize越远越大

全部代码下载

我的网盘
提取码:waxk

gl_VertexID

gl_Positiongl_PointSize都是输出变量,因为它们的值是作为顶点着色器的输出被读取的。我们可以对它们进行写入,来改变结果。顶点着色器还为我们提供了一个有趣的输入变量,我们只能对它进行读取,它叫做gl_VertexID

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

虽然现在它没有什么具体的用途,但知道我们能够访问这个信息总是好的。

片段着色器变量

在片段着色器中,我们也能访问到一些有趣的变量。GLSL提供给我们两个有趣的输入变量:gl_FragCoordgl_FrontFacing

gl_FragCoord

在讨论深度测试的时候,我们已经见过gl_FragCoord很多次了,因为gl_FragCoord的z分量等于对应片段的深度值。然而,我们也能使用它的x和y分量来实现一些有趣的效果。

gl_FragCoord的x和y分量是片段的窗口空间(Window-space)坐标,其原点为窗口的左下角。我们已经使用glViewport设定了一个800x600的窗口了,所以片段窗口空间坐标的x分量将在0到800之间,y分量在0到600之间。

通过利用片段着色器,我们可以根据片段的窗口坐标,计算出不同的颜色。gl_FragCoord的一个常见用处是用于对比不同片段计算的视觉输出效果,这在技术演示中可以经常看到。比如说,我们能够将屏幕分成两部分,在窗口的左侧渲染一种输出,在窗口的右侧渲染另一种输出。下面这个例子片段着色器会根据窗口坐标输出不同的颜色:

我将第一个三角形的片段着色器进行了修改:

//片段着色器
const char *fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"void main()\n"
"{\n"
"if(gl_FragCoord.x < 400)\n"
"FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n"
"else\n"
"   FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
"}\n\0";
gl_FragCoord的使用

全部代码下载

我的网盘
提取码:waxk

gl_FragDepth

输入变量gl_FragCoord能让我们读取当前片段的窗口空间坐标,并获取它的深度值,但是它是一个只读(Read-only)变量。我们不能修改片段的窗口空间坐标,但实际上修改片段的深度值还是可能的。GLSL提供给我们一个叫做gl_FragDepth的输出变量,我们可以使用它来在着色器内设置片段的深度值。

要想设置深度值,我们直接写入一个0.0到1.0之间的float值到输出变量就可以了:

gl_FragDepth = 1.0; // 这个片段现在的深度值为 1.0

如果着色器没有写入值到gl_FragDepth,它会自动取用gl_FragCoord.z的值。

然而,由我们自己设置深度值有一个很大的缺点,只要我们在片段着色器中对gl_FragDepth进行写入,OpenGL就会(像深度测试小节中讨论的那样)禁用所有的提前深度测试(Early Depth Testing)。它被禁用的原因是,OpenGL无法在片段着色器运行之前得知片段将拥有的深度值,因为片段着色器可能会完全修改这个深度值。

在写入gl_FragDepth时,你就需要考虑到它所带来的性能影响。然而,从OpenGL 4.2起,我们仍可以对两者进行一定的调和,在片段着色器的顶部使用深度条件(Depth Condition)重新声明gl_FragDepth变量:

layout (depth_<condition>) out float gl_FragDepth;

 condition可以为下面的值:

条件描述
any默认值。提前深度测试是禁用的,你会损失很多性能
greater你只能让深度值比gl_FragCoord.z更大
less你只能让深度值比gl_FragCoord.z更小
unchanged如果你要写入gl_FragDepth,你将只能写入gl_FragCoord.z的值

通过将深度条件设置为greater或者less,OpenGL就能假设你只会写入比当前片段深度值更大或者更小的值了。这样子的话,当深度值比片段的深度值要小的时候,OpenGL仍是能够进行提前深度测试的。

下面这个例子中,我们对片段的深度值进行了递增,但仍然也保留了一些提前深度测试:

#version 420 core // 注意GLSL的版本!
out vec4 FragColor;
layout (depth_greater) out float gl_FragDepth;

void main()
{             
    FragColor = vec4(1.0);
    gl_FragDepth = gl_FragCoord.z + 0.1;
}  

注意这个特性只在OpenGL 4.2版本或以上才提供。笔者没有使用这么高版本的,故没有测试。

更多OpenGL知识:现代OpenGL入门教程

有问题请下方评论,转载请注明出处,并附有原文链接,谢谢!如有侵权,请及时联系。

  • 2
    点赞
  • 0
    评论
  • 5
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
<p>欢迎来到OpenGL系列教程之《OpenGL进阶》的OSX版。由于OpenGL的发展历史比较久远,因此给萌新们带来了不小的入门门槛,没有理清头绪一头扎进OpenGL里面进行学习的萌新们,可能很难理清头绪。不过不用怕,跟着我们的OpenGL系列教程一起学习,你将由浅入深的逐步掌握OpenGL的精髓,我们为萌新准备的是一套成体系的知识系统,而不是凌乱的知识碎片。你将在我们这里把OpenGL学的清清楚楚,明明白白。无论你是小白学生、还是职场员工,都可以跟着我们的教程,一步一个脚印把整个图形学领域的知识学个痛快。在正式进入学习之前,我们需要提醒同学们注意下面这些点。比起一般的教程,我们这里会让你掌握整个图形学硬件API的全貌。</p> <p>在学习OpenGL进阶课程之前,我们期望你最好是已经掌握了我们的OpenGL入门课程,因为在入门课程中我们会为你构建足够多的知识框架来帮助你更好的理解OpenGL进阶这边的知识点。</p> <p>OpenGL是一个直接与GPU接触的API,这种方便硬件操作,而且还是面向图形学编程的语言,当之无愧的要选C++。你不必担心的是,我们并不使用花里胡哨的C++语法,我们使用最基本的C With Class的编程风格,来保证程序的可移植性和可阅读性。如果你是来自其他行业的程序员,你觉得学起来难,纯粹是因为你的编程思路还是单线程的思路,算你厉害,你是个多线程思路那又如何。我们图形学编程里的程序是几百几千个线程同时运行的。怕不怕?如果你是完全没什么头绪的程序员,你的最好策略是,先把所有的视频浏览一遍,把随堂代码都拖出来编译运行一次,看看都是些什么东西。然后再第二遍精读课程。图形学编程更多的是硬件操作和数学,编程语言只是个工具和皮毛。没有高层语言那么多API让你用。</p> <p>如果存在,那么一定是某个个人提供的,OpenGL的API是由显卡驱动直接提供,也就是说,是显卡的硬件生产者提供了OpenGL的API,如果你的显卡是GTX1060,那么他的OpenGLAPI就是由nVidia提供的。如果你用的是Intel的集成显卡HD4600,那么他的OpenGLAPI就是由Intel提供的。如果是移动端比如Mali的GPU,那么它的OpenGLES的API就是由arm实现的。而硬件厂商提供的都是标准的C语言API,所以高层语言是不存在OpenGL的API的。</p>
©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值