MCJE着色器教程——从开发入门到游戏崩溃(四)——后处理着色器(Post Json)

|

后处理json被放置在post文件夹中,它的名字决定了Minecraft会在什么时候调用它。当你看到这个文件夹在minecraft命名空间下时,你就应该想到,没错,这是原版的东西。我们目前只能通过替换原版的后处理json来达到自定义着色器的效果。目前可用的后处理json名字如下:

  • creeper.json:旁观模式下的苦力怕视角
  • invert.json:旁观模式下的末影人视角
  • spider.json:旁观模式下的蜘蛛视角
  • entity_outline.json:当屏幕上有发光实体时使用
  • transparency.json“极佳!”画质时被使用

后两者因为容易操控等特效是最常替换的JSON文件。在原版的资源包中,你可以在post文件夹下找到许多其他的json文件,这些文件在1.9之前被用于“超级秘密选项”。随着这一功能的移除,这些json文件不再被调用,但是仍然被保留了下来。

后处理json的文件格式如下:

着色器 – Minecraft Wiki_BWIKI_哔哩哔哩 (biligame.com)

targets

在第一节中,我们介绍了缓冲。targets列表中就定义了一系列的缓冲(渲染目标)。这些缓冲将用于此json后面连接着色器的时候使用。注意,正如图中看到的,transparency.json中必须包含名为translucentitemEntityparticlesweatherclouds的缓冲,而entity_outline.json中必须包含名为final的渲染目标。如果没有包含需要的缓冲,会导致严重的错误,轻则资源包卸载,重则游戏崩溃。

列表中的每一个元素都定义了一个缓冲,这些缓冲可以不全被使用。元素可以是一个字符串,也可以是一个单独的object。前者默认大小为屏幕分辨率,后者则可以手动指定缓冲的高度和宽度。

缓冲手动指定宽度和高度的时候,便是以左下角为原点,向上为y正轴,向右为x正轴,长宽为指定大小的画面。例如指定了一个200*200的缓冲,让它输出到minecraft:main显示,效果如下:

图中按下F1隐藏了HUD

passes

passes列表定义了从输入到输出的完整渲染的过程。每一个元素就指定了使用某一个着色器程序,这些着色器程序合起来,就构成了完整的渲染过程。

name是一个着色器程序JSON的名字(上面才讲过的东西,可别晕了)。

intargetouttarget分别是一个输入缓冲和一个输出缓冲。输入缓冲在经过着色器程序处理以后,将会赋给输出缓冲。两者不可相同。若指定为"minecraft:main",则表示游戏屏幕(最初的画面)。特别的,若前面的name指定为"blit"(这也是一个着色器程序,并不是什么有特殊含义的字符串),就相当于将intarget中的数据直接复制到了outtarget中。

auxtargets就是我们前面提到的采样器。name对应于着色器程序json中的samplers中的name。id图中说的还算清楚。值得注意的是,DiffuseSampler并不需要在这里声明就能调用。DiffuseSampler可以理解为游戏画面,将会被游戏自动赋给处于intarget中的着色器。

双线性插值(bilinear)

双线性插值,又称为双线性内插。在数学上,双线性插值是有两个变量的插值函数的线性插值扩展,其核心思想是在两个方向分别进行一次线性插值。双线性插值作为数值分析中的一种插值算法,广泛应用在信号处理,数字图像和视频处理等方面。
看下面的图就懂了。图片来源:(原版着色器指导 | spgoding.com
image.png

关于深度缓冲(深度缓冲_百度百科 (baidu.com)

当三维图形卡渲染物体的时候,每一个所生成的像素的深度(即z坐标)就保存在一个缓冲区中。这个缓冲区叫作z缓冲区或者深度缓冲区,这个缓冲区通常组织成一个保存每个屏幕像素深度的x-y二维数组。如果场景中的另外一个物体也在同一个像素生成渲染结果,那么图形处理卡(GPU)就会比较二者的深度,并且保留距离观察者较近的物体。然后这个所保留的物体点深度保存到深度缓冲区中。最后,图形卡就可以根据深度缓冲区正确地生成通常的深度感知效果:较近的物体遮挡较远的物体。这个过程叫作z消隐

uniforms很有意思。在着色器程序JSON中我们事实上已经定义了uniform变量并对其进行了赋值。在这里是对已经声明并赋值的uniform变量进行再赋值,就好像调用函数传入参数一样(只是这个函数的每一个形参都有默认值)。

下面是一个完整的后处理JSON的例子(使用了entity_outline.json):

{
    "targets": [
        "final"
    ],
    "passes": [
        {
            "name": "prog1",
            "intarget": "minecraft:main",
            "outtarget": "final",
            "auxtargets":[
                {
                    "id":"test",
                    "name":"test",
                    "width":100,
                    "height":100,
                    "bilinear":false
                }
            ]
        },
        {
            "name": "blit",
            "intarget": "final",
            "outtarget": "minecraft:main"
        }
    ]
}

例子中声明了final这一个缓冲。当然,这个缓冲也是必须声明的缓冲。然后使用了两个着色器程序,一个是prog1,一个是blit。因为blit是起到单纯的数据传输作用,因此实际起作用的是prog1(这个着色器程序JSON就是我们上文讲解着色器程序JSON中使用的例子)。在prog1中我们向其中传递了一个采样器,这个采样器对应的是我们资源包中<命名空间>/textures/effect文件夹下的文件(不包含拓展名)。因为我直接将图片放到了minecraft命名空间下,因此就不额外写命名空间了。

着色器渲染的顺序

在Minecraft中着色器是按照一定的渲染顺序调用:

  1. 渲染普通方块和实体到minecraft:main缓冲中
  2. 渲染纯色的发光实体到final(发光实体实际上是先按照实体的边缘渲染了一层纯色的图层,之后再将实体叠加到这个图层上)
  3. 调用entity_outline.json,对minecraft:mainfinal进行处理
  4. 渲染其他的东西(手、水、方块实体)到minecraft:main
  5. final覆盖到minecraft:main
  6. 运行实体视角着色器(例如creeper.json),处理minecraft:main

通过发光着色器向 minecraft:main 写入数据(第 3 步)似乎会破坏掉有关深度的信息,使得其他的东西(第 4 步)被渲染到 minecraft:main 的所有东西之上。

https://spgoding.com/translation/2021/03/12/guite-to-vanilla-shader.html

由此可以看出,final中其实储存了特定的数据。同时,为了避免minecraft:main深度被破坏的问题,我们大部分情况会采用更改transparency.json来应用着色器。如果你在使用着色器之后出现了一些奇怪的现象,可以参考原版资源包中的json文件对比看看。

终于,资源包调用着色器的两个重要JSON文件讲完了(摊)。其中大量涉及硬核代码知识的部分我都经可能进行了通俗化处理,当然也可能因此会导致一些偏差产生,还望各位读者指出斧正。下一节中,我们就会讲解一个详细的例子了,即使你现在尚有些糊涂,我相信你在看了例子以后必然会有豁然开朗之感。加油,最难的一部分已经过去了!

彩蛋:test.png

狐萝卜

类似文章

发表回复