Minecraft(我的世界)中文论坛

 找回密码
 注册(register)

!header_login!

只需一步,立刻登录

查看: 8459|回复: 25

[教程] 原版着色器指导

  [复制链接]
SPGoding 当前离线
积分
22722
帖子
主题
精华
贡献
爱心
钻石
人气
下界之星
最后登录
1970-1-1
注册时间
2018-1-18
查看详细资料
 楼主| 发表于 2019-9-28 17:22:27 | 显示全部楼层 |阅读模式

您尚未登录,立即登录享受更好的浏览体验!

您需要 登录 才可以下载或查看,没有帐号?注册(register)

x
本帖最后由 SPGoding 于 2021-3-13 06:03 编辑

由于贵论坛并不具备传播知识的基本条件,译文首发于 spgoding.com

感谢土球球制作的第三方工具 MM2BC,是它使得本文在贵论坛糟糕的编辑环境下有了发布的可能。





原版着色器指导

  • 原 文: Guide to vanilla shaders
  • 原作者: SirBenet
  • 鸣 谢: 森林蝙蝠 - 提供了各种专业名词的专业机翻
  • 截止翻译时,原文最后更新: 2021-03-10 for Minecraft 21w10a (WIP)

授权



前言

原版 Minecraft 有着许多 GLSL 着色器。这些着色器可以直接通过资源包更改,进而嵌在地图里,或者是在玩家进入服务器时自动启用。

着色器以 OpenGL Shading Language(GLSL)语言编写,这是一个类似 C 的语言,支持浮点数、向量、矩阵,以及函数、条件、循环以及其他各种你能想到的编程语言应有的特性。

后处理着色器在 1.7 首次加入,会在游戏已经渲染好画面以后再起效。它们能够接受整个屏幕的像素作为输入,然后逐像素地输出。除了一些有限的特例以外,着色器所能接受到的唯一数据就是正显示在屏幕上的内容。以下是一些原版自带的后处理着色器示例:

  
"Pencil"


  
"Bits"


  
"NTSC"


后处理着色器在以下情况下会被使用:

  • 以特定生物的视角旁观时(苦力怕、蜘蛛、末影人)
  • 屏幕中有具有发光状态效果的实体时
  • 选择了「极佳」图像品质时

核心着色器在 1.17 加入 —— 所有你能在屏幕上看到的东西都是由一个核心着色器渲染的。以下是一些通过修改核心着色器所能达到的效果:

  
(Onnowhere)


  
(Aeldrion)


  
(Onnowhere)


梗概:着色器的组成部分

后处理着色器使用储存在 assets/minecraft/shaders/post 目录下的 「后处理文件」(post) 定义由一系列「着色器程序」(program)组成的渲染管线(pipeline)。下图展示了由 creeper.json 定义的管线:



「着色器程序」(例如 color_convolve)应当被定义在另一个 JSON 文件中(储存在 shaders/program 目录下)。该文件主要包括:

  • 一个要使用的「顶点着色器」(vertex shader)的路径(一个以 GLSL 语言编写的 .vsh 文件)
  • 一个要使用的「片段着色器」(fragment shader)的路径(一个以 GLSL 语言编写的 .fsh 文件)

shaders/core 下则储存了由游戏(而非上面提到的后处理管线)直接调用的着色器程序。

「顶点着色器」会对每个顶点起效,将顶点的位置作为输入,并产生一个经过变换的位置作为输出。一个扭曲屏幕的顶点着色器示例见下:



「片段着色器」会对每个像素起效,并逐像素产生输出层。一个不改变任何内容的片段着色器将直接把输入的像素原样产生。一个交换红色和蓝**道的片段着色器示例见下:



开始

  • 建立一个资源包
  • 在里面建好以下文件夹:
    • assets/minecraft/shaders/post
    • assets/minecraft/shaders/program

如果你想要参考原版的着色器,可以直接从游戏 jar 文件里面解压:%APPDATA%/.minecraft/versions/1.16.5/1.16.5.jar/assets/minecraft/shaders/

你可能还需要给你用的编辑器装一个 GLSL 的语法高亮插件。

打开游戏日志 —— 任何着色器的错误都会显示在这里。





创建一个「后处理」JSON

后处理 JSON 文件应该放置在 assets/minecraft/shaders/post 目录当中,并且命名为:

  • creeper.json 将在以苦力怕视角旁观时启用
  • invert.json 将在以末影人视角旁观时启用
  • spider.json 将在以蜘蛛视角旁观时启用
  • entity_outline.json 将在屏幕中有具有发光状态效果的实体时启用
  • transparency.json 将在玩家启用了「极佳」图像品质时启用

我们只能通过替换以上五个现存的文件来自定义后处理着色器。

后处理文件由两个数组构成:

  1. {
  2.    "targets": [ … ],
  3.    "passes": [ … ]
  4. }
复制代码

Targets

"targets" 数组声明了一些帧缓冲(frame buffers) —— 把它们想象成我们可以往上面读取或写入像素的画布。该数组中的每一项都可以是以下两者之一:

  • 一个有着 "name"(名称,字符串)、"width"(宽度,整型数字)、"height"(高度,整型数字)三个参数的对象。这三个参数描述了帧缓冲。
  • 一个字符串名称。宽度和高度将会默认为游戏窗口的宽度和高度。

你可以在声明帧缓冲时随意混用两种方式:

  1. "targets": [
  2.    "foo",
  3.    "bar",
  4.    {"name":"baz", "width":73, "height":10},
  5.    "qux"
  6. ]
复制代码

除了这些手动声明的缓冲层,还有一些特殊的、预先定义好的缓冲。这些缓冲里面会自带内容,不需要声明就可以直接使用。它们是:

  • 发光着色器准备的、预先填充好的特殊缓冲区

        
      minecraft:main  
      没有水、方块实体和其他一些东西


        
      final  
      纯色的实体。颜色是该实体所在队伍的颜色
  • 实体视角着色器准备的、预先填充好的特殊缓冲区

        
      minecraft:main  
      所有内容都已渲染完成

Passes

"passes" 数组定义了一系列的按顺序执行的步骤。每一个步骤都包含一个输入缓冲层("in_target"),并运行一个着色器程序("name"),来将数据写入到输出缓冲层("out_target")当中。一个着色器程序不能把数据输出到它读取的那个缓冲层。

  1. "passes": [
  2.    {
  3.        "name": "prog1",
  4.        "intarget": "minecraft:main",
  5.        "outtarget": "foo"
  6.    },
  7.    {
  8.        "name": "prog2",
  9.        "intarget": "foo",
  10.        "auxtargets": [ … ],
  11.        "outtarget": "bar"
  12.    },
  13.    {
  14.        "name": "blit",
  15.        "uniforms": { … },
  16.        "intarget": "bar",
  17.        "outtarget": "minecraft:main"
  18.    }
  19. ]
复制代码

"blit" 是一个不改变任何内容的着色器程序,它只是简单地把数据从一个缓冲复制到另一个缓冲当中(注意:[在不同大小的缓冲之间复制数据时会有问题](#Blit 缩放问题))。

你想要显示的内容应当最终输出到 "minecraft:main" 缓冲当中(如果是发光着色器,还可以进一步修改 final 缓冲,该缓冲的内容会覆盖到一切内容上面

Passes.Auxtargets

可选的 "auxtargets" 数组提供了一系列补充的缓冲或图片,使得着色器程序能够读取它们。在 auxtargets 数组中的对象应当包含:

  • "id" - 指定一个现存缓冲的名称(即定义在 "targets" 中的名称),或者是一张位于资源包 minecraft/textures/effect 目录下的图片的文件名。
  • "name" - 能让你给该缓冲或图片分配任意的一个名称,使得着色器程序的 GLSL 代码中能够访问它们。
  • 如果指定的是一张图片,还必须指定以下参数:
    • "width" - 以像素为单位的图片宽度(似乎没有实际效果?
    • "height" - 以像素为单位的图片高度(似乎没有实际效果?
    • "bilinear" - 指定该图片被采样时使用的缩放算法

缩放算法:  

一个示例 auxtargets 数组如下,它使得着色器程序能够访问 qux 缓冲,以及一张叫作 abc.png 的图片:

  1. "auxtargets": [
  2.    {"id":"qux", "name":"QuxSampler"},
  3.    {"id":"abc", "name":"ImageSampler", "width":64, "height":64, "bilinear":false}
  4. ]
复制代码

Passes.Uniforms

可选的 "uniforms" 参数能够向着色器程序传递一个浮点数数组。下方的示例使用 uniforms 为 blur 着色器程序指定了一个半径(radius)和方向(direction):

  1. {
  2.    "name": "blur",
  3.    "intarget": "swap",
  4.    "outtarget": "minecraft:main",
  5.    "uniforms": [
  6.        {
  7.            "name": "BlurDir",
  8.            "values": [ 0.0, 1.0 ]
  9.        },
  10.        {
  11.            "name": "Radius",
  12.            "values": [ 10.0 ]
  13.        }
  14.    ]
  15. },
复制代码

可运作的示例

以下是一个可以正常运作的完整的后处理 JSON 文件。它添加了一个 "notch" 抖动效果,并减少了颜色饱和度。



assets/minecraft/shaders/post/spider.json

  1. {
  2.    "targets": [
  3.        "swap"
  4.    ],
  5.    "passes": [
  6.        {
  7.            "name": "notch",
  8.            "intarget": "minecraft:main",
  9.            "outtarget": "swap",
  10.            "auxtargets": [
  11.                {
  12.                    "name": "DitherSampler",
  13.                    "id": "dither",
  14.                    "width": 4,
  15.                    "height": 4,
  16.                    "bilinear": false
  17.                }
  18.            ]
  19.        },
  20.        {
  21.            "name": "color_convolve",
  22.            "intarget": "swap",
  23.            "outtarget": "minecraft:main",
  24.            "uniforms": [
  25.                { "name": "Saturation", "values": [ 0.3 ] }
  26.            ]
  27.        }
  28.    ]
  29. }
复制代码

创建一个「着色器程序」JSON

着色器程序 JSON 文件应该放置在 assets/minecraft/shaders/program 文件夹中。

可以使用任何名称(只要遵守正常资源包的文件命名规则即可,例如没有大写字母什么的)。

  1. {
  2.    "blend": { … },
  3.    "vertex": "foo",
  4.    "fragment": "foo",
  5.    "attributes": [ … ],
  6.    "samplers": [ … ],
  7.    "uniforms": [ … ]
  8. }
复制代码

"vertex" 指定了将要使用的顶点着色器 .vsh 文件的文件名。

"fragment" 指定了将要使用的片段着色器 .fsh 文件的文件名。

"attributes" 是一个字符串数组,指定顶点的哪些属性能够被顶点着色器访问到。目前只能写 "Position"(位置)。

  1. "attributes": [ "Position" ],
复制代码

"samplers" 定义了片段着色器想要访问缓冲需要用到的变量名(采样器)。"DiffuseSampler" 是被自动给予 "intarget" 中定义的缓冲的采样器。其他的任何名字都需要与你在 后处理文件的 "auxtargets" 中指定的一致。

  1. "samplers": [
  2.    { "name": "DiffuseSampler" },
  3.    { "name": "DitherSampler" }
  4. ]
复制代码

"uniforms" 定义了各种全局量(又译「统一量」。对每个顶点或像素都保持不变的值)的名称、类型和默认值。

  1. "uniforms": [
  2.    { "name": "ProjMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] },
  3.    { "name": "InSize",  "type": "float", "count": 2,  "values": [ 1.0, 1.0 ] },
  4.    { "name": "OutSize", "type": "float", "count": 2,  "values": [ 1.0, 1.0 ] },
  5.    { "name": "BlurDir", "type": "float", "count": 2,  "values": [ 1.0, 1.0 ] },
  6.    { "name": "Radius",  "type": "float", "count": 1,  "values": [ 5.0 ] }
  7. ]
复制代码

"name" 字符串定义了在 GLSL 代码中或者在向该着色器程序的该全局量传值时用的名称。游戏自动给了一些特殊的全局量:

  • "Time" - 0 到 1 的一个值,表示在一秒内的时间,每秒自动重置
  • "InSize" - 以像素为单位的输入缓冲(input buffer)的宽度和高度
  • "OutSize" - 以像素为单位的输出缓冲(output buffer)的宽度和高度
  • "ProjMat" - 由顶点着色器使用的投影矩阵(projection matrix)

"values" 应为一个浮点数数组,而 "type" 则定义了这些浮点数会在 GLSL 代码中被解析为的数据类型

  • "float" - 一个 floatvec2/vec3/vec4,具体取决于在 "values" 中指定的数字个数
  • "matrix4x4" - 一个由 "values" 中指定的 16 个值产生的 mat4
  • "matrix3x3" - 可用的类型,但仍然需要输入 16 个值?
  • "matrix2x2" - 可用的类型,但仍然需要输入 16 个值?

"blend" 理论上定义了着色器程序的输出应当怎样与目标缓冲(destination buffer)上已有的内容合并,但目前好像没有效果。

  1. "blend": {
  2.    "func": "add",
  3.    "srcrgb": "one",
  4.    "dstrgb": "zero"
  5. }
复制代码

更多关于这些模式本应怎样运作的信息可以查看:khronos.org/opengl/wiki/Blending

可运作的示例

以下是一个可以正常运作的完整的着色器程序 JSON 文件。这是原版的 "wobble" 着色器,未经修改。

assets/minecraft/shaders/program/wobble.json

  1. {
  2.     "blend": {
  3.         "func": "add",
  4.         "srcrgb": "one",
  5.         "dstrgb": "zero"
  6.     },
  7.     "vertex": "sobel",
  8.     "fragment": "wobble",
  9.     "attributes": [ "Position" ],
  10.     "samplers": [
  11.         { "name": "DiffuseSampler" }
  12.     ],
  13.     "uniforms": [
  14.         { "name": "ProjMat", "type": "matrix4x4", "count": 16,
  15.           "values": [ 1.0, 0.0, 0.0, 0.0,
  16.                       0.0, 1.0, 0.0, 0.0,
  17.                       0.0, 0.0, 1.0, 0.0,
  18.                       0.0, 0.0, 0.0, 1.0 ] },
  19.         { "name": "InSize", "type": "float", "count": 2,
  20.           "values": [ 1.0, 1.0 ] },
  21.         { "name": "OutSize", "type": "float", "count": 2,
  22.           "values": [ 1.0, 1.0 ] },
  23.         { "name": "Time", "type": "float", "count": 1,
  24.           "values": [ 0.0 ] },
  25.         { "name": "Frequency", "type": "float", "count": 2,
  26.           "values": [ 512.0, 288.0 ] },
  27.         { "name": "WobbleAmount", "type": "float", "count": 2,
  28.           "values": [ 0.002, 0.002 ] }
  29.     ]
  30. }
复制代码

GLSL 基础

该部分假设读者已经熟悉了基本的编程概念(变量、函数、循环等),并且主要讲解 GLSL 特有的特性。如果你之前从来没有接触过编程,我不建议你用 GLSL 上手。

有关核心语言的更多信息:khronos.org/opengl/wiki/Core_Language_(GLSL)

有关所有可用函数的详细文档:docs.gl/sl4/all

注意: 原版 jar 文件中的着色器是用的 GLSL 版本是 110,本文为保持一致也将使用该版本。不过,由于 Minecraft 需要 OpenGL 4.4,你可以放心地使用最高到 440 版本的 GLSL。版本需要在 GLSL 代码的开头声明(可以看示例)。

数据类型

标量

bool: 布尔值 true / false  
int / unit: 有符号 / 无符号 32 位整型数字  
float / double: 单精度 / 双精度浮点数


向量

bvenivenuvecnvecndvecn: 分别代表由 > nboolintuintfloatdouble 组成的向量

n 必须为 2、3 或 4。

矩阵

matn: 由 float 组成的矩阵,大小为 n × n  
matnxm: 由 float 组成的矩阵,大小为 n × m  \


nm 都必须为 2、3 或 4。

矩阵是列优先的(3×2 = 3 列,2 行)。

在 GLSL 里最常处理的就是 float 了。

想要构造向量和矩阵的话,可以任意组合其他的标量 / 向量:

  1. vec2 v2 = vec2(0.4, 0.5);
  2. vec3 v3 = vec3(v2, 0.6);
  3. mat4 m3 = mat3(0.1, 0.2, 0.3,
  4.                v3,
  5.                v2, 0.7);
复制代码

请注意,列优先的矩阵可能有点反直觉:

  1. mat2(a, b,   // 第一列(不是第一行!)
  2.      c, d);  // 第二列
复制代码

除了普通的[索引]以外,混合 xyzwrgba 是种方便的获取某个向量的 1 个或多个元素的方式:

  1. vec3 v3 = vec3(0.1, 0.2, 0.3);
  2. v3.x;     // == 0.1
  3. v3.yzyz;  // == vec4(0.2, 0.3, 0.2, 0.3)
复制代码

着色器程序工作流程

顶点着色器或片段着色器都有一个 void main() 函数,作为代码的起始点。该函数没有任何参数,也不返回任何东西,它只应更新一个现存的特殊变量(gl_Positiongl_FragColor,将在下方解释。)

由于 GLSL 代码是在无法任意写入内存的硬件上执行的,因此 GLSL 不支持递归操作。不过循环是可以的:

  1. for (int i = 0; i < 10; i++) { ... }
  2. while (x < 20) { ... }
复制代码

属性、全局量、传递变量

如果你选择了更高版本的 GLSL,该部分在 GLSL 140 及以上版本有所改动。见:https://www.khronos.org/opengl/wiki/Type_Qualifier_(GLSL)#Shader_stage_inputs_and_outputs。

全局变量可以被声明为 attribute(属性)、uniform(全局量)或 varying(传递变量)。

attributes(属性)只能由顶点着色器读取,它自动包含了当前顶点的一些信息。对于 Minecraft 的着色器来说,它只包含了顶点的 Position(位置)属性。

uniform(全局量)可以被顶点着色器与片段着色器读取,对所有的顶点或像素都会保持恒定不变。它们的值可以通过后处理 JSON 文件传入,否则将会使用在着色器程序 JSON 文件中定义的默认值。

varying(传递变量)由顶点着色器声明并赋值,然后可在片段着色器中被读取。这些值会在顶点间插值计算出来。举个例子:

  1. varying vec2 texCoord;
复制代码



编辑:从 21w10a 起,原版的着色器从 GLSL 版本 120 升级到了 150。varyingattribute 现在变成了 inout。  
对于顶点着色器,attribute 应该改为 invarying 应该改为 out。  
对于片段着色器,varying 应该改为 in,而特殊值 fragColor 应该改为 out。  
uniform 保持不变。  
可以查看原版 jar 文件中的着色器查看示例。  
我会在 1.17 更新后的某些时间更新本篇教程。

编写一个顶点着色器

顶点着色器会在每个顶点上运行,以对顶点进行变换。通常来讲这指的是世界几何体的每一个顶点 —— 但是在 Minecraft 中,它指的只是缓冲四个角上的顶点。这使得目前的顶点着色器十分受限。不对顶点缓冲器做任何修改是很常见的做法(绝大多数原版的着色器程序都重复使用了 sobel.vsh)。



编辑:从 21w10a 起,核心着色器引入了真正的顶点着色器。以下的图片现在可以实现了。本节有待更新。

顶点着色器的主要工作是用一个「投影矩阵」乘坐标。一般来讲这会把一个处于视锥内的顶点转换到一个 2×2×2 的立方体中,这样能够更方便地拍扁到屏幕上:



不过在 Minecraft 中,顶点着色器直接就从一个处于 3 维空间内的平面开始,并且直接变换四个角上的顶点,而不是变换任何实际上的几何体的顶点。Minecraft 使用 ProjMat 这样一个特殊的全局量用来计算,虽然这完全可以用 4 个硬编码(hardcoded)的特例来替代(因为这些顶点总是会被计算到相同的位置上)
  • (#Blit 缩放问题)。



    顶点的初始位置可以从 Position 属性获取,而计算结果应当储存到特殊的 gl_Position 变量当中(这两者都是 vec4)。

    顶点着色器还定义并赋值了一些有用的传递变量,以供片段着色器使用:

    • texCoord: 范围从 0,0(左下角)到 1,1(右上角)的坐标。
    • oneTexel: 在 texCoord 所表示的坐标中,一个像素所占的大小。例如:对于一个像素数为 4×2 的输入缓冲,其中每个像素的大小为 0.25×0.5(1/4 的高度,1/2 的宽度)

    可运作的示例

    sobel.vsh 顶点着色器,它被绝大多数原版的着色器程序所调用。它并没有什么特别涉及到 sobel(索伯算子?)的东西 —— 这么命名可能只是因为它是 Mojang 第一个使用的着色器程序。

    1. #version 110

    2. attribute vec4 Position;

    3. uniform mat4 ProjMat;
    4. uniform vec2 InSize;
    5. uniform vec2 OutSize;

    6. varying vec2 texCoord;
    7. varying vec2 oneTexel;

    8. void main(){
    9.     vec4 outPos = ProjMat * vec4(Position.xy, 0.0, 1.0);
    10.     gl_Position = vec4(outPos.xy, 0.2, 1.0);

    11.     oneTexel = 1.0 / InSize;

    12.     texCoord = Position.xy / OutSize;
    13. }
    复制代码

    编写一个片段着色器

    片段着色器会为每一个输出像素执行一次,可以读取它的输入缓冲中的任何像素。

    每一个像素都是异步计算的,因此片段着色器不能读取输出缓冲中的其他像素,因为那些像素有可能还没有被计算 *

    Sampler

    texture2D 函数允许你用一个采样器来获取一个缓冲中的像素。例如:

    1. vec4 centerPixel = texture2D(DiffuseSampler, vec2(0.5, 0.5));
    复制代码

    在从缓冲中获取像素时,0.0, 0.0 是左下角,1.0 1.0 是右上角。



    这和顶点着色器设置的 texCoord 传递变量一致,十分方便。因此对于当前正在输出的像素,你可以用以下代码来获取相应的位于输入缓冲中的像素:

    1. texture2D(DiffuseSampler, texCoord)
    复制代码

    该函数返回一个包含 red(红)、green(绿)、blue(蓝)、opacity(不透明度)的 vec4 —— 这四者都由一个从 0 到 1 的 float 表示。

    oneTexel 代表一个像素(在输入缓冲中)的大小,所以可以用它来偏移指定数量的像素。例如:获取往上数第 3 个像素的颜色:

    1. texture2D(DiffuseSampler, texCoord + vec2(0.0, 3 * oneTexel.y));
    复制代码

    如果想要得到以像素为单位的坐标,而不是 0-1 的小数的话,用 OutSize 乘即可。例如:

    1. OutSize; // == vec2(1920, 1080)
    2. texCoord; // == vec2(0.5, 0.5)
    3. OutSize * texCoord; // == vec2(960, 540)
    复制代码

    片段着色器应当把输出像素写入到 gl_FragColor 中 —— 另一个由 red(红)、green(绿)、blue(蓝)、opacity(不透明度)构成的 vec4

    可运作的示例

    以下的着色器:

    • 当距离中心的距离在 0.38 至 0.4 之间时,把这个像素变成橙色
    • 当距离中心的距离小于 0.38 时,读取放大过的(距离中心更近的)像素
    • 当距离中心的距离大于 0.38 时,正常读取对应的输入像素

    1. #version 110

    2. uniform sampler2D DiffuseSampler;

    3. varying vec2 texCoord;
    4. varying vec2 oneTexel;

    5. void main(){
    6.     float distFromCenter = distance(texCoord, vec2(0.5, 0.5));

    7.     if (distFromCenter < 0.38) {
    8.         // 在圆圈里面
    9.         vec2 zoomedCoord = ((texCoord - vec2(0.5, 0.5)) * 0.2) + vec2(0.5, 0.5);
    10.         gl_FragColor = texture2D(DiffuseSampler, zoomedCoord);
    11.     } else if (distFromCenter >= 0.38 && distFromCenter < 0.4) {
    12.         // 橙色边框
    13.         gl_FragColor = vec4(0.7, 0.4, 0.1, 1.0);
    14.     } else {
    15.         // 在圆圈外面,正常的像素
    16.         gl_FragColor = texture2D(DiffuseSampler, texCoord);
    17.     }
    18. }
    复制代码



    技巧与难题

    发光颜色

    发光的实体是一种不易被察觉的向 entity_outline 着色器传递数据的方式,这是因为该着色器可以读取 final 缓冲,并且使其不渲染可见的发光边框效果。

    一个具有发光状态效果的实体的轮廓颜色由它所处队伍的颜色决定。实体本身具有的透明度会保留。这使得片段着色器可以读取并使用 16 种不同的颜色以及 256 种不同的透明度值。



    (图中添加了棋盘背景以显示出透明度)

    阴影

    由于片段着色器可以读取像素,因此可以通过像素的颜色信息来传递信息。不过请小心,有许多因素会使你的材质变暗:

    光照等级

    远离光源并且不在太阳照射下的方块或实体会变暗。

    可以用夜视效果或光源避免。



    面阴影

    顶面是最亮的,其次是北面和南面,再其次是东面和西面,最暗的是底面。方块和实体都受到这一影响。

    若想为方块禁用该阴影效果,可以在模型中将每个元素(element)设置为 "shade": false

    不能为实体禁用!



    环境遮挡(「平滑光照」)

    角落会变暗。只有方块受到这一影响。

    若想为方块禁用该阴影效果,可以在模型中设置 "ambientocclusion":false


    编译器优化问题

    GLSL 编译器会优化那些它认为不会对着色器的输出有影响的代码与变量(输出指的是 gl_FragColorgl_Position 以及其他任何传递变量)。

    GLSL 编译器找到一个没有被使用的采样器后会把它优化出去,但 Minecraft 仍然会传输同样的缓冲,这就导致了问题:所有的采样器都会读取前一个缓冲的内容。



    如果想避免让编译器优化出某个变量,你可以让编译器认为该变量有可能影响着色器的输出结果。例如,texCoord.x 永远不会是 731031,但编译器并不知道这一点:

    1. if (texCoord.x == 731031) { gl_FragColor = texture2D(DiffuseSampler, texCoord); }
    复制代码

    跨帧储存信息

    缓冲并不会被自动清空,因此着色器可以在不同帧之间传递信息。

    想要获取在上一帧存储的数据很简单:

    • 在这一帧中,把数据写入到缓冲
    • 在下一帧中,在新的数据被写入缓冲之前读取它

    为了让缓冲中的数据能跨刻存在,向一个缓冲写入的着色器程序必须要能让该缓冲中的数据不变。这就有些麻烦了,因为一个着色器程序必须要写入每一个像素,并且在这个过程中不能读取它正在写入的那个缓冲。

    一个解决方案是,首先把信息复制到另一个缓冲当中。这时着色器程序就可以读取被复制出来的那个缓冲,同时把新数据写到原有的缓冲当中了,进而能够为每个像素写入它们在上一帧的值。



    1. "passes": [
    2.    {
    3.        "name": "blit",
    4.        "intarget": "foo",
    5.        "outtarget": "foo_copy"
    6.    },
    7.    {
    8.        "name": "maybe_update",
    9.        "intarget": "minecraft:main",
    10.        "auxtargets": [ { "name": "BackupSampler", "id": "foo_copy" } ],
    11.        "outtarget": "foo"
    12.    }
    13. ]
    复制代码

    着色器启用顺序

    • Minecraft 渲染普通方块和实体到 minecraft:main
    • Minecraft 渲染纯色的发光实体到 final
    • 运行发光着色器(entity_outline.json),其能够访问 minecraft:mainfinal 缓冲
    • Minecraft 渲染其他的东西(手、水、方块实体)到 minecraft:main
    • Minecraft 把 final 覆盖到 minecraft:main
    • 运行实体视角着色器,其能够访问 minecraft:main 缓冲

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



    Blit 缩放问题

    原版默认的 blit 着色器程序在相同大小的缓冲间复制数据时能正常运作,但是会在缓冲大小不一样时出问题。

    这是因为顶点着色器 blit.vsh 没有匹配整个输出缓冲。例如,如果输出缓冲是输入缓冲一半的大小,只有一半的输出缓冲会被写入数据:



    下方链接是一个 “Clone” 着色器,能够正确地拉伸输入,使其完全匹配到输出缓冲当中:



    下载:着色器程序 JSON 文件.vsh GLSL 文件

    读取玩家输入

    玩家的 Motion 不会在服务端更新,但如果玩家尝试在骑着猪或者矿车时移动的话则会更新,尽管玩家事实上并没有移动。通过让玩家骑着猪或矿车,命令能够读取玩家的 Motion,并(结合玩家的视角方向)确定玩家当前正按下的方向键。

    TODO:示例命令

    左键或右键的检测和平时一样(击打生物、右键使用胡萝卜钓竿或与村民交谈)。

    任何由命令获取到的信息(例如玩家目前正按着的按键)可以通过一个发光实体的队伍的颜色传递给着色器

    TODO:Rotation

    旁观死亡技巧

    假设你在旁观一个实体时死亡(如使用 /kill @s),该实体的旁观着色器在你重生后依然会生效 —— 甚至你再切换到别的游戏模式都不会失效。

    在 1.15 快照中加入的 /spectate 命令和 doImmediateRespawn 游戏规则使这成为了一个能够几乎无缝应用任何实体旁观着色器的方式。

    以下函数会向玩家应用苦力怕的旁观着色器:

    1. TODO 示例命令
    复制代码

    不过请注意:

    • 这是个 bug,因此可能在任何时刻被修复。
    • 某些行为,例如按 F5 进入第三人称视角,会使着色器失效。

    一些还没有加入本指导的新内容

    20w22a 起允许着色器访问深度缓冲,并加入了用于「极佳」图像品质的新的着色器。请参考原版 jar 文件中的 shaders/post/transparency.json

    21w10a 加入了全新的「核心」着色器程序(由游戏直接调用,没有对应的后处理 JSON 文件),并且还引入了真正的顶点着色器。请参考原版 jar 文件中的 shaders/core 目录,以及 Felix 的示例数据包:https://gist.github.com/felixjones/d5bec1ab0c83ee134fa43a142692a09b

    方块渲染类型:https://gist.github.com/boq/4514320b590de1fbe84349d23b542b28

    https://docs.google.com/document/d/18AhcnAI55liax72yh70njUomIzezOKshCurfdZPTKwM/edit

    从源代码和文件引用  

    工具和参考

    GLSL

    文档:  
    http://docs.gl/sl4/all


    教程:  
    https://www.shadertoy.com/view/Md23DV  
    https://thebookofshaders.com/


    非 Minecraft 的示例:  
    https://www.shadertoy.com/


    SpiderEye



    使用 GLIntercept 做的小工具,能让你查看每个缓冲的数据。

    下载https://drive.google.com/open?id=1p_FOalR0LzKmQu9negjtaVzobO9YSeiu

    v0.1:初步发布

    由于该软件的运作会操作游戏的 OpenGL32.dll 文件,可能会被杀软误报

    着色器示例



    可以看过去的传送门

    从一个角度捕获画面并显示

    完整、复杂的着色器运用





    调试文字

    通过着色器向屏幕上写入文字或数字

    用来调试着色器的工具





    屏幕方块

    简单的着色器,将 minecraft:main 缓存显示在一个方块上

    易于理解的着色器





    水体模糊/折射

    给水加入了模糊和折射




    来自群组: Command Block Logic
  • 评分

    参与人数 25人气 +61 金粒 +544 宝石 +20 贡献 +7 收起 理由
    大大的咸鱼 + 2 + 30 大佬 tql
    。—。 + 3 MCBBS有你更精彩~
    晴路卡 + 2 + 50 居然没评分
    (=°ω°)丿 + 2 6 月 15 号 右上角宣传栏
    ⊙v⊙ + 2 + 50 sdl awsl
    △@← + 1 神乎其技!6的飞起!
    command末影 + 1 萌新:放弃了
    Argon_gas + 2 对萌新来说还是太黑了(
    00ll00 + 2 + 30 爽啦
    乙烯_中国 + 6 + 55 + 20 + 1 精华I
    ff98sha + 2 + 33 + 1 MCBBS有你更精彩~
    ⊙u⊙ + 1 + 20 sdl awsl
    卡狗 + 4 + 50 + 1 香草沙得斯指导
    ETW_Derp + 2 + 33 MCBBS有你更精彩~
    森林蝙蝠 + 2 + 25 MCBBS有你更精彩~
    GiNYAi + 2 + 50 指导到香草着色器
    zyjking + 3 Ssssssssssssssssssss
    wshycaa + 2 MCBBS有你更精彩~
    爱心魔王FHC + 3 + 25 不能加贡献好烦啊
    海螺螺 + 4 指导到香草着色器

    查看全部评分

    GeForceLegend 当前离线
    积分
    4989
    帖子
    主题
    精华
    贡献
    爱心
    钻石
    人气
    下界之星
    最后登录
    1970-1-1
    注册时间
    2016-7-7
    查看详细资料
    发表于 2021-6-15 12:24:03 | 显示全部楼层
    顺带推荐一下写 Minecraft shader 的工作环境:vscode + Minecraft GLSL Shaders 插件,这个插件会在保存文件后自动尝试编译并高亮编译错误的代码段(虽然说好像会标到前一行),虽然说不支持最新快照的 #moj_import

    语法高亮由我自己瞎写的插件 + Monokai 颜色主题提供(由于我插件的语法高亮会被 Minecraft GLSL Shaders 的依赖项顶掉,我替换了依赖插件的语法高亮文件)

    (下图为删掉一个分号并保存后报错)
    QQ截图20210615121705.png
    QQ截图20210615121715.png

    回复

    使用道具 举报

    wshycaa 当前离线
    积分
    6455
    帖子
    主题
    精华
    贡献
    爱心
    钻石
    人气
    下界之星
    最后登录
    1970-1-1
    注册时间
    2014-1-27
    查看详细资料
    发表于 2019-9-28 18:37:29 | 显示全部楼层
    这个。。太强了,如果做好了,感觉可以模拟很多种游戏的视觉效果。
    不知道目前有没有什么做好的着色器可以一试效果,记得之前不知在哪儿看过一款沙漠平滑特效的着色器
    回复

    使用道具 举报

    brooke_zb 当前离线
    积分
    14749
    帖子
    主题
    精华
    贡献
    爱心
    钻石
    人气
    下界之星
    最后登录
    1970-1-1
    注册时间
    2015-3-27
    查看详细资料
    发表于 2019-9-28 19:50:39 | 显示全部楼层
    原版着色器在国内几乎没有什么应用案例,如果能推广开来的话在冒险地图等应用上会有更多花样吧2333

    @爱心大魔王 11级以后不是能加贡献吗= = 跟你版主权限是分开的啊

    评分

    参与人数 2人气 +1 金粒 +5 收起 理由
    爱心魔王FHC + 1 我评分也只能3 25的,不是我不多评,版主要.
    xmdhs + 5 12级不能加贡献(x

    查看全部评分

    回复

    使用道具 举报

    爱心魔王FHC 当前离线
    积分
    69048
    帖子
    主题
    精华
    贡献
    爱心
    钻石
    人气
    下界之星
    最后登录
    1970-1-1
    注册时间
    2014-6-7
    查看详细资料
    发表于 2019-9-28 20:58:43 | 显示全部楼层
    brooke_zb 发表于 2019-9-28 19:50
    原版着色器在国内几乎没有什么应用案例,如果能推广开来的话在冒险地图等应用上会有更多花样吧2333

    @爱心 ...

    版主用户组不可以版外加贡献的,你12级都没用的,我怕被混乱记过
    回复

    使用道具 举报

    brooke_zb 当前离线
    积分
    14749
    帖子
    主题
    精华
    贡献
    爱心
    钻石
    人气
    下界之星
    最后登录
    1970-1-1
    注册时间
    2015-3-27
    查看详细资料
    发表于 2019-9-28 21:48:08 | 显示全部楼层
    本帖最后由 brooke_zb 于 2019-9-28 21:50 编辑
    爱心大魔王 发表于 2019-9-28 20:58
    版主用户组不可以版外加贡献的,你12级都没用的,我怕被混乱记过

    我的意思是本来11级开始就能每天1点贡献了,你不超出每天1点的限制在板外加的话我记得是无所谓的吧,好多艺术家兼版主都有在板外加过贡献的(比如我

    实在不行备注上屠龙者贡献23333


    啊,没想到原版着色器还能有这么多应用啊(这样就不是版聊了吧2333

    评分

    参与人数 2金粒 +6 收起 理由
    SPGoding + 5 艺术家版主可版外加贡献是明文规定.
    爱心魔王FHC + 1 我被锤过了,希望理解(我丢过版主的你应该.

    查看全部评分

    回复

    使用道具 举报

    mohist 当前离线
    积分
    3486
    帖子
    主题
    精华
    贡献
    爱心
    钻石
    人气
    下界之星
    最后登录
    1970-1-1
    注册时间
    2019-8-24
    查看详细资料
    发表于 2019-9-28 21:59:41 | 显示全部楼层
    文章不错,还可以
    回复

    使用道具 举报

    chyx 当前离线
    积分
    18657
    帖子
    主题
    精华
    贡献
    爱心
    钻石
    人气
    下界之星
    最后登录
    1970-1-1
    注册时间
    2014-3-20
    查看详细资料
    发表于 2019-9-29 21:50:50 | 显示全部楼层
    本帖最后由 chyx 于 2019-9-29 21:52 编辑


    谢谢如此详细的翻译!
    这个是我以前做的。现在有了发光,很希望能够以此想出什么新的东西!




    觉得最有趣的一个图

    a8319c389b504fc2800bf811eddde71190ef6d69[1].jpg

    评分

    参与人数 1人气 +1 收起 理由
    SPGoding + 1 现在甚至有了 spectate(然而我懒了.

    查看全部评分

    回复

    使用道具 举报

    chyx 当前离线
    积分
    18657
    帖子
    主题
    精华
    贡献
    爱心
    钻石
    人气
    下界之星
    最后登录
    1970-1-1
    注册时间
    2014-3-20
    查看详细资料
    发表于 2019-9-30 13:24:30 | 显示全部楼层
    本帖最后由 chyx 于 2019-9-30 13:25 编辑



    又搞了一个这样的

    回复

    使用道具 举报

    Rays_Beam 当前离线
    积分
    551
    帖子
    主题
    精华
    贡献
    爱心
    钻石
    人气
    下界之星
    最后登录
    1970-1-1
    注册时间
    2017-5-17
    查看详细资料
    发表于 2020-3-4 11:36:05 来自手机 | 显示全部楼层
    奇怪的知识增加了!
    回复

    使用道具 举报

    唯爱醉 当前离线
    积分
    38
    帖子
    主题
    精华
    贡献
    爱心
    钻石
    人气
    下界之星
    最后登录
    1970-1-1
    注册时间
    2014-8-26
    查看详细资料
    发表于 2021-3-28 11:23:13 | 显示全部楼层
    66666感谢分享
    回复

    使用道具 举报

    Cac46 当前离线
    积分
    977
    帖子
    主题
    精华
    贡献
    爱心
    钻石
    人气
    下界之星
    最后登录
    1970-1-1
    注册时间
    2021-6-5
    查看详细资料
    发表于 2021-6-15 11:40:35 | 显示全部楼层
    高版本能将着色器加入到资源包内 似乎会发生更多有意思的事情
    回复

    使用道具 举报

    GeForceLegend 当前离线
    积分
    4989
    帖子
    主题
    精华
    贡献
    爱心
    钻石
    人气
    下界之星
    最后登录
    1970-1-1
    注册时间
    2016-7-7
    查看详细资料
    发表于 2021-6-15 12:01:30 | 显示全部楼层
    通过发光着色器向 minecraft:main 写入数据(第 3 步)似乎会破坏掉有关深度的信息,使得其他的东西(第 4 步)被渲染到 minecraft:main 的所有东西之上

    我有一个 可能 能够解决的办法,就是在片元着色器里面向 gl_FragDepth 写入数据。似乎Minecraft的后处理里面每一个buffer都会有一个绑定的depth buffer,我怀疑采用默认的顶点着色器而不修改 gl_FragDepth 会导致与 minecraft:main 绑定的depth buffer被写入默认顶点着色器产生的深度。

    当然以上只是猜测,没有经过实际的检测,但是我觉得有可能行得通


    回复

    使用道具 举报

    Cassell_XinYu 当前离线
    积分
    926
    帖子
    主题
    精华
    贡献
    爱心
    钻石
    人气
    下界之星
    最后登录
    1970-1-1
    注册时间
    2017-3-24
    查看详细资料
    发表于 2021-6-15 22:09:12 | 显示全部楼层

    高版本能将着色器加入到资源包内 似乎会发生更多有意思的事情
    回复

    使用道具 举报

    黒猫の橙 当前离线
    积分
    1203
    帖子
    主题
    精华
    贡献
    爱心
    钻石
    人气
    下界之星
    最后登录
    1970-1-1
    注册时间
    2020-3-29
    查看详细资料
    发表于 2021-6-16 20:18:09 | 显示全部楼层
    DH守卫者 发表于 2021-6-16 05:54
    小白路过,看不明白

    但对于,那些需要使用原版着色器的大佬来说,大有用哦。还有,水帖时注意点
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 注册(register)

    本版积分规则

    Archiver|小黑屋|Mcbbs.net ( 京ICP备15023768号-1 ) | 京公网安备 11010502037624号 | 手机版

    GMT+8, 2021-11-27 21:47 , Processed in 0.085593 second(s), Total 30, Slave 29 queries, Release: Build.2021.11.26 1022, Gzip On, Redis On.

    "Minecraft"以及"我的世界"为Mojang Synergies AB的商标 本站与Mojang以及微软公司没有从属关系

    © 2010-2020 我的世界中文论坛 版权所有 本站原创图文内容版权属于原创作者,未经许可不得转载

    快速回复 返回顶部 返回列表