โ Light Caster
์ง๊ธ๊น์ง ์ฌ์ฉํ ์กฐ๋ช
์ ํ ์ ์์ ๋์ค๋ ๋จ์ผ ์์ค์์ ๋น๋กฏ๋์๋ค. ํ์ง๋ง ํ์ค ์ธ๊ณ์์๋ ์๋ก ๋ค๋ฅด๊ฒ ์์ฉํ๋ ์ฌ๋ฌ ์ข
๋ฅ์ ์กฐ๋ช
์ด ์กด์ฌํ๋ค. ๋ฌผ์ฒด์ ๋น์ ๋น์ถ๋ ๊ด์์ Light caster๋ผ๊ณ ํ๋ค. ๋ค์ํ ๊ด์์ ์๋ฎฌ๋ ์ด์
ํ๋ ๋ฐฉ๋ฒ์ ๋ฐฐ์ฐ๋ ๊ฒ์ ํ๊ฒฝ์ ๋์ฑ ํ๋ถํ๊ฒ ๋ง๋๋ ๋ ํ๋์ ๋๊ตฌ์ด๋ค.
โ Directional Light (๋ฐฉํฅ์ฑ ๊ด์)
๊ด์์ด ๋ฌดํํ ๋ฉ๋ฆฌ ์์ ๋ ๋ชจ๋ ๊ด์ ๋ค์ด ๋์ผํ ๋ฐฉํฅ(์ด๋ ๊ด์์ ์์น์ ๋
๋ฆฝ์ ์ด๋ค)์ ๊ฐ์ง๊ธฐ ๋๋ฌธ์ ์ด ๊ด์์ Directional Light๋ผ๊ณ ๋ถ๋ฅธ๋ค.
Directional Light์ ์ข์ ์๋ ์ฐ๋ฆฌ๊ฐ ์๊ณ ์๋ ํ์์ด๋ค. ํ์์ ์ฐ๋ฆฌ์ ๋ฌดํ์ ์ผ๋ก ๋ฉ๋ฆฌ ์์ง๋ ์์ง๋ง lighting ๊ณ์ฐ์์ ๋ฌดํ์ ์ผ๋ก ๋ฉ๋ฆฌ ์๋ค๊ณ ๊ฐ์ฃผํ ์ ๋๋ก ์ฐ๋ฆฌ์ ๋ฉ๋ฆฌ ๋จ์ด์ ธ ์๋ค. ํ์์์ ์ค๋ ๋ชจ๋ ๊ด์ ๋ค์ ์๋ก ํํํ๋ค.
๋ชจ๋ ๊ด์ ๋ค์ด ํํํ๊ธฐ ๋๋ฌธ์ ๊ฐ ์ค๋ธ์ ํธ๋ค์ด ๊ด์์ ์์น์ ์ด๋ ํ ๊ด๊ณ๊ฐ ์๋์ง์ ๋ํด์๋ ์๊ด์ด ์๋ค. ๋น์ ๋ฐฉํฅ์ scene์ ์กด์ฌํ๋ ๊ฐ๊ฐ์ ์ค๋ธ์ ํธ์ ๋ชจ๋ ๋์ผํ๊ธฐ ๋๋ฌธ์ด๋ค. ๋น์ ๋ฐฉํฅ ๋ฒกํฐ๊ฐ ๋์ผํ๊ฒ ์ ์ง๋๊ธฐ ๋๋ฌธ์ lighting ๊ณ์ฐ์ด scene์ ์กด์ฌํ๋ ๊ฐ ์ค๋ธ์ ํธ๋ค์๊ฒ ๋น์ทํ๊ฒ ์ ์ฉ๋๋ค.
๋น์ ๋ํ ์์น ๋ฒกํฐ ๋์ ์ ๋ฐฉํฅ ๋ฒกํฐ๋ฅผ ์ ์ํ๋ฉด ์ด๋ฌํ Directional Light๋ฅผ ๋ง๋ค ์ ์๋ค. shader ๊ณ์ฐ์ ๋๋ถ๋ถ ๋์ผํ์ง๋ง ์ด๋ฒ์๋ ๋น์ position ๋ฒกํฐ๋ฅผ ์ฌ์ฉํ์ฌ ๊ณ์ฐ๋ lightDir ๋ฒกํฐ ๋์ ์ ๋น์ direction ๋ฒกํฐ๋ฅผ ์ง์ ์ ์ผ๋ก ์ฌ์ฉํ ๊ฒ์ด๋ค.
struct Light
{
// Directional Light์ ๊ฒฝ์ฐ์ position์ ํ์ํ์ง ์๋ค.
// vec3 position;
vec3 direction;
vec3 ambient;
vec3 diffuse;
vec3 specular;
};
...
void main()
{
vec3 lightDir = normalize(-light.direction);
...
}
์ง๊ธ๊น์ง ์ฌ์ฉํ lighting ๊ณ์ฐ์ Fragment๋ก๋ถํฐ ๊ด์์ผ๋ก ํฅํ๋ ๋น์ ๋ฐฉํฅ์ด ํ์ํ์ผ๋ Directional Light๋ ๊ด์์ผ๋ก๋ถํฐ Fragment๋ก ํฅํ๋ ๋ฐฉํฅ์ ๊ตฌํ์ฌ ์ฌ์ฉํ๋ค. (๋ถํธ ๋ฐ๋)
๊ด์์ ๋ฐฉํฅ์ ์ง์ ์ง์ ํ๋ค.
// main
lightingShader.setVec3("light.direction", -0.2f, -1.0f, -0.3f);
* ๋ฒกํฐ์ ํ์
์ ๋น์ ์ ํ์ ํ๋จํ ์ ์๋ ์ญํ ๋ ํ๋ค. w ์์๊ฐ 1.0์ด๋ผ๋ฉด ๋น์ ์์น ๋ฒกํฐ์ด๊ณ , 0.0์ด๋ผ๋ฉด ๋น์ ๋ฐฉํฅ๋ฒกํฐ์ธ ๊ฒ์ด๋ค.
โ Point Light (์ ๊ด์)
์ฌ ์ ์ฒด์ ์ฐ๋๋๋ ๊ด์์ผ๋ก world ์ด๋๊ฐ์ ์ฃผ์ด์ง ์์น๋ฅผ ๊ฐ๊ณ ๋ชจ๋ ๋ฐฉํฅ์ผ๋ก ๋น์ ๋ฐํ๋ฉฐ ๊ด์ ์ ์ ์ ํฌ๋ฏธํด์ง๋ค.
โ Attenuation (๊ฐ์ )
๊ด์ ์ด ์ง๋๊ฐ ๊ฑฐ๋ฆฌ์ ๋ฐ๋ผ ๋น์ ์ธ๊ธฐ๊ฐ ์ค์ด๋๋ ๊ฒ์ Attenuation(๊ฐ์ )๋ผ๊ณ ๋ถ๋ฅธ๋ค. ๋ค์ ๊ณต์์ ๊ด์๊ณผ Fragment ์ฌ์ด์ ๊ฑฐ๋ฆฌ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํ๋ Attenuation ๊ฐ์ ๊ณ์ฐํ๋ ๊ณต์์ด๋ค. ๋์ค์ ์ด ๊ฐ์ ๋น์ ์ธ๊ธฐ ๋ฒกํฐ์ ๊ณฑํ ๊ฒ์ด๋ค.

- d : Fragment์์ ๊ด์๊น์ง์ ๊ฑฐ๋ฆฌ
- Kc : ์ผ๋ฐ์ ์ผ๋ก 1.0์ ์ ์งํ๋ ์์ํญ (๋ถ๋ชจ๋ฅผ 1๋ณด๋ค ์๊ฒ ๋ง๋ค์ง ์๊ฒ ํจ)
- Kl : ๊ฑฐ๋ฆฌ ๊ฐ๊ณผ ๊ณฑํด์ ธ 1์ฐจ์์ ์ผ๋ก ์ธ๊ธฐ๋ฅผ ๊ฐ์์ํค๋ 1์ฐจํญ
- Kq : ๊ฑฐ๋ฆฌ์ ์ฌ๋ถ๋ฉด๊ณผ ๊ณฑํด์ง๊ณ 2์ฐจ์์ ์ผ๋ก ์ธ๊ธฐ๋ฅผ ๊ฐ์์ํค๋ 2์ฐจํญ
๋ค์ ๊ทธ๋ํ๋ ์ด๋ฌํ Attenuation์ด ๊ฐ์ง๋ ํจ๊ณผ๋ฅผ ๋ณด์ฌ์ค๋ค.

Attenuation์ ๊ตฌํํ๊ธฐ ์ํด Fragment์ 3๊ฐ์ ๊ฐ์ด ์ถ๊ฐ๋ก ํ์ํ๋ค. (์์ํญ, ์ผ์ฐจํญ, ์ด์ฐจํญ)
// Fragment Shader
struct Light
{
vec3 position;
vec3 ambient;
vec3 diffuse;
vec3 specular;
float constant; // ์์ํญ
float linear; // ์ผ์ฐจํญ
float quadratic; // ์ด์ฐจํญ
};
๊ณต์์ ๊ธฐ๋ฐ์ผ๋ก Attenuation ๊ฐ์ ๊ณ์ฐํ๊ณ ambient, diffuse, specular ์์์ ๊ณฑํ๊ธฐ๋ง ํ๋ฉด ๋๋ค.
float distance = length(light.position - FragPos);
float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * (distance * distance));
ambient *= attenuation;
diffuse *= attenuation;
specular *= attenuation;
๊ทธ ๋ค์ OpenGL์์ ์ ์ ํ ๊ฐ์ ์ค์ ํ๋ค.
// main
glUniform1f(glGetUniformLocation(lightingShader.ID, "light.constant"), 1.0f);
glUniform1f(glGetUniformLocation(lightingShader.ID, "light.linear"), 0.09f);
glUniform1f(glGetUniformLocation(lightingShader.ID, "light.quadratic"), 0.032f);
โ ๊ฒฐ๊ณผ

โ Spotlight (์คํฌํธ๋ผ์ดํธ)
Spotlight๋ ํ๊ฒฝ์ ์ด๋๊ฐ์ ์์นํ ๊ด์์ผ๋ก ๋ชจ๋ ๋ฐฉํฅ์ผ๋ก ๊ด์ ์ ์์ง ์๊ณ ํน์ ๋ฐฉํฅ์ผ๋ก๋ง ์๋ค. ๊ฒฐ๊ณผ์ ์ผ๋ก Spotlight ๋ฐฉํฅ์ ํน์ ๋ฐ์ง๋ฆ ๋ด๋ถ์ ์๋ ์ค๋ธ์ ํธ๋ง ๋ฐ์์ง๊ณ ๋๋จธ์ง๋ ์ด๋์์ง๋ค.
OpenGL์ Spotlight๋ World Space์์์ ์์น, ๋ฐฉํฅ, cutoff ๊ฐ(Spotlight์ ๋ฐ์ง๋ฆ ์ง์ )์ผ๋ก ํํ๋๋ค. ๊ฐ Fragment์ ๋ํด Spotlight์ cutoff ๋ฐฉํฅ ์ฌ์ด(์๋ฟ ๋ด๋ถ)์ ์๋์ง ๊ณ์ฐํ์ฌ ๋ง์ฝ ๊ทธ๋ ๋ค๋ฉด ๊ทธ์ ๋ง์ถฐ์ Fragment๋ฅผ ๋ฐ๊ฒ ํ๋ค. ๋ค์ ์ด๋ฏธ์ง๋ Spotlight๊ฐ ์ด๋ป๊ฒ ๋์ํ๋์ง์ ๋ํ ๊ฐ๋
์ ๋ณด์ฌ์ค๋ค.

- LightDir : Fragment์์ ๊ด์๊น์ง์ ๋ฐฉํฅ ๋ฒกํฐ
- SpotDir : Spotlight๊ฐ ๋ฐ๋ผ๋ณด๋ ๋ฐฉํฅ
- Phi Φ : Spotlight์ ๋ฐ์ง๋ฆ์ ์ ํ๋ cutoff ๊ฐ, ์ด ๊ฐ ์ธ๋ถ๋ Spotlight์ ์ํ ๋น์ ๋ฐ์ง ๋ชปํ๋ค.
- Theta θ : LightDir ๋ฒกํฐ์ SpotDir ๋ฒกํฐ ์ฌ์ด์ ๊ฐ๋. Spotlight์ ๋ด๋ถ์ ์๊ธฐ ๋๋ฌธ์ θ ๊ฐ์ Φ ๊ฐ๋ณด๋ค ์์์ผํ๋ค.
๊ทธ๋์ ์ฐ๋ฆฌ๊ฐ ํ์ํ ๊ฒ์ LightDir ๋ฒกํฐ์ SpotDir ๋ฒกํฐ๋ฅผ ๋ด์ ํ์ฌ ์ด๋ฅผ cutoff ๊ฐ Φ์ ๋น๊ตํ๋ ๊ฒ์ด๋ค.
โ Flashlight
Flashlight๋ Viewer์ ์์น์ ์๊ณ ์ผ๋ฐ์ ์ผ๋ก ํ๋ ์ด์ด์ ๊ด์ ์ ํฅํ๊ณ ์๋ Spotlight์ด๋ค. ๊ธฐ๋ณธ์ ์ผ๋ก Flashlight๋ ์ผ๋ฐ์ ์ธ Spotlight์ด์ง๋ง ์์น์ ๋ฐฉํฅ์ด ํ๋ ์ด์ด์ ์์น์ ๋ฐฉํฅ์ ๋ฐ๋ผ ๊ณ์ํด์ ์
๋ฐ์ดํธ๋๋ค๋ ์ ์ด ํน์ง์ด๋ค.
๊ทธ๋์ Fragment Shader๋ฅผ ์ํด ํ์ํ ๊ฐ๋ค์ Spotlight์ ์์น ๋ฒกํฐ(๋น์ ๋ฐฉํฅ์ ๊ณ์ฐํ๊ธฐ ์ํด), ๋ฐฉํฅ ๋ฒกํฐ, cutoff ๊ฐ์ด๋ค. ์ด ๊ฐ๋ค์ Light struct์ ์ ์ฅํ๋ค.
// Fragment Shader
struct Light
{
vec3 position;
vec3 direction;
float cutOff;
...
};
๊ทธ๋ฆฌ๊ณ ์ ์ ํ ๊ฐ์ ์ค์ ํ๋ค.
// main
lightingShader.setVec3("light.position", camera.Position);
lightingShader.setVec3("light.direction", camera.Front);
lightingShader.setFloat("light.cutOff", glm::cos(glm::radians(12.5f)));
* Cutoff ๊ฐ์ ๊ฐ๋๋ก ์ค์ ํ์ง ์๊ณ ๊ฐ์ ๋ํ ์ฝ์ฌ์ธ ๊ฐ์ ๊ณ์ฐํ๊ณ ์ด ๊ฐ์ Fragment Shader๋ก ์ ๋ฌํ๋ ๊ฒ์ ๋ณผ ์ ์๋ค. ์ด์ ๋ Fragment Shader์์ LightDir์ SpotDir ๋ฒกํฐ์ ๋ด์ ์ ๊ณ์ฐํ๊ณ ์๋๋ฐ ๋ด์ ์ ๊ฐ์ด ์๋ ์ฝ์ฌ์ธ ๊ฐ์ ๋ฐํํ๋ฏ๋ก ์ฝ์ฌ์ธ ๊ฐ๊ณผ ๊ฐ๋๋ฅผ ์ง์ ๋น๊ตํ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค. ๊ฐ์ ๊ตฌํ๊ธฐ ์ํด์๋ ๋ด์ ์ ๊ฒฐ๊ณผ์ ๋ํด ์ฝ์ฌ์ธ์ ์ญํจ์๋ฅผ ์ฌ์ฉํด์ผ ํ๋๋ฐ ์ด๋ ๋น์ฉ์ด ๋ง์ด ๋๋ ์ฐ์ฐ์ด๋ค. ๋ฐ๋ผ์ ์ฝ๊ฐ์ ์ฑ๋ฅ ํฅ์์ ์ํด ์ฃผ์ด์ง cutoff ๊ฐ์ ๋ํ ์ฝ์ฌ์ธ ๊ฐ์ ๊ณ์ฐํ๊ณ ์ด ๊ฒฐ๊ณผ๋ฅผ Fragment Shader์ ์ ๋ฌํ๋ค.
์ด์ ๋จ์ ๊ฒ์ Theta θ ๊ฐ์ ๊ณ์ฐํ๊ณ ์ด๋ฅผ cutoff Φ ๊ฐ๊ณผ ๋น๊ตํ์ฌ Spotlight์ ๋ด๋ถ์ ์๋์ง ์ธ๋ถ์ ์๋์ง ํ๋จํ๋ ๊ฒ์ด๋ค.
float theta = dot(lightDir, normalize(-light.direction));
if (theta > light.cutOff)
{
// Lighting ๊ณ์ฐ ์ํ
}
else // Spotlight ์ธ๋ถ์ ์๋๋ผ๋ ์ฃผ๋ณ๊ด(Ambient Light)์ด ์์ด ์์ ํ ์ด๋ก์ง๋ ์๋ค.
{
color = vec4(light.ambient * vec3(texture(material.diffuse, TexCoords)), 1.0);
}
๋จผ์ lightDir ๋ฒกํฐ์ ๋ถํธ๋ฅผ ๋ฐ๊พผ light์ direction ๋ฒกํฐ(๊ด์์ผ๋ก๋ถํฐ ๋์ค๋ ๊ฒ์ด ์๋ ๊ด์์ ํฅํ๋ ๋ฒกํฐ๊ฐ ํ์ํ๊ธฐ ๋๋ฌธ)์ ๋ด์ ์ ๊ณ์ฐํ๋ค. (๊ด๋ จ๋ ๋ชจ๋ ๋ฒกํฐ๋ค์ ์ ๊ทํ ํ๋์ง ํ์ธํ๋ค)
theta์ light.cutOff๋ ์ฝ์ฌ์ธ ๊ฐ์ด๊ธฐ ๋๋ฌธ์ ๊ฐ๋๊ฐ 0๋์ผ ๋ ์ฝ์ฌ์ธ ๊ฐ 1 ๋ก, 90๋์ผ ๋ ์ฝ์ฌ์ธ ๊ฐ 0 ์ผ๋ก ๋ํ๋ด์ด์ง๋ค. ๋ฐ๋ผ์ theta๊ฐ light.cutOff๋ณด๋ค ํด ๋ Fragment๊ฐ Spotlight ๋ด๋ถ์ ์์นํ๋ ๊ฒ์ ์๋ฏธํ๋ค.
โ ๊ฒฐ๊ณผ

โ ๋ถ๋๋ฌ์ด ์ธ๊ณฝ์
๋ณด๋ค์ํผ Spotlight๊ฐ ๋ช
๋ฐฑํ ์ธ๊ณฝ์ ์ ๊ฐ์ง๊ณ ์๋ค. ํ์ค์ ์ธ Spotlight๋ ์ธ๊ณฝ์ ์์ ์ ์ฐจ์ ์ผ๋ก ๋น์ ๊ฐ์์ํจ๋ค.
๋ถ๋๋ฌ์ด ์ธ๊ณฝ์ ์ ๊ฐ์ง Spotlight๋ฅผ ์์ฑํ๊ธฐ ์ํด inner(๋ด๋ถ) ์๋ฟ๊ณผ outer(์ธ๋ถ) ์๋ฟ์ ๊ฐ์ง๋ Spotlight๋ฅผ ์๋ฎฌ๋ ์ด์
ํด์ผ ํ๋ค. ๋ด๋ถ ์๋ฟ์ ์ด์ ์ ์ ์ํ๋ ์๋ฟ๋ก ์ค์ ํ๊ณ ๋ด๋ถ ์๋ฟ์์ ์ธ๋ถ ์๋ฟ๋ก ๊ฐ์๋ก ์ ์ ๋น์ด ์ด๋์์ง๋ ํจ๊ณผ๋ฅผ ๋ผ ๊ฒ์ด๋ค.
์ธ๋ถ ์๋ฟ์ ์์ฑํ๊ธฐ ์ํด ๊ฐ๋จํ Spotlight์ ๋ฐฉํฅ ๋ฒกํฐ์ ์ธ๋ถ ์๋ฟ์ ๋ฒกํฐ(์๋ฟ์ ๋ฐ์ง๋ฆ) ์ฌ์ด์ ๊ฐ์ ๋ํ ์ฝ์ฌ์ธ ๊ฐ์ ์ ์ํ๋ฉด ๋๋ค. ๊ทธ ๋ค์ Fragment๊ฐ ๋ด๋ถ ์๋ฟ๊ณผ ์ธ๋ถ ์๋ฟ ์ฌ์ด์ ์์ผ๋ฉด ๋น์ ์ธ๊ธฐ ๊ฐ์ 0.0 ~ 1.0 ์ฌ์ด๋ก ๊ณ์ฐํ๋ค. Fragment๊ฐ ๋ด๋ถ ์๋ฟ ์์ ์กด์ฌํ๋ค๋ฉด ๋น์ ์ธ๊ธฐ๋ 1.0๊ณผ ๊ฐ๊ณ ์ธ๋ถ ์๋ฟ์ ๋ฐ๊นฅ์ ์กด์ฌํ๋ค๋ฉด 0.0๊ณผ ๊ฐ๋ค.
๋ค์ ๊ณต์์ ์ฌ์ฉํ์ฌ ๊ฐ๋ค์ ๊ณ์ฐํ ์ ์๋ค.

์ฌ๊ธฐ์ ๐ (์ก์ค๋ก )์ ๋ด๋ถ ์๋ฟ(๐)๊ณผ ์ธ๋ถ ์๋ฟ(๐พ) ์ฌ์ด์ ์ฝ์ฌ์ธ ๊ฐ์ ์ฐจ์ด์ด๋ค. I ๊ฐ์ ํ์ฌ Fragment์์์ Spotlight์ ๊ฐ๋๊ฐ ๋๋ค.
์ด์ ์ฐ๋ฆฌ๋ ๋น์ ์ธ๊ธฐ ๊ฐ์ ๊ฐ์ง๊ณ ์์ง๋ง ์ด ๊ฐ์ Spotlight ์ธ๋ถ์ ์์ผ๋ฉด ์์ ๊ฐ์ ๊ฐ์ง๊ณ , ๋ด๋ถ ์๋ฟ์ ์์ ๋๋ 1.0๋ณด๋ค ํฐ ๊ฐ์ ๊ฐ์ง๊ฒ ๋๋ค. ์ด ๊ฐ์ ์ ์ ํ๊ฒ ๋ณด์ ํ ๋ค์ ๊ณ์ฐ๋ ์ธ๊ธฐ ๊ฐ์ light ์์๋ค์ ๊ณฑํด์ค ์ ์๋ค.
float theta = dot(lightDir, normalize(-light.direction));
float epsilon = light.cutOff - light.outerCutOff;
float intensity = clamp((theta - light.outerCutOff) / epsilon, 0.0, 1.0);
...
// ambient์๋ ์ํฅ์ ๋ฏธ์น์ง ์๋๋ค.
diffuse *= intensity;
specular *= intensity;
...
* ์ฒซ ๋ฒ์งธ ํ๋ผ๋ฏธํฐ๋ฅผ 0.0 ~ 1.0 ์ฌ์ด์ ๊ฐ์ผ๋ก clamp ํ์ฌ ์ธ๊ธฐ ๊ฐ์ด [0, 1] ๋ฒ์์์ ๋ฒ์ด๋์ง ์๋๋ก ํ๋ค.
* outerCutOff ๊ฐ์ Light struct์ ์ถ๊ฐํ๊ณ ์์ฉ ํ๋ก๊ทธ๋จ์์ ์ด๊ฒ์ unifrom ๊ฐ์ ์ค์ ํ๋ค. (๋ด๋ถ cutoff ๊ฐ 12.5, ์ธ๋ถ cutoff ๊ฐ 17.5)
โ ๊ฒฐ๊ณผ

โ ์์ค ์ฝ๋
- main
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <Shader.h>
#include <Camera.h>
#include <iostream>
unsigned int loadTexture(char const* path);
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
void processInput(GLFWwindow* window);
// settings
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;
// camera
Camera camera(glm::vec3(0.0f, 0.0f, 3.0f));
float lastX = SCR_WIDTH / 2.0f;
float lastY = SCR_HEIGHT / 2.0f;
bool firstMouse = true;
// timing
float deltaTime = 0.0f;
float lastFrame = 0.0f;
// lighting
glm::vec3 lightPos = glm::vec3(2.0f, 1.0f, 2.0f);
int main()
{
// ---------- [ 1. GLFW, GLAD ์ธํ
] ----------
// GLFW ์ด๊ธฐํ ๋ฐ ์ค์
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
// GLFW ์๋์ฐ ์์ฑ
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
// ์ฝ๋ฐฑ ํจ์๋ค ๋ฑ๋ก
glfwSetCursorPosCallback(window, mouse_callback);
glfwSetScrollCallback(window, scroll_callback);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
// ๋ง์ฐ์ค ์ปค์ ์จ๊ธฐ๊ธฐ
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
// GLAD - OpenGL ํจ์ ํฌ์ธํฐ๋ฅผ ๋ก๋
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
// ์
ฐ์ด๋ ํ๋ก๊ทธ๋จ ๋น๋ ๋ฐ ์ปดํ์ผ
Shader lightingShader("Shaders/colors.vs", "Shaders/colors.fs");
Shader lightCubeShader("Shaders/light_cube.vs", "Shaders/light_cube.fs");
// ---------- [ 2. ์ ์ ๋ฐ์ดํฐ ์
๋ ฅ / ์ ์ ์์ฑ ๊ตฌ์ฑ ] ----------
float vertices[] = {
// positions // normals // texture coords
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f,
0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f,
0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
-0.5f, 0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f,
-0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f,
0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f,
0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f,
0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f,
0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,
0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f,
0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f
};
// world space positions of our cubes
glm::vec3 cubePositions[] = {
glm::vec3(0.0f, 0.0f, 0.0f),
glm::vec3(2.0f, 5.0f, -15.0f),
glm::vec3(-1.5f, -2.2f, -2.5f),
glm::vec3(-3.8f, -2.0f, -12.3f),
glm::vec3(2.4f, -0.4f, -3.5f),
glm::vec3(-1.7f, 3.0f, -7.5f),
glm::vec3(1.3f, -2.0f, -2.5f),
glm::vec3(1.5f, 2.0f, -2.5f),
glm::vec3(1.5f, 0.2f, -1.5f),
glm::vec3(-1.3f, 1.0f, -1.5f)
};
// ๊น์ด ํ
์คํธ ํ์ฑํ
glEnable(GL_DEPTH_TEST);
// VAO, VBO ์์ฑ
// VAO: VBO์ ์ ์ ์์ฑ ํฌ์ธํฐ์ ์ํ๋ฅผ ์ ์ฅํ๊ธฐ ์ํ ๊ฐ์ฒด
// VBO: GPU ๋ฉ๋ชจ๋ฆฌ์ ์ ์ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๊ธฐ ์ํ ๋ฒํผ ๊ฐ์ฒด
unsigned int VBO, cubeVAO;
glGenVertexArrays(1, &cubeVAO);
glGenBuffers(1, &VBO);
// ์ ์ ๋ฒํผ ๊ฐ์ฒด(VBO)๋ฅผ ๋ฐ์ธ๋ฉ
glBindBuffer(GL_ARRAY_BUFFER, VBO);
// ๋ฐ์ธ๋ฉ๋ ๋ฒํผ ๊ฐ์ฒด์ vertices ๋ฐ์ดํฐ๋ฅผ ๋ณต์ฌํ๊ณ ,๋ฒํผ ๊ฐ์ฒด์ ํฌ๊ธฐ์ ๋ฐ์ดํฐ ์ค์
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// ์ ์ ๋ฐฐ์ด ๊ฐ์ฒด(VAO) ๋ฐ์ธ๋ฉ
glBindVertexArray(cubeVAO);
// Position Attribute ๊ตฌ์ฑ
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// Normal Attribute ๊ตฌ์ฑ
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
// Texture(Diffuse Map) Attribute ๊ตฌ์ฑ
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
glEnableVertexAttribArray(2);
// ๊ด์ ํ๋ธ์ฉ VAO ์์ฑ
unsigned int lightCubeVAO;
glGenVertexArrays(1, &lightCubeVAO);
glBindVertexArray(lightCubeVAO);
// VBO ๋ฐ์ธ๋๋ง ํ๋ฉด ๋๋ค (์ด๋ฏธ ๋ฐ์ดํฐ ํฌํจ๋์ด ์์)
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// ---------- [ 3. Diffuse map, Specular map ๋ก๋ (ํจ์ํ) ] ----------
unsigned int diffuseMap = loadTexture("Textures/container2.png");
unsigned int specularMap = loadTexture("Textures/container2_specular.png");
// shader configuration
lightingShader.use();
glUniform1i(glGetUniformLocation(lightingShader.ID, "material.diffuse"), 0);
glUniform1i(glGetUniformLocation(lightingShader.ID, "material.specular"), 1);
// ---------- [ 4. ๋ ๋ ๋ฃจํ ] ----------
while (!glfwWindowShouldClose(window))
{
// ํ๋ ์ ์ฌ์ด deltaTime ์ธก์
float currentFrame = static_cast<float>(glfwGetTime());
deltaTime = currentFrame - lastFrame;
lastFrame = currentFrame;
// ์
๋ ฅ ์ฒ๋ฆฌ
processInput(window);
// ๋ฐฐ๊ฒฝ ๋ ๋
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// ---------- Uniform ๋ณ์ ํ ๋น ----------
lightingShader.use();
// ์นด๋ฉ๋ผ ์์น ์์ฑ
glUniform3fv(glGetUniformLocation(lightingShader.ID, "viewPos"), 1, &(camera.Position)[0]);
// ๊ด์ ์์น ์์ฑ
glUniform3fv(glGetUniformLocation(lightingShader.ID, "light.position"), 1, &camera.Position[0]);
glUniform3fv(glGetUniformLocation(lightingShader.ID, "light.direction"), 1, &camera.Front[0]);
glUniform1f(glGetUniformLocation(lightingShader.ID, "light.cutOff"), glm::cos(glm::radians(12.5f)));
glUniform1f(glGetUniformLocation(lightingShader.ID, "light.outerCutOff"), glm::cos(glm::radians(17.5f)));
// ๊ด์ ์์ ์์ฑ
glm::vec3 lightColor = glm::vec3(1.0f);
glUniform3f(glGetUniformLocation(lightingShader.ID, "light.ambient"), 0.2f, 0.2f, 0.2f);
glUniform3f(glGetUniformLocation(lightingShader.ID, "light.diffuse"), 0.8f, 0.8f, 0.8f);
glUniform3f(glGetUniformLocation(lightingShader.ID, "light.specular"), 1.0f, 1.0f, 1.0f);
glUniform1f(glGetUniformLocation(lightingShader.ID, "light.constant"), 1.0f);
glUniform1f(glGetUniformLocation(lightingShader.ID, "light.linear"), 0.09f);
glUniform1f(glGetUniformLocation(lightingShader.ID, "light.quadratic"), 0.032f);
// ๋จธํฐ๋ฆฌ์ผ ์์ฑ (diffuse: ์๋ ์ค๋ธ์ ํธ์ ์์์ ์๋ฏธ, ์กฐ๋ช
๋ชจ๋ธ์์ Diffuse ์กฐ๋ช
๊ณ์ฐ ์ ์ฌ์ฉ)
glUniform3f(glGetUniformLocation(lightingShader.ID, "material.specular"), 0.5f, 0.5f, 0.5f);
glUniform1f(glGetUniformLocation(lightingShader.ID, "material.shininess"), 64.0f);
// View / Projection ๋ณํ
glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
glm::mat4 view = camera.GetViewMatrix();
glUniformMatrix4fv(glGetUniformLocation(lightingShader.ID, "projection"), 1, GL_FALSE, glm::value_ptr(projection));
glUniformMatrix4fv(glGetUniformLocation(lightingShader.ID, "view"), 1, GL_FALSE, glm::value_ptr(view));
// World ๋ณํ
glm::mat4 model = glm::mat4(1.0f);
glUniformMatrix4fv(glGetUniformLocation(lightingShader.ID, "model"), 1, GL_FALSE, glm::value_ptr(model));
// Diffuse map ๋ฐ์ธ๋ฉ
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, diffuseMap);
// Specular map ๋ฐ์ธ๋ฉ
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, specularMap);
// render boxes
glBindVertexArray(cubeVAO);
for (unsigned int i = 0; i < 10; i++)
{
// calculate the model matrix for each object and pass it to shader before drawing
glm::mat4 model = glm::mat4(1.0f);
model = glm::translate(model, cubePositions[i]);
float angle = 20.0f * i;
model = glm::rotate(model, glm::radians(angle), glm::vec3(1.0f, 0.3f, 0.5f));
glUniformMatrix4fv(glGetUniformLocation(lightingShader.ID, "model"), 1, GL_FALSE, glm::value_ptr(model));
glDrawArrays(GL_TRIANGLES, 0, 36);
}
// ๊ด์ ํ๋ธ ๋ ๋
lightCubeShader.use();
glUniformMatrix4fv(glGetUniformLocation(lightCubeShader.ID, "projection"), 1, GL_FALSE, glm::value_ptr(projection));
glUniformMatrix4fv(glGetUniformLocation(lightCubeShader.ID, "view"), 1, GL_FALSE, glm::value_ptr(view));
model = glm::mat4(1.0f);
model = glm::translate(model, lightPos);
model = glm::scale(model, glm::vec3(0.2f)); // a smaller cube
glUniformMatrix4fv(glGetUniformLocation(lightCubeShader.ID, "model"), 1, GL_FALSE, glm::value_ptr(model));
glBindVertexArray(lightCubeVAO);
glDrawArrays(GL_TRIANGLES, 0, 36);
// ํ๋ก ํธ, ๋ฐฑ ๋ฒํผ ๊ต์ฒด
glfwSwapBuffers(window);
// ์ด๋ฒคํธ ์ฒ๋ฆฌ
glfwPollEvents();
}
// VAO, VBO ๋ฉ๋ชจ๋ฆฌ ํด์
glDeleteVertexArrays(1, &cubeVAO);
glDeleteVertexArrays(1, &lightCubeVAO);
glDeleteBuffers(1, &VBO);
// ์ฌ์ฉ๋ ๋ฆฌ์์ค ํด์ , ์ด๊ธฐํ ๋ฐ ์ ๋ฆฌ ์์
glfwTerminate();
return 0;
}
// ์ฌ์ฉ์ ์
๋ ฅ ์ฒ๋ฆฌ ํจ์
void processInput(GLFWwindow* window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
camera.ProcessKeyboard(FORWARD, deltaTime);
if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
camera.ProcessKeyboard(BACKWARD, deltaTime);
if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
camera.ProcessKeyboard(LEFT, deltaTime);
if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
camera.ProcessKeyboard(RIGHT, deltaTime);
}
// ์๋์ฐ ์ฌ์ด์ฆ ๋ณ๊ฒฝ ์ฝ๋ฐฑ ํจ์
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
glViewport(0, 0, width, height);
}
// ๋ง์ฐ์ค ์ด๋ ์ฝ๋ฐฑ ํจ์
void mouse_callback(GLFWwindow* window, double xposIn, double yposIn)
{
float xpos = static_cast<float>(xposIn);
float ypos = static_cast<float>(yposIn);
if (firstMouse)
{
lastX = xpos;
lastY = ypos;
firstMouse = false;
}
float xoffset = xpos - lastX;
float yoffset = lastY - ypos; // reversed since y-coordinates go from bottom to top
lastX = xpos;
lastY = ypos;
camera.ProcessMouseMovement(xoffset, yoffset);
}
// ๋ง์ฐ์ค ํ ์ฝ๋ฐฑ ํจ์
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
camera.ProcessMouseScroll(static_cast<float>(yoffset));
}
// ํ
์ค์ฒ ๋ก๋ ํจ์
unsigned int loadTexture(char const* path)
{
unsigned int textureID;
glGenTextures(1, &textureID);
int width, height, nrComponents;
unsigned char* data = stbi_load(path, &width, &height, &nrComponents, 0);
if (data)
{
GLenum format{};
if (nrComponents == 1)
format = GL_RED;
else if (nrComponents == 3)
format = GL_RGB;
else if (nrComponents == 4)
format = GL_RGBA;
glBindTexture(GL_TEXTURE_2D, textureID);
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
stbi_image_free(data);
}
else
{
std::cout << "Texture failed to load at path: " << path << std::endl;
stbi_image_free(data);
}
return textureID;
}
- Fragment Shader
#version 330 core
out vec4 FragColor;
struct Material
{
sampler2D diffuse;
sampler2D specular;
float shininess;
};
struct Light
{
vec3 position;
vec3 direction;
float cutOff;
float outerCutOff;
vec3 ambient;
vec3 diffuse;
vec3 specular;
float constant;
float linear;
float quadratic;
};
in vec3 Normal;
in vec3 FragPos;
in vec2 TexCoords;
uniform vec3 viewPos;
uniform Material material;
uniform Light light;
void main()
{
// ambient
vec3 ambient = light.ambient * texture(material.diffuse, TexCoords).rgb;
// diffuse
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(light.position - FragPos);
float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = light.diffuse * diff * texture(material.diffuse, TexCoords).rgb;
// specular
vec3 viewDir = normalize(viewPos - FragPos);
vec3 reflectDir = reflect(-lightDir, norm);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
vec3 specular = light.specular * spec * texture(material.specular, TexCoords).rgb;
// Attenuation (๊ฐ์ )
float distance = length(light.position - FragPos);
float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * (distance * distance));
ambient *= attenuation;
diffuse *= attenuation;
specular *= attenuation;
// Spotlight (๋ถ๋๋ฌ์ด ์ธ๊ณฝ์ )
float theta = dot(lightDir, normalize(-light.direction));
float epsilon = (light.cutOff - light.outerCutOff);
float intensity = clamp((theta - light.outerCutOff) / epsilon, 0.0, 1.0);
diffuse *= intensity;
specular *= intensity;
vec3 result = ambient + diffuse + specular;
FragColor = vec4(result, 1.0);
}
- ๋งํฌ
'๐จ Graphics > ๐ต OpenGL' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
| [OpenGL] Multiple Lights (0) | 2024.08.15 |
|---|---|
| [OpenGL] Lighting Maps (0) | 2024.08.15 |
| [OpenGL] ๋จธํฐ๋ฆฌ์ผ (Material) (0) | 2024.08.12 |
| [OpenGL] Phong ์กฐ๋ช ๋ชจ๋ธ (0) | 2024.08.12 |
| [OpenGL] ์นด๋ฉ๋ผ ์ค์ ๋ฐ ๋ณํ (์ด๋, ํ์ ) (0) | 2024.08.08 |