Sprites en 3D

Des gemmes Sprite3D en billboard sur une texture peinte pixel par pixel en code, chacune nommée par un Label3D. Le panneau central garde le billboard désactivé pour comparer.

L'éditeur ci-dessus fait tourner ce projet. Changez une ligne et il se recharge.

Classes Godot utilisées

Sprite3DLabel3DImageTexture

Le code

scripts/main.gd
extends Node

const ICON: int = 16
const RING: float = 5.0
const COLORS: Array[String] = ['#8da5f3', '#8eef97', '#fc7f7f', '#f3d48d']

var camera: Camera3D
var angle: float = 0.0


func _ready() -> void:
	_add_environment()
	_add_light()
	_add_ground()
	_add_fixed_sign()

	for i in COLORS.size():
		var turn: float = TAU * i / COLORS.size() + PI * 0.25
		_add_gem(Vector3(cos(turn), 0, sin(turn)) * RING, COLORS[i])

	camera = Camera3D.new()
	camera.fov = 50.0
	add_child(camera)
	camera.current = true
	_place_camera()

	print('the camera keeps circling, the gems and their names stay flat on screen, the center sign does not')


func _process(delta: float) -> void:
	angle += delta * 0.3
	_place_camera()


func _place_camera() -> void:
	camera.position = Vector3(sin(angle), 0, cos(angle)) * 11.0 + Vector3(0, 4.4, 0)
	camera.look_at(Vector3(0, 1.5, 0), Vector3.UP)


func _add_gem(at: Vector3, hex: String) -> void:
	var sprite: Sprite3D = Sprite3D.new()
	sprite.texture = _make_icon(Color(hex))
	sprite.pixel_size = 0.15
	sprite.billboard = BaseMaterial3D.BILLBOARD_ENABLED
	sprite.texture_filter = BaseMaterial3D.TEXTURE_FILTER_NEAREST
	sprite.alpha_cut = SpriteBase3D.ALPHA_CUT_DISCARD
	sprite.shaded = false
	sprite.position = at + Vector3(0, 1.5, 0)
	add_child(sprite)

	var label: Label3D = Label3D.new()
	label.text = hex
	label.font_size = 64
	label.pixel_size = 0.0055
	label.billboard = BaseMaterial3D.BILLBOARD_ENABLED
	label.alpha_cut = Label3D.ALPHA_CUT_DISCARD
	label.modulate = Color(hex)
	label.outline_size = 14
	label.outline_modulate = Color(0, 0, 0, 0.9)
	label.position = at + Vector3(0, 2.9, 0)
	add_child(label)


func _add_fixed_sign() -> void:
	var label: Label3D = Label3D.new()
	label.text = 'billboard off'
	label.font_size = 90
	label.pixel_size = 0.011
	label.billboard = BaseMaterial3D.BILLBOARD_DISABLED
	label.alpha_cut = Label3D.ALPHA_CUT_DISCARD
	label.modulate = Color('#c8ccd8')
	label.outline_size = 16
	label.outline_modulate = Color(0, 0, 0, 0.9)
	label.position = Vector3(0, 4.8, 0)
	add_child(label)


func _make_icon(tint: Color) -> ImageTexture:
	var image: Image = Image.create(ICON, ICON, false, Image.FORMAT_RGBA8)
	image.fill(Color(0, 0, 0, 0))

	var center: float = (ICON - 1) * 0.5

	for y in ICON:
		for x in ICON:
			var reach: float = abs(x - center) + abs(y - center)

			if reach > center:
				continue

			var shade: float = 1.0 - reach / center * 0.6
			image.set_pixel(x, y, Color(tint.r * shade, tint.g * shade, tint.b * shade, 1.0))

	return ImageTexture.create_from_image(image)


func _add_ground() -> void:
	var mesh: PlaneMesh = PlaneMesh.new()
	mesh.size = Vector2(120, 120)

	var material: StandardMaterial3D = StandardMaterial3D.new()
	material.albedo_color = Color('#242938')
	material.roughness = 0.95

	var ground: MeshInstance3D = MeshInstance3D.new()
	ground.mesh = mesh
	ground.material_override = material
	add_child(ground)


func _add_environment() -> void:
	var sky_material: ProceduralSkyMaterial = ProceduralSkyMaterial.new()
	sky_material.sky_top_color = Color(0.07, 0.09, 0.18)
	sky_material.sky_horizon_color = Color(0.26, 0.29, 0.4)
	sky_material.ground_bottom_color = Color(0.05, 0.06, 0.09)
	sky_material.ground_horizon_color = Color(0.18, 0.2, 0.28)

	var sky: Sky = Sky.new()
	sky.sky_material = sky_material

	var env: Environment = Environment.new()
	env.background_mode = Environment.BG_SKY
	env.sky = sky
	env.ambient_light_source = Environment.AMBIENT_SOURCE_SKY
	env.ambient_light_energy = 0.9

	var world: WorldEnvironment = WorldEnvironment.new()
	world.environment = env
	add_child(world)


func _add_light() -> void:
	var light: DirectionalLight3D = DirectionalLight3D.new()
	light.rotation_degrees = Vector3(-55, -35, 0)
	light.light_energy = 1.2
	add_child(light)

Plus d'exemples de Scène 3D