LoginSignup
30
11

4頂点楕円体描画

Last updated at Posted at 2023-12-17

弾としての表現

は色々とありますが(要するにシューティングゲームの弾です)

  • 球状だと進行方向がわかりにくい、味気ない
  • 板状は視線と平行になるとつぶれてしまう

ので、私は楕円体の描画を好んでよく使っています。ビルボードと同様に少ない頂点で描画でき、カメラの位置で破綻することもありません。

output.gif

今回は Godot での実装例を紹介します。
(Unityの実装例は・・・だいぶ前にソースを公開しただけだったかも)

頂点の算出

進行方向を持つ弾は、2点で表現されます。

image.png

カメラに対してこのような位置関係にある場合、頂点シェーダで

image.png

このようなオレンジ色の頂点を算出します。このようにカメラに対して垂直な点を作れば、ワールド座標の2点が視線方向に重なっても

image.png

こうなりますから、潰れることはありません。楕円体を真横から見れば円になる理屈に沿っていますね。

GDScript

func _ready():
	var vertices := PackedVector3Array([
		Vector3(0,0,0),
		Vector3(0,0,1),
		Vector3(0,0,0),
		Vector3(0,0,1),
	])
	var normals := PackedVector3Array([
		Vector3(-0.5,-0.5,-1),
		Vector3(-0.5, 0.5, 1),
		Vector3( 0.5,-0.5,-1),
		Vector3( 0.5, 0.5, 1),
	])
	var UVs := PackedVector2Array([
		Vector2(0, 0),
		Vector2(0, 1),
		Vector2(1, 0),
		Vector2(1, 1),
	])
	var indices := PackedInt32Array([
		0, 1, 2,
		2, 1, 3,
	])

頂点シェーダの計算に都合の良い4頂点のメッシュを用意します。どうせ頂点は計算で出すので、ここがモデルとしてどんな形をしていようがかまいません。
手元では、大量に出すために instancing の設定をしていますが、本筋ではないので割愛。

シェーダー

shader_type spatial;
render_mode cull_disabled, blend_add, depth_draw_never, unshaded, specular_disabled, shadows_disabled, ambient_light_disabled, skip_vertex_transform;

void vertex()
{
	vec3 objSpaceViewDir = normalize(transpose(MODEL_NORMAL_MATRIX) * (CAMERA_POSITION_WORLD - NODE_POSITION_WORLD) + VERTEX);
	float width = 1.0;
	vec3 forward = vec3(0, 0, 1);
	vec3 side = normalize(cross(objSpaceViewDir, forward));
	vec3 vert = normalize(cross(objSpaceViewDir, side));
	vec3 tv = NORMAL.x * width * side;
	tv += NORMAL.y * width * vert;
	float len = 2.0 - width;
	tv -= VERTEX.xyz * len;
    VERTEX = (MODELVIEW_MATRIX * vec4(tv, 1.0)).xyz;
}

void fragment()
{
	float x = (UV.x-0.5)*2.0;
	float y = (UV.y-0.5)*2.0;
	float dist2 = x*x + y*y;
	ALBEDO = COLOR.rgb;
	ALPHA = (1.0-dist2)*0.5;
}

こんなふうに頂点シェーダで外積を多用すると望んだ結果が得られます。
わざわざNORMALを用意せずUVの値で計算してもいいのですが、UVの値はテクスチャなしで円を描画するのに使用しているため、このような実装になっています。

おまけ: objSpaceViewDir

	vec3 objSpaceViewDir = normalize(transpose(MODEL_NORMAL_MATRIX) * (CAMERA_POSITION_WORLD - NODE_POSITION_WORLD) + VERTEX);

これ。カメラの方向をオブジェクト空間で欲しいってことはよくありますが・・
Unityのシェーダならすぐにとってこれる値なのですが、Godotだとなかなか複雑な演算を必要とします(それなりに探しましたが他に方法を見つけられなかった)。
イディオムとして使いまわしていきましょう。これを標準で用意すべきとも、あまり思いませんね・・書けばいいのですよ。

そんな小ネタでした

というか Advent Calendar の援護射撃でした。

30
11
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
30
11