์นดํ…Œ๊ณ ๋ฆฌ ์—†์Œ

Godot 4 ๋ Œ๋”๋ง ํŒŒ์ดํ”„๋ผ์ธ์˜ ์…ฐ์ด๋” ์ปดํŒŒ์ผ ์Šคํ„ฐํ„ฐ๋ง ํ•ด๊ฒฐ: ํŒŒ์ดํ”„๋ผ์ธ ์บ์‹ฑ๊ณผ ์›Œ๋ฐ์—… ์ตœ์ ํ™” ์‹ค๋ฌด ๊ฐ€์ด๋“œ

๊ฒŒ์ž„๊ต์ˆ˜ 2026. 8. 30. 16:00
๋ฐ˜์‘ํ˜•

๐ŸŒ English Abstract

This technical article explores the root causes of frame stuttering caused by runtime shader compilation in Godot 4's Vulkan-based rendering pipeline. It details practical troubleshooting strategies, including shader pipeline state object (PSO) caching and asynchronous shader pre-warming techniques using custom off-screen viewport passes. By implementing these optimizations, developers can eliminate micro-stutters and achieve smooth, production-ready frame rates across diverse GPU architectures.

Godot 4 ์—”์ง„์œผ๋กœ ํ”„๋กœ์ ํŠธ๋ฅผ ์ „ํ™˜ํ•˜๊ฑฐ๋‚˜ ์ƒˆ๋กญ๊ฒŒ ๊ฒŒ์ž„์„ ๊ฐœ๋ฐœํ•  ๋•Œ, ๋งŽ์€ ๊ฐœ๋ฐœ์ž๊ฐ€ ๊ฐ€์žฅ ๋จผ์ € ์ง๋ฉดํ•˜๋Š” ๊ธฐ์ˆ ์  ๋‚œ๊ด€ ์ค‘ ํ•˜๋‚˜๊ฐ€ ๋ฐ”๋กœ ์‹ค์‹œ๊ฐ„ ์…ฐ์ด๋” ์ปดํŒŒ์ผ ์ง€์—ฐ(Shader Compilation Stuttering)์ž…๋‹ˆ๋‹ค. ํ”Œ๋ ˆ์ด์–ด๊ฐ€ ์ƒˆ๋กœ์šด ๋ชฌ์Šคํ„ฐ๋ฅผ ์ฒ˜์Œ ๋งˆ์ฃผ์น˜๊ฑฐ๋‚˜, ์ดํŽ™ํŠธ(Particle System)๊ฐ€ ๋ฐœ์‚ฌ๋  ๋•Œ ํ™”๋ฉด์ด ์ˆ˜์‹ญ ๋ฐ€๋ฆฌ์ดˆ(ms) ๋™์•ˆ ๋ฉˆ์นซํ•˜๋Š” ํ˜„์ƒ์€ ๊ฒŒ์ž„์˜ ๋ชฐ์ž…๊ฐ์„ ๊ทน๋„๋กœ ํ•ด์นฉ๋‹ˆ๋‹ค.

์ด ๊ธ€์—์„œ๋Š” Godot 4์˜ Forward+ ๋ฐ Mobile ๋ฐฑ์—”๋“œ(Vulkan/RenderingDevice) ์•„ํ‚คํ…์ฒ˜ ๋‚ด๋ถ€์—์„œ ์™œ ์ด๋Ÿฌํ•œ ํ”„๋ ˆ์ž„ ๋“œ๋ž์ด ๋ฐœ์ƒํ•˜๋Š”์ง€ ๊ทผ๋ณธ์ ์ธ ์›์ธ์„ ๋ถ„์„ํ•˜๊ณ , ์ด๋ฅผ ์™„๋ฒฝํžˆ ์ œ๊ฑฐํ•˜๊ธฐ ์œ„ํ•œ ์…ฐ์ด๋” ํŒŒ์ดํ”„๋ผ์ธ ์บ์‹ฑ(Shader Pipeline Caching) ๊ธฐ๋ฒ•๊ณผ ์…ฐ์ด๋” ์›Œ๋ฐ์—…(Shader Warmup / Pre-warming) ์‹œ์Šคํ…œ ๊ตฌ์ถ• ๋ฐฉ์•ˆ์„ ๊นŠ์ด ์žˆ๊ฒŒ ๋‹ค๋ฃน๋‹ˆ๋‹ค.



1. Godot 4 ๋ Œ๋”๋ง ํŒŒ์ดํ”„๋ผ์ธ๊ณผ ์…ฐ์ด๋” ์ปดํŒŒ์ผ ์Šคํ„ฐํ„ฐ๋ง์˜ ์›์ธ

Godot 3์˜ OpenGL ES ๊ธฐ๋ฐ˜ ๋ Œ๋”๋Ÿฌ์™€ ๋‹ฌ๋ฆฌ, Godot 4๋Š” ํ˜„๋Œ€์ ์ธ Vulkan API๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•œ RenderingDevice ์•„ํ‚คํ…์ฒ˜๋ฅผ ์ฑ„ํƒํ–ˆ์Šต๋‹ˆ๋‹ค. Vulkan์€ ๊ทธ๋ž˜ํ”ฝ ๋“œ๋ผ์ด๋ฒ„์˜ ์˜ค๋ฒ„ํ—ค๋“œ๋ฅผ ์ตœ์†Œํ™”ํ•˜๊ณ  ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ ์„ฑ๋Šฅ์„ ๊ทน๋Œ€ํ™”ํ•  ์ˆ˜ ์žˆ๋Š” ์žฅ์ ์ด ์žˆ์ง€๋งŒ, ๊ทธ ๋Œ€๊ฐ€๋กœ ํŒŒ์ดํ”„๋ผ์ธ ์ƒํƒœ ๊ฐ์ฒด(PSO: Pipeline State Object)์˜ ๊ด€๋ฆฌ๋ฅผ ์—”์ง„ ์ธก์—์„œ ์ง์ ‘ ์ „๋‹ดํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

ํŒŒ์ดํ”„๋ผ์ธ ์ƒํƒœ ๊ฐ์ฒด(PSO) ์—ฐ์‚ฐ์˜ ๋ณ‘๋ชฉ

Vulkan์—์„œ ์…ฐ์ด๋”๋ฅผ ์‹คํ–‰ํ•˜๋ ค๋ฉด ๊ทธ๋ž˜ํ”ฝ ์นด๋“œ๊ฐ€ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋Š” ์™„์ „ํ•œ ํŒŒ์ดํ”„๋ผ์ธ ์ƒํƒœ๊ฐ€ ์ •์˜๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. PSO๋Š” ๋‹จ์ˆœํ•œ ์…ฐ์ด๋” ์ฝ”๋“œ(SPIR-V)๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ Œ๋” ์ƒํƒœ ์ •๋ณด๊ฐ€ ๋ชจ๋‘ ๊ฒฐํ•ฉ๋˜์–ด ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค:

  • ์ •์  ์…ฐ์ด๋” ๋ฐ”์ดํŠธ์ฝ”๋“œ(Vertex/Fragment Shader Code)
  • ์ž…๋ ฅ ๋ฒ„ํ…์Šค ๋ ˆ์ด์•„์›ƒ(Vertex Input Bindings & Attributes)
  • ๋ธ”๋ Œ๋”ฉ ๋ชจ๋“œ(Alpha Blend, Additive Blend ๋“ฑ)
  • ์ปฌ๋ง ๋ฐ ๊นŠ์ด/์Šคํ…์‹ค ์ƒํƒœ(Culling Mode, Depth Test/Write Options)
  • ๋ Œ๋” ํƒ€๊ฒŸ์˜ ๋ฉ€ํ‹ฐ์ƒ˜ํ”Œ๋ง(MSAA) ์ •๋ณด ๋ฐ ์ƒ‰์ƒ ํฌ๋งท

๋ฌธ์ œ๋Š” ์ด๋Ÿฌํ•œ ์กฐํ•ฉ์ด ์‹ค์‹œ๊ฐ„ ๋ Œ๋”๋ง ํƒ€์ž„์— ํ™”๋ฉด์— "์ฒ˜์Œ ๊ทธ๋ ค์ง€๋Š” ์ˆœ๊ฐ„"์— ๋น„๋กœ์†Œ ํ™•์ •๋œ๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค. ์—”์ง„์ด ์ƒˆ๋กœ์šด ์กฐํ•ฉ์˜ PSO๋ฅผ ๊ฐ์ง€ํ•˜๋ฉด ๊ทธ๋ž˜ํ”ฝ ๋“œ๋ผ์ด๋ฒ„๋Š” ๋“œ๋ผ์ด๋ฒ„ ๋ ˆ๋ฒจ์—์„œ ๊ธฐ๊ณ„์–ด(Machine Code)๋กœ ์…ฐ์ด๋”๋ฅผ ์ปดํŒŒ์ผํ•˜๊ฒŒ ๋˜๋ฉฐ, ์ด ์ž‘์—…์€ ๋ฉ”์ธ ๋ Œ๋”๋ง ์Šค๋ ˆ๋“œ๋ฅผ ์ฐจ๋‹จ(Blocking)ํ•˜์—ฌ ์ˆ˜์‹ญ~์ˆ˜๋ฐฑms์— ๋‹ฌํ•˜๋Š” Spike๋ฅผ ์œ ๋ฐœํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ’ก ํ•ต์‹ฌ ์š”์•ฝ: ์…ฐ์ด๋” ํŒŒ์ผ(.gdshader) ์ž์ฒด๊ฐ€ ๋กœ๋“œ๋˜๋Š” ์‹œ๊ฐ„์ด ์•„๋‹ˆ๋ผ, ํŠน์ • ๋จธํ‹ฐ๋ฆฌ์–ผ(Material)๊ณผ ๋ Œ๋” ์ƒํƒœ(Render State)๊ฐ€ ๊ฒฐํ•ฉ๋˜์–ด GPU ํŒŒ์ดํ”„๋ผ์ธ(PSO)์— ์ฒ˜์Œ ์ œ์ถœ(Submit)๋  ๋•Œ ์ปดํŒŒ์ผ ์ง€์—ฐ์ด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

2. Godot 4์˜ ๊ธฐ๋ณธ ํŒŒ์ดํ”„๋ผ์ธ ์บ์‹ฑ ๋ฉ”์ปค๋‹ˆ์ฆ˜๊ณผ ํ•œ๊ณ„

Godot 4๋Š” ์ด๋Ÿฌํ•œ ๋ฌธ์ œ๋ฅผ ์™„ํ™”ํ•˜๊ธฐ ์œ„ํ•ด ์ž์ฒด์ ์ธ ์…ฐ์ด๋” ์บ์‹œ(Shader Cache) ๊ธฐ๋Šฅ์„ ํƒ‘์žฌํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ํ”„๋กœ์ ํŠธ ์„ค์ •(Project Settings)์—์„œ rendering/shader_compiler/shader_cache/enabled ์˜ต์…˜์„ ์ผœ๋‘๋ฉด, ์—”์ง„์€ ์ด์ „์— ์ปดํŒŒ์ผ๋œ ์…ฐ์ด๋” ํŒŒ์ดํ”„๋ผ์ธ ๋ฐ”์ด๋„ˆ๋ฆฌ๋ฅผ ๋””์Šคํฌ์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.

๋””์Šคํฌ ์บ์‹œ์˜ ํ•œ๊ณ„์  ๋ฐ ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ… ์ผ€์ด์Šค

๊ธฐ๋ณธ ๋””์Šคํฌ ์บ์‹ฑ ๊ธฐ๋Šฅ๋งŒ์œผ๋กœ๋Š” ํ˜„์—… ํ”„๋กœ์ ํŠธ์˜ ์Šคํ„ฐํ„ฐ๋ง์„ ์™„์ „ํžˆ ์˜ˆ๋ฐฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์—ฃ์ง€ ์ผ€์ด์Šค(Edge Cases)๊ฐ€ ์กด์žฌํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค:

  1. ์ตœ์ดˆ ์‹คํ–‰์ž(First-Time Users) ๋ฌธ์ œ: ๊ฒŒ์ด๋จธ๊ฐ€ ๊ฒŒ์ž„์„ ์„ค์น˜ํ•˜๊ณ  ์ฒ˜์Œ ํ”Œ๋ ˆ์ดํ•  ๋•Œ ๋””์Šคํฌ ์บ์‹œ๋Š” ๋น„์–ด์žˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๊ฒŒ์ž„ ์ดˆ๋ฐ˜ ํ”Œ๋ ˆ์ด ์‹œ ์Šคํ„ฐํ„ฐ๋ง์„ ํ”ผํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.
  2. GPU ๋“œ๋ผ์ด๋ฒ„ ์—…๋ฐ์ดํŠธ ๋ฐ ํ•˜๋“œ์›จ์–ด ๋ณ€๊ฒฝ: ๊ทธ๋ž˜ํ”ฝ ์นด๋“œ ๋“œ๋ผ์ด๋ฒ„๊ฐ€ ์—…๋ฐ์ดํŠธ๋˜๋ฉด ๊ธฐ์กด ๋””์Šคํฌ ์บ์‹œ๋Š” ๋ฌดํšจํ™”(Invalidated)๋˜์–ด ์ „๋Ÿ‰ ์žฌ์ปดํŒŒ์ผ๋ฉ๋‹ˆ๋‹ค.
  3. ๋™์  ๋จธํ‹ฐ๋ฆฌ์–ผ ๋ฐ”์ธ๋”ฉ: ๋Ÿฐํƒ€์ž„์— ๋จธํ‹ฐ๋ฆฌ์–ผ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๋™์ ์œผ๋กœ ๋ณ€๊ฒฝํ•˜์—ฌ ์ƒˆ๋กœ์šด ์…ฐ์ด๋” ๋ณ€์ข…(Variant)์ด ํŠธ๋ฆฌ๊ฑฐ๋˜๋Š” ๊ฒฝ์šฐ, ์‹ค์‹œ๊ฐ„ ์ปดํŒŒ์ผ์ด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.


3. ์‹ค์ „ ํ•ด๋ฒ•: ์˜คํ”„์Šคํฌ๋ฆฐ ๋ทฐํฌํŠธ๋ฅผ ํ™œ์šฉํ•œ ์…ฐ์ด๋” ์›Œ๋ฐ์—…(Warmup) ๊ตฌ์ถ•

๊ฐ€์žฅ ์™„๋ฒฝํ•œ ํ•ด๊ฒฐ์ฑ…์€ ๊ฒŒ์ž„ ํ”Œ๋ ˆ์ด ์”ฌ์œผ๋กœ ์ „ํ™˜๋˜๊ธฐ ์ „, ๊ฒŒ์ž„ ๋‚ด ์‚ฌ์šฉ๋˜๋Š” ๋ชจ๋“  ๋จธํ‹ฐ๋ฆฌ์–ผ๊ณผ ํŒŒํ‹ฐํด, ๋ฉ”์‹œ ์กฐํ•ฉ์„ ํ™”๋ฉด์— ๋ณด์ด์ง€ ์•Š๋Š” ๋ Œ๋” ํƒ€๊ฒŸ(Off-screen Viewport)์— ๋ฏธ๋ฆฌ ํ•œ ๋ฒˆ ๋ Œ๋”๋งํ•˜์—ฌ GPU PSO๋ฅผๅผทๅˆถ๋กœ ์ƒ์„ฑ ๋ฐ ์บ์‹ฑํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ž๋™ํ™”๋œ ์…ฐ์ด๋” Pre-warming ์‹œ์Šคํ…œ ๊ตฌํ˜„

๋‹ค์Œ์€ ๋กœ๋”ฉ ํ™”๋ฉด ๋‹จ๊ณ„์—์„œ ์ฃผ์š” ์…ฐ์ด๋”์™€ ํŒŒํ‹ฐํด ์‹œ์Šคํ…œ์„ 1ํ”„๋ ˆ์ž„ ๋™์•ˆ ๊ฐ•์ œ ๊ตฌ๋™ํ•˜๋Š” GDScript ์‹ค๋ฌด ์˜ˆ์ œ ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค.

# ShaderWarmupManager.gd
extends Node

class_name ShaderWarmupManager

signal warmup_completed

@export var materials_to_warmup: Array[Material] = []
@export var mesh_to_use: Array[Mesh] = []

var _sub_viewport: SubViewport
var _camera: Camera3D

func _ready() -> void:
	_setup_offscreen_viewport()

func _setup_offscreen_viewport() -> void:
	# ์˜คํ”„์Šคํฌ๋ฆฐ ๋ทฐํฌํŠธ ์ƒ์„ฑ (ํ™”๋ฉด์— ํ‘œ์‹œ๋˜์ง€ ์•Š์Œ)
	_sub_viewport = SubViewport.new()
	_sub_viewport.size = Vector2i(64, 64) # ์ตœ์†Œ ํฌ๊ธฐ๋กœ ๋ฉ”๋ชจ๋ฆฌ ์ ˆ์•ฝ
	_sub_viewport.render_target_update_mode = SubViewport.UPDATE_ONCE
	
	_camera = Camera3D.new()
	_camera.transform.origin = Vector3(0, 0, 5)
	_sub_viewport.add_child(_camera)
	
	add_child(_sub_viewport)

func execute_warmup() -> void:
	if materials_to_warmup.is_empty():
		warmup_completed.emit()
		return

	var container = Node3D.new()
	_sub_viewport.add_child(container)

	# ๋“ฑ๋ก๋œ ๋ชจ๋“  ๋จธํ‹ฐ๋ฆฌ์–ผ์„ ๋”๋ฏธ ๋ฉ”์‹œ์— ์ ์šฉํ•˜์—ฌ ๋ทฐํฌํŠธ์— ์ถ”๊ฐ€
	for i in range(materials_to_warmup.size()):
		var mat = materials_to_warmup[i]
		var mesh_instance = MeshInstance3D.new()
		
		# ๋ฉ”์‹œ ํƒ€์ž… ์ง€์ • (๊ธฐ๋ณธ๊ฐ’: QuadMesh)
		if i < mesh_to_use.size() and mesh_to_use[i] != null:
			mesh_instance.mesh = mesh_to_use[i]
		else:
			var quad = QuadMesh.new()
			mesh_instance.mesh = quad
			
		mesh_instance.material_override = mat
		mesh_instance.position = Vector3(0, 0, 0) # ์นด๋ฉ”๋ผ์ธ ์‹œ์•ผ ๋‚ด ๋ฐฐ์น˜
		container.add_child(mesh_instance)

	# ๋ Œ๋”๋ง ์ œ์ถœ ๊ฐ•์ œ ํŠธ๋ฆฌ๊ฑฐ
	_sub_viewport.render_target_update_mode = SubViewport.UPDATE_ONCE
	
	# RenderingServer์˜ ๋ Œ๋”๋ง ํ ์ฒ˜๋ฆฌ๋ฅผ ๊ธฐ๋‹ค๋ฆผ
	await RenderingServer.frame_post_draw
	
	# ์ •๋ฆฌ ์ž‘์—…
	container.queue_free()
	print("[ShaderWarmup] %d๊ฐœ ๋จธํ‹ฐ๋ฆฌ์–ผ ํŒŒ์ดํ”„๋ผ์ธ ์‚ฌ์ „ ์ปดํŒŒ์ผ ์™„๋ฃŒ" % materials_to_warmup.size())
	warmup_completed.emit()

ํŒŒํ‹ฐํด ์…ฐ์ด๋”(GPUParticles3D)์˜ ์›Œ๋ฐ์—… ํŠน์ˆ˜ ์ฒ˜๋ฆฌ

์ผ๋ฐ˜ ๋ฉ”์‰ฌ ์…ฐ์ด๋”์™€ ๋‹ฌ๋ฆฌ GPUParticles3D ๋ฐ GPUParticles2D๋Š” ์…ฐ์ด๋” ์ปดํŒŒ์ผ ์™ธ์—๋„ ํŒŒํ‹ฐํด ์‹œ๋ฎฌ๋ ˆ์ด์…˜ ์ƒํƒœ(Compute/Process Shader)๊ฐ€ ์ตœ์ดˆ ๋ฐœ์‚ฌ ์‹œ ์ปดํŒŒ์ผ๋ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ํŒŒํ‹ฐํด ๋จธํ‹ฐ๋ฆฌ์–ผ์˜ ๊ฒฝ์šฐ emitting = true ์ƒํƒœ๋กœ ์˜คํ”„์Šคํฌ๋ฆฐ ๋ทฐํฌํŠธ์— ์ตœ์†Œ 1ํ”„๋ ˆ์ž„ ์ด์ƒ ๋ Œ๋”๋ง์„ ์‹œ์ผœ์•ผ ํ•ฉ๋‹ˆ๋‹ค.

func warmup_particles(particle_scenes: Array[PackedScene]) -> void:
	var temp_node = Node3D.new()
	_sub_viewport.add_child(temp_node)
	
	for scene in particle_scenes:
		var p_instance = scene.instantiate()
		if p_instance is GPUParticles3D:
			temp_node.add_child(p_instance)
			p_instance.emitting = true
			p_instance.restart()
			
	_sub_viewport.render_target_update_mode = SubViewport.UPDATE_ONCE
	await RenderingServer.frame_post_draw
	temp_node.queue_free()

4. ์„ฑ๋Šฅ ์ตœ์ ํ™” ๊ธฐ๋ฒ• ๋ฐ ํ˜„์—… ์—ฃ์ง€ ์ผ€์ด์Šค ๋Œ€์‘

์…ฐ์ด๋” ์›Œ๋ฐ์—… ๊ธฐ๋ฒ•์„ ๋„์ž…ํ•  ๋•Œ ์ฃผ์˜ํ•ด์•ผ ํ•  ๊ณ ๊ธ‰ ๊ธฐ์ˆ  ์‚ฌ์–‘๊ณผ ์—ฃ์ง€ ์ผ€์ด์Šค๋“ค์„ ์ •๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

โ‘  ์ „์—ญ ๋จธํ‹ฐ๋ฆฌ์–ผ ์ˆ˜์ง‘ ์ž๋™ํ™” (Asset Pipeline Engine Extension)

ํ”„๋กœ์ ํŠธ ๋‚ด ์ˆ˜๋ฐฑ ๊ฐœ์˜ ๋จธํ‹ฐ๋ฆฌ์–ผ์„ ์†์œผ๋กœ ๋ฐฐ์—ด์— ํ• ๋‹นํ•˜๋Š” ๊ฒƒ์€ ์œ ์ง€๋ณด์ˆ˜ ์ธก๋ฉด์—์„œ ๋ถˆ๊ฐ€๋Šฅ์— ๊ฐ€๊น์Šต๋‹ˆ๋‹ค. EditorPlugin์„ ํ™•์žฅํ•˜๊ฑฐ๋‚˜ ๋นŒ๋“œ ๋‹จ๊ณ„์—์„œ ProjectSettings ๋ชฉ๋ก์„ ๋ž˜ํ•‘ํ•˜์—ฌ res:// ๋””๋ ‰ํ† ๋ฆฌ ๋‚ด๋ถ€์˜ ๋ชจ๋“  .tres, .material ๋ฆฌ์†Œ์Šค๋ฅผ ๊ฒ€์ƒ‰ํ•ด ์ž๋™์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šคํ™”ํ•˜๋Š” ์Šคํฌ๋ฆฝํŠธ๋ฅผ ๋นŒ๋“œ ํŒŒ์ดํ”„๋ผ์ธ์— ํฌํ•จํ•˜๋Š” ๊ฒƒ์ด ๊ถŒ์žฅ๋ฉ๋‹ˆ๋‹ค.

โ‘ก ๋™์  ์…ฐ์ด๋” ๋ถ„๊ธฐ(Dynamic Shader Branching) ๊ด€๋ฆฌ

#ifdef ์กฐ๊ฑด๋ถ€ ์ปดํŒŒ์ผ์ด๋‚˜ Godot Shader ์–ธ์–ด์˜ ๊ธฐ๋Šฅ ํ”Œ๋ž˜๊ทธ(Feature Flags)(์˜ˆ: render_mode use_depth_prepass, depth_draw_opaque)๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์…ฐ์ด๋”๋Š” ํ”Œ๋ž˜๊ทธ ์ƒํƒœ๊ฐ€ ๋ฐ”๋€” ๋•Œ๋งˆ๋‹ค ์„œ๋กœ ๋‹ค๋ฅธ PSO๋ฅผ ๋นŒ๋“œํ•ฉ๋‹ˆ๋‹ค. ์›Œ๋ฐ์—… ๋‹จ๊ณ„์—์„œ ์ด๋Ÿฌํ•œ ์˜ต์…˜ ์กฐํ•ฉ๋ณ„๋กœ ๋ Œ๋”๋ง ํ…Œ์ŠคํŠธ๋ฅผ ์„ ํ–‰ํ•ด์•ผ๋งŒ ๋Ÿฐํƒ€์ž„ ํ”„๋ ˆ์ž„ ๋“œ๋ž์„ ๋ง‰์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

โ‘ข ์•ˆ๋“œ๋กœ์ด๋“œ(Android) ๋ฐ ๋ชจ๋ฐ”์ผ Vulkan ๋“œ๋ผ์ด๋ฒ„์˜ ์บ์‹œ ํŠน์„ฑ

๋ชจ๋ฐ”์ผ GPU(Qualcomm Adreno, ARM Mali ๋“ฑ)๋Š” ํ…์Šค์ฒ˜ ํฌ๋งท(ASTC, ETC2)๊ณผ MSAA ์„ค์ • ๋ณ€๊ฒฝ์— ๋”ฐ๋ฅธ PSO ์ปดํŒŒ์ผ ๋น„์šฉ์ด PC ๊ทธ๋ž˜ํ”ฝ ์นด๋“œ๋ณด๋‹ค ํ›จ์”ฌ ํฝ๋‹ˆ๋‹ค. ๋ชจ๋ฐ”์ผ ํƒ€๊ฒŸํŒ… ํ”„๋กœ์ ํŠธ๋ผ๋ฉด rendering/vulkan/pipeline_cache/save_chunk_size ์˜ต์…˜์„ ์กฐ์ •ํ•˜๊ณ  ์•ฑ ๊ตฌ๋™ ์‹œ **์Šคํ”Œ๋ž˜์‹œ ์Šคํฌ๋ฆฐ(Splash Screen)**์„ ๋„์šด ์ƒํƒœ์—์„œ ๋ฐฑ๊ทธ๋ผ์šด๋“œ๋กœ ์…ฐ์ด๋” ์ปดํŒŒ์ผ ํŒจ์Šค๋ฅผ ์กฐ์šฉํžˆ ๊ตฌ๋™ํ•˜๋Š” ๊ตฌ์กฐ๋ฅผ ๊ตฌ์ถ•ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.



5. ๊ฒฐ๋ก  ๋ฐ ์š”์•ฝ

Godot 4 ๋ Œ๋”๋ง ์—”์ง„์˜ Vulkan ๋„์ž…์€ ๊ทธ๋ž˜ํ”ฝ์  ํ‘œํ˜„๋ ฅ๊ณผ ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋“œ ์„ฑ๋Šฅ์„ ๋Œ€ํญ ํ–ฅ์ƒ์‹œ์ผฐ์ง€๋งŒ, PSO ์ปดํŒŒ์ผ ์ง€์—ฐ์œผ๋กœ ์ธํ•œ ์‹ค์‹œ๊ฐ„ ํ”„๋ ˆ์ž„ ์Šคํ„ฐํ„ฐ๋ง์ด๋ผ๋Š” ๋ช…ํ™•ํ•œ ๊ณผ์ œ๋ฅผ ์•ˆ๊ฒจ์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค. ์ด ๋ฌธ์ œ๋ฅผ ๊ทผ๋ณธ์ ์œผ๋กœ ๊ทน๋ณตํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์•„๋ž˜ ์„ธ ๊ฐ€์ง€ ์š”์†Œ๋ฅผ ๋ฐ˜๋“œ์‹œ ์ ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

  • ๋””์Šคํฌ ์…ฐ์ด๋” ์บ์‹ฑ ํ™œ์„ฑํ™”: Project Settings์—์„œ ์…ฐ์ด๋” ์บ์‹œ ์˜ต์…˜ ๊ฒ€์ฆ ๋ฐ ์ ์šฉ.
  • ์˜คํ”„์Šคํฌ๋ฆฐ ๋ทฐํฌํŠธ ์›Œ๋ฐ์—… ์‹œ์Šคํ…œ ๋„์ž…: ๋กœ๋”ฉ ์”ฌ์—์„œ ๋ฉ”์‰ฌ, ๋จธํ‹ฐ๋ฆฌ์–ผ, ํ…์Šค์ฒ˜, GPUParticles3D๋ฅผ ์ตœ์†Œ 1ํ”„๋ ˆ์ž„ ์‚ฌ์ „ ์ œ์ถœ(Pre-pass Submit).
  • ๋™์  ๋จธํ‹ฐ๋ฆฌ์–ผ ์ƒํƒœ ๊ด€๋ฆฌ: ๋Ÿฐํƒ€์ž„์— ๋ฐœ์ƒ ๊ฐ€๋Šฅํ•œ ์…ฐ์ด๋” ๋ณ€์ข… ์กฐํ•ฉ์„ ์ตœ์†Œํ™”ํ•˜๊ณ , ์ดˆ๊ธฐํ™” ์‹œ์ ์— ๋ Œ๋” ํŒŒ์ดํ”„๋ผ์ธ ์ƒ์„ฑ ์™„๋ฃŒ.

์œ„ ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ… ํŒจํ„ด์„ ํ”„๋กœ์ ํŠธ ์•„ํ‚คํ…์ฒ˜์— ๋‚ด์žฌํ™”ํ•˜๋ฉด, ๊ณ ๋„ํ™”๋œ 3D ์”ฌ๊ณผ ํ™”๋ คํ•œ VFX ์ดํŽ™ํŠธ ์†์—์„œ๋„ 60 FPS ์ด์ƒ์˜ ๋งค๋„๋Ÿฝ๊ณ  ์ผ์ •ํ•œ ๋ Œ๋”๋ง ์„ฑ๋Šฅ์„ ํ™•๋ณดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ฐ˜์‘ํ˜•