728x90

์›๋ณธ : https://ogldev.org/www/tutorial38/tutorial38.html

๋ฐฐ๊ฒฝ

๋งˆ์นจ๋‚ด ์ˆ˜๋ฐฑ๋งŒ๋ช…์˜ ๋…์ž๋“ค์ด ์š”๊ตฌํ•ด์˜จ ํŠœํ† ๋ฆฌ์–ผ. ์Šคํ‚ค๋‹์œผ๋กœ๋„ ์•Œ๋ ค์ง„ ๊ณจ๊ฒฉ ์• ๋‹ˆ๋ฉ”์ด์…˜์œผ๋กœ, Assimp ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

 

๊ณจ๊ฒฉ ์• ๋‹ˆ๋ฉ”์ด์…˜์€ ์‚ฌ์‹ค ๋‘ ๋ถ€๋ถ„์œผ๋กœ ์ด๋ฃจ์–ด์ง„ ๊ณผ์ •์ž…๋‹ˆ๋‹ค.

์ฒซ ๋ฒˆ์งธ๋Š” ์•„ํ‹ฐ์ŠคํŠธ๊ฐ€ ์‹คํ–‰ํ•˜๊ณ  ๋‘ ๋ฒˆ์งธ๋Š” ํ”„๋กœ๊ทธ๋ž˜๋จธ์ธ ์‚ฌ์šฉ์ž(๋˜๋Š” ์ž‘์„ฑํ•œ ์—”์ง„)๊ฐ€ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

์ฒซ ๋ฒˆ์งธ ๋ถ€๋ถ„์€ ๋ชจ๋ธ๋ง ์†Œํ”„ํŠธ์›จ์–ด ๋‚ด๋ถ€์—์„œ ๋ฐœ์ƒํ•˜๋ฉฐ ๋ฆฌ๊น…์ด๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์„œ ์ผ์–ด๋‚˜๋Š” ์ผ์€ ์•„ํ‹ฐ์ŠคํŠธ๊ฐ€ mesh ์•„๋ž˜ ๋ผˆ(skeleton)์˜ ๊ณจ๊ฒฉ(bones)์„ ์ •์˜ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

mesh๋Š” ๋ฌผ์ฒด์˜ ํ”ผ๋ถ€(์ธ๊ฐ„์ด๋“  ๊ดด๋ฌผ์ด๋“  ๋ฌด์—‡์ด๋“ )๋ฅผ ๋‚˜ํƒ€๋‚ด๋ฉฐ skeleton์€ ์‹ค์ œ ์„ธ๊ณ„์—์„œ ์‹ค์ œ ์›€์ง์ž„์„ ๋ชจ๋ฐฉํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ mesh๋ฅผ ์›€์ง์ด๋Š”๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

์ด ์ž‘์—…์€ ๊ฐ ์ •์ ์„ ํ•˜๋‚˜ ์ด์ƒ์˜ ๋ผˆ์— ํ• ๋‹นํ•˜์—ฌ ์ˆ˜ํ–‰๋ฉ๋‹ˆ๋‹ค.

์ •์ ์ด ๋ผˆ์— ํ• ๋‹น๋˜๋ฉด ๋ผˆ๊ฐ€ ์ด๋™ํ•  ๋•Œ ์ •์ ์— ๋ฏธ์น˜๋Š” ์˜ํ–ฅ์˜ ์–‘์„ ๊ฒฐ์ •ํ•˜๋Š” ๊ฐ€์ค‘์น˜๊ฐ€ ์ •์˜๋ฉ๋‹ˆ๋‹ค.

์ผ๋ฐ˜์ ์ธ ๋ฐฉ๋ฒ•์€ ๋ชจ๋“  ๊ฐ€์ค‘์น˜์˜ ํ•ฉ์„ (์ •์ ๋‹น) 1๋กœ ๋งŒ๋“œ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด, ๊ผญ์ง“์ ์ด ๋‘ ๋ผˆ ์‚ฌ์ด์— ์ •ํ™•ํžˆ ์œ„์น˜ํ•œ๋‹ค๋ฉด ์šฐ๋ฆฌ๋Š” ๋ผˆ๊ฐ€ ๊ผญ์ง“์ ์— ๋ฏธ์น˜๋Š” ์˜ํ–ฅ์ด ๊ฐ™์„ ๊ฒƒ์œผ๋กœ ์˜ˆ์ƒํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ ๋ผˆ์— 0.5์˜ ๊ฐ€์ค‘์น˜๋ฅผ ํ• ๋‹นํ•˜๊ณ  ์‹ถ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๊ผญ์ง“์ ์ด ์™„์ „ํžˆ ๋‹จ์ผ ๋ผˆ์˜ ์˜ํ–ฅ ๋ฒ”์œ„ ๋‚ด์— ์žˆ๋Š” ๊ฒฝ์šฐ ๊ฐ€์ค‘์น˜๋Š” 1์ด ๋ฉ๋‹ˆ๋‹ค(๋ผˆ๊ฐ€ ์ •์ ์˜ ์›€์ง์ž„์„ ์ž์œจ์ ์œผ๋กœ ์ œ์–ดํ•จ).

 

๋ธ”๋ Œ๋”์—์„œ ์ƒ์„ฑ๋œ ๊ณจ๊ฒฉ ๊ตฌ์กฐ์˜ ์˜ˆ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค:

 

์šฐ๋ฆฌ๊ฐ€ ์œ„์—์„œ ๋ณธ ๊ฒƒ์€ ์‚ฌ์‹ค ์• ๋‹ˆ๋ฉ”์ด์…˜์˜ ์ค‘์š”ํ•œ ๋ถ€๋ถ„์ž…๋‹ˆ๋‹ค. ์•„ํ‹ฐ์ŠคํŠธ๋Š” ๊ณจ๊ฒฉ ๊ตฌ์กฐ๋ฅผ ์—ฐ๊ฒฐํ•˜๊ณ  ๊ฐ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์œ ํ˜•("walk","run","die" ๋“ฑ)์— ๋Œ€ํ•œ ํ‚ค ํ”„๋ ˆ์ž„ ์„ธํŠธ๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.

ํ‚ค ํ”„๋ ˆ์ž„์—๋Š” ์• ๋‹ˆ๋ฉ”์ด์…˜ ๊ฒฝ๋กœ๋ฅผ ๋”ฐ๋ผ ์ž„๊ณ„์ ์— ์žˆ๋Š” ๋ชจ๋“  ๋ผˆ์˜ ๋ณ€ํ™˜์ด ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋ž˜ํ”ฝ ์—”์ง„์€ ํ‚ค ํ”„๋ ˆ์ž„์˜ ๋ณ€ํ™˜ ์‚ฌ์ด๋ฅผ ๋ณด๊ฐ„ํ•˜์—ฌ ํ‚ค ํ”„๋ ˆ์ž„ ๊ฐ„์˜ ๋ถ€๋“œ๋Ÿฌ์šด ์›€์ง์ž„์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

 

๊ณจ๊ฒฉ ์• ๋‹ˆ๋ฉ”์ด์…˜์— ์‚ฌ์šฉ๋˜๋Š” ๊ณจ๊ฒฉ ๊ตฌ์กฐ๋Š” ์ข…์ข… ๊ณ„์ธต ๊ตฌ์กฐ์ž…๋‹ˆ๋‹ค.

์ด๊ฒƒ์€ skeleton์ด ์ž์‹/๋ถ€๋ชจ ๊ด€๊ณ„๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์œผ๋ฏ€๋กœ skeleton tree๊ฐ€ ์ƒ์„ฑ๋œ๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

root bone์„ ์ œ์™ธํ•œ ๋ชจ๋“  ๋ผˆ์—๋Š” ๋ถ€๋ชจ๊ฐ€ ํ•˜๋‚˜์”ฉ ์žˆ์Šต๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด, ์ธ์ฒด์˜ ๊ฒฝ์šฐ ํŒ”, ๋‹ค๋ฆฌ, ์†๊ฐ€๋ฝ ๋ผˆ์™€ ๊ฐ™์€ ์ž์‹ ๋ผˆ๋ฅผ ๋‹ค์Œ ๋‹จ๊ณ„๋กœ ํ•˜์—ฌ ๋“ฑ๋ผˆ๋ฅผ root๋กœ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ถ€๋ชจ์˜ ๋ผˆ๊ฐ€ ์›€์ง์ด๋ฉด ๋ชจ๋“  ์ž์‹ ๋ผˆ๋“ค๋„ ์›€์ง์ด์ง€๋งŒ, ์ž์‹์˜ ๋ผˆ๊ฐ€ ์›€์ง์ด๋ฉด ๋ถ€๋ชจ๋Š” ์›€์ง์ด์ง€ ์•Š์Šต๋‹ˆ๋‹ค(์šฐ๋ฆฌ์˜ ์†๊ฐ€๋ฝ์€ ์†์„ ์›€์ง์ด์ง€ ์•Š๊ณ ๋„ ์›€์ง์ผ ์ˆ˜ ์žˆ์ง€๋งŒ, ์†์ด ์›€์ง์ด๋ฉด ๋ชจ๋“  ์†๊ฐ€๋ฝ์ด ์›€์ง์ž…๋‹ˆ๋‹ค). ์‹ค์งˆ์ ์ธ ๊ด€์ ์—์„œ ์ด๊ฒƒ์€ ์šฐ๋ฆฌ๊ฐ€ ๋ผˆ์˜ ๋ณ€ํ˜•์„ ์ฒ˜๋ฆฌํ•  ๋•Œ ๊ทธ๊ฒƒ์„ ๊ทธ๊ฒƒ์œผ๋กœ๋ถ€ํ„ฐ root๋กœ ์ด๋„๋Š” ๋ชจ๋“  ๋ถ€๋ชจ ๋ผˆ์˜ ๋ณ€ํ˜•๊ณผ ๊ฒฐํ•ฉํ•  ํ•„์š”๊ฐ€ ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

 

๋” ์ด์ƒ ์กฐ์ž‘์— ๋Œ€ํ•ด ๋…ผ์˜ํ•˜์ง€ ์•Š์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ์€ ๊ทธ๋ž˜ํ”ฝ ํ”„๋กœ๊ทธ๋ž˜๋จธ์˜ ์˜์—ญ ๋ฐ–์— ์žˆ๋Š” ๋ณต์žกํ•œ ์ฃผ์ œ์ž…๋‹ˆ๋‹ค. ๋ชจ๋ธ๋ง ์†Œํ”„ํŠธ์›จ์–ด๋Š” ์˜ˆ์ˆ ๊ฐ€๊ฐ€ ์ด ์ผ์„ ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ฃผ๋Š” ๊ณ ๊ธ‰ ๋„๊ตฌ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์œผ๋ฉฐ ๋‹น์‹ ์€ ๋ฉ‹์ง‘ mesh์™€ skeleton์„ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด ํ›Œ๋ฅญํ•œ ์˜ˆ์ˆ ๊ฐ€๊ฐ€ ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. skeletal ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด ๊ทธ๋ž˜ํ”ฝ ์—”์ง„์ด ๋ฌด์—‡์„ ํ•ด์•ผ ํ•˜๋Š”์ง€ ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

 

์ฒซ ๋ฒˆ์žฌ ๋‹จ๊ณ„๋Š” ์ •์  ๋ฒ„ํผ๋ฅผ ์ •์ ๋ณ„ ๊ณจ๊ฒฉ ์ •๋ณด๋กœ ๋ณด๊ฐ•ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋ช‡ ๊ฐ€์ง€ ์˜ต์…˜์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ์šฐ๋ฆฌ๊ฐ€ ํ•˜๋ ค๋Š” ๊ฒƒ์€ ๋งค์šฐ ๊ฐ„๋‹จํ•ฉ๋‹ˆ๋‹ค.

๊ฐ ๊ผญ์ง“์ ์— ๋Œ€ํ•ด ๊ฐ ์Šฌ๋กฏ์— ๊ณจ๊ฒฉ ID์™€ ๊ฐ€์ค‘์น˜๊ฐ€ ํฌํ•จ๋œ ์Šฌ๋กฏ ๋ฐฐ์—ด์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

์šฐ๋ฆฌ์˜ ์‚ถ์„ ๋” ๋‹จ์ˆœํ•˜๊ฒŒ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด ์šฐ๋ฆฌ๋Š” 4๊ฐœ์˜ ์Šฌ๋กฏ์ด ์žˆ๋Š” ๋ฐฐ์—ด์„ ์‚ฌ์šฉํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ด๊ฒƒ์€ 4๊ฐœ ์ด์ƒ์˜ ๋ผˆ์— ์˜ํ•ด ์˜ํ–ฅ์„ ๋ฐ›์„ ์ˆ˜ ์žˆ๋Š” ์ •์ ์ด ์—†๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

๊ณจ๊ฒฉ์ด ๋” ๋งŽ์€ ๋ชจ๋ธ์„ ๋กœ๋“œํ•˜๋ ค๋ฉด ๋ฐฐ์—ด ํฌ๊ธฐ๋ฅผ ์กฐ์ •ํ•ด์•ผ ํ•˜์ง€๋งŒ ์ด ํŠœํ† ๋ฆฌ์–ผ ๋ฐ๋ชจ์— ํฌํ•จ๋œ Doom 3 ๋ชจ๋ธ์˜ ๊ฒฝ์šฐ ๊ณจ๊ฒฉ 4๊ฐœ๋กœ ์ถฉ๋ถ„ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์šฐ๋ฆฌ์˜ ์ƒˆ๋กœ์šด ๊ผญ์ง“์  ๊ตฌ์กฐ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ณด์ผ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

 

 

bone ID๋Š” bone ๋ณ€ํ™˜ ๋ฐฐ์—ด์˜ ์ธ๋ฑ์Šค์ž…๋‹ˆ๋‹ค.

์ด๋Ÿฌํ•œ ๋ณ€ํ™˜์€ WVP ํ–‰๋ ฌ ์•ž์˜ ์œ„์น˜์™€ ์ •๊ทœ์— ์ ์šฉ๋ฉ๋‹ˆ๋‹ค(์ฆ‰, ์ •์ ์„ "bone space"์—์„œ ๋กœ์ปฌ ๊ณต๊ฐ„์œผ๋กœ ๋ณ€ํ™˜).

๊ฐ€์ค‘์น˜๋Š” ์—ฌ๋Ÿฌ ๋ผˆ์˜ ๋ณ€ํ™˜์„ ํ•˜๋‚˜์˜ ๋ณ€ํ™˜์œผ๋กœ ๊ฒฐํ•ฉํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋˜๋ฉฐ, ์–ด๋–ค ๊ฒฝ์šฐ์—๋„ ์ด ๊ฐ€์ค‘์น˜๋Š” ์ •ํ™•ํžˆ 1(๋ชจ๋ธ๋ง ์†Œํ”„ํŠธ์›จ์–ด์˜ ์ฑ…์ž„)์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ผ๋ฐ˜์ ์œผ๋กœ ์• ๋‹ˆ๋ฉ”์ด์…˜ ํ‚ค ํ”„๋ ˆ์ž„ ์‚ฌ์ด๋ฅผ ๋ณด๊ฐ„ํ•˜๊ณ  ๋ชจ๋“  ํ”„๋ ˆ์ž„์˜ ๊ณจ๊ฒฉ ๋ณ€ํ™˜ ๋ฐฐ์—ด์„ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค.

 

๋ผˆ ๋ณ€ํ˜•์˜ ๋ฐฐ์—ด์ด ๋งŒ๋“ค์–ด์ง€๋Š” ๋ฐฉ๋ฒ•์€ ๋Œ€๊ฐœ ๊นŒ๋‹ค๋กœ์šด ๋ถ€๋ถ„์ž…๋‹ˆ๋‹ค.

๋ณ€ํ™˜์€ ๊ณ„์ธต ๊ตฌ์กฐ(์ฆ‰, ํŠธ๋ฆฌ)๋กœ ์„ค์ •๋˜๋ฉฐ ์ผ๋ฐ˜์ ์ธ ๋ฐฉ๋ฒ•์€ ํŠธ๋ฆฌ์˜ ๋ชจ๋“  ๋…ธํŠธ์— ์Šค์ผ€์ผ๋ง ๋ฒกํ„ฐ, ํšŒ์ „ ์ฟผํ„ฐ ๋ฐ ๋ณ€ํ™˜ ๋ฒกํ„ฐ๋ฅผ ๊ฐ–๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์‹ค์ œ๋กœ ๊ฐ ๋…ธ๋“œ์—๋Š” ์ด๋Ÿฌํ•œ ํ•ญ๋ชฉ์˜ ๋ฐฐ์—ด์ด ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฐฐ์—ด์˜ ๋ชจ๋“  ํ•ญ๋ชฉ์—๋Š” ํƒ€์ž„์Šคํƒฌํ”„๊ฐ€ ์žˆ์–ด์•ผํ•ฉ๋‹ˆ๋‹ค. ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ ์‹œ๊ฐ„์ด ํƒ€์ž„์Šคํƒฌํ”„ ์ค‘ ํ•˜๋‚˜์™€ ์ •ํ™•ํžˆ ์ผ์น˜ํ•˜๋Š” ๊ฒฝ์šฐ๋Š” ๋“œ๋ฌผ๊ธฐ ๋•Œ๋ฌธ์— ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์˜ ์‹œ์ ์— ๋Œ€ํ•œ ์ •ํ™•ํ•œ ๋ณ€ํ™˜์„ ์–ป์œผ๋ ค๋ฉด ์ฝ”๋“œ๊ฐ€ ์Šค์ผ€์ผ๋ง/ํšŒ์ „/๋ฒˆ์—ญ์„ ๋ณด๊ฐ„ํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์šฐ๋ฆฌ๋Š” ํ˜„์žฌ์˜ ๋ผˆ์—์„œ root๊นŒ์ง€ ๊ฐ ๋…ธ๋“œ์— ๋Œ€ํ•ด ๋™์ผํ•œ ํ”„๋กœ์„ธ์Šค๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ณ  ์ด ๋ณ€ํ™˜ ์ฒด์ธ์„ ํ•จ๊ป˜ ๊ณฑํ•˜์—ฌ ์ตœ์ข… ๊ฒฐ๊ณผ๋ฅผ ์–ป์Šต๋‹ˆ๋‹ค. ๊ฐ ๊ณจ๊ฒฉ์— ๋Œ€ํ•ด ์ด ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•œ ๋‹ค์Œ ์‰์ด๋”๋ฅผ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค.

 

์ง€๊ธˆ๊นŒ์ง€ ์šฐ๋ฆฌ๊ฐ€ ์ด์•ผ๊ธฐํ•œ ๋ชจ๋“  ๊ฒƒ์€ ๊ฝค ์ผ๋ฐ˜์ ์ด์—ˆ์Šต๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ์ด๊ฒƒ์€ Assimp์˜ ๊ณจ๊ฒฉ ์• ๋‹ˆ๋ฉ”์ด์…˜์— ๋Œ€ํ•œ ํŠœํ† ๋ฆฌ์–ผ ์ž…๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ ์šฐ๋ฆฌ๋Š” ๊ทธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ๋‹ค์‹œ ๋“ค์–ด๊ฐ€์„œ ์–ด๋–ป๊ฒŒ ์Šคํ‚ค๋‹์„ ํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ๋ณผ ํ•„์š”๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

Assimip์˜ ์ข‹์€ ์ ์€ ์—ฌ๋Ÿฌ ํ˜•์‹์˜ ๊ณจ๊ฒฉ ์ •๋ณด ๋กœ๋“œ๋ฅผ ์ง€์›ํ•œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋‚˜์œ์ ์€ ์‰์ด๋”์— ํ•„์š”ํ•œ ๊ณจ๊ฒฉ ๋ณ€ํ™˜์„ ์ƒ์„ฑํ•˜๊ธฐ ์œ„ํ•ด ์ƒ์„ฑ๋˜๋Š” ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ์— ๋Œ€ํ•ด ์—ฌ์ „ํžˆ ์ƒ๋‹นํ•œ ์ž‘์—…์ด ํ•„์š”ํ•˜๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

 

์ •์  ์ˆ˜์ค€์˜ ๋ผˆ ์ •๋ณด๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ์€ Assimp ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ์˜ ๊ด€๋ จ ๋ถ€๋ถ„์ž…๋‹ˆ๋‹ค:

 

 

์•„๋งˆ Assimp์— ๋Œ€ํ•œ ํŠœํ† ๋ฆฌ์–ผ์—์„œ ๊ธฐ์–ตํ•˜์‹œ๊ฒ ์ง€๋งŒ, ๋ชจ๋“  ๊ฒƒ์ด aiScene ํด๋ž˜์Šค(๋งค์‰ฌ ํŒŒ์ผ์„ ๊ฐ€์ ธ์˜ฌ ๋•Œ ๋ฐ›๋Š” ๊ฐœ์ฒด)์— ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. aiScene์—๋Š” aiMesh ๊ฐœ์ฒด์˜ ๋ฐฐ์—ด์ด ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. aiMesh๋Š” ๋ชจ๋ธ์˜ ์ผ๋ถ€์ด๋ฉฐ ์œ„์น˜, ์ •๊ทœ ํ…์Šค์ฒ˜ ์ขŒํ‘œ ๋“ฑ๊ณผ ๊ฐ™์€ ์ •์  ์ˆ˜์ค€์˜ ํ•ญ๋ชฉ์„ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค. ์ด์ œ aiMesh์—๋Š” aiBone ๊ฐ์ฒด์˜ ๋ฐฐ์—ด๋„ ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

๋†€๋ž„ ๊ฒƒ๋„ ์—†์ด, aiBone์€ mesh์˜ ๊ณจ๊ฒฉ์— ์žˆ๋Š” ํ•˜๋‚˜์˜ ๋ผˆ๋ฅผ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค. ๊ฐ ๊ณจ๊ฒฉ์—๋Š” ๊ณจ๊ฒฉ ๊ณ„์ธต ๊ตฌ์กฐ(์•„๋ž˜ ์ฐธ์กฐ), ์ •์  ๊ฐ€์ค‘์น˜ ๋ฐฐ์—ด ๋ฐ 4x4 ์˜คํ”„์…‹ ํ–‰๋ ฌ์—์„œ ์ฐพ์„ ์ˆ˜ ์žˆ๋Š” ์ด๋ฆ„์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ํ–‰๋ ฌ์ด ํ•„์š”ํ•œ ์ด์œ ๋Š” ์ •์ ์ด ์ผ๋ฐ˜์ ์ธ ๋กœ์ปฌ ๊ณต๊ฐ„์— ์ €์žฅ๋˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ์ด๋Š” ๊ณจ๊ฒฉ ์• ๋‹ˆ๋ฉ”์ด์…˜์ด ์ง€์›๋˜์ง€ ์•Š๋”๋ผ๋„ ๊ธฐ์กด ์ฝ”๋“œ ๊ธฐ๋ฐ˜์ด ๋ชจ๋ธ์„ ๋กœ๋“œํ•˜๊ณ  ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๋ Œ๋”๋งํ•  ์ˆ˜ ์žˆ์Œ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๊ณ„์ธต ๊ตฌ์กฐ์˜ ๋ผˆ ๋ณ€ํ™˜์€ ๋ผˆ ๊ณต๊ฐ„์—์„œ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.(๊ทธ๋ฆฌ๊ณ  ๋ชจ๋“  ๋ผˆ๋Š” ๊ณ ์œ ํ•œ ๊ณต๊ฐ„์„ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด ์šฐ๋ฆฌ๊ฐ€ ํ•จ๊ป˜ ๋ณ€ํ™˜์„ ๊ณฑํ•ด์•ผ ํ•˜๋Š” ์ด์œ ์ž…๋‹ˆ๋‹ค). ์˜คํ”„์…‹ ๋งคํŠธ๋ฆญ์Šค์˜ ์ž‘์—…์€ ๋งค์‰ฌ์˜ ๊ตญ์†Œ ๊ณต๊ฐ„์—์„œ ์ •์  ์œ„์น˜๋ฅผ ํŠน์ • ๋ผˆ์˜ ๊ณต๊ฐ„์œผ๋กœ ์ด๋™์‹œํ‚ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

 

์ •์  ๊ฐ€์ค‘์น˜ ๋ฐฐ์—ด์€ ์‚ฌ๋ฌผ๋“ค์ด ํฅ๋ฏธ๋กœ์›Œ์ง€๊ธฐ ์‹œ์ž‘ํ•˜๋Š” ๊ณณ์ž…๋‹ˆ๋‹ค.

์ด ๋ฐฐ์—ด์˜ ๊ฐ ํ•ญ๋ชฉ์—๋Š” aiMesh์˜ ์ •์  ๋ฐฐ์—ด์— ๋Œ€ํ•œ ์ธ๋ฑ์Šค(๊ผญ์ง“์ ์ด ๋™์ผํ•œ ๊ธธ์ด๋ฅผ ๊ฐ€์ง„ ์—ฌ๋Ÿฌ ๋ฐฐ์—ด์— ๊ฑธ์ณ ๋ถ„์‚ฐ๋จ)์™€ ๊ฐ€์ค‘์น˜๊ฐ€ ํฌํ•จ๋ฉ๋‹ˆ๋‹ค. ๋ชจ๋“  ๊ผญ์ง“์  ๊ฐ€์ค‘์น˜์˜ ํ•ฉ์€ 1์ด์–ด์•ผ ํ•˜์ง€๋งŒ ์ด๋ฅผ ์ฐพ์œผ๋ ค๋ฉด ๋ชจ๋“  ๋ผˆ๋ฅผ ํ†ต๊ณผํ•˜๊ณ  ๊ฐ ํŠน์ • ๊ผญ์ง“์ ์— ๋Œ€ํ•œ ์ผ์ข…์˜ ๋ชฉ๋ก์œผ๋กœ ๊ฐ€์ค‘์น˜๋ฅผ ๋ˆ„์ ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

 

์ •์  ์ˆ˜์ค€์—์„œ ๊ณจ๊ฒฉ ์ •๋ณด๋ฅผ ๊ตฌ์ถ•ํ•œ ํ›„ ๊ณจ๊ฒฉ ๋ณ€ํ™˜ ๊ณ„์ธต ๊ตฌ์กฐ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ณ  ์‰์ด๋”์— ๋กœ๋“œํ•  ์ตœ์ข… ๋ณ€ํ™˜์„ ์ƒ์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋‹ค์Œ ๊ทธ๋ฆผ์—๋Š” ๊ด€๋ จ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๊ฐ€ ํ‘œ์‹œ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค:

 

 

๋‹ค์‹œ, ์šฐ๋ฆฌ๋Š” aiScene์—์„œ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค. aiScene ๊ฐœ์ฒด์—๋Š” ๋…ธ๋“œ ๊ณ„์ธต ๊ตฌ์กฐ(์ฆ‰, ํŠธ๋ฆฌ)์˜ ๋ฃจํŠธ์ธ aiNode ํด๋ž˜์Šค์˜ ๊ฐœ์ฒด์— ๋Œ€ํ•œ ํฌ์ธํ„ฐ๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ํŠธ๋ฆฌ์˜ ๊ฐ ๋…ธ๋“œ์—๋Š” ๋ถ€๋ชจ์— ๋Œ€ํ•œ ํฌ์ธํ„ฐ์™€ ์ž์‹์— ๋Œ€ํ•œ ํฌ์ธํ„ฐ ๋ฐฐ์—ด์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์šฐ๋ฆฌ๊ฐ€ tree๋ฅผ ํŽธ๋ฆฌํ•˜๊ฒŒ ์™”๋‹ค๊ฐ”๋‹ค ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค๋‹ˆ๋‹ค.

๋˜ํ•œ ๋…ธ๋“œ๋Š” ๋…ธ๋“œ ๊ณต๊ฐ„์—์„œ ๋ถ€๋ชจ ๊ณต๊ฐ„์œผ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๋ณ€ํ™˜ ํ–‰๋ ฌ์„ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค. ๋งˆ์ง€๋ง‰์œผ๋กœ ๋…ธ๋“œ์— ์ด๋ฆ„์ด ์žˆ์„ ์ˆ˜๋„ ์žˆ๊ณ  ์—†์„ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ๋…ธ๋“œ๊ฐ€ ๊ณ„์ธต์—์„œ ๊ณจ๊ฒฉ์„ ๋‚˜ํƒ€๋‚ด๋Š” ๊ฒฝ์šฐ ๋…ธ๋“œ ์ด๋ฆ„์ด ๊ณจ๊ฒฉ ์ด๋ฆ„๊ณผ ์ผ์น˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋…ธ๋“œ์— ์ด๋ฆ„์ด ์—†๋Š” ๊ฒฝ์šฐ(์ฆ‰, ๋Œ€์‘ํ•˜๋Š” ๊ณจ๊ฒฉ์ด ์—†์Œ) ๋…ธ๋“œ์˜ ์—ญํ• ์€ ๋‹จ์ˆœํžˆ ๋ชจ๋ธ์„ ๋ถ„ํ•ดํ•˜๊ณ  ์ค‘๊ฐ„ ๋ณ€ํ™˜์„ ๋ฐฐ์น˜ํ•˜๋Š” ๊ฒƒ์„ ๋•๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

 

ํผ์ฆ์˜ ๋งˆ์ง€๋ง‰ ์กฐ๊ฐ์€ aiScene ๊ฐ์ฒด์—๋„ ์ €์žฅ๋œ aiAnimation ๋ฐฐ์—ด์ž…๋‹ˆ๋‹ค. ๋‹จ์ผ aiAnimation ๊ฐ์ฒด๋Š” "walk", "run", "shoot" ๋“ฑ๊ณผ ๊ฐ™์€ ์ผ๋ จ์˜ ์• ๋‹ˆ๋ฉ”์ด์…˜ ํ”„๋ ˆ์ž„์„ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค. ํ”„๋ ˆ์ž„ ์‚ฌ์ด๋ฅผ ๋ณด๊ฐ„ํ•จ์œผ๋กœ์จ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ด๋ฆ„๊ณผ ์ผ์น˜ํ•˜๋Š” ์›ํ•˜๋Š” ์‹œ๊ฐ์  ํšจ๊ณผ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์• ๋‹ˆ๋ฉ”์ด์…˜์—๋Š” ๋ˆˆ๊ธˆ์˜ ์ง€์† ์‹œ๊ฐ„๊ณผ ์ดˆ๋‹น ๋ˆˆ๊ธˆ ์ˆ˜(์˜ˆ: 100๊ฐœ์˜ ๋ˆˆ๊ธˆ๊ณผ 25๊ฐœ์˜ ๋ˆˆ๊ธˆ์€ 4์ดˆ์˜ ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ๋‚˜ํƒ€๋ƒ„)๊ฐ€ ์žˆ์–ด ์• ๋‹ˆ๋ฉ”์ด์…˜์ด ๋ชจ๋“  ํ•˜๋“œ์›จ์–ด์—์„œ ๋™์ผํ•˜๊ฒŒ ๋ณด์ด๋„๋ก ์ง„ํ–‰ ์‹œ๊ฐ„์„ ์กฐ์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ์ด ์• ๋‹ˆ๋ฉ”์ด์…˜์—๋Š” ์ฑ„๋„์ด๋ผ๊ณ  ํ•˜๋Š” aiNodeAnim ๊ฐ์ฒด์˜ ๋ฐฐ์—ด์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ๊ฐ์˜ ์ฑ„๋„์€ ์‚ฌ์‹ค ๋ชจ๋“  ๋ณ€ํ˜•์ด ์žˆ๋Š” ๋ผˆ์ž…๋‹ˆ๋‹ค. ์ฑ„๋„์—๋Š” ๊ณ„์ธต ๊ตฌ์กฐ์˜ ๋…ธ๋“œ ์ค‘ ํ•˜๋‚˜์™€ ์„ธ ๊ฐœ์˜ ๋ณ€ํ™˜ ๋ฐฐ์—ด๊ณผ ์ผ์น˜ํ•ด์•ผ ํ•˜๋Š” ์ด๋ฆ„์ด ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

 

ํŠน์ • ์‹œ์ ์˜ ์ตœ์ข… ๋ผˆ ๋ณ€ํ™˜์„ ๊ณ„์‚ฐํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์ด ์„ธ ๋ฐฐ์—ด์—์„œ ๊ฐ๊ฐ ์‹œ๊ฐ„๊ณผ ์ผ์น˜ํ•˜๋Š” ๋‘ ๊ฐœ์˜ ํ•ญ๋ชฉ์„ ์ฐพ๊ณ  ๊ทธ ์‚ฌ์ด๋ฅผ ๋ณด๊ฐ„ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ ๋ณ€ํ™˜์„ ํ•˜๋‚˜์˜ ํ–‰๋ ฌ๋กœ ๊ฒฐํ•ฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•œ ํ›„์—๋Š” ๊ณ„์ธต ๊ตฌ์กฐ์—์„œ ํ•ด๋‹น ๋…ธ๋“œ๋ฅผ ์ฐพ์•„ ์ƒ์œ„ ๋…ธ๋“œ๋กœ ์ด๋™ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ๋ถ€๋ชจ์— ํ•ด๋‹นํ•˜๋Š” ์ฑ„๋„์ด ํ•„์š”ํ•˜๊ณ  ๋™์ผํ•œ ๋ณด๊ฐ„ ๊ณผ์ •์„ ์ˆ˜ํ–‰ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ๋‘ ๋ณ€ํ™˜์„ ํ•จ๊ป˜ ๊ณฑํ•˜๊ณ  ๊ณ„์ธต์˜ root์— ๋„๋‹ฌํ•  ๋•Œ๊นŒ์ง€ ๊ณ„์†ํ•ฉ๋‹ˆ๋‹ค.

Source Walthru

(mesh.cpp:75)

bool Mesh::LoadMesh(const string& Filename)
{
    // Release the previously loaded mesh (if it exists)
    Clear();

    // Create the VAO
    glGenVertexArrays(1, &m_VAO);
    glBindVertexArray(m_VAO);

    // Create the buffers for the vertices attributes
    glGenBuffers(ARRAY_SIZE_IN_ELEMENTS(m_Buffers), m_Buffers);

    bool Ret = false;

    m_pScene = m_Importer.ReadFile(Filename.c_str(), aiProcess_Triangulate | aiProcess_GenSmoothNormals |
                                    aiProcess_FlipUVs);

    if (m_pScene) {
       m_GlobalInverseTransform = m_pScene->mRootNode->mTransformation;
       m_GlobalInverseTransform = m_GlobalInverseTransform.Inverse();
       Ret = InitFromScene(m_pScene, Filename);
    }
    else {
       printf("Error parsing '%s': '%s'\n", Filename.c_str(), m_Importer.GetErrorString());
    }

    // Make sure the VAO is not changed from the outside
    glBindVertexArray(0);

    return Ret;
}

๋‹ค์Œ์€ ๋งค์‰ฌ ํด๋ž˜์Šค์˜ ์—…๋ฐ์ดํŠธ๋œ ์ง„์ž…์ ์œผ๋กœ, ๋ณ€๊ฒฝ ์‚ฌํ•ญ์€ ๊ตต์€ ๊ธ€์”จ๋กœ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๊ฐ€ ์ฃผ๋ชฉํ•ด์•ผ ํ•  ๋ช‡ ๊ฐ€์ง€ ๋ณ€ํ™”๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜๋‚˜๋Š” ์ž„ํฌํ„ฐ์™€ aiScene ๊ฐœ์ฒด๊ฐ€ ์ด์ œ ๋ณ€์ˆ˜๋ฅผ ์Œ“์ง€ ์•Š๊ณ  ํด๋ž˜์Šค ๋ฉค๋ฒ„๊ฐ€ ๋œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ ์ด์œ ๋Š” ๋Ÿฐํƒ€์ž„ ์ค‘์— ์šฐ๋ฆฌ๋Š” aiScene ๊ฐ์ฒด๋กœ ๋‹ค์‹œ ๋Œ์•„๊ฐˆ ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์— ์ˆ˜์ž…์ž์™€ ์žฅ๋ฉด์˜ ๋ฒ”์œ„๋ฅผ ๋ชจ๋‘ ํ™•์žฅํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์‹ค์ œ ๊ฒŒ์ž„์—์„œ๋Š” ํ•„์š”ํ•œ ๊ฒƒ์„ ๋ณต์‚ฌํ•˜์—ฌ ์ตœ์ ํ™”๋œ ํ˜•์‹์œผ๋กœ ์ €์žฅํ•˜๊ณ  ์‹ถ์„ ์ˆ˜ ์žˆ์ง€๋งŒ ๊ต์œก์ ์ธ ๋ชฉ์ ์„ ์œ„ํ•ด์„œ๋Š” ์ด๊ฒƒ์œผ๋กœ ์ถฉ๋ถ„ํ•ฉ๋‹ˆ๋‹ค.

 

๋‘ ๋ฒˆ์งธ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์€ ๊ณ„์ธต ๊ตฌ์กฐ์˜ ๋ฃจํŠธ์— ๋Œ€ํ•œ ๋ณ€ํ™˜ ํ–‰๋ ฌ์ด ์ถ”์ถœ๋˜๊ณ  ์—ญ๋ฐฉํ–ฅ์œผ๋กœ ์ €์žฅ๋œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ์•ž์œผ๋กœ ๋” ๋ฉ€๋ฆฌ์„œ ๊ทธ๊ฒƒ์„ ์‚ฌ์šฉํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ํ–‰๋ ฌ ์—ญ์ฝ”๋“œ๊ฐ€ Assimp ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ Matrix4f ํด๋ž˜์Šค๋กœ ๋ณต์‚ฌ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

 

(mesh.h:69)

struct VertexBoneData
{
    uint IDs[NUM_BONES_PER_VERTEX];
    float Weights[NUM_BONES_PER_VERTEX];
}

(mesh.cpp:107)

bool Mesh::InitFromScene(const aiScene* pScene, const string& Filename)
{
    ...
    vector<VertexBoneData> Bones;
    ...
    Bones.resize(NumVertices);
    ...
    glBindBuffer(GL_ARRAY_BUFFER, m_Buffers[BONE_VB]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(Bones[0]) * Bones.size(), &Bones[0], GL_STATIC_DRAW);
    glEnableVertexAttribArray(BONE_ID_LOCATION);
    glVertexAttribIPointer(BONE_ID_LOCATION, 4, GL_INT, sizeof(VertexBoneData), (const GLvoid*)0);
    glEnableVertexAttribArray(BONE_WEIGHT_LOCATION);
    glVertexAttribPointer(BONE_WEIGHT_LOCATION, 4, GL_FLOAT, GL_FALSE, sizeof(VertexBoneData), (const GLvoid*)16);
    ...
}

์œ„์˜ ๊ตฌ์กฐ๋Š” ๊ผญ์ง“์  ์ˆ˜์ค€์—์„œ ์šฐ๋ฆฌ๊ฐ€ ํ•„์š”๋กœ ํ•˜๋Š” ๋ชจ๋“  ๊ฒƒ์„ ํฌํ•จํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๊ธฐ๋ณธ์ ์œผ๋กœ 4๊ฐœ์˜ ๋ผˆ(๋ผˆ๋‹น ID ๋ฐ ๋ฌด๊ฒŒ)๋ฅผ ์ €์žฅํ•  ์ˆ˜ ์žˆ๋Š” ์ถฉ๋ถ„ํ•œ ์ €์žฅ ๊ณต๊ฐ„์ด ์žˆ์Šต๋‹ˆ๋‹ค.

VertexBoneData๋Š” ์‰์ด๋”์— ์‰ฝ๊ฒŒ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ทธ๋ ‡๊ฒŒ ๊ตฌ์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ์ด๋ฏธ ์œ„์น˜ 0, 1, 2์— ๊ฐ๊ฐ ์œ„์น˜, ํ…์Šค์ฒ˜ ์ขŒํ‘œ ๋ฐ ์ผ๋ฐ˜ ๊ฒฝ๊ณ„๋ฅผ ํ™•๋ณดํ–ˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์œ„์น˜ 3์—์„œ ๊ณจ๊ฒฉ ID๋ฅผ ๋ฐ”์ธ๋”ฉํ•˜๊ณ  ์œ„์น˜ 4์—์„œ ๊ฐ€์ค‘์น˜๋ฅผ ๋ฐ”์ธ๋”ฉํ•˜๋„๋ก VAO๋ฅผ ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๊ฐ€ glVertexAttribute๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์ฃผ๋ชฉํ•˜๋Š” ๊ฒƒ๋„ ๋งค์šฐ ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. ID๋ฅผ ๋ฐ”์ธ๋”ฉํ•  glVertexAttributePointer๊ฐ€ ์•„๋‹Œ IPointer์ž…๋‹ˆ๋‹ค. ์ด ์ ์— ์ฃผ์˜ํ•˜์‹ญ์‹œ์˜ค. ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด ์‰์ด๋”์— ์†์ƒ๋œ ๋ฐ์ดํ„ฐ๊ฐ€ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.

 

(mesh.cpp:213)

void Mesh::LoadBones(uint MeshIndex, const aiMesh* pMesh, vector& Bones)
{
    for (uint i = 0 ; i < pMesh->mNumBones ; i++) {
        uint BoneIndex = 0;
        string BoneName(pMesh->mBones[i]->mName.data);

        if (m_BoneMapping.find(BoneName) == m_BoneMapping.end()) {
            BoneIndex = m_NumBones;
            m_NumBones++;
            BoneInfo bi;
            m_BoneInfo.push_back(bi);
        }
        else {
            BoneIndex = m_BoneMapping[BoneName];
        }

        m_BoneMapping[BoneName] = BoneIndex;
        m_BoneInfo[BoneIndex].BoneOffset = pMesh->mBones[i]->mOffsetMatrix;

        for (uint j = 0 ; j < pMesh->mBones[i]->mNumWeights ; j++) {
            uint VertexID = m_Entries[MeshIndex].BaseVertex + pMesh->mBones[i]->mWeights[j].mVertexId;
            float Weight = pMesh->mBones[i]->mWeights[j].mWeight;
            Bones[VertexID].AddBoneData(BoneIndex, Weight);
        }
    }
}

์œ„์˜ ํ•จ์ˆ˜๋Š” ๋‹จ์ผ aiMesh ๊ฐœ์ฒด์— ๋Œ€ํ•œ ์ •์  ๊ณจ๊ฒฉ ์ •๋ณด๋ฅผ ๋กœ๋“œํ•ฉ๋‹ˆ๋‹ค. Mesh::InitMesh()์—์„œ ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค. ์ด ํ•จ์ˆ˜๋Š” VertexBoneData ๊ตฌ์กฐ๋ฅผ ์ฑ„์šฐ๋Š” ๊ฒƒ ์™ธ์—๋„ ๊ณจ๊ฒฉ ์ด๋ฆ„๊ณผ ๊ณจ๊ฒฉ ID(์ด ํ•จ์ˆ˜์— ์˜ํ•ด ๊ด€๋ฆฌ๋˜๋Š” ์‹คํ–‰์ค‘์ธ ์ธ๋ฑ์Šค) ์‚ฌ์ด์˜ ๋งต์„ ์—…๋ฐ์ดํŠธํ•˜๊ณ  ๊ณจ๊ฒฉ ID๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๋ฒกํ„ฐ์— ์˜คํ”„์…‹ ํ–‰๋ ฌ์„ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค. ์ •์  ID๊ฐ€ ๊ณ„์‚ฐ๋˜๋Š” ๋ฐฉ๋ฒ•์„ ๊ธฐ๋กํ•ฉ๋‹ˆ๋‹ค. ์ •์  ID๋Š” ๋‹จ์ผ ๋งค์‰ฌ์™€ ๊ด€๋ จ์ด ์žˆ๊ณ  ๋ชจ๋“  ๋งค์‰ฌ๋ฅผ ๋‹จ์ผ ๋ฒกํ„ฐ์— ์ €์žฅํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํ˜„์žฌ aiMesh์˜ ๊ธฐ๋ณธ ์ •์  ID๋ฅผ mWeights ๋ฐฐ์—ด์˜ ์ •์  ID์— ์ถ”๊ฐ€ํ•˜์—ฌ ์ ˆ๋Œ€ ์ •์  ID๋ฅผ ์–ป์Šต๋‹ˆ๋‹ค.

 

(mesh.cpp:29)

void Mesh::VertexBoneData::AddBoneData(uint BoneID, float Weight)
{
    for (uint i = 0 ; i < ARRAY_SIZE_IN_ELEMENTS(IDs) ; i++) {
        if (Weights[i] == 0.0) {
            IDs[i] = BoneID;
            Weights[i] = Weight;
            return;
        }
    }

    // should never get here - more bones than we have space for
    assert(0);
}

์ด ์œ ํ‹ธ๋ฆฌํ‹ฐ ํ•จ์ˆ˜๋Š” VertexBoneData ๊ตฌ์กฐ์—์„œ ๋นˆ ์Šฌ๋กฏ์„ ์ฐพ์•„ ๊ณจ๊ฒฉ ID์™€ ๊ฐ€์ค‘์น˜๋ฅผ ๋ฐฐ์น˜ํ•ฉ๋‹ˆ๋‹ค. ์ผ๋ถ€ ์ •์ ์€ 4๊ฐœ ๋ฏธ๋งŒ์˜ ๋ผˆ์— ์˜ํ•ด ์˜ํ–ฅ์„ ๋ฐ›์ง€๋งŒ ์กด์žฌํ•˜์ง€ ์•Š๋Š” ๋ผˆ์˜ ๋ฌด๊ฒŒ๊ฐ€ 0์œผ๋กœ ์œ ์ง€๋˜๊ธฐ ๋•Œ๋ฌธ์—(VertexBoneData ์ƒ์„ฑ์ž ์ฐธ์กฐ), ์ด๋Š” ๋ชจ๋“  ๋ผˆ ์ˆ˜์— ๋Œ€ํ•ด ๋™์ผํ•œ ๊ฐ€์ค‘์น˜ ๊ณ„์‚ฐ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

 (mesh.cpp:473)

Matrix4f Mesh::BoneTransform(float TimeInSeconds, vector<Matrix4f>& Transforms)
{
    Matrix4f Identity;
    Identity.InitIdentity();

    float TicksPerSecond = m_pScene->mAnimations[0]->mTicksPerSecond != 0 ?
                            m_pScene->mAnimations[0]->mTicksPerSecond : 25.0f;
    float TimeInTicks = TimeInSeconds * TicksPerSecond;
    float AnimationTime = fmod(TimeInTicks, m_pScene->mAnimations[0]->mDuration);

    ReadNodeHierarchy(AnimationTime, m_pScene->mRootNode, Identity);

    Transforms.resize(m_NumBones);

    for (uint i = 0 ; i < m_NumBones ; i++) {
        Transforms[i] = m_BoneInfo[i].FinalTransformation;
    }
}

์•ž์„œ ๋ณธ ์ •์  ์ˆ˜์ค€์˜ ๊ณจ๊ฒฉ ์ •๋ณด ๋กœ๋“œ๋Š” ๋งค์‰ฌ๊ฐ€ ์‹œ์ž‘ ์ค‘์— ๋กœ๋“œ๋  ๋•Œ ํ•œ ๋ฒˆ๋งŒ ์ˆ˜ํ–‰๋ฉ๋‹ˆ๋‹ค. ์ด์ œ ์šฐ๋ฆฌ๋Š” ๋งค ํ”„๋ ˆ์ž„๋งˆ๋‹ค ์‰์ด๋”์— ๋“ค์–ด๊ฐ€๋Š” ๋ผˆ ๋ณ€ํ˜•์„ ๊ณ„์‚ฐํ•˜๋Š” ๋‘ ๋ฒˆ์žฌ ๋ถ€๋ถ„์œผ๋กœ ๋„˜์–ด๊ฐ‘๋‹ˆ๋‹ค. ์œ„์˜ ๊ธฐ๋Šฅ์€ ์ด ํ™œ๋™์˜ ์‹œ์ž‘์ ์ž…๋‹ˆ๋‹ค. ํ˜ธ์ถœ์ž๋Š” ํ˜„์žฌ ์‹œ๊ฐ„์„ ์ดˆ ๋‹จ์œ„๋กœ ๋ณด๊ณ ํ•˜๊ณ (๋ถ„์ˆ˜์ผ ์ˆ˜ ์žˆ์Œ), ์—…๋ฐ์ดํŠธํ•ด์•ผํ•˜๋Š” ํ–‰๋ ฌ์˜ ๋ฒกํ„ฐ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ฃผ๊ธฐ ๋‚ด์—์„œ ์ƒ๋Œ€์ ์ธ ์‹œ๊ฐ„์„ ์ฐพ๊ณ  ๋…ธ๋“œ ๊ณ„์ธต ๊ตฌ์กฐ๋ฅผ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ๊ฒฐ๊ณผ๋Š” ํ˜ธ์ถœ์ž์—๊ฒŒ ๋ฐ˜ํ™˜๋˜๋Š” ์ผ๋ จ์˜ ๋ณ€ํ™˜์ž…๋‹ˆ๋‹ค.

 

(mesh.cpp:428)

void Mesh::ReadNodeHierarchy(float AnimationTime, const aiNode* pNode, const Matrix4f& ParentTransform)
{
    string NodeName(pNode->mName.data);

    const aiAnimation* pAnimation = m_pScene->mAnimations[0];

    Matrix4f NodeTransformation(pNode->mTransformation);

    const aiNodeAnim* pNodeAnim = FindNodeAnim(pAnimation, NodeName);

    if (pNodeAnim) {
        // Interpolate scaling and generate scaling transformation matrix
        aiVector3D Scaling;
        CalcInterpolatedScaling(Scaling, AnimationTime, pNodeAnim);
        Matrix4f ScalingM;
        ScalingM.InitScaleTransform(Scaling.x, Scaling.y, Scaling.z);

        // Interpolate rotation and generate rotation transformation matrix
        aiQuaternion RotationQ;
        CalcInterpolatedRotation(RotationQ, AnimationTime, pNodeAnim);
        Matrix4f RotationM = Matrix4f(RotationQ.GetMatrix());

        // Interpolate translation and generate translation transformation matrix
        aiVector3D Translation;
        CalcInterpolatedPosition(Translation, AnimationTime, pNodeAnim);
        Matrix4f TranslationM;
        TranslationM.InitTranslationTransform(Translation.x, Translation.y, Translation.z);

        // Combine the above transformations
        NodeTransformation = TranslationM * RotationM * ScalingM;
    }

    Matrix4f GlobalTransformation = ParentTransform * NodeTransformation;

    if (m_BoneMapping.find(NodeName) != m_BoneMapping.end()) {
        uint BoneIndex = m_BoneMapping[NodeName];
        m_BoneInfo[BoneIndex].FinalTransformation = m_GlobalInverseTransform * GlobalTransformation *
                                                    m_BoneInfo[BoneIndex].BoneOffset;
    }

    for (uint i = 0 ; i < pNode->mNumChildren ; i++) {
        ReadNodeHierarchy(AnimationTime, pNode->mChildren[i], GlobalTransformation);
    }
}

์ด ํ•จ์ˆ˜๋Š” ๋…ธ๋“œ ํŠธ๋ฆฌ๋ฅผ ํ†ต๊ณผํ•˜๊ณ  ์ง€์ •๋œ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์‹œ๊ฐ„์— ๋”ฐ๋ผ ๊ฐ ๋…ธ๋“œ/๋ณธ์— ๋Œ€ํ•œ ์ตœ์ข… ๋ณ€ํ™˜์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ๋งค์‰ฌ๊ฐ€ ํ•˜๋‚˜์˜ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์‹œํ€€์Šค๋งŒ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•œ๋‹ค๋Š” ์ ์—์„œ ์ œํ•œ์ ์ž…๋‹ˆ๋‹ค.

์—ฌ๋Ÿฌ ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ์ง€์›ํ•˜๋ ค๋ฉด ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ด๋ฆ„์„ ๋งํ•˜๊ณ  m_pScene->mAnimations[] ๋ฐฐ์—ด์—์„œ ๊ฒ€์ƒ‰ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์œ„์˜ ์ฝ”๋“œ๋Š” ์šฐ๋ฆฌ๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” ๋ฐ๋ชจ ๋งค์‰ฌ์— ์ถฉ๋ถ„ํ•ฉ๋‹ˆ๋‹ค.

 

๋…ธ๋“œ ๋ณ€ํ™˜์€ ๋…ธ๋“œ์˜ mTransformation ๋ฉค๋ฒ„์—์„œ ์ดˆ๊ธฐํ™”๋ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ ๋…ธ๋“œ๊ฐ€ ๋ผˆ์™€ ์ผ์น˜ํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด ๊ทธ๊ฒƒ์ด ๋ผˆ์˜ ์ตœ์ข…๋ณ€ํ™˜์ž…๋‹ˆ๋‹ค. ๊ทธ๋ ‡๊ฒŒ ๋˜๋ฉด ์ƒ์„ฑํ•œ ํ–‰๋ ฌ๋กœ ๋ฎ์–ด์”๋‹ˆ๋‹ค. ์ด ์ž‘์—…์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ˆ˜ํ–‰๋ฉ๋‹ˆ๋‹ค. ๋จผ์ € ์• ๋‹ˆ๋ฉ”์ด์…˜์˜ ์ฑ„๋„ ๋ฐฐ์—ด์—์„œ ๋…ธ๋“œ ์ด๋ฆ„์„ ๊ฒ€์ƒ‰ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์‹œ๊ฐ„์„ ๊ธฐ๋ฐ˜์œผ๋กœ ์Šค์ผ€์ผ๋ง ๋ฒกํ„ฐ, ํšŒ์ „ ์ฟผํ„ฐ ๋ฐ ๋ณ€ํ™˜ ๋ฒกํ„ฐ๋ฅผ ๋ณด๊ฐ„ํ•ฉ๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ๊ทธ๊ฒƒ๋“ค์„ ํ•˜๋‚˜์˜ ํ–‰๋ ฌ๋กœ ๊ฒฐํ•ฉํ•˜๊ณ  ๋งค๊ฐœ ๋ณ€์ˆ˜๋กœ ์–ป์€ ํ–‰๋ ฌ(GlobablTransformation)๋กœ ๊ณฑํ•ฉ๋‹ˆ๋‹ค.

์ด ํ•จ์ˆ˜๋Š” ์žฌ๊ท€์ ์ด๋ฉฐ ๊ธ€๋กœ๋ฒŒ ๋ณ€ํ™˜ ๋งค๊ฐœ ๋ณ€์ˆ˜๊ฐ€ ID ๋งคํŠธ๋ฆญ์Šค์ธ root ๋…ธ๋“œ์— ๋Œ€ํ•ด ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค. ๊ฐ ๋…ธ๋“œ๋Š” ๋ชจ๋“  ์ž์‹์— ๋Œ€ํ•ด ์ด ํ•จ์ˆ˜๋ฅผ ์žฌ๊ท€์ ์œผ๋กœ ํ˜ธ์ถœํ•˜๊ณ  ์ž์ฒด ๋ณ€ํ™˜์„ ์ „์—ญ ๋ณ€ํ™˜์œผ๋กœ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค. ์ฒ˜์Œ๋ถ€ํ„ฐ ๋๊นŒ์ง€ ๋ชจ๋“  ๋…ธ๋“œ์—์„œ ํ†ตํ•ฉ๋œ ๋ณ€ํ™˜ ์ฒด์ž„์„ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

m_BoneMapping ๋ฐฐ์—ด์€ ๋…ธ๋“œ ์ด๋ฆ„์„ ์ƒ์„ฑํ•˜๋Š” ์ธ๋ฑ์Šค์— ๋งคํ•‘ํ•˜๊ณ  ์ด ์ธ๋ฑ์Šค๋ฅผ ์ตœ์ข… ๋ณ€ํ™˜์ด ์ €์žฅ๋˜๋Š” m_BoneInfo ๋ฐฐ์—ด์˜ ํ•ญ๋ชฉ์œผ๋กœ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ตœ์ข… ๋ณ€ํ™˜์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๊ณ„์‚ฐ๋ฉ๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ๋กœ์ปฌ ๊ณต๊ฐ„ ์œ„์น˜์—์„œ ์ •์ ์„ ๋…ธ๋“œ ๊ณต๊ฐ„์œผ๋กœ ๊ฐ€์ ธ์˜ค๋Š” ๋…ธ๋“œ ์˜คํ”„์…‹ ํ–‰๋ ฌ๋กœ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ ๋ชจ๋“  ๋…ธ๋“œ ๋ถ€๋ชจ์˜ ๊ฒฐํ•ฉ๋œ ๋ณ€ํ™˜๊ณผ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์‹œ๊ฐ„์— ๋”ฐ๋ผ ๋…ธ๋“œ์— ๋Œ€ํ•ด ๊ณ„์‚ฐํ•œ ํŠน์ • ๋ณ€ํ™˜์„ ๊ณฑํ•ฉ๋‹ˆ๋‹ค.

 

์—ฌ๊ธฐ์„œ๋Š” Assimp ์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ˆ˜ํ•™ ๋ฌธ์ œ๋ฅผ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ๋‚˜๋Š” ๊ทธ๊ฒƒ์„ ์šฐ๋ฆฌ๋งŒ์˜ ์ฝ”๋“œ ๋ฒ ์ด์Šค๋กœ ๋ณต์ œํ•˜๋Š” ๊ฒƒ์ด ์˜๋ฏธ๊ฐ€ ์—†๋‹ค๊ณ  ์ƒ๊ฐํ•ด์„œ ๊ทธ๋ƒฅ Assimp๋ฅผ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.

 

(mesh.cpp:387)

void Mesh::CalcInterpolatedRotation(aiQuaternion& Out, float AnimationTime, const aiNodeAnim* pNodeAnim)
{
    // we need at least two values to interpolate...
    if (pNodeAnim->mNumRotationKeys == 1) {
        Out = pNodeAnim->mRotationKeys[0].mValue;
        return;
    }

    uint RotationIndex = FindRotation(AnimationTime, pNodeAnim);
    uint NextRotationIndex = (RotationIndex + 1);
    assert(NextRotationIndex < pNodeAnim->mNumRotationKeys);
    float DeltaTime = pNodeAnim->mRotationKeys[NextRotationIndex].mTime - pNodeAnim->mRotationKeys[RotationIndex].mTime;
    float Factor = (AnimationTime - (float)pNodeAnim->mRotationKeys[RotationIndex].mTime) / DeltaTime;
    assert(Factor >= 0.0f && Factor <= 1.0f);
    const aiQuaternion& StartRotationQ = pNodeAnim->mRotationKeys[RotationIndex].mValue;
    const aiQuaternion& EndRotationQ = pNodeAnim->mRotationKeys[NextRotationIndex].mValue;
    aiQuaternion::Interpolate(Out, StartRotationQ, EndRotationQ, Factor);
    Out = Out.Normalize();
}

์ด ๋ฐฉ๋ฒ•์€ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์‹œ๊ฐ„์„ ๊ธฐ์ค€์œผ๋กœ ์ง€์ •๋œ ์ฑ„๋„์˜ ํšŒ์ „์ฟผํ„ฐ๋ฅผ ๋ณด๊ฐ„ํ•ฉ๋‹ˆ๋‹ค(์ฑ„๋„์— ํ‚ค ์ฟผํ„ฐ ๋ฐฐ์—ด์ด ํฌํ•จ๋˜์–ด ์žˆ์Œ). ๋จผ์ € ํ•„์š”ํ•œ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์‹œ๊ฐ„ ์ง์ „์˜ ํ•ต์‹ฌ ์ฟผํ„ฐ์˜ ์ธ๋ฑ์Šค๋ฅผ ์ฐพ์Šต๋‹ˆ๋‹ค. ์• ๋‹ˆ๋ฉ”์ด์…˜ ์‹œ๊ฐ„์—์„œ ์ด์ „ ํ‚ค๊นŒ์ง€์˜ ๊ฑฐ๋ฆฌ์™€ ํ•ด๋‹น ํ‚ค์™€ ๋‹ค์Œ ํ‚ค ์‚ฌ์ด์˜ ๊ฑฐ๋ฆฌ ์‚ฌ์ด์˜ ๋น„์œจ์„ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ๊ทธ ์š”์†Œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ด ๋‘ ํ‚ค ์‚ฌ์ด๋ฅผ ๋ณด๊ฐ„ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” Assimp ์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ณด๊ฐ„์„ ์ˆ˜ํ–‰ํ•˜๊ณ  ๊ฒฐ๊ณผ๋ฅผ ์ •๊ทœํ™”ํ•ฉ๋‹ˆ๋‹ค. ์œ„์น˜ ๋ฐ ์Šค์ผ€์ผ๋ง์— ๋Œ€ํ•œ ํ•ด๋‹น ๋ฐฉ๋ฒ•์€ ๋งค์šฐ ์œ ์‚ฌํ•˜๋ฏ€๋กœ ์—ฌ๊ธฐ์„œ๋Š” ์ธ์šฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

 

(mesh.cpp:335)

uint Mesh::FindRotation(float AnimationTime, const aiNodeAnim* pNodeAnim)
{
    assert(pNodeAnim->mNumRotationKeys > 0);

    for (uint i = 0 ; i < pNodeAnim->mNumRotationKeys - 1 ; i++) {
        if (AnimationTime < (float)pNodeAnim->mRotationKeys[i + 1].mTime) {
            return i;
        }
    }

    assert(0);
}

์ด ์œ ํ‹ธ๋ฆฌํ‹ฐ ๋ฐฉ๋ฒ•์€ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์‹œ๊ฐ„ ์ง์ „์˜ ํ‚ค ํšŒ์ „์„ ์ฐพ์Šต๋‹ˆ๋‹ค. N ํ‚ค ํšŒ์ „์ด ์žˆ์œผ๋ฉด ๊ฒฐ๊ณผ๋Š” 0์—์„œ N-2๊ฐ€ ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์• ๋‹ˆ๋ฉ”์ด์…˜ ์‹œ๊ฐ„์€ ํ•ญ์ƒ ์ฑ„๋„์˜ ์ง€์† ์‹œ๊ฐ„ ๋‚ด์— ํฌํ•จ๋˜๋ฏ€๋กœ ๋งˆ์ง€๋ง‰ ํ‚ค(N-1)๋Š” ์œ ํšจํ•œ ๊ฒฐ๊ณผ๊ฐ€ ๋  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

 

(skihnning.vs)

#version 330

layout (location = 0) in vec3 Position;
layout (location = 1) in vec2 TexCoord;
layout (location = 2) in vec3 Normal;
layout (location = 3) in ivec4 BoneIDs;
layout (location = 4) in vec4 Weights;

out vec2 TexCoord0;
out vec3 Normal0;
out vec3 WorldPos0;

const int MAX_BONES = 200;

uniform mat4 gWVP;
uniform mat4 gWorld;
uniform mat4 gBones[MAX_BONES];

void main()
{
    mat4 BoneTransform = gBones[BoneIDs[0]] * Weights[0];
    BoneTransform += gBones[BoneIDs[1]] * Weights[1];
    BoneTransform += gBones[BoneIDs[2]] * Weights[2];
    BoneTransform += gBones[BoneIDs[3]] * Weights[3];

    vec4 PosL = BoneTransform * vec4(Position, 1.0);
    gl_Position = gWVP * PosL;
    TexCoord0 = TexCoord;
    vec4 NormalL = BoneTransform * vec4(Normal, 0.0);
    Normal0 = (gWorld * NormalL).xyz;
    WorldPos0 = (gWorld * PosL).xyz;
}

์ด์ œ ๋งค์‰ฌ ํด๋ž˜์Šค์˜ ๋ณ€๊ฒฝ ์ž‘์—…์„ ๋งˆ์ณค์œผ๋ฏ€๋กœ ์‰์ด๋” ์ˆ˜์ค€์—์„œ ์ˆ˜ํ–‰ํ•ด์•ผ ํ•  ์ž‘์—…์„ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

๋จผ์ € VSInput ๊ตฌ์กฐ์— ๊ณจ๊ฒฉ ID ๋ฐ ๊ฐ€์ค‘์น˜ ๋ฐฐ์—ด์„ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ์œผ๋กœ, ๊ณจ๊ฒฉ ๋ณ€ํ™˜์„ ํฌํ•จํ•˜๋Š” ์ƒˆ๋กœ์šด ๊ท ์ผ ๋ฐฐ์—ด์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์‰์ด๋” ์ž์ฒด์—์„œ ์šฐ๋ฆฌ๋Š” ์ •์ ์˜ ๋ผˆ ๋ณ€ํ™˜ ํ–‰๋ ฌ๊ณผ ๊ฐ€์ค‘์น˜์˜ ์กฐํ•ฉ์œผ๋กœ ์ตœ์ข… ๋ผˆ ๋ณ€ํ™˜์„ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค. ์ด ์ตœ์ข… ํ–‰๋ ฌ์€ ์œ„์น˜์™€ ๋…ธ๋ง์„ ๊ณจ๊ฒฉ ๊ณต๊ฐ„์—์„œ ๋กœ์ปฌ ๊ณต๊ฐ„์œผ๋กœ ๋ณ€ํ™˜ํ•˜๋Š”๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ ๋ถ€ํ„ฐ ๋ชจ๋“  ๊ฒƒ์ด ๋™์ผํ•ฉ๋‹ˆ๋‹ค.

 

(tutorial38.cpp:140)

float RunningTime = (float)((double)GetCurrentTimeMillis() - (double)m_startTime) / 1000.0f;

m_mesh.BoneTransform(RunningTime, Transforms);

for (uint i = 0 ; i < Transforms.size() ; i++) {
    m_pEffect->SetBoneTransform(i, Transforms[i]);
}

์šฐ๋ฆฌ๊ฐ€ ๋งˆ์ง€๋ง‰์œผ๋กœ ํ•ด์•ผ ํ•  ์ผ์€ ์ด ๋ชจ๋“  ๊ฒƒ๋“ค์„ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ฝ”๋“œ์— ํ†ตํ•ฉํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ด ์ž‘์—…์€ ์œ„์˜ ๊ฐ„๋‹จํ•œ ์ฝ”๋“œ๋กœ ์ˆ˜ํ–‰๋ฉ๋‹ˆ๋‹ค. ์ด ์ž‘์—…์€ ์œ„์˜ ๊ฐ„๋‹จํ•œ ์ฝ”๋“œ๋กœ ์ˆ˜ํ–‰๋ฉ๋‹ˆ๋‹ค.

GetCurrentTimeMillis() ํ•จ์ˆ˜๋Š” ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ ์‹œ์ž‘ ์ดํ›„์˜ ์‹œ๊ฐ„์„ ๋ฐ€๋ฆฌ์ดˆ ๋‹จ์œ„๋กœ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค(๋ถ„์ˆ˜๋ฅผ ์ˆ˜์šฉํ•˜๋ ค๋ฉด ๋ถ€๋™ ์†Œ์ˆ˜์ ์„ ์ฐธ๊ณ ํ•˜์‹ญ์‹œ์˜ค).

 

๋ชจ๋“  ์ž‘์—…์„ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ˆ˜ํ–‰ํ•œ ๊ฒฝ์šฐ ์ตœ์ข… ๊ฒฐ๊ณผ๋Š” ์ด๊ฒƒ๊ณผ ์œ ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

 

์†Œ์Šค ์ฝ”๋“œ ์ฃผ์†Œ : https://github.com/emeiri/ogldev/tree/master/tutorial28_youtube

๋ฐ˜์‘ํ˜•

'๊ณต๋ถ€ > OpenGL ์ž๋ฃŒ ๋ฒˆ์—ญ' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

[๋ฒˆ์—ญ] Tutorial 16 : Shadow mapping  (0) 2023.04.23
728x90

์›๋ณธ : http://www.opengl-tutorial.org/kr/intermediate-tutorials/tutorial-16-shadow-mapping/

 

ํŠœํ† ๋ฆฌ์–ผ 15์—์„œ๋Š” ์ •์  ์กฐ๋ช…์„ ํฌํ•จํ•˜๋Š” ๋ผ์ดํŠธ ๋งต์„ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•์„ ๋ฐฐ์› ์Šต๋‹ˆ๋‹ค.

์•„์ฃผ ๋ฉ‹์ง„ ๊ทธ๋ฆผ์ž๋ฅผ ๋งŒ๋“ค์–ด๋‚ด๊ธฐ๋Š” ํ•˜์ง€๋งŒ, ์• ๋‹ˆ๋ฉ”์ด์…˜ ๋ชจ๋ธ์€ ๋‹ค๋ฃจ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

 

Shadow Map์€ ํ˜„์žฌ(2016๋…„ ๊ธฐ์ค€) ๋™์  Shadow๋ฅผ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. ์ด ๋ฐฉ๋ฒ•์˜ ์ข‹์€ ์ ์€ ์ž‘์—…์„ ์‹œ์ž‘ํ•˜๊ธฐ์— ๊ฝค ์‰ฝ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋‚˜์œ์ ์€ ์ œ๋Œ€๋กœ ์ž‘์—… ํ•˜๊ธฐ๊ฐ€ ๋งค์šฐ ์–ด๋ ต๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

 

์ด ํŠœํ† ๋ฆฌ์–ผ์—์„œ๋Š” ๋จผ์ € ๊ธฐ๋ณธ ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์†Œ๊ฐœํ•˜๊ณ  ๋‹จ์ ์„ ํ™•์ธํ•œ ๋‹ค์Œ ๋ช‡ ๊ฐ€์ง€ ๊ธฐ์ˆ ์„ ๊ตฌํ˜„ํ•˜์—ฌ ๋” ๋‚˜์€ ๊ฒฐ๊ณผ๋ฅผ ์–ป์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. Shadow Map์„ ์ž‘์„ฑํ•  ๋‹น์‹œ(2012๋…„)์—๋Š” ์—ฌ์ „ํžˆ ๋งŽ์€ ์—ฐ๊ตฌ๊ฐ€ ์ง„ํ–‰ ์ค‘์ธ ์ฃผ์ œ์ด๊ธฐ ๋•Œ๋ฌธ์— ํ•„์š”์— ๋”ฐ๋ผ ์ž์ฒด Shadow Map์„ ๋”์šฑ ๊ฐœ์„ ํ•  ์ˆ˜ ์žˆ๋Š” ๋ช‡ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์„ ์•Œ๋ ค๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค.

 

๊ธฐ๋ณธ Shadowmap

๊ธฐ๋ณธ Shadow Map ์•Œ๊ณ ๋ฆฌ์ฆ˜์€ ๋‘ ๊ฐœ์˜ ํŒจ์Šค๋กœ ๊ตฌ์„ฑ๋ฉ๋‹ˆ๋‹ค.

๋จผ์ €, ์žฅ๋ฉด์€ ๋น›์˜ ๊ด€์ ์—์„œ ๋ Œ๋”๋ง๋ฉ๋‹ˆ๋‹ค. ๊ฐ fragment์˜ ๊นŠ์ด๋งŒ ๊ณ„์‚ฐ๋ฉ๋‹ˆ๋‹ค.

๊ทธ๋Ÿฐ ๋‹ค์Œ ์”ฌ(scene)์ด ํ‰์†Œ์™€ ๊ฐ™์ด ๋ Œ๋”๋ง๋˜์ง€๋งŒ ์ถ”๊ฐ€ ํ…Œ์ŠคํŠธ๋ฅผ ํ†ตํ•ด ํ˜„์žฌ ์กฐ๊ฐ์ด ๊ทธ๋ฆผ์ž์— ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.

 

"๊ทธ๋ฆผ์ž ์†์— ์žˆ๋Š” ๋ฌผ์ฒด" ํ…Œ์ŠคํŠธ๋Š” ์‚ฌ์‹ค ๋งค์šฐ ๊ฐ„๋‹จํ•ฉ๋‹ˆ๋‹ค.

ํ˜„์žฌ ์ƒ˜ํ”Œ์ด ๋™์ผํ•œ ์ ์—์„œ ๊ทธ๋ฆผ์ž ๋งต๋ณด๋‹ค ๋น›์—์„œ ๋ฉ€๋ฆฌ ๋–จ์–ด์ ธ ์žˆ์œผ๋ฉด ์”ฌ(scene)์— ๋น›์— ๋” ๊ฐ€๊นŒ์šด ๊ฐœ์ฒด๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์Œ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

์ฆ‰, ํ˜„์žฌ fragment๋Š” ๊ทธ๋ฆผ์ž์— ์žˆ์Šต๋‹ˆ๋‹ค.

 

๋‹ค์Œ ์ด๋ฏธ์ง€๋Š” ์›๋ฆฌ๋ฅผ ์ดํ•ดํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

Shadowmap ๋ Œ๋”๋งํ•˜๊ธฐ

์ด ํŠœํ† ๋ฆฌ์–ผ์—์„œ๋Š” directional lights(๋ชจ๋“  ๊ด‘์„ ์ด ํ‰ํ–‰ํ•˜๋‹ค๊ณ  ๊ฐ„์ฃผ๋  ์ˆ˜ ์žˆ์„ ์ •๋„๋กœ ๋ฉ€๋ฆฌ ๋–จ์–ด์ ธ ์žˆ๋Š” ์กฐ๋ช…)๋งŒ ๊ณ ๋ คํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ Shadow Map ๋ Œ๋”๋ง์€ ์ง๊ต ํˆฌ์˜ ํ–‰๋ ฌ์„ ์‚ฌ์šฉํ•˜์—ฌ ์ˆ˜ํ–‰๋ฉ๋‹ˆ๋‹ค. ์ง๊ต ํ–‰๋ ฌ์€ ์›๊ทผ๋ฒ•์ด ๊ณ ๋ ค๋˜์ง€ ์•Š๋Š”๋‹ค๋Š” ์ ์„ ์ œ์™ธํ•˜๊ณ ๋Š” ์ผ๋ฐ˜์ ์ธ ์›๊ทผ๋ฒ• ํˆฌ์˜ ํ–‰๋ ฌ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๋ฌผ์ฒด๋Š” ์นด๋ฉ”๋ผ ๊ฐ€๊นŒ์ด์— ์žˆ๋Š” ๋ฉ€๋ฆฌ์žˆ๋“  ๋™์ผํ•˜๊ฒŒ ๋ณด์ž…๋‹ˆ๋‹ค.

 

๋ Œ๋” ๋Œ€์ƒ ๋ฐ MVP ๋งคํŠธ๋ฆญ์Šค ์„ค์ •

ํŠœํ† ๋ฆฌ์–ผ 14๋ถ€ํ„ฐ๋Š” ์‰์ด๋”์—์„œ ๋‚˜์ค‘์— ์•ก์„ธ์Šคํ•˜๊ธฐ ์œ„ํ•ด ์žฅ๋ฉด์„ ํ…์Šค์ฒ˜๋กœ ๋ Œ๋”๋งํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ฐฐ์› ์Šต๋‹ˆ๋‹ค.

 

์—ฌ๊ธฐ์„œ๋Š” Shadow Map์„ ํฌํ•จํ•˜๊ธฐ ์œ„ํ•ด 1024x1024 16๋น„ํŠธ depth ํ…์Šค์ฒ˜๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

Shadow Map์—๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ 16๋น„ํŠธ๋กœ ์ถฉ๋ถ„ํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ฐ’๋“ค์„ ์ž์œ ๋กญ๊ฒŒ ์‹œํ—˜ํ•ด๋ณด์„ธ์š”. ๋‚˜์ค‘์— ์ƒ˜ํ”Œ๋งํ•ด์•ผ ํ•˜๋ฏ€๋กœ depth ๋ Œ๋” ๋ฒ„ํผ๊ฐ€ ์•„๋‹Œ depth ํ…์Šค์ฒ˜๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

 

// The framebuffer, which regroups 0, 1, or more textures, and 0 or 1 depth buffer.
 GLuint FramebufferName = 0;
 glGenFramebuffers(1, &FramebufferName);
 glBindFramebuffer(GL_FRAMEBUFFER, FramebufferName);

 // Depth texture. Slower than a depth buffer, but you can sample it later in your shader
 GLuint depthTexture;
 glGenTextures(1, &depthTexture);
 glBindTexture(GL_TEXTURE_2D, depthTexture);
 glTexImage2D(GL_TEXTURE_2D, 0,GL_DEPTH_COMPONENT16, 1024, 1024, 0,GL_DEPTH_COMPONENT, GL_FLOAT, 0);
 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

 glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthTexture, 0);

 glDrawBuffer(GL_NONE); // No color buffer is drawn to.

 // Always check that our framebuffer is ok
 if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
 return false;

๋น›์˜ ๊ด€์ ์—์„œ ์žฅ๋ฉด์„ ๋ Œ๋”๋งํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋˜๋Š” MVP ๋งคํŠธ๋ฆญ์Šค๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๊ณ„์‚ฐ๋ฉ๋‹ˆ๋‹ค:

  • Projection ํ–‰๋ ฌ์€ X,Y ๋ฐ Z ์ถ•์—์„œ ๊ฐ๊ฐ ์ถ• ์ •๋ ฌ ์ƒ์ž(-10, 10), (-10,10), (-10, 20)์˜ ๋ชจ๋“  ๊ฒƒ์„ ํฌํ•จํ•˜๋Š” ์ง๊ต ํ–‰๋ ฌ์ž…๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๊ฐ’์€ ์ „์ฒด *๊ฐ€์‹œ์ ์ธ* ์žฅ๋ฉด์„ ํ•ญ์ƒ ๋ณผ ์ˆ˜ ์žˆ๋„๋ก ๋งŒ๋“ค์–ด์กŒ์œผ๋ฉฐ, ์ด์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์ถ”๊ฐ€ ์ง„ํ–‰ ์„น์…˜์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.
  • View ํ–‰๋ ฌ์€ ์นด๋ฉ”๋ผ ๊ณต๊ฐ„์—์„œ ์กฐ๋ช… ๋ฐฉํ–ฅ์ด -Z๊ฐ€ ๋˜๋„๋ก ์›”๋“œ๋ฅผ ํšŒ์ „์‹œํ‚ต๋‹ˆ๋‹ค(ํŠœํ† ๋ฆฌ์–ผ 3 ์ฐธ์กฐ)
  • Model ํ–‰๋ ฌ์€ ์›ํ•˜๋Š” ๋Œ€๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
glm::vec3 lightInvDir = glm::vec3(0.5f,2,2);

 // Compute the MVP matrix from the light's point of view
 glm::mat4 depthProjectionMatrix = glm::ortho<float>(-10,10,-10,10,-10,20);
 glm::mat4 depthViewMatrix = glm::lookAt(lightInvDir, glm::vec3(0,0,0), glm::vec3(0,1,0));
 glm::mat4 depthModelMatrix = glm::mat4(1.0);
 glm::mat4 depthMVP = depthProjectionMatrix * depthViewMatrix * depthModelMatrix;

 // Send our transformation to the currently bound shader,
 // in the "MVP" uniform
 glUniformMatrix4fv(depthMatrixID, 1, GL_FALSE, &depthMVP[0][0])

์‰์ด๋”

์ด ํŒจ์Šค ๋™์•ˆ ์‚ฌ์šฉ๋˜๋Š” ์‰์ด๋”๋Š” ๋งค์šฐ ๊ฐ„๋‹จํ•ฉ๋‹ˆ๋‹ค. vertex ์‰์ด๋”๋Š” ๊ท ์งˆ ์ขŒํ‘œ์—์„œ ์ •์ ์˜ ์œ„์น˜๋ฅผ ๊ฐ„๋‹จํžˆ ๊ณ„์‚ฐํ•˜๋Š” ํ†ต๊ณผ ์‰์ด๋”์ž…๋‹ˆ๋‹ค:

#version 330 core

// Input vertex data, different for all executions of this shader.
layout(location = 0) in vec3 vertexPosition_modelspace;

// Values that stay constant for the whole mesh.
uniform mat4 depthMVP;

void main(){
 gl_Position =  depthMVP * vec4(vertexPosition_modelspace,1);
}

fragment ์‰์ด๋”๋Š” ๋‹จ์ˆœํ•˜๊ธฐ ๋•Œ๋ฌธ์— fragment์˜ ๊นŠ์ด๋ฅผ ์œ„์น˜ 0(์ฆ‰, ๊นŠ์ด ํ…์Šค์ฒ˜)์— ๊ธฐ๋กํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

#version 330 core

// Ouput data
layout(location = 0) out float fragmentdepth;

void main(){
    // Not really needed, OpenGL does it anyway
    fragmentdepth = gl_FragCoord.z;
}

Shadow Map์„ ๋ Œ๋”๋งํ•˜๋Š” ์†๋„๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ ์ผ๋ฐ˜ ๋ Œ๋”๋ง๋ณด๋‹ค ๋‘ ๋ฐฐ ์ด์ƒ ๋น ๋ฆ…๋‹ˆ๋‹ค. ๊นŠ์ด์™€ ์ƒ‰์ƒ ๋Œ€์‹  ์ •๋ฐ€๋„๊ฐ€ ๋‚ฎ์€ ๊นŠ์ด๋งŒ ๊ธฐ๋ก๋˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ๋ฉ”๋ชจ๋ฆฌ ๋Œ€์—ญํญ์€ GPU์—์„œ ๊ฐ€์žฅ ํฐ ์„ฑ๋Šฅ ๋ฌธ์ œ์ž…๋‹ˆ๋‹ค.

๊ฒฐ๊ณผ

๊ฒฐ๊ณผ ํ…์Šค์ฒ˜๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

์–ด๋‘์šด ์ƒ‰์ƒ์€ ์ž‘์€ z๋ฅผ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๋ฒฝ์˜ ์˜ค๋ฅธ์ชฝ ์ƒ๋‹จ ๋ชจ์„œ๋ฆฌ๊ฐ€ ์นด๋ฉ”๋ผ ๊ทผ์ฒ˜์— ์žˆ์Šต๋‹ˆ๋‹ค.

๋ฐ˜๋Œ€๋กœ ํฐ์ƒ‰์€ z=1(๋™์ผํ•œ ์ขŒํ‘œ)์„ ์˜๋ฏธํ•˜๋ฏ€๋กœ ๋งค์šฐ ๋ฉ€๋ฆฌ ๋–จ์–ด์ ธ ์žˆ์Šต๋‹ˆ๋‹ค.

Shadowmap ์‚ฌ์šฉํ•˜๊ธฐ

๊ธฐ๋ณธ ์‰์ด๋”

์ด์ œ ํ‰์†Œ์˜ ์‰์ด๋”๋กœ ๋Œ์•„๊ฐ‘๋‹ˆ๋‹ค. ๊ณ„์‚ฐํ•˜๋Š” ๊ฐ fragment์— ๋Œ€ํ•ด Shadow Map ๋’ค์— ์žˆ๋Š”์ง€ ์—ฌ๋ถ€๋ฅผ ํ…Œ์ŠคํŠธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

 

์ด๋ฅผ ์œ„ํ•ด Shadow Map์„ ๋งŒ๋“ค ๋•Œ ์‚ฌ์šฉํ•œ ๊ณต๊ฐ„๊ณผ ๋™์ผํ•œ ๊ณต๊ฐ„์—์„œ ํ˜„์žฌ fragment์˜ ์œ„์น˜๋ฅผ ๊ณ„์‚ฐํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์ผ๋ฐ˜์ ์ธ MVP ํ–‰๋ ฌ๋กœ ํ•œ ๋ฒˆ ๋ณ€ํ™˜ํ•˜๊ณ  ๊นŠ์ด MVP ํ–‰๋ ฌ๋กœ ํ•œ ๋ฒˆ ๋ณ€ํ™˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

 

ํ•˜์ง€๋งŒ ์•ฝ๊ฐ„์˜ ์†์ž„์ˆ˜๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ •์  ์œ„์น˜์— ๊นŠ์ด๋ฅผ ๊ณฑํ•˜๋ฉด MVP๋Š” [-1,1]์— ์žˆ๋Š” ๊ท ์ผํ•œ ์ขŒํ‘œ๋ฅผ ์ œ๊ณตํ•˜์ง€๋งŒ ํ…์Šค์ฒ˜ ์ƒ˜ํ”Œ๋ง์€ [0,1]์—์„œ ์ˆ˜ํ–‰ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

 

์˜ˆ๋ฅผ ๋“ค์–ด, ํ™”๋ฉด ์ค‘์•™์— ์žˆ๋Š” fragment๋Š” ๊ท ์ผํ•œ ์ขŒํ‘œ๋กœ (0,0)์— ์žˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์งˆ๊ฐ ์ค‘๊ฐ„์€ ์ƒ˜ํ”Œ๋งํ•ด์•ผ ํ•˜๋ฏ€๋กœ UV๋Š” (0.5,05)์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

 

์ด๊ฒƒ์€ fragment shader์—์„œ ์ง์ ‘ fetch ์ขŒํ‘œ๋ฅผ ์กฐ์ •ํ•˜์—ฌ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ๋‹จ์ˆœํžˆ ์ขŒํ‘œ๋ฅผ 2(๋Œ€๊ฐ์„  : [-1.1] -> [-0.5,0.5])๋กœ ๋‚˜๋ˆ„๊ณ  ๋ณ€ํ™˜ํ•˜๋Š” ํ–‰๋ ฌ(์•„๋ž˜ ํ–‰ : [-0.5,0.5]->[0,1])์„ ๊ณฑํ•˜๋Š” ๊ฒƒ์ด ๋” ํšจ์œจ์ ์ž…๋‹ˆ๋‹ค.

glm::mat4 biasMatrix(
0.5, 0.0, 0.0, 0.0,
0.0, 0.5, 0.0, 0.0,
0.0, 0.0, 0.5, 0.0,
0.5, 0.5, 0.5, 1.0
);
glm::mat4 depthBiasMVP = biasMatrix*depthMVP;

์ด์ œ vertex ์‰์ด๋”๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด์ „๊ณผ ๋™์ผํ•˜์ง€๋งŒ 1:1์ด ์•„๋‹Œ 2๊ฐœ์˜ ์œ„์น˜๋ฅผ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค:

  • gl_Position ์€ ํ˜„์žฌ ์นด๋ฉ”๋ผ์—์„œ ๋ณผ ์ˆ˜ ์žˆ๋Š” ์ •์ ์˜ ์œ„์น˜์ž…๋‹ˆ๋‹ค.
  • ShadowCoord๋Š” ๋งˆ์ง€๋ง‰ ์นด๋ฉ”๋ผ(๋น›)์—์„œ ๋ณผ ์ˆ˜ ์žˆ๋Š” ์ •์ ์˜ ์œ„์น˜์ž…๋‹ˆ๋‹ค.
// Output position of the vertex, in clip space : MVP * position
gl_Position =  MVP * vec4(vertexPosition_modelspace,1);

// Same, but with the light's view matrix
ShadowCoord = DepthBiasMVP * vec4(vertexPosition_modelspace,1);

๊ทธ๋Ÿฐ ๋‹ค์Œ fragment ์‰์ด๋”๋Š” ๋งค์šฐ ๊ฐ„๋‹จํ•ฉ๋‹ˆ๋‹ค:

  • texture(shadowMap, ShadowCoord.xy).z ๋Š” ๋น›๊ณผ ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด ๋น›์„ ์ฐจ๋‹จํ•˜๋Š” ํ์ƒ‰์ž ์‚ฌ์ด์˜ ๊ฑฐ๋ฆฌ์ž…๋‹ˆ๋‹ค.
  • ShadowCoord.z๋Š” ๋น›๊ณผ ํ˜„์žฌ fragment ์‚ฌ์ด์˜ ๊ฑฐ๋ฆฌ์ž…๋‹ˆ๋‹ค.

...๋”ฐ๋ผ์„œ ํ˜„์žฌ fragment๊ฐ€ ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด ํ์ƒ‰์ž๋ณด๋‹ค ๋ฉ€๋ฆฌ ์žˆ์œผ๋ฉด, ์ด๊ฒƒ์€ ์šฐ๋ฆฌ๊ฐ€ (ํ•ด๋‹น ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด ํ์ƒ‰์ž์˜) ๊ทธ๋ฆผ์ž์— ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

float visibility = 1.0;
if ( texture( shadowMap, ShadowCoord.xy ).z  <  ShadowCoord.z){
    visibility = 0.5;
}

์šฐ๋ฆฌ๋Š” ์ด ์ง€์‹์„ ์ด์šฉํ•˜์—ฌ ์Œ์˜์„ ์ˆ˜์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋ฌผ๋ก  ์ฃผ๋ณ€ ์ƒ‰์ƒ์€ ์ˆ˜์ •๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ƒํ™œ ์†์˜ ๋ชฉ์ ์€ ์šฐ๋ฆฌ๊ฐ€ ๊ทธ๋ฆผ์ž ์†์— ์žˆ์„ ๋•Œ๋„ ๋“ค์–ด์˜ค๋Š” ๋น›์„ ๊ฐ€์งœ๋กœ ๋งŒ๋“œ๋Š” ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค(๋˜๋Š” ๋ชจ๋“  ๊ฒƒ์ด ์ˆœ์ˆ˜ํ•œ ๊ฒ€์€์ƒ‰์ผ ๊ฒƒ์ž…๋‹ˆ๋‹ค)

color =
 // Ambient : simulates indirect lighting
 MaterialAmbientColor +
 // Diffuse : "color" of the object
 visibility * MaterialDiffuseColor * LightColor * LightPower * cosTheta+
 // Specular : reflective highlight, like a mirror
 visibility * MaterialSpecularColor * LightColor * LightPower * pow(cosAlpha,5);

๊ฒฐ๊ณผ - ์‰๋„์šฐ ์•„ํฌ๋„ค

๋‹ค์Œ์€ ํ˜„์žฌ ์ฝ”๋“œ์˜ ๊ฒฐ๊ณผ์ž…๋‹ˆ๋‹ค. ๋ถ„๋ช…ํžˆ, ์•„์ด๋””์–ด๋Š” ๋›ฐ์–ด๋‚˜์ง€๋งŒ , ํ’ˆ์งˆ์€ ๋ฐ›์•„๋“ค์ผ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

์ด ์ด๋ฏธ์ง€์˜ ๊ฐ ๋ฌธ์ œ๋ฅผ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

์ฝ”๋“œ์—๋Š” ShadowMap๊ณผ ShadowMap_simple์˜ ๋‘ ๊ฐ€์ง€ ํ”„๋กœ์ ํŠธ๊ฐ€ ์žˆ์œผ๋ฉฐ, ๋‘˜ ์ค‘ ๋” ๋งˆ์Œ์— ๋“œ๋Š” ๊ฒƒ์œผ๋กœ ์‹œ์ž‘ํ•˜์„ธ์š”.

๊ฐ„๋‹จํ•œ ๋ฒ„์ „์€ ์œ„์˜ ์ด๋ฏธ์ง€๋งŒํผ ํ’ˆ์งˆ์ด ์ข‹์ง€ ์•Š์ง€๋งŒ ์ดํ•ดํ•˜๊ธฐ๊ฐ€ ๋” ์‰ฝ์Šต๋‹ˆ๋‹ค.

๋ฌธ์ œ์ ๋“ค

Shadow acne

๊ฐ€์žฅ ๋ช…๋ฐฑํ•œ ๋ฌธ์ œ์ ์€ shadow acne์ด๋‹ค:

์ด ํ˜„์ƒ์€ ๊ฐ„๋‹จํ•œ ์ด๋ฏธ์ง€๋กœ ์‰ฝ๊ฒŒ ์„ค๋ช…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

์ด์— ๋Œ€ํ•œ ์ผ๋ฐ˜์ ์ธ "์ˆ˜์ •"์€ error margin์„ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ํ˜„์žฌ fragment์˜ depth(๋‹ค์‹œ๋งํ•ด, ๋น› ๊ณต๊ฐ„)๊ฐ€ ๋ผ์ดํŠธ๋งต ๊ฐ’์—์„œ ์ •๋ง ๋ฉ€๋ฆฌ ๋–จ์–ด์ ธ ์žˆ๋Š” ๊ฒฝ์šฐ์—๋งŒ ์Œ์˜ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ํŽธํ–ฅ์„ ์ถ”๊ฐ€ํ•˜์—ฌ ์ด๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค:

float bias = 0.005;
float visibility = 1.0;
if ( texture( shadowMap, ShadowCoord.xy ).z  <  ShadowCoord.z-bias){
    visibility = 0.5;
}

๊ฒฐ๊ณผ๋ฌผ์€ ํ›จ์”ฌ ๋‚˜์•„์ง‘๋‹ˆ๋‹ค:

ํ•˜์ง€๋งŒ, ์—ฌ๋Ÿฌ๋ถ„์€ ์šฐ๋ฆฌ์˜ ํŽธ๊ฒฌ ๋•Œ๋ฌธ์— ๋•…๊ณผ ๋ฒฝ ์‚ฌ์ด์˜ ์ธ๊ณต๋ฌผ์ด ๋” ๋‚˜๋น ์กŒ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ์•„์ฐจ๋ฆด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ฒŒ๋‹ค๊ฐ€, 0.005์˜ ํŽธํ–ฅ์€ ๋•…์—์„œ๋Š” ๋„ˆ๋ฌด ๋งŽ์ด ๋ณด์ด์ง€๋งŒ, ๊ณก๋ฉด์—์„œ๋Š” ์ถฉ๋ถ„ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค: ๋ช‡๋ช‡ ์ธ๊ณต๋ฌผ๋“ค์€ ์‹ค๋ฆฐ๋”์™€ ๊ตฌ์— ๋‚จ์•„ ์žˆ์Šต๋‹ˆ๋‹ค.

 

์ผ๋ฐ˜์ ์ธ ์ ‘๊ทผ ๋ฐฉ์‹์€ ๊ธฐ์šธ๊ธฐ์— ๋”ฐ๋ผ ์น˜์šฐ์นจ์„ ์ˆ˜์ •ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค:

float bias = 0.005*tan(acos(cosTheta)); // cosTheta is dot( n,l ), clamped between 0 and 1
bias = clamp(bias, 0,0.01);

Shadow acne๋Š” ์ด์ œ, ๊ตฌ๋ถ€๋Ÿฌ์ง„ ํ‘œ๋ฉด์—์„œ๋„ ์‚ฌ๋ผ์กŒ์Šต๋‹ˆ๋‹ค.

์ง€์˜ค๋ฉ”ํŠธ๋ฆฌ์— ๋”ฐ๋ผ ์ž‘๋™ํ•˜๊ฑฐ๋‚˜ ์ž‘๋™ํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ๋Š” ๋˜ ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์€ ShadowMap์—์„œ ๋’ท๋ฉด๋งŒ ๋ Œ๋”๋งํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์šฐ๋ฆฌ๊ฐ€ ๋‘๊บผ์šด ๋ฒฝ์„ ๊ฐ€์ง„ ํŠน๋ณ„ํ•œ ๊ธฐํ•˜ํ•™์  ๊ตฌ์กฐ(๋‹ค์Œ ์„น์…˜ - Peter Panning ์ฐธ์กฐ)๋ฅผ ๊ฐ–๋„๋ก ๊ฐ•์š”ํ•˜์ง€๋งŒ, ์ ์–ด๋„ acne๋Š” ๊ทธ๋ฆผ์ž์— ์žˆ๋Š” ํ‘œ๋ฉด์— ์žˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

ShadowMap์„ ๋ Œ๋”๋งํ•  ๋•Œ ์ „๋ฉด ์‚ผ๊ฐํ˜•์„ ์ž˜๋ผ๋ƒ…๋‹ˆ๋‹ค.

        // We don't use bias in the shader, but instead we draw back faces,
        // which are already separated from the front faces by a small distance
        // (if your geometry is made this way)
        glCullFace(GL_FRONT); // Cull front-facing triangles -> draw only back-facing triangles

๊ทธ๋ฆฌ๊ณ  ์”ฌ(scene)์„ ๋ Œ๋”๋งํ•  ๋•Œ๋Š” ์ •์ƒ์ ์œผ๋กœ ๋ Œ๋”๋งํ•ฉ๋‹ˆ๋‹ค(๋’ท๋ฉด ์ ˆ๋‹จ)

         glCullFace(GL_BACK); // Cull back-facing triangles -> draw only front-facing triangles

์ด ๋ฐฉ๋ฒ•์€ ํŽธํ–ฅ ์™ธ์—๋„ ์ฝ”๋“œ์— ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

Peter Panning

์šฐ๋ฆฌ๋Š” ๋” ์ด์ƒ Shadow acne๊ฐ€ ์—†์ง€๋งŒ, ์—ฌ์ „ํžˆ ์ž˜๋ชป๋œ ์Œ์˜์˜ ๋•…์„ ๊ฐ€์ง€๊ณ  ์žˆ๊ณ , ๋ฒฝ์„ ๋‚ ์•„๋‹ค๋‹ˆ๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ด๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค(์ดํ›„ "ํ”ผํ„ฐ ํŒจ๋‹"์ด๋ผ๋Š” ์šฉ์–ด). ์‚ฌ์‹ค, ํŽธํ–ฅ(bias)์„ ๋”ํ•˜๋Š” ๊ฒƒ์€ ์ƒํ™ฉ์„ ๋” ์•…ํ™”์‹œ์ผฐ์Šต๋‹ˆ๋‹ค.

์ด๊ฒƒ์€ ๋งค์šฐ ์‰ฝ๊ฒŒ ๊ณ ์น  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์–‡์€ ๊ธฐํ•˜ํ•™์  ๊ตฌ์กฐ๋ฅผ ํ”ผํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. ๋‘ ๊ฐ€์ง€ ์ด์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

  • ๋จผ์ €, ํ”ผํ„ฐ ํŒจ๋‹์„ ํ•ด๊ฒฐํ•ฉ๋‹ˆ๋‹ค. ๊ธฐํ•˜ํ•™์ด ์—ฌ๋Ÿฌ๋ถ„์˜ ํŽธ๊ฒฌ๋ณด๋‹ค ๋” ๊นŠ๋‹ค๋ฉด, ์—ฌ๋Ÿฌ๋ถ„์€ ์ค€๋น„๊ฐ€ ๋‹ค ๋œ ๊ฒƒ์ž…๋‹ˆ๋‹ค.
  • ๋‘ ๋ฒˆ์งธ๋กœ, ๋ผ์ดํŠธ๋งต์„ ๋ Œ๋”๋งํ•  ๋•Œ ํ›„๋ฉด ์ ˆ๋‹จ ๊ธฐ๋Šฅ์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์™œ๋ƒํ•˜๋ฉด ์ด์ œ ๋ฒฝ์˜ ๋‹ค๊ฐํ˜•์ด ๋น›์„ ํ–ฅํ•˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ์ด ๋‹ค๊ฐํ˜•์€ ๋’ท๋ฉด ์ ˆ๋‹จ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ Œ๋”๋ง๋˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹จ์ ์€ ๋ Œ๋”๋งํ•  ์‚ผ๊ฐํ˜•์ด ๋” ๋งŽ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค(ํ”„๋ ˆ์ž„๋‹น ๋‘ ๋ฒˆ!)

Aliasing

์ด ๋‘ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ , ์—ฌ๋Ÿฌ๋ถ„์€ ๊ทธ๋ฆผ์ž์˜ ๊ฒฝ๊ณ„์— ์—ฌ์ „ํžˆ ์•จ๋ฆฌ์–ด์‹ฑ์ด ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ์•„์ฐจ๋ฆด ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ฆ‰, ํ•œ ํ”ฝ์…€์€ ํฐ์ƒ‰์ด๊ณ  ๋‹ค์Œ ํ”ฝ์…€์€ ๊ฒ€์€์ƒ‰์ž…๋‹ˆ๋‹ค. ๊ทธ ์‚ฌ์ด์— ๋งค๋„๋Ÿฌ์šด ์ „ํ™˜์ด ์—†์Šต๋‹ˆ๋‹ค.

PCF

์ด๋ฅผ ๊ฐœ์„ ํ•˜๋Š” ๊ฐ€์žฅ ์‰ฌ์šด ๋ฐฉ๋ฒ•์€ ShadowMap์˜ ์ƒ˜ํ”Œ๋Ÿฌ ์œ ํ˜•์„ Sampler2D ์‰์ด๋”๋กœ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๊ฒฐ๊ณผ์ ์œผ๋กœ ShadowMap์„ ํ•œ ๋ฒˆ ์ƒ˜ํ”Œ๋งํ•˜๋ฉด ํ•˜๋“œ์›จ์–ด๊ฐ€ ์‹ค์ œ๋กœ ์ธ์ ‘ํ•œ ํ…์…€๋„ ์ƒ˜ํ”Œ๋งํ•˜๊ณ  ๋ชจ๋“  ํ…์…€์— ๋Œ€ํ•ด ๋น„๊ต๋ฅผ ์ˆ˜ํ–‰ํ•˜๋ฉฐ ๋น„๊ต ๊ฒฐ๊ณผ์˜ ์ด์ค‘ ์„ ํ˜• ํ•„ํ„ฐ๋ง๊ณผ ํ•จ๊ป˜ [0,1]์˜ ๋ถ€๋™ ์†Œ์ˆ˜์ ์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

 

์˜ˆ๋ฅผ ๋“ค์–ด, 0.5๋Š” ๋‘ ๊ฐœ์˜ ํ‘œ๋ณธ์ด ๊ทธ๋ฆผ์ž์— ์žˆ๊ณ  ๋‘ ๊ฐœ์˜ ํ‘œ๋ณธ์ด ๋น›์— ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

 

ํ•„ํ„ฐ๋ง๋œ ๊นŠ์ด ๋งต์˜ ๋‹จ์ผ ์ƒ˜ํ”Œ๋ง๊ณผ ๋™์ผํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค! ๋น„๊ต๋Š” ํ•ญ์ƒ true ๋˜๋Š” false๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. PCF๋Š” 4 "true ๋˜๋Š” false"์˜ ๋ณด๊ฐ„๊ฐ’์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

๋ณด์‹œ๋‹ค์‹œํ”ผ ๊ทธ๋ฆผ์ž ํ…Œ๋‘๋ฆฌ๋Š” ๋งค๋„๋Ÿฝ์ง€๋งŒ ๊ทธ๋ฆผ์ž ๋งต์˜ ํ…์Šค์ฒ˜๋Š” ์—ฌ์ „ํžˆ ๋ณด์ž…๋‹ˆ๋‹ค.

Poisson Sampling

์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ์‰ฌ์šด ๋ฐฉ๋ฒ•์€ ์‰๋„์šฐ ๋งต์„ ํ•œ ๋ฒˆ์ด ์•„๋‹ˆ๋ผ N๋ฒˆ ์ƒ˜ํ”Œ๋งํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. PCF์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋ฉด N์ด ์ž‘๋”๋ผ๋„ ๋งค์šฐ ์ข‹์€ ๊ฒฐ๊ณผ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ƒ˜ํ”Œ 4๊ฐœ์— ๋Œ€ํ•œ ์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค:

for (int i=0;i<4;i++){
  if ( texture( shadowMap, ShadowCoord.xy + poissonDisk[i]/700.0 ).z  <  ShadowCoord.z-bias ){
    visibility-=0.2;
  }
}

poissonDisk๋Š” ์˜ˆ๋ฅผ ๋“ค์–ด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ •์˜๋œ ์ƒ์ˆ˜ ๋ฐฐ์—ด์ž…๋‹ˆ๋‹ค:

vec2 poissonDisk[4] = vec2[](
  vec2( -0.94201624, -0.39906216 ),
  vec2( 0.94558609, -0.76890725 ),
  vec2( -0.094184101, -0.92938870 ),
  vec2( 0.34495938, 0.29387760 )
);

์ด ๋ฐฉ๋ฒ•์€ ํ†ต๊ณผํ•  ์‰๋„์šฐ ๋งต ์ƒ˜ํ”Œ ์ˆ˜์— ๋”ฐ๋ผ ์ƒ์„ฑ๋œ fragment๊ฐ€ ๋‹ค์†Œ ์–ด๋‘์›Œ์ง‘๋‹ˆ๋‹ค.

700.0 ์ƒ์ˆ˜๋Š” ํ‘œ๋ณธ์ด "ํ™•์‚ฐ"๋˜๋Š” ์ •๋„๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค. ๋„ˆ๋ฌด ์ ๊ฒŒ ํผ๋œจ๋ฆฌ๋ฉด ๋‹ค์‹œ ์•จ๋ฆฌ์–ด์‹ฑ์ด ๋ฐœ์ƒํ•˜๊ณ , ๋„ˆ๋ฌด ๋งŽ์ด ํผ๋œจ๋ฆฌ๋ฉด ๋ฐด๋”ฉ์ด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.*(์ด ์Šคํฌ๋ฆฐ์ƒท์€ ๋” ๊ทน์ ์ธ ํšจ๊ณผ๋ฅผ ์œ„ํ•ด PCF๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ๋Œ€์‹  16๊ฐœ์˜ ์ƒ˜ํ”Œ์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.*

Stratified Poisson Sampling

ํ”ฝ์…€๋งˆ๋‹ค ๋‹ค๋ฅธ ์ƒ˜ํ”Œ์„ ์„ ํƒํ•˜์—ฌ ์ด ๋ฐด๋”ฉ์„ ์ œ๊ฑฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‘ ๊ฐ€์ง€ ์ฃผ์š” ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๋‹ค : Stratified Poisson ๋˜๋Š” Rotated Poisson

Stratified๋Š” ์„œ๋กœ ๋‹ค๋ฅธ ํ‘œ๋ณธ์„ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค. Rotated๋Š” ํ•ญ์ƒ ๊ฐ™์€ ๊ฒƒ์„ ์‚ฌ์šฉํ•˜์ง€๋งŒ ๋žœ๋ค ํšŒ์ „์„ ์‚ฌ์šฉํ•˜์—ฌ ์„œ๋กœ ๋‹ค๋ฅด๊ฒŒ ๋ณด์ž…๋‹ˆ๋‹ค. ์ด ํŠœํ† ๋ฆฌ์–ผ์—์„œ๋Š” Stratified ๋ฒ„์ „๋งŒ ์„ค๋ช…ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

 

์ด์ „ ๋ฒ„์ „๊ณผ์˜ ์œ ์ผํ•œ ์ฐจ์ด์ ์€ ๋žœ๋ค ์ธ๋ฑ์Šค๋กœ poissonDisk๋ฅผ ์ธ๋ฑ์‹ฑํ•œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค:

    for (int i=0;i<4;i++){
        int index = // A random number between 0 and 15, different for each pixel (and each i !)
        visibility -= 0.2*(1.0-texture( shadowMap, vec3(ShadowCoord.xy + poissonDisk[index]/700.0,  (ShadowCoord.z-bias)/ShadowCoord.w) ));
    }

๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ฝ”๋“œ๋กœ ๋‚œ์ˆ˜๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ,[0,1]์—์„œ ๋‚œ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค:

    float dot_product = dot(seed4, vec4(12.9898,78.233,45.164,94.673));
    return fract(sin(dot_product) * 43758.5453);

์šฐ๋ฆฌ์˜ ๊ฒฝ์šฐ seed4๋Š” i(4๊ฐœ์˜ ์„œ๋กœ ๋‹ค๋ฅธ ์œ„์น˜์—์„œ ์ƒ˜ํ”Œ์„ ์ฑ„์ทจํ•  ์ˆ˜ ์žˆ๋„๋ก)์™€ ...์˜ ์กฐํ•ฉ์ด ๋ฉ๋‹ˆ๋‹ค.

gl_FragCord(ํ™”๋ฉด์˜ ํ”ฝ์…€ ์œ„์น˜)๋˜๋Š” Position_worldspace๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

        //  - A random sample, based on the pixel's screen location.
        //    No banding, but the shadow moves with the camera, which looks weird.
        int index = int(16.0*random(gl_FragCoord.xyy, i))%16;
        //  - A random sample, based on the pixel's position in world space.
        //    The position is rounded to the millimeter to avoid too much aliasing
        //int index = int(16.0*random(floor(Position_worldspace.xyz*1000.0), i))%16;

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์œ„์˜ ๊ทธ๋ฆผ๊ณผ ๊ฐ™์€ ํŒจํ„ด์ด ์‚ฌ๋ผ์ง€๊ณ  ์‹œ๊ฐ์  ๋…ธ์ด์ฆˆ๊ฐ€ ์‚ฌ๋ผ์ง‘๋‹ˆ๋‹ค.

๊ทธ๋Ÿผ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ  ์ž˜ ์ˆ˜ํ–‰๋œ ๋…ธ์ด์ฆˆ๋Š” ์ข…์ข… ์ด๋Ÿฌํ•œ ํŒจํ„ด๋ณด๋‹ค ๋œ ๋ถˆ์พŒํ•ฉ๋‹ˆ๋‹ค.

์„ธ ๊ฐ€์ง€ ๊ตฌํ˜„์— ๋Œ€ํ•œ ์˜ˆ๋Š” tutorial16/ShadowMapping.fragmentshader ๋ฅผ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

๋” ๋‚˜์•„๊ฐ€๊ธฐ

์ด ๋ชจ๋“  ๋ฐฉ๋ฒ•์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ , ์šฐ๋ฆฌ์˜ ๊ทธ๋ฆผ์ž๋ฅผ ๊ฐœ์„ ํ•  ์ˆ˜ ์žˆ๋Š” ๋งŽ์€ ๋ฐฉ๋ฒ•๋“ค์ด ์žˆ์Šต๋‹ˆ๋‹ค.

๊ฐ€์žฅ ์ผ๋ฐ˜์ ์ธ ๊ฒƒ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

Early bailing

๊ฐ fragment์— ๋Œ€ํ•ด 16๊ฐœ์˜ ํ‘œ๋ณธ์„ ์ถ”์ถœํ•˜๋Š” ๋Œ€์‹ (๋‹ค์‹œ ๋งํ•˜๋ฉด ๋งŽ์€ ์–‘) 4๊ฐœ์˜ ๋ฉ€๋ฆฌ ์žˆ๋Š” ํ‘œ๋ณธ์„ ์ถ”์ถœํ•ฉ๋‹ˆ๋‹ค.

๋งŒ์•ฝ ๊ทธ๋“ค ๋ชจ๋‘๊ฐ€ ๋น›์ด๋‚˜ ๊ทธ๋ฆผ์ž ์†์— ์žˆ๋‹ค๋ฉด, ์—ฌ๋Ÿฌ๋ถ„์€ 16๊ฐœ์˜ ์ƒ˜ํ”Œ ๋ชจ๋‘๊ฐ€ ๊ฐ™์€ ๊ฒฐ๊ณผ๋ฅผ ๋ƒˆ์„ ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค: Early bailing. ๋งŒ์•ฝ ์–ด๋–ค ๊ฒƒ๋“ค์ด ๋‹ค๋ฅด๋‹ค๋ฉด, ์—ฌ๋Ÿฌ๋ถ„์€ ์•„๋งˆ๋„ ๊ทธ๋ฆผ์ž ๊ฒฝ๊ณ„์— ์žˆ์„ ๊ฒƒ์ด๊ธฐ ๋Œ€๋ฌธ์— 16๊ฐœ์˜ ์ƒ˜ํ”Œ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

Spot lights

์ŠคํฌํŠธ๋ผ์ดํŠธ๋ฅผ ๋‹ค๋ฃจ๋Š” ๋ฐ๋Š” ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ด ๊ฑฐ์˜ ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ฐ€์žฅ ํ™•์‹คํ•œ ๊ฒƒ์€ ์ง๊ต ํˆฌ์šฉ ํ–‰๋ ฌ์„ ์›๊ทผ ํˆฌ์˜ ํ–‰๋ ฌ๋กœ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

glm::vec3 lightPos(5, 20, 20);
glm::mat4 depthProjectionMatrix = glm::perspective<float>(glm::radians(45.0f), 1.0f, 2.0f, 50.0f);
glm::mat4 depthViewMatrix = glm::lookAt(lightPos, lightPos-lightInvDir, glm::vec3(0,1,0));

๊ฐ™์€ ๊ฒƒ์ด์ง€๋งŒ, orthographic frustum ๋Œ€์‹ ์— perspective frustum์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

texture2Dproj ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์›๊ทผ ๋ถ„ํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.(์ž์Šต์„œ4- ํ–‰๋ ฌ์˜ ๊ฐ์ฃผ ์ฐธ์กฐ)

 

๋‘๋ฒˆ์งธ ๋‹จ๊ณ„๋Š” ์‰์ด๋”์˜ ๊ด€์ ์—์„œ ๊ณ ๋ คํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.(์ž์Šต์„œ 4- ํ–‰๋ ฌ์˜ ๊ฐ์ฃผ ์ฐธ์กฐ). ๊ฐ„๋‹จํžˆ ๋งํ•ด์„œ, ํˆฌ์‹œ ํˆฌ์˜ ํ–‰๋ ฌ์€ ์‹ค์ œ๋กœ ์–ด๋–ค ํˆฌ์‹œ๋„ ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด ์ž‘์—…์€ ํ•˜๋“œ์›จ์–ด์—์„œ ์˜ˆ์ƒ ์ขŒํ‘œ๋ฅผ w๋กœ ๋‚˜๋ˆ„์–ด ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์„œ, ์šฐ๋ฆฌ๋Š” ์‰์ด๋”์˜ ๋ณ€ํ˜•์„ ๋ชจ๋ฐฉํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ์šฐ๋ฆฌ๋Š” ์šฐ๋ฆฌ ์ž์‹ ์„ ๋ถ„ํ• ํ•˜๋Š” ๊ด€์ ์„ ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ฐธ๊ณ ๋กœ, orthographic ํ–‰๋ ฌ์€ ํ•ญ์ƒ w=1์˜ ๊ท ์งˆ ๋ฒกํ„ฐ๋ฅผ ์ƒ์„ฑํ•˜๋ฏ€๋กœ ์›๊ทผ๋ฒ•์„ ์ƒ์„ฑํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค).

 

GLSL์—์„œ ์ด๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋‘ ๋ฒˆ์งธ์ค„์€ ๋‚ด์žฅ textureProj ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์ง€๋งŒ ๋‘ ๋ฐฉ๋ฒ• ๋ชจ๋‘ ์ •ํ™•ํ•˜๊ฒŒ ๋™์ผํ•œ ๊ฒฐ๊ณผ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

if ( texture( shadowMap, (ShadowCoord.xy/ShadowCoord.w) ).z  <  (ShadowCoord.z-bias)/ShadowCoord.w )
if ( textureProj( shadowMap, ShadowCoord.xyw ).z  <  (ShadowCoord.z-bias)/ShadowCoord.w )

Point lights

๋™์ผํ•˜์ง€๋งŒ depth ๊ทœ๋ธŒ ๋งต์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ํ๋ธŒ ๋งต์€ ํ๋ธŒ์˜ ๊ฐ ๋ฉด์— ํ•˜๋‚˜์”ฉ ์žˆ๋Š” 6๊ฐœ์˜ ํ…์Šค์ฒ˜ ์„ธํŠธ์ž…๋‹ˆ๋‹ค. ๋˜ํ•œ ํ‘œ์ค€ UV ์ขŒํ‘œ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ๋ฐฉํ–ฅ์„ ๋‚˜ํƒ€๋‚ด๋Š” 3D ๋ฒกํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์•ก์„ธ์Šค ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

๊นŠ์ด๋Š” ๊ณต๊ฐ„์˜ ๋ชจ๋“  ๋ฐฉํ–ฅ์— ๋Œ€ํ•ด ์ €์žฅ๋˜๋ฏ€๋กœ ์ ๊ด‘ ์ฃผ์œ„์— ๊ทธ๋ฆผ์ž๊ฐ€ ๋“œ๋ฆฌ์šธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์—ฌ๋Ÿฌ๊ฐœ์˜ ๋น› ํ˜ผํ•ฉ

์•Œ๊ณ ๋ฆฌ์ฆ˜์€ ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์กฐ๋ช…์„ ์ฒ˜๋ฆฌํ•˜์ง€๋งŒ ๊ฐ ์กฐ๋ช…์€ ๊ทธ๋ฆผ์ž ๋งต์„ ์ƒ์„ฑํ•˜๊ธฐ ์œ„ํ•ด ์žฅ๋ฉด์„ ์ถ”๊ฐ€๋กœ ๋ Œ๋”๋งํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. Shadow๋ฅผ ์ ์šฉํ•  ๋•Œ ์—„์ฒญ๋‚œ ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ํ•„์š”ํ•˜๋ฉฐ ๋Œ€์—ญํญ์ด ๋งค์šฐ ๋น ๋ฅด๊ฒŒ ์ œํ•œ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ž๋™ ๋น› frustum

์ด ํŠœํ† ๋ฆฌ์–ผ์—์„œ๋Š” ์ „์ฒด ์žฅ๋ฉด์„ ํฌํ•จํ•˜๋„๋ก ์†์œผ๋กœ ๋งŒ๋“  ๋น› frustum์ž…๋‹ˆ๋‹ค. ์ด ์ œํ•œ๋œ ์˜ˆ์ œ์—์„œ๋Š” ์ด ์ž‘์—…์ด ์ž‘๋™ํ•˜์ง€๋งŒ ์ด ์ž‘์—…์€ ํ”ผํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋งŒ์•ฝ ๋‹น์‹ ์˜ ์ง€๋„๊ฐ€ 1Km x 1Km์ด๋ผ๋ฉด, ๋‹น์‹ ์˜ 1024x1024 ๊ทธ๋ฆผ์ž ์ง€๋„์˜ ๊ฐ ํ…์ŠคํŠธ๋Š” 1ํ‰๋ฐฉ๋ฏธํ„ฐ๋ฅผ ์ฐจ์ง€ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ํ˜•ํŽธ ์—†์Šต๋‹ˆ๋‹ค. ์กฐ๋ช…์˜ ํˆฌ์˜ ํ–‰๋ ฌ์€ ๊ฐ€๋Šฅํ•œํ•œ ํƒ€์ดํŠธํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.

 

์ŠคํฌํŠธ๋ผ์ดํŠธ์˜ ๊ฒฝ์šฐ ๋ฒ”์œ„๋ฅผ ์กฐ์ •ํ•˜์—ฌ ์‰ฝ๊ฒŒ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

ํƒœ์–‘๊ณผ ๊ฐ™์€ ๋ฐฉํ–ฅ ์กฐ๋ช…์€ ๋” ๊นŒ๋‹ค๋กญ์Šต๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ๋“ค์€ ์ •๋ง๋กœ ์ „์ฒด ์žฅ๋ฉด์„ ๋น„์ถ”์ฃ . ๊ด‘๊ฒฉ์ž๋ฅผ ๊ณ„์‚ฐํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค:

  1. ์ž ์žฌ์  ๊ทธ๋ฆผ์ž ์ˆ˜์‹ ๊ธฐ ๋˜๋Š” ์ค„์—ฌ์„œ PSR์€ ๋น›์˜ frustum, view frustum ๋ฐ ์žฅ๋ฉด ๊ฒฝ๊ณ„ ์ƒ์ž์— ๋™์‹œ์— ์†ํ•˜๋Š” ๊ฐ์ฒด ์ž…๋‹ˆ๋‹ค. ์ด๋ฆ„์—์„œ ์•Œ ์ˆ˜ ์žˆ๋“ฏ์ด, ์ด ๋ฌผ์ฒด๋“ค์€ ์Œ์˜์„ ๋ฐ›๊ธฐ ์‰ฝ์Šต๋‹ˆ๋‹ค. ์นด๋ฉ”๋ผ์™€ ๋น›์œผ๋กœ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  2. ์ž ์žฌ์  ๊ทธ๋ฆผ์ž ์บ์Šคํ„ฐ(PCF)๋Š” ๋ชจ๋‘ ์ž ์žฌ์  ๊ทธ๋ฆผ์ž ์ˆ˜์‹ ๊ธฐ์ด๋ฉฐ, ๊ทธ ์‚ฌ์ด์— ์žˆ๋Š” ๋ชจ๋“  ๋ฌผ์ฒด(๋ฌผ์ฒด๋Š” ๋ณด์ด์ง€ ์•Š์ง€๋งŒ ์—ฌ์ „ํžˆ ๋ณด์ด๋Š” ๊ทธ๋ฆผ์ž๋ฅผ ๋“œ๋ฆฌ์šธ ์ˆ˜ ์žˆ์Œ)์ž…๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ ๋น› ํˆฌ์˜ ํ–‰๋ ฌ์„ ๊ณ„์‚ฐํ•˜๋ ค๋ฉด ๋ณด์ด๋Š” ๋ชจ๋“  ๊ฐ์ฒด๋ฅผ ๊ฐ€์ ธ์™€์„œ ๋„ˆ๋ฌด ๋ฉ€๋ฆฌ ์žˆ๋Š” ๊ฐ์ฒด๋ฅผ ์ œ๊ฑฐํ•˜๊ณ  ๊ฒฝ๊ณ„ ์ƒ์ž๋ฅผ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ฒฝ๊ณ„ ์ƒ์ž์™€ ์กฐ๋ช… ์‚ฌ์ด์— ์žˆ๋Š” ๊ฐ์ฒด๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  ์ƒˆ ๊ฒฝ๊ณ„ ์ƒ์ž๋ฅผ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค(๊ทธ๋Ÿฌ๋‚˜ ์ด๋ฒˆ์—๋Š” ์กฐ๋ช… ๋ฐฉํ–ฅ์„ ๋”ฐ๋ผ ์ •๋ ฌ๋จ).

 

์ด๋Ÿฌํ•œ ์ง‘ํ•ฉ์˜ ์ •ํ™•ํ•œ ๊ณ„์‚ฐ์—๋Š” ๋ณผ๋ก ๊ป์งˆ ๊ต์ฐจ์  ๊ณ„์‚ฐ์ด ํฌํ•จ๋˜์ง€๋งŒ, ์ด ๋ฐฉ๋ฒ•์€ ๊ตฌํ˜„ํ•˜๊ธฐ๊ฐ€ ํ›จ์”ฌ ์‰ฝ์Šต๋‹ˆ๋‹ค.

 

์ด ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•˜๋ฉด ๊ทธ๋ฆผ์ž ๋งต ํ•ด์ƒ๋„๊ฐ€ ๊ฐ‘์ž๊ธฐ ์ฆ๊ฐ€ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฌผ์น˜๊ฐ€ frustum์—์„œ ์‚ฌ๋ผ์ง€๋ฉด ํŒํ•‘์ด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ๊ณ„๋‹จ์‹ Shadow Map์—๋Š” ์ด๋Ÿฌํ•œ ๋ฌธ์ œ๊ฐ€ ์—†์ง€๋งŒ ๊ตฌํ˜„ํ•˜๊ธฐ๊ฐ€ ๋” ์–ด๋ ค์šฐ๋ฉฐ ์‹œ๊ฐ„์ด ์ง€๋‚จ์— ๋”ฐ๋ผ ๊ฐ’์„ ๋งค๋„๋Ÿฝ๊ฒŒ ํ•˜์—ฌ ๋ณด์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ง€์ˆ˜ shadow maps

์ง€์ˆ˜ ๊ทธ๋ฆผ์ž ๋งต์€ ๊ทธ๋ฆผ์ž์— ์žˆ์ง€๋งŒ ๋น› ํ‘œ๋ฉด ๊ทผ์ฒ˜์— ์žˆ๋Š” fragment๊ฐ€ ์‹ค์ œ๋กœ "๊ฐ€์šด๋ฐ ์–ด๋”˜๊ฐ€"๋ผ๊ณ  ๊ฐ€์ •ํ•˜์—ฌ ์•จ๋ฆฌ์–ด์‹ฑ์„ ์ œํ•œํ•˜๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ํ…Œ์ŠคํŠธ๊ฐ€ ๋”์ด์ƒ ์ด์ง„๋ฒ•์ด ์•„๋‹ˆ๋ผ๋Š” ์ ์„ ์ œ์™ธํ•˜๋ฉด ์ด๋Š” ํŽธํ–ฅ๊ณผ ๊ด€๋ จ ์žˆ์Šต๋‹ˆ๋‹ค. ๋น›์ด ์žˆ๋Š” ํ‘œ๋ฉด๊นŒ์ง€์˜ ๊ฑฐ๋ฆฌ๊ฐ€ ์ฆ๊ฐ€ํ•˜๋ฉด fragment๊ฐ€ ์ ์  ์–ด๋‘์›Œ์ง‘๋‹ˆ๋‹ค.

 

์ด๊ฒƒ์€ ๋ช…๋ฐฑํžˆ ๋ถ€์ •ํ–‰์œ„์ด๋ฉฐ, ๋‘ ๋ฌผ์ฒด๊ฐ€ ๊ฒน์น  ๋•Œ ์ธ๊ณต๋ฌผ์ด ๋‚˜ํƒ€๋‚  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋น›-๊ณต๊ฐ„ ์›๊ทผ๋ฒ• shadow maps

LiSPSM์€ ์นด๋ฉ”๋ผ ๊ทผ์ฒ˜์—์„œ ๋” ์ •ํ™•ํ•˜๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•ด ๋น› ํˆฌ์˜ ํ–‰๋ ฌ์„ ์กฐ์ •ํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ "๋ถˆ์•ˆ์ •ํ•œ frustra"์˜ ๊ฒฝ์šฐ์—๋Š” ํŠนํžˆ ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ๋ถ„์€ ๋ฐฉํ–ฅ์„ ๋ฐ”๋ผ์ง€๋งŒ, ์Šคํฟ๋ผ์ดํŠธ๋Š” ๋ฐ˜๋Œ€ ๋ฐฉํ–ฅ์„ ๋ฐ”๋ผ๋ด…๋‹ˆ๋‹ค. ๋‹น์‹ ์€ ๋น› ๊ทผ์ฒ˜, ์ฆ‰ ๋‹น์‹ ์œผ๋กœ๋ถ€ํ„ฐ ๋ฉ€๋ฆฌ ๋–จ์–ด์ง„ ๊ณณ์— ๋งŽ์€ ๊ทธ๋ฆผ์ž ์ง€๋„ ์ •๋ฐ€๋„๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๊ณ , ๋‹น์‹ ์ด ๊ฐ€์žฅ ํ•„์š”๋กœ ํ•˜๋Š” ์นด๋ฉ”๋ผ ๊ทผ์ฒ˜์— ๋‚ฎ์€ ํ•ด์ƒ๋„๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

 

๊ทธ๋Ÿฌ๋‚˜ LiSPSM์€ ๊ตฌํ˜„ํ•˜๊ธฐ๊ฐ€ ๊นŒ๋‹ค๋กญ์Šต๋‹ˆ๋‹ค. ๊ตฌํ˜„์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ ๋ ˆํผ๋Ÿฐ์Šค๋ฅผ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

๊ณ„๋‹จ์‹ shadow maps

CSM์€ LiSPSM๊ณผ ์™„์ „ํžˆ ๋™์ผํ•œ ๋ฌธ์ œ๋ฅผ ๋‹ค๋ฃจ์ง€๋งŒ ๋‹ค๋ฅธ ๋ฐฉ์‹์œผ๋กœ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ๋‹จ์ˆœํžˆ view frustum์˜ ์—ฌ๋Ÿฌ ๋ถ€๋ถ„์— ๋Œ€ํ•ด ์—ฌ๋Ÿฌ ๊ฐœ์˜(2-4) ํ‘œ์ค€ ๊ทธ๋ฆผ์ž ๋งต์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

์ฒซ ๋ฒˆ์งธ ๊ฒƒ์€ ์ฒซ ๋ฒˆ์งธ ๋ฏธํ„ฐ๋ฅผ ๋‹ค๋ฃจ๊ธฐ ๋•Œ๋ฌธ์— ๊ฝค ์ž‘์€ ๊ตฌ์—ญ์— ๋Œ€ํ•ด์„œ๋Š” ํ›Œ๋ฅญํ•œ ํ•ด์ƒ๋„๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ ๊ทธ๋ฆผ์ž ์ง€๋„๋Š” ๋” ๋จผ ๋ฌผ์ฒด๋ฅผ ๋‹ค๋ฃน๋‹ˆ๋‹ค. ๋งˆ์ง€๋ง‰ shadow map์€ ์žฅ๋ฉด์˜ ํฐ ๋ถ€๋ถ„์„ ๋‹ค๋ฃจ์ง€๋งŒ, ๊ด€์  ๋•Œ๋ฌธ์— ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด ๊ตฌ์—ญ๋ณด๋‹ค ์‹œ๊ฐ์ ์œผ๋กœ ๋” ์ค‘์š”ํ•˜์ง€๋Š” ์•Š์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

 

๊ณ„๋‹จ์‹ ์‰๋„์šฐ ๋งต์€ ์ž‘์„ฑ ๋‹น์‹œ(2012๋…„) ์ตœ๊ณ ์˜ ๋ณต์žก์„ฑ/ํ’ˆ์งˆ ๋น„์œจ์„ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ๋งŽ์€ ๊ฒฝ์šฐ์— ์„ ํƒ์˜ ํ•ด๊ฒฐ์ฑ…์ž…๋‹ˆ๋‹ค.

๊ฒฐ๋ก 

๋ณด์‹œ๋‹ค์‹œํ”ผ, ๊ทธ๋ฆผ์ž ์ง€๋„๋Š” ๋ณต์žกํ•œ ์ฃผ์ œ์ž…๋‹ˆ๋‹ค. ๋งค๋…„ ์ƒˆ๋กœ์šด ๋ณ€ํ˜•๊ณผ ๊ฐœ์„ ์‚ฌํ•ญ์ด ๋ฐœํ‘œ๋˜๊ณ  ์žˆ์œผ๋ฉฐ, ํ˜„์žฌ๋กœ์„œ๋Š” ์–ด๋–ค ์†”๋ฃจ์…˜๋„ ์™„๋ฒฝํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

 

๋‹คํ–‰ํžˆ, ์ œ์‹œ๋œ ๋Œ€๋ถ€๋ถ„์˜ ๋ฐฉ๋ฒ•์€ ํ•จ๊ป˜ ํ˜ผํ•ฉ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋น› ๊ณต๊ฐ„ ๊ด€์ ์—์„œ ๊ณ„์‚ฐ์‹ shadow map์„ PCF๋กœ ๋งค๋„๋Ÿฝ๊ฒŒ ๋งŒ๋“œ๋Š” ๊ฒƒ์ด ์™„๋ฒฝํ•˜๊ฒŒ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.. ์ด ๋ชจ๋“  ๊ธฐ์ˆ ์„ ์‚ฌ์šฉํ•˜์—ฌ ์‹คํ—˜ํ•ด ๋ณด์‹ญ์‹œ์˜ค.

 

๊ฒฐ๋ก ์ ์œผ๋กœ, ์ €๋Š” ๋‹น์‹ ์ด ๊ฐ€๋Šฅํ•  ๋•Œ๋งˆ๋‹ค ๋ฏธ๋ฆฌ ๊ณ„์‚ฐ๋œ ๋ผ์ดํŠธ๋งต์„ ๊ณ ์ˆ˜ํ•˜๊ณ , ๋™์  ๊ฐ์ฒด์—๋งŒ shadowmap์„ ์‚ฌ์šฉํ•  ๊ฒƒ์„ ์ œ์•ˆํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋‘ ๊ฐ€์ง€์˜ ์‹œ์ž‘์  ํ’ˆ์งˆ์ด ๋™์ผํ•œ์ง€ ํ™•์ธํ•˜์‹ญ์‹œ์˜ค. ์™„๋ฒฝํ•œ ์ •์  ํ™˜๊ฒฝ๊ณผ ๋ชจ์–‘์ƒˆ๊ฐ€ ์ข‹์ง€ ์•Š์€ ๊ทธ๋ฆผ์ž๊ฐ€ ์žˆ๋Š” ๊ฒƒ๋„ ์ข‹์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

 

 

 

 

๋ฐ˜์‘ํ˜•

+ Recent posts