Camera - kuimoani/defold GitHub Wiki
Cameras
Defold๋ ๊ธฐ๋ณธ์ ์ธ ์นด๋ฉ๋ผ ์ปดํฌ๋ํธ(primitive camera component)๋ฅผ ๊ฐ์ง๊ณ ์์ต๋๋ค. ์ด ๋ฉ๋ด์ผ์ ์ด ์ปดํฌ๋ํธ์ ๊ธฐ๋ฅ๊ณผ ์ฉ๋์ ๋ํ์ฌ ์ค๋ช ํฉ๋๋ค.
์นด๋ฉ๋ผ๋ ๊ฒ์ ์๋์ ๋ทฐ(view)๋ฅผ ์ ๊ณตํ๋๋ฐ ์ฌ์ฉํ๋ ์ปดํฌ๋ํธ์ ๋๋ค. ์นด๋ฉ๋ผ๋ ์๋ ๊ธฐ๋ฅ์ ์ํํ๋ ์์ฃผ ๊ฐ๋จํ ์ค๋ธ์ ํธ์ ๋๋ค.
- 2D๋ 3D ๊ณต๊ฐ์ ์์นํจ
- ๊ฒ์ ์ค๋ธ์ ํธ๋ฅผ ์ด ๊ณต๊ฐ์์ ์์ง์ผ ์ ์์
- ํฌ์ ๋งคํธ๋ฆญ์ค(projection matrix)์ ๋ทฐ(computed view)๋ฅผ ๊ณ์ฐํด์ ๋ ๋๋ง์ ํ์ํ ๋ฐ์ดํฐ๋ฅผ ๋ ๋ ์คํฌ๋ฆฝํธ์ ์ ๊ณตํจ
OpenGL์ ์นด๋ฉ๋ผ๋ viewer, eye, position, near or far clipping plane ๋ฅผ ์ฌ์ฉํ์ฌ ์ขํ ์์คํ ์ผ๋ก ํํ ํ ์ ์์ต๋๋ค. near clipping plane์ viewing plane ํน์ ํ๋ฉด(screen)๊ณผ ๋์ผํฉ๋๋ค.

๋ณดํต 3D์นด๋ฉ๋ผ๋ viewing volume, frustum, cut off pyramid ๊ณผ ๊ฐ์ ๋ชจ์(shape)์ ๊ฐ์ง๊ณ ์์ต๋๋ค. ์ด๋ค์ ํจ๊ณผ๋ก๋ ์นด๋ฉ๋ผ๋ก๋ถํฐ ๋ฉ๋ฆฌ ์๋ ์ค๋ธ์ ํธ๋ฅผ ์๊ฒ ๋ ๋๋ง ํด์, ์๊ฐ(perspective)์ ์ผ๋ก ํ์ค๊ฐ ์๊ฒ ๋ณด์ด๊ฒ ํฉ๋๋ค. ์์ผ๊ฐ(field of view)์ด ๋์ ์๋ก, ์นด๋ฉ๋ผ๋ก ๋ ๋์ ํ๊ฒฝ์ ๋ณผ ์ ์๊ณ , ๊ฐ๊น๊ณ ๋จผ ์ค๋ธ์ ํธ๋ค์ ๋์ฑ ๋๋ผ๋งํฑํ๊ฒ ๋ณด์ฌ์ค๋๋ค.

Creating a camera
์นด๋ฉ๋ผ๋ฅผ ์์ฑํ๋ ค๋ฉด, ๊ฒ์ ์ค๋ธ์ ํธ์ Camera component๋ฅผ ์ถ๊ฐํ์ธ์.

์นด๋ฉ๋ผ ์ปดํฌ๋ํธ๋ ์นด๋ฉ๋ผ์ frustum(์ ๋์ฒด)๋ฅผ ์ ์ํ๋ ํ๋กํผํฐ์ ๋ชจ์์ ๊ฐ์ง๊ณ ์์ต๋๋ค.

ํ์ฌ ๊ธฐ๋ณธ 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

๋ฌผ๋ก , ์ฐ๋ฆฌ๋ ์นด๋ฉ๋ผ๋ฅผ ์์ง์ด๋๋ฐ ์ ์ฝ์ ๋๊ณ ์์ง ์์ต๋๋ค. ๋ํ X,Y,Z axis ์ถ์ ๊ธฐ๋ฐ์ผ๋ก ํ์ ํ ์๋ ์์ต๋๋ค.
-- 0.314 ๋ผ๋์(radians)์ ๋๋ต 18๋(degrees)์...
go.set_rotation(vmath.quat_rotation_z(0.314) * vmath.quat_rotation_y(0.314))

Orthographic projection
๋ง์ 2D ๊ฒ์์์, ์ ๋ค๋ก ์์ง์ด๋ ์นด๋ฉ๋ผ๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์์ด ํฝ์ ํผํํธ(pixel perfect)๋ก ๋ ๋๋ง ํ๋ ค๋ ์ปจํ ์ธ ๊ฐ ์๋ค๋ฉด ๋ฌธ์ ๊ฐ ๋ ์ ์์ต๋๋ค. ์นด๋ฉ๋ผ๋ฅผ ์๋ฒฝํ Z ๊ฑฐ๋ฆฌ(perfect Z distance)์ ๋ฐฐ์นํด์ ์ํ๋ ์์ผ(view)๋ฅผ ์ป๋ ๋์ , ์ง๊ต ํฌ์(orthographic projection)์ผ๋ก ์นด๋ฉ๋ผ๋ฅผ ์ค์ ํ์ฌ ๋์ ํ๋ ๊ฒ์ด ์ข์ต๋๋ค. ์ฆ, ์นด๋ฉ๋ผ์ ์์ผ(view)๊ฐ ๋ ์ด์ ์ ๋์ฒด(frustum)์ ์ํด ์ข์ฐ๋์ง ์๊ณ , ๋์ฑ ๊ฐ๋จํ ์์(box)์ ์ํด ์ข์ฐ๋๋ค๋ ๊ฒ์ ์๋ฏธํฉ๋๋ค.

์ง๊ต ํฌ์(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 ์ ์ํด ์ ์๋์์ต๋๋ค.)