Minecraft GLSL Shader着色器基础教程#2 核心着色器

回顾
在上节教程中,我们介绍了后处理着色器中的渲染管线以及着色器程序(json)的格式,有5个可选的渲染管线,分别在旁观末影人(invert)、苦力怕(creeper)、蜘蛛(spider),屏幕内有发光实体(entity_outline),1.16+启用极佳画质(transparency)时启用。后处理着色器对整个屏幕生效,所有像素将被同时读取。

核心着色器
核心着色器(Core Shader)是Java版1.17加入的全新功能,允许玩家更加全面地更改游戏渲染内容。核心着色器负责渲染游戏的各个部分(如方块、实体、界面元素等等)。和后处理着色器类似,每个核心着色器都是由JSON文件定义的单个渲染程序,其中指定了顶点和片段着色器文件,不同的游戏内容使用不同的核心着色器,学习核心着色器最大的难点在于如何检测特定的游戏内容并更改。
下面展示几个核心着色器能做到的效果:



原版所有的核心着色器存储在shaders/core目录中。
后续我会在动态和该专栏评论区发布每个方块对应的核心着色器,关于每个核心着色器渲染的内容可以在Minecraft Fandom Wiki上查看"着色器"页面
核心着色器渲染的对象是游戏内容的每个像素,能够获取比后处理着色器更多的信息,如:顶点的位置、颜色信息,纹理采样器,包括运算需要的投影矩阵和旋转矩阵。(后处理着色器只能获得屏幕上某个坐标的颜色),关于这些内容将在下文的fsh、vsh编写中介绍。
核心着色器JSON
核心着色器也由一个着色器程序json调用vsh和fsh,json格式和后处理一致,但在attributes列表中多了一些列可以使用的属性UV、UV0、UV1、UV2、Normal、Color等。(其中UV0-2以及Normal作用不明,可能会在未来的某些时间更新教程)

GLSL语言
片段着色器(fsh)、和顶点着色器(vsh)的内容都采用GLSL编写,支持变量、函数、循环等基本的编程要素,但由于无法自由访问内存,GLSL并不支持递归操作。
有关核心语言的更多信息:khronos.org/opengl/wiki/Core_Language_(GLSL)
有关所有可用函数的详细文档:docs.gl/sl4/all
GLSL的语法类似于C语言,有编程基础的读者可以选择性阅读本段内容
工作流程
vsh和fsh中都应有且只有一个main函数,没有参数和返回值(void),主函数在vsh和fsh中的任务分别是更新gl_Position或gl_FragColor这两个变量,这两个变量是GLSL中特殊的变量,使用时不需要声明,且值的类型都为vec4。gl_FragColor也可以被一个声明为out vec4 fragColor的变量代替(原版所有的fsh都使用了后面一种写法,但仍然可以使用gl_FragColor的写法,而不会出现错误)
数据类型
GLSL有三种数据类型、分别是标量、向量、矩阵,用于一系列计算
GLSL允许在构造矩阵或向量时随意组合其他其他的标量/向量,但要注意数据的个数以及类型,如:
除了[索引]外GLSL允许使用rgba或xyzw来访问向量的(0-3号)成员,这些字符也可以随意组合
注意:形如rgba的数据是一个vec4,类似foo.rgba = 1.0的代码是错误的,应该使用vec4(1.0)或vec4(1.0, 1.0, 1.0, 1.0)进行赋值。尽管rgba和xyzw之间可以混用,但一般还是用rgba表示颜色,xyzw表示位置,防止混淆
变量修饰符
GLSL中可以将变量以uniform(全局量)、in(传入变量)、out(传出变量)
PS:在21w10a(1.17)后,原版着色器的GLSL版本由120升级到了150,原本varying(传递量)和attribute(属性)现在变为了in和out,具体可在原版.jar文件中查看示例,这里采用新版本的写法和译法
uniform全局量和后处理一样,可以通过着色器程序对应的json来声明,同样,在专栏末尾再次给出特殊作用的全局量列表
in传入变量和out传出变量配合使用,可以将变量在不同着色器流程中传递,vsh中使用in传入的变量似乎是写死的,但对于fsh则可以将变量声明为in来表示vsh中声明为out的变量,下面是原版核心着色器中的代码
Position、UV、Color为传入变量,对于核心着色器来说、Position是顶点的初始位置(值是该顶点相对于区块原点的三维坐标,在0.0和16.0之间);UV是一个二维坐标,每项的值在0.0和1.0之间、用于在一张名为atlas的纹理合集上获取需要的纹理,可以通过UV检查着色器正在渲染的方块,但atlas会随着资源包中纹理数量的改变而改变,这种方法可能会因为纹理数量的改变而受影响;Color则是该顶点的纹理颜色
后处理着色器似乎只有Position这个传入变量,表示顶点在屏幕上的二维坐标。
因为后处理着色器的对象是一个平面,vsh和fsh的坐标值都相同且都为vec2类型的屏幕坐标,整个屏幕左下角的坐标是(0.0, 0.0),右上角为(1.0, 1.0)坐标的值为负或大于1.0时并不会报错,但并没有实际意义。此外,我们还可以通过全局量InSize或OutSize两个全局量来检测玩家窗口的大小以及点的实际像素坐标。
核心着色器则有两个不同的坐标,在vsh中则有顶点的空间坐标,fsh中则有该点的纹理坐标,同样纹理左下角为(0.0, 0.0),右上角为(1.0, 1.0),坐标并不以纹理中的像素为单位。
texCoord、vertexColor是由vsh中同名变量传递而来,在vsh声明为out,在fsh声明为in,这样就可以将变量在不同着色器流程中传递。这两个变量的计算过程在vsh中,前者在核心着色器中被赋值为变量UV,即atlas纹理集中的UV坐标(同样名称的变量在后处理着色器中则为被采样点的屏幕坐标)后者只在核心着色器中使用,被赋值为Color,即该点的纹理颜色,读者可以自己在原版文件中阅读计算过程。
采样器
GLSL中还有一种特殊的变量,称为采样器Sampler,原版中只使用了平面采样器(sampler2D),这里就不再说明其他的采样器,感兴趣的读者可以在glsl的官方文档上学习。
sampler2D类型的变量可以通过一些列以textrue开头的函数获取数据,在原版中则是使用了texture()和texture2D()来获取纹理颜色。以texture2D为例,其语法为:
(vec4) texture2D(sampler2D sampler, vec2 texCoord);
一些特殊的着色器可能需要不止一张纹理数据,所以会出现多个采样器,可以通过原版的代码来分辨采样器的作用。原版一般将以DiffuseSampler、Sampler0为变量名的sampler2D作为纹理的采样器,以DepthSampler为变量名作为深度采样器
着色器中,和颜色相关的变量一般是vec4类型,分别对应Red(红色)、Green(绿色)、Blue(蓝色)、Alpha(不透明度)四个通道(有些可能为vec3,则对应RGB三个通道)。和纹理坐标一样,每个颜色通道的值一般也都在0.0到1.0之间,和常用的0-255不同,检测颜色时需要注意这一点。
原版着色器还有一种深度信息,在之后正式制作着色器的教程中会介绍。
全局量列表
下节预告:后处理着色器应用篇

本篇教程描述比较简短,但内容非常重要,编写着色器时可能会因为一些细节问题导致错误,所以建议多次消化本篇教程。
下面是一个简单的核心着色器,读者可以通过拆包了解

下载地址:https://github.com/ShockMicro/CorePerspectiveModels
添加了一种在玩家手上和物品栏里显示不同模型的方法。注意要把物品栏模型的纹理alpha通道设置为253,把手上物品模型的纹理alpha通道设置为254,将手上和物品栏中的模型放在一个模型文件中,并加上正确的纹理,把文件都放在正确的位置,即可使用