Camera - kuimoani/defold GitHub Wiki

Cameras

Defold๋Š” ๊ธฐ๋ณธ์ ์ธ ์นด๋ฉ”๋ผ ์ปดํฌ๋„ŒํŠธ(primitive camera component)๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๋ฉ”๋‰ด์–ผ์€ ์ด ์ปดํฌ๋„ŒํŠธ์˜ ๊ธฐ๋Šฅ๊ณผ ์šฉ๋„์— ๋Œ€ํ•˜์—ฌ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.

์นด๋ฉ”๋ผ๋Š” ๊ฒŒ์ž„ ์›”๋“œ์˜ ๋ทฐ(view)๋ฅผ ์ œ๊ณตํ•˜๋Š”๋ฐ ์‚ฌ์šฉํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ์ž…๋‹ˆ๋‹ค. ์นด๋ฉ”๋ผ๋Š” ์•„๋ž˜ ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰ํ•˜๋Š” ์•„์ฃผ ๊ฐ„๋‹จํ•œ ์˜ค๋ธŒ์ ํŠธ์ž…๋‹ˆ๋‹ค.

  1. 2D๋‚˜ 3D ๊ณต๊ฐ„์— ์œ„์น˜ํ•จ
  2. ๊ฒŒ์ž„ ์˜ค๋ธŒ์ ํŠธ๋ฅผ ์ด ๊ณต๊ฐ„์—์„œ ์›€์ง์ผ ์ˆ˜ ์žˆ์Œ
  3. ํˆฌ์˜ ๋งคํŠธ๋ฆญ์Šค(projection matrix)์™€ ๋ทฐ(computed view)๋ฅผ ๊ณ„์‚ฐํ•ด์„œ ๋ Œ๋”๋ง์— ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ Œ๋” ์Šคํฌ๋ฆฝํŠธ์— ์ œ๊ณตํ•จ

OpenGL์˜ ์นด๋ฉ”๋ผ๋Š” viewer, eye, position, near or far clipping plane ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ขŒํ‘œ ์‹œ์Šคํ…œ์œผ๋กœ ํ‘œํ˜„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. near clipping plane์€ viewing plane ํ˜น์€ ํ™”๋ฉด(screen)๊ณผ ๋™์ผํ•ฉ๋‹ˆ๋‹ค.

Camera planes

๋ณดํ†ต 3D์นด๋ฉ”๋ผ๋Š” viewing volume, frustum, cut off pyramid ๊ณผ ๊ฐ™์€ ๋ชจ์–‘(shape)์„ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋“ค์˜ ํšจ๊ณผ๋กœ๋Š” ์นด๋ฉ”๋ผ๋กœ๋ถ€ํ„ฐ ๋ฉ€๋ฆฌ ์žˆ๋Š” ์˜ค๋ธŒ์ ํŠธ๋ฅผ ์ž‘๊ฒŒ ๋ Œ๋”๋ง ํ•ด์„œ, ์‹œ๊ฐ(perspective)์ ์œผ๋กœ ํ˜„์‹ค๊ฐ ์žˆ๊ฒŒ ๋ณด์ด๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค. ์‹œ์•ผ๊ฐ(field of view)์ด ๋„“์„ ์ˆ˜๋ก, ์นด๋ฉ”๋ผ๋กœ ๋” ๋„“์€ ํ’๊ฒฝ์„ ๋ณผ ์ˆ˜ ์žˆ๊ณ , ๊ฐ€๊น๊ณ  ๋จผ ์˜ค๋ธŒ์ ํŠธ๋“ค์„ ๋”์šฑ ๋“œ๋ผ๋งˆํ‹ฑํ•˜๊ฒŒ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.

Camera field of view

Creating a camera

์นด๋ฉ”๋ผ๋ฅผ ์ƒ์„ฑํ•˜๋ ค๋ฉด, ๊ฒŒ์ž„ ์˜ค๋ธŒ์ ํŠธ์— Camera component๋ฅผ ์ถ”๊ฐ€ํ•˜์„ธ์š”.

Create camera component

์นด๋ฉ”๋ผ ์ปดํฌ๋„ŒํŠธ๋Š” ์นด๋ฉ”๋ผ์˜ frustum(์ ˆ๋‘์ฒด)๋ฅผ ์ •์˜ํ•˜๋Š” ํ”„๋กœํผํ‹ฐ์˜ ๋ชจ์Œ์„ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

Camera properties

ํ˜„์žฌ ๊ธฐ๋ณธ FOV ๊ฐ’์€ ์˜คํ•ด์˜ ์†Œ์ง€๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ๋„(degrees)๊ฐ€ ์•„๋‹ˆ๋ผ ๋ผ๋””์•ˆ(radians)์œผ๋กœ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค. 45 degree FOV๋ฅผ ํ•˜๋ ค๋ฉด, 0.785 (PI / 4) ๊ฐ’์œผ๋กœ ๋ณ€๊ฒฝํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

aspect_ratio

์ข…ํšก๋น„(aspect_ratio)๋Š” frustum์˜ ๋„“์ด์™€ ๋†’์ด ์‚ฌ์ด์˜ ๋น„์œจ(ratio)์ž…๋‹ˆ๋‹ค. 1.0์€ ์ด์ฐจ ๋ทฐ(quadratic view)๋กœ ๊ฐ€์ •ํ•œ๋‹ค๋Š”๊ฑธ ์˜๋ฏธํ•˜๋ฉฐ, 1.33์€ 1024x768 ๊ฐ™์€ 4:3 ๋น„์œจ ํ™”๋ฉด์—์„œ ์ž˜ ๋‚˜์˜ค๊ณ . 1.78์€ 16:9 ๋น„์œจ์—์„œ ์ž˜ ๋‚˜์˜ต๋‹ˆ๋‹ค.

fov

๋ผ๋””์•ˆ(radians)์œผ๋กœ ํ‘œํ˜„๋˜๋Š” ์นด๋ฉ”๋ผ์˜ ์‹œ์•ผ๊ฐ(field of view)

near_z

near clipping plane์˜ Z-value

far_z

far clipping plane์˜ Z-value

auto_aspect_ratio

์ด ๊ฐ’์„ 1๋กœ ์„ค์ •ํ•˜๋ฉด, ์นด๋ฉ”๋ผ๋Š” ๊ฒŒ์ž„ ํ™”๋ฉด ์„ค์ •์„ ๊ธฐ๋ฐ˜์œผ๋กœํ•ด์„œ ์ž๋™์œผ๋กœ ์ข…ํšก๋น„(aspect ratio)๋ฅผ ์…‹ํŒ…ํ•ฉ๋‹ˆ๋‹ค.

Camera focus

์นด๋ฉ”๋ผ๋ฅผ ํ™œ์„ฑํ™”ํ•˜๊ณ  view์™€ projection์˜ matrix๋ฅผ ๋ณด๋‚ด๋ ค๋ฉด, ์นด๋ฉ”๋ผ ์ปดํฌ๋„ŒํŠธ์—๊ฒŒ acquire_camera_focus ๋ฉ”์„ธ์ง€๋ฅผ ๋ณด๋‚ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

msg.post("#camera", "acquire_camera_focus")

์นด๋ฉ”๋ผ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์นด๋ฉ”๋ผ ํฌ์ปค์Šค๋ฅผ ํš๋“ํ•˜๋ฉด, ๋ Œ๋” ์Šคํฌ๋ฆฝํŠธ ๊ฐ™์€ ๊ณณ์˜ ๋งค ํ”„๋ ˆ์ž„ ๋งˆ๋‹ค set_view_projection ๋ฉ”์„ธ์ง€๋ฅผ @render ์†Œ์ผ“์œผ๋กœ ๋ณด๋ƒ…๋‹ˆ๋‹ค.

-- example.render_script
--
function update(self)
    ...
    render.set_view(self.view)

    render.set_projection(self.projection)
    ...
end

function on_message(self, message_id, message)
    if message_id == hash("set_view_projection") then
    	-- ์นด๋ฉ”๋ผ์˜ view์™€ projection์ด ์—ฌ๊ธฐ ๋„์ฐฉํ•จ. ์ €์žฅํ•ด ๋‘์ž.
        self.view = message.view
        self.projection = message.projection
    end
end

๋งŒ์•ฝ ๋ Œ๋” ์Šคํฌ๋ฆฝํŠธ์˜ ์นด๋ฉ”๋ผ view์™€ projection๋ฅผ ๋‘˜ ๋‹ค ์‚ฌ์šฉํ•˜๋ฉด, ๊ฒŒ์ž„ ์ปจํ…์ธ ๊ฐ€ ์™„์ „ํžˆ 2D ์ž„์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ  3D perspective๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฒŒ์ž„์›”๋“œ์˜ ์นด๋ฉ”๋ผ view๋ฅผ ์–ป๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ๋•Œ๋•Œ๋กœ ์œ ์šฉ ํ•˜๊ธฐ๋„ ํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ์นด๋ฉ”๋ผ๋ฅผ ๋’ค๋กœ ์›€์ง์—ฌ์„œ ๋ ˆ๋ฒจ์˜ ์ „์ฒด ๋ชจ์Šต์„ ๋” ๋“œ๋Ÿฌ๋‚˜๊ฒŒ ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์•„๋ž˜๋Š” ํ˜„์žฌ ์นด๋ฉ”๋ผ์˜ ์ด๋™ ์†๋„๋ฅผ ์ธก์ •ํ•˜๊ณ  ์ด ์†๋„์— ๋งž์ถฐ ์นด๋ฉ”๋ผ๋ฅผ ์•ž๋’ค๋กœ ๋•ก๊ธฐ๋Š” ๊ฐ„๋‹จํ•œ ์Šคํฌ๋ฆฝํŠธ์ž…๋‹ˆ๋‹ค.

-- camera.script
--
function init(self)
	msg.post("#camera", "acquire_camera_focus")

    -- ํ˜„์žฌ ์œ„์น˜๋ฅผ ์ €์žฅํ•˜๊ณ  "look at" ๋ฉ”์„ธ์ง€๋กœ ๋ฐ›์„ ์œ„์น˜๊ฐ’์„ ๋ฐ›์„ ๋ณ€์ˆ˜ ์…‹ํŒ…
	self.pos = go.get_world_position()
	self.look_at = self.pos
    -- ์ธก์ • ์†๋„ ์ €์žฅํ•˜๊ธฐ
	self.speed = 0
end

function update(self, dt)
	-- ํ˜„์žฌ ์œ„์น˜์™€ ๋ชฉ์ ์ง€ ์œ„์น˜๋ฅผ ๋ณด๊ฐ„ํ•œ ๊ฐ’์„ ๊ธฐ์ค€์œผ๋กœ ์ƒˆ ์œ„์น˜๋ฅผ ๊ณ„์‚ฐํ•จ
	self.pos = vmath.lerp(0.03, self.pos, self.look_at)

	-- 2D ํ‰๋ฉด(2D plane)์—์„œ ์†๋„ ์ธก์ •ํ•˜๊ธฐ (Z ๋Š” 0)
	local v1 = go.get_world_position()
	v1.z = 0
	local v2 = self.pos
	v2.z = 0
	local speed = vmath.length(v2 - v1)

	-- ํ”Œ๋ ˆ์ด์–ด์˜ ์ด๋™ ์†๋„์— ๋”ฐ๋ผ์„œ ์นด๋ฉ”๋ผ๋ฅผ ๋’ค๋กœ ๋•ก๊ธฐ๋“ ๊ฐ€ ์•ž์œผ๋กœ ๋ฐ€๋“ ๊ฐ€ ํ•˜๊ธฐ
	self.pos.z = 500 + speed * speed * 10
	go.set_position(self.pos)
end

function on_message(self, message_id, message, sender)
    -- ์ด ์นด๋ฉ”๋ผ๋Š” ์–ด๋А ์œ„์น˜๋กœ ์ด๋™ํ•˜๋“  "look_at" ๋ฉ”์„ธ์ง€์— ๋ฐ˜์‘ํ•จ
	if message_id == hash("look_at") then
		self.look_at = message.position
	end
end

Camera speed distance

๋ฌผ๋ก , ์šฐ๋ฆฌ๋Š” ์นด๋ฉ”๋ผ๋ฅผ ์›€์ง์ด๋Š”๋ฐ ์ œ์•ฝ์„ ๋‘๊ณ  ์žˆ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ X,Y,Z axis ์ถ•์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํšŒ์ „ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

-- 0.314 ๋ผ๋””์•ˆ(radians)์€ ๋Œ€๋žต 18๋„(degrees)์ž„...
go.set_rotation(vmath.quat_rotation_z(0.314) * vmath.quat_rotation_y(0.314))

Rotated camera

Orthographic projection

๋งŽ์€ 2D ๊ฒŒ์ž„์—์„œ, ์•ž ๋’ค๋กœ ์›€์ง์ด๋Š” ์นด๋ฉ”๋ผ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒŒ์ž„์ด ํ”ฝ์…€ ํผํŽ™ํŠธ(pixel perfect)๋กœ ๋ Œ๋”๋ง ํ•˜๋ ค๋Š” ์ปจํ…์ธ ๊ฐ€ ์žˆ๋‹ค๋ฉด ๋ฌธ์ œ๊ฐ€ ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์นด๋ฉ”๋ผ๋ฅผ ์™„๋ฒฝํ•œ Z ๊ฑฐ๋ฆฌ(perfect Z distance)์— ๋ฐฐ์น˜ํ•ด์„œ ์›ํ•˜๋Š” ์‹œ์•ผ(view)๋ฅผ ์–ป๋Š” ๋Œ€์‹ , ์ง๊ต ํˆฌ์˜(orthographic projection)์œผ๋กœ ์นด๋ฉ”๋ผ๋ฅผ ์„ค์ •ํ•˜์—ฌ ๋Œ€์‹ ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. ์ฆ‰, ์นด๋ฉ”๋ผ์˜ ์‹œ์•ผ(view)๊ฐ€ ๋” ์ด์ƒ ์ ˆ๋‘์ฒด(frustum)์— ์˜ํ•ด ์ขŒ์šฐ๋˜์ง€ ์•Š๊ณ , ๋”์šฑ ๊ฐ„๋‹จํ•œ ์ƒ์ž(box)์— ์˜ํ•ด ์ขŒ์šฐ๋œ๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

Orthographic projection

์ง๊ต ํˆฌ์˜(Orthographic projection)์€ ๊ฑฐ๋ฆฌ์— ๋”ฐ๋ผ ์˜ค๋ธŒ์ ํŠธ์˜ ํฌ๊ธฐ๋ฅผ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ ๋น„ํ˜„์‹ค์ ์œผ๋กœ ๋ณด์ด๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ ํ•œ ์‚ฌ๋žŒ์ด ์นด๋ฉ”๋ผ 10000 ๋ฏธํ„ฐ ๋ฉ€๋ฆฌ ์„œ ์žˆ์–ด๋„ ์นด๋ฉ”๋ผ ๋ฐ”๋กœ ์•ž์— ์„œ ์žˆ๋Š” ์‚ฌ๋žŒ๊ณผ ๋™์ผํ•œ ํฌ๊ธฐ๋กœ ๋ Œ๋”๋ง ๋ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ, ์ด๋Ÿฌํ•œ ๊ทธ๋ž˜ํ”ฝ ํˆฌ์˜ ๋ฐฉ๋ฒ•์€ ๋•Œ๋กœ ์œ ์šฉํ•˜๋ฉฐ 2D ๊ฒŒ์ž„์—์„œ ์ž์ฃผ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ์ง๊ต ํˆฌ์˜(orthographic projection)์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ๋ Œ๋” ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ˆ˜์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

-- example.render_script
--
function update(self)
    ...
    render.set_view(self.view)
    -- ๊ฒŒ์ž„์ฐฝ์˜ ๋„“์ด์™€ ๋†’์ด๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์ง๊ต ํˆฌ์˜(orthographic projection) ์„ค์ •ํ•˜๊ธฐ
	local w = render.get_width()
	local h = render.get_height()
	render.set_projection(vmath.matrix4_orthographic(- w / 2, w / 2, -h / 2, h / 2, -1000, 1000))
    ...
end

function on_message(self, message_id, message)
    if message_id == hash("set_view_projection") then
    	-- ์นด๋ฉ”๋ผ view์™€ projection์ด ์—ฌ๊ธฐ ๋„์ฐฉํ•จ. ์šฐ๋ฆฐ view๋งŒ ์žˆ์œผ๋ฉด ๋จ
        self.view = message.view
    end
end

์œ„์˜ ์˜ˆ์ œ๋Š” ํ™”๋ฉด์„ ์นด๋ฉ”๋ผ ์œ„์น˜์˜ ์ค‘์‹ฌ์— ๋งž์ถ”๋Š” ๊ฒƒ์„ ์ œ์™ธํ•˜๊ณ ๋Š” ๊ธฐ๋ณธ ๋ Œ๋” ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ํ•˜๋Š” ๊ฒƒ๊ณผ ๊ฑฐ์˜ ๊ฐ™์Šต๋‹ˆ๋‹ค.

(๋ช‡๋ช‡ ๊ทธ๋ž˜ํ”ฝ ์—์…‹์€ Kenney: http://kenney.nl/assets ์— ์˜ํ•ด ์ œ์ž‘๋˜์—ˆ์Šต๋‹ˆ๋‹ค.)