4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Minecraftっぽいアンビエントオクルージョン(AO)

Last updated at Posted at 2020-10-24

アンビエントオクルージョン(AO)とは

遮蔽や環境光の反射などを模して陰影をつけることでより自然な影を演出する効果のこと。

AO無し / AOあり

MinecraftにおけるAO

本来AOは曲線などの複雑な影を計算する必要がありますが、Minecraftではキューブ状の地形なので、これを単純なアルゴリズムで再現することができます。
この図はキューブの各頂点の明るさを4段階に分類したものです。
Minecraftでは他にも各ブロックに対する明るさレベルがありますが、このような分類のAOマップで成り立っています。
aovoxel2.png

Minecraftでは完全にはこれに当てはまらず、各面2枚の3角形ポリゴンに対してそれぞれAO計算を行っており、それによる不具合もありますが、ここでは省きます。

AOテクスチャの作成

では先程の頂点の明るさの分類を元にテクスチャを作成してみます。
各頂点の側面と角の3方向の空気/障害物を0/1とする値として、以下の関数を使ってこの明度値を取得します。

function vertexAO(side1, side2, corner) {
  if(side1 && side2) {
    return 0
  }
  return 3 - (side1 + side2 + corner)
}

それぞれ4つの頂点の明るさを取得したら、各頂点を1ピクセルとするグレイスケールのAO画像を作成します。

// 4x4のcanvasを作成
let canvas = document.createElement("canvas");
canvas.setAttribute("width", 2);
canvas.setAttribute("height", 2);
let ctx = canvas.getContext('2d');

//明度曲線テーブル
const cols = [0.4*255, 0.6*255, 0.75*255, 1.0*255];

//light_level: 頂点の明るさ

let col = cols[light_level[0]];
ctx.fillStyle = "rgb(" + col + ", " + col + ", " + col + ")";
ctx.fillRect(0, 0, 1, 1);

col = cols[light_level[1]];
ctx.fillStyle = "rgb(" + col + ", " + col + ", " + col + ")";
ctx.fillRect(1, 0, 1, 1);

col = cols[light_level[2]];
ctx.fillStyle = "rgb(" + col + ", " + col + ", " + col + ")";
ctx.fillRect(1, 1, 1, 1);

col = cols[light_level[3]];
ctx.fillStyle = "rgb(" + col + ", " + col + ", " + col + ")";
ctx.fillRect(0, 1, 1, 1);

let texture = new THREE.CanvasTexture(canvas);
texture.magFilter = THREE.LinearFilter; //アンチエイリアスで拡大させる(省略可)

//メッシュに適用させる
mesh.material.aoMap = texture;

作成されるAOテクスチャの例
アンチエイリアス無し / アンチエイリアス有り
ao_texture.png scaled_ao.png

通常のテクスチャのみ
texture_only.png

AOマップ適用後
bombed.png

Three.jsにおけるAOマップの注意

先程のソースはMeshのMaterialにそのまま適用させましたが、Three.jsではそのままでは正しく表示されません。
なぜかというと、テクスチャマップである、GeometryのfaceVertexUVsにはデフォルトではAOマップ用のレイヤーが無いからです。
ただ、これは普通のテクスチャマップのコピーを追加させるだけで良いので難しくはありません。

THREE.Geometry.jsのソースの一部を拝借して作成した関数。

// faceVertexUvsにAOマップ用のレイヤーを追加する
function addAOVertexUVs(mesh){
	mesh.geometry.faceVertexUvs[1] = [];
	
	for(let j=0; j<mesh.geometry.faceVertexUvs[0].length; j++){

		let uvs = mesh.geometry.faceVertexUvs[0][j];
		let uvsCopy = [];

		for(let k=0; k<uvs.length; k++){

			uvsCopy.push( uvs[k].clone() );

		}
		mesh.geometry.faceVertexUvs[1].push( uvsCopy );
	}
}

サンプル

「Ignite」ボタンで発火してね!

See the Pen Minecraft: TNT Explosion by Urushibara (@pneuma01) on CodePen.

参考元ページ

以下のページの一部画像を使用、内容を参考にさせていただきました。
0 FPS - Ambient occlusion for Minecraft-like worlds

4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?