【Minecraft】デタパについて書かれてたからリソパシェーダーについて（GLSL）

この記事は[Nihon University Advent Calendar 2024](https://qiita.com/advent-calendar/2024/nihon-u)の24日目の記事です。

:::note warn
この記事はMinecraft Java版1.21.4向けの記事です。
リソースパックは、Java版（PC版）とBedrock版（コンソール版）のどちらにも存在しますが、これらは仕様が大きく異なるのでご注意ください。
:::

:::note warn
この記事は、Minecraftのリソースパック機能について紹介する記事であり、リソースパックシェーダーのチュートリアルを目的とするものではありません。
:::

<br>

# はじめに
Minecraftには、プレイヤーがMinecraftをカスタマイズするための仕組みがたくさん用意されています。

**しかし、Minecraftをカスタムする機能としてMod、ShaderPack、Pluginなどが非常に有名である一方、```ResourcePack```や```DataPack```が日の目を見ることはあまりないように感じています。**

この記事では、Java版Minecraftに用意されているリソースパック機能の一つであるリソパシェーダーについて私が学習したことを共有して、
**<font color="Green">「リソースパックってこんなことまでできるのかスゲー」</font>**
という気持ちを一方的に共有したい（？）と思います。

### シェーダーってなに？
そもそも、シェーダーとはなんでしょうか？シェーダーはシェーディングに利用されます。シェーディングとは、物体に陰影を与えることで立体感をだす処理のことを指します。
![shader_image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3781827/61431c5e-1261-d718-4170-05797b84a67a.png)

### ResourcePacksってなに？
では、この記事で紹介している「リソースパック」ってどんなものでしょうか？[Minecraft Wiki日本語版](https://ja.minecraft.wiki/)では、このように紹介されています。
> リソースパック（英：Resource Pack）は、テクスチャ、翻訳文や言語、フォント、サウンド、モデル、およびエンドポエムやスプラッシュテキストなどを、ゲーム本体を書き換えることなくカスタマイズするための仕組みである。

つまり、リソースパックを使うことで、Minecraftの見た目や音を、**<font color="Red">プログラムを変更せずに</font>** 自由にカスタマイズすることができます！

### リソパシェーダーってなに？
名前の通り、リソパシェーダー（リソースパックシェーダー / Resource Pack Shader）は **<font color="Red">リソースパックで作成するシェーダー</font>** のことです。GLSLで記述します。

:::note warn
Minecraftには、影Modを代表とするShaderPackという仕組みがありますが、これはModの一部でリソパシェーダーとは大きく異なります。
:::

# 作ろう！リソパシェーダー
### 1. リソースパックを作ろう！
#### リソパの基本
リソースパックは、`.minecraft/resourcepacks`に格納します。リソースパックは以下のような形となります。
``` plainText:一般的なリソースパックの構造
testResourcePack
├─ assets
│  └─ minecraft
│     ├─ models
│     ├─ sounds
│     ├─ textures
│     └─ shaders
├─ pack.mcmeta
└─ pack.png
```

``pack.mcmeta``には、以下のような内容を記述します。pack_formatにはリソースパックのバージョンを記載します。<font color="Purple">``46``</font>はMinecraft 1.21.4に対応しています。
参照：[Mincraft Wiki / Pack format](https://minecraft.wiki/w/Pack_format)

``` JSON:pack.mcmeta
{
  "pack": {
    "pack_format": 46,
    "description": "モノクロなリソパシェーダー"
  }
}
```

#### おすすめの環境

<font color="#474747">エディタ：</font>

https://code.visualstudio.com/

<font color="#474747">VSCode拡張機能：</font>

https://marketplace.visualstudio.com/items?itemName=SuperAnt.mc-dp-icons

https://marketplace.visualstudio.com/items?itemName=kuba-p.glsl-lsp

### 2. リソパシェーダーの構成
#### リソパシェーダーの構成１（Coreとpost）
リソパシェーダーは大きく分けて、

1. ``Core shaders``
2. ``Post-processing shaders``
 
の二つに分類することができます。 それぞれの役割は以下のようになっています。

| 名前 | 役割 | イメージ |
|:-:|:-:|:-:|
| Core | 画面上に見える物体のレンダリング |![core_image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3781827/60c6ffa8-0d45-69cc-71a4-d1ba6acc5e3d.png)|
| post | 2Dになった画像（映像）をレンダリング |![post_image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3781827/b3ed040d-b093-8cfb-1121-7bcf1c8e009a.png)|

参考：Guide to vanilla shaders

https://docs.google.com/document/d/15TOAOVLgSNEoHGzpNlkez5cryH3hFF3awXL5Py81EMk/preview?tab=t.0

***

#### リソパシェーダーの構成２（json / vsh / fsh）
Coreシェーダーとpostシェーダーは以下のようなファイルで構成されています。

1. ``json``
2. ``vsh``
3. ``fsh``

それぞれのファイルには以下のような役割があります。また、vshとfshはGLSLで記述します。

| 名前 | 役割 |
|:-:|:-:|
| json | vsh、fshファイルを呼び出す <br> vsh、fshに渡すデータを指定する |
| vsh | 頂点データを編集する |
| fsh | 色データを指定する |

全体の流れを図にするとこのようになります。
![shader_flow_image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3781827/0c657400-8e29-1de9-01ca-ca77d643a6d2.png)

::: note
リソパシェーダーは、
**core(json→vsh→fsh) → post(json→vsh→fsh)**
の順に処理されます。
:::

参考：【リソパシェーダー紹介】第1回 リソパシェーダーとは？

https://gb.huedpaw.com/blog/2023/08/rp-shader-1/

### 3. Re: リソパシェーダーを作ろう
この記事では、ブロックをモノクロにするリソパシェーダーを作成してみます。

#### シェーダーファイルの準備
Minecraftの中で、ブロックは``terrain.vsh``と``terrain.fsh``というファイルを使ってシェーディングされています。今回はブロック色のみを変更したいので、リソースパックには``terrain.fsh``のみを追加します。このファイルは作成したリソースパックの``shaders``下の``core``内に追加しておきます。（``./assets/minecraft/shaders/core/terrain.fsh``）
それぞれのシェーダーファイルの担当については以下のページが参考になります。

https://github.com/McTsts/Minecraft-Shaders-Wiki/tree/main

::: note warn
以前は、ブロックの種類によって異なるシェーダーファイルが使われていましたが、Minecraft 1.21.2以降はすべて、terrainというファイルで処理されるように変更されました。
:::

また、リソースパックを導入する前のMinecraftで利用されているシェーダーファイルは、以下のページを参照できます。

https://github.com/misode/mcmeta

こちらが、通常、Minecraftで利用されている``terrain.fsh``です。

``` GLSL: terrain.fsh
#version 150

#moj_import <minecraft:fog.glsl>

uniform sampler2D Sampler0;

uniform vec4 ColorModulator;
uniform float FogStart;
uniform float FogEnd;
uniform vec4 FogColor;

in float vertexDistance;
in vec4 vertexColor;
in vec2 texCoord0;

out vec4 fragColor;

void main() {
    vec4 color = texture(Sampler0, texCoord0) * vertexColor * ColorModulator;
#ifdef ALPHA_CUTOUT
    if (color.a < ALPHA_CUTOUT) {
        discard;
    }
#endif
    fragColor = linear_fog(color, vertexDistance, FogStart, FogEnd, FogColor);
}
```



#### コードの追加

``terrain.fsh``にコードを付け加えていきます。このシェーダーファイルでは最終的に``fragColor`` という変数が **<font color="Orange">``out``</font>** しているので、Minecraftで描画されるブロックの色味は、この変数で決定していることが分かります。一番下のこの行を書き換えれば、描画されるブロックの色味は異なるものになるはずです！

![2024-12-24_00.05.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3781827/6fdfa26f-c941-6d1a-e5f1-55cf48cabb2b.png)

``` GLSL: 元のコード
fragColor = linear_fog(color, vertexDistance, FogStart, FogEnd, FogColor);
```

そこで、上のコードを以下のように書き換えると、

``` diff_GLSL
- fragColor = linear_fog(color, vertexDistance, FogStart, FogEnd, FogColor);
+ float brightness = dot(color.rgb, vec3(0.299, 0.587, 0.114));
+ float levels = 10.0;
+ brightness = floor(brightness * levels) / (levels - 1.0);
+ fragColor = vec4(vec3(brightness), 1.0);
```

![2024-12-24_00.05.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3781827/a7c086b3-f855-f210-8b67-b39f26abdde0.png)

``` GLSL: 新しいコード
float brightness = dot(color.rgb, vec3(0.299, 0.587, 0.114));
float levels = 10.0;
brightness = floor(brightness * levels) / (levels - 1.0);
fragColor = vec4(vec3(brightness), 1.0);
```

このコードでは、テクスチャの色味にもろもろ加工した変数colorのrgb値と``vec3(0.299, 0.587, 0.114)``という定数のドット積をとったのちに、10段階にスケーリングしています。ちなみに、``vec3(0.299, 0.587, 0.114)`` は、人間の視覚に基づいた重みらしいです。こちらの記事を参考にしました。

https://qiita.com/yoya/items/96c36b069e74398796f3

#### 完成
この処理をいろいろなシェーダーファイルに追加すれば、エンティティや空の色をモノクロにすることもできます。

https://x.com/boo_manKnow408/status/1871214112405135388

# まとめ
Minecraftをカスタマイズする機能は、ModやPluginだけではありません！
この記事を通して、**<font color="Purple">ResourcePack</font>** や **<font color="Orange">DataPack</font>** の面白さが布教できたら嬉しいです。