LearnOpenGL(11) - light casters
여기서 우리는 다양한 조명을 어떻게 구현하는지 살펴봤음
그럼 우리는 하나 더 하고싶은거지 이제
이 다양한 조명을 하나의 씬 내부에서 사용하려면 어떻게 해야할까?
커스텀 함수를 사용하면됨
#version 330 core
struct Material
{
sampler2D diffuse;
sampler2D specular;
float shininess;
};
struct Light
{
vec3 position;
vec3 direction;
vec3 ambient;
vec3 diffuse;
vec3 specular;
float constant;
float linear;
float quadratic;
float cutoff; //radian degree
float outerCutoff; //radian degree
};
out vec4 FragColor;
in vec3 FragPos;
in vec3 Normal;
in vec2 TexCoords; //light map용 텍스쳐 좌표
uniform vec3 viewPos;
uniform Material mat;
uniform Light light;
void main()
{
vec3 lightDir = normalize(light.position - FragPos);
// light dir과 spot dir의 내적
float theta = dot(lightDir, normalize(-light.direction));
float epsilon = light.cutoff - light.outerCutoff;
float I = clamp((theta - light.outerCutoff) / (epsilon), 0.0f, 1.0f);
// ambient
vec3 ambient = light.ambient * texture(mat.diffuse, TexCoords).rgb;
// diffuse
vec3 norm = normalize(Normal);
float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = light.diffuse * diff * texture(mat.diffuse, TexCoords).rgb;
// specular
vec3 viewDir = normalize(viewPos - FragPos);
vec3 reflectDir = reflect(-lightDir, norm);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), mat.shininess);
vec3 specular = light.specular * spec * texture(mat.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 * I;
specular *= attenuation * I;
vec3 result = ambient + diffuse + specular;
FragColor = vec4(result, 1.0);
}
이게 지금 우리 fragment shader임
우리는 directional light와 point light를 한번에 다루고 싶음
주의사항!!!
구조체 내부에 LearnOpenGL(10) - light maps 여기서 설명한것처럼 opaque타입 변수가 있으면 함수 파라미터로 못넘김!!!
먼저 directional light구조체를 만들어줌
struct DirLight
{
vec3 direction;
vec3 ambient;
vec3 diffuse;
vec3 specular;
};
그리고 C, C++처럼 함수가 main보다 아래에 선언되어있으면
컴파일이 안됨!!
그러므로, 위에다가 선언하든지
아래에다 선언하고 위에 전방선언 해주던지 ㅇㅇ
아래가 그 코드!
struct DirLight
{
vec3 direction;
vec3 ambient;
vec3 diffuse;
vec3 specular;
};
//...
vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir);
void main()
{
//... some shading code
}
vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir)
{
vec3 lightDir = normalize(-light.direction);
// diffuse shading
float diff = max(dot(normal, lightDir), 0.0);
// specular shading
vec3 reflectDir = reflect(-lightDir, normal);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
// combine results
vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));
vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));
vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));
return (ambient + diffuse + specular);
}
마찬가지로 먼저 구조체를 만든다음
함수 전방선언하고
함수 정의함
struct PointLight {
vec3 position;
float constant;
float linear;
float quadratic;
vec3 ambient;
vec3 diffuse;
vec3 specular;
};
#define NR_POINT_LIGHTS 4
uniform PointLight pointLights[NR_POINT_LIGHTS];
구조체는 특이사항이 없는데
밑에 배열을 선언했음
저렇게 배열의 크기를 만들어주고 사용하던지
뭐그냥 하드코딩하든지 ㅇㅇ
가변배열도 가능하긴 한데
SSBO, UBO 이런 키워드로 찾아보면 됨
아래는 함수
#version 330 core
struct Material
{
sampler2D diffuse;
sampler2D specular;
float shininess;
};
struct DirLight
{
vec3 direction;
vec3 ambient;
vec3 diffuse;
vec3 specular;
};
vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir);
vec3 CalcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir);
void main()
{
//... some shading code
}
vec3 CalcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir)
{
vec3 lightDir = normalize(light.position - fragPos);
// diffuse shading
float diff = max(dot(normal, lightDir), 0.0);
// specular shading
vec3 reflectDir = reflect(-lightDir, normal);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
// attenuation
float distance = length(light.position - fragPos);
float attenuation = 1.0 / (light.constant + light.linear * distance +
light.quadratic * (distance * distance));
// combine results
vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));
vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));
vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));
ambient *= attenuation;
diffuse *= attenuation;
specular *= attenuation;
return (ambient + diffuse + specular);
}
struct SpotLight
{
vec3 position;
vec3 direction;
vec3 ambient;
vec3 diffuse;
vec3 specular;
float constant;
float linear;
float quadratic;
float cutoff; //radian degree
float outerCutoff; //radian degree
};
vec3 CalcSpotLight(SpotLight light, vec3 normal, vec3 fragPos, vec3 viewDir);
//...
vec3 CalcSpotLight(SpotLight light, vec3 normal, vec3 fragPos, vec3 viewDir)
{
vec3 lightDir = normalize(light.position - fragPos);
// diffuse shading
float diff = max(dot(normal, lightDir), 0.0);
// specular shading
vec3 reflectDir = reflect(-lightDir, normal);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
// attenuation
float distance = length(light.position - fragPos);
float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * (distance * distance));
// spotlight intensity
float theta = dot(lightDir, normalize(-light.direction));
float epsilon = light.cutoff - light.outerCutoff;
float intensity = clamp((theta - light.outerCutoff) / epsilon, 0.0, 1.0);
// combine results
vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));
vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));
vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));
ambient *= attenuation * intensity;
diffuse *= attenuation * intensity;
specular *= attenuation * intensity;
return (ambient + diffuse + specular);
}
이제 합칠차례!!
그전에!!!
지금 각 Calc함수에서
각각 reflectDir을 계산하고
texture를 만들고 해서 추가하고 이러고 있는데
이건 최적화 하면됨 나중에 ㅇㅇ
전체 코드를 보면
#version 330 core
struct Material
{
sampler2D diffuse;
sampler2D specular;
float shininess;
};
struct DirLight
{
vec3 direction;
vec3 ambient;
vec3 diffuse;
vec3 specular;
};
struct PointLight {
vec3 position;
float constant;
float linear;
float quadratic;
vec3 ambient;
vec3 diffuse;
vec3 specular;
};
#define NR_POINT_LIGHTS 4
struct SpotLight
{
vec3 position;
vec3 direction;
vec3 ambient;
vec3 diffuse;
vec3 specular;
float constant;
float linear;
float quadratic;
float cutoff; //radian degree
float outerCutoff; //radian degree
};
out vec4 FragColor;
in vec3 FragPos;
in vec3 Normal;
in vec2 TexCoords; //light map용 텍스쳐 좌표
uniform vec3 viewPos;
uniform Material mat;
uniform DirLight dirLight;
uniform PointLight pointLights[NR_POINT_LIGHTS];
uniform SpotLight spotLight;
vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir);
vec3 CalcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir);
vec3 CalcSpotLight(SpotLight light, vec3 normal, vec3 fragPos, vec3 viewDir);
void main()
{
// properties
vec3 norm = normalize(Normal);
vec3 viewDir = normalize(viewPos - FragPos);
// phase 1: directional lighting
vec3 result = CalcDirLight(dirLight, norm, viewDir);
// phase 2: point lights
for(int i = 0; i < NR_POINT_LIGHTS; i++)
{
result += CalcPointLight(pointLights[i], norm, FragPos, viewDir);
}
// phase 3: spot light
result += CalcSpotLight(spotLight, norm, FragPos, viewDir);
FragColor = vec4(result, 1.0);
}
vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir)
{
vec3 lightDir = normalize(-light.direction);
// diffuse shading
float diff = max(dot(normal, lightDir), 0.0);
// specular shading
vec3 reflectDir = reflect(-lightDir, normal);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
// combine results
vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));
vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));
vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));
return (ambient + diffuse + specular);
}
vec3 CalcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir)
{
vec3 lightDir = normalize(light.position - fragPos);
// diffuse shading
float diff = max(dot(normal, lightDir), 0.0);
// specular shading
vec3 reflectDir = reflect(-lightDir, normal);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
// attenuation
float distance = length(light.position - fragPos);
float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * (distance * distance));
// combine results
vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));
vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));
vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));
ambient *= attenuation;
diffuse *= attenuation;
specular *= attenuation;
return (ambient + diffuse + specular);
}
vec3 CalcSpotLight(SpotLight light, vec3 normal, vec3 fragPos, vec3 viewDir)
{
vec3 lightDir = normalize(light.position - fragPos);
// diffuse shading
float diff = max(dot(normal, lightDir), 0.0);
// specular shading
vec3 reflectDir = reflect(-lightDir, normal);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
// attenuation
float distance = length(light.position - fragPos);
float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * (distance * distance));
// spotlight intensity
float theta = dot(lightDir, normalize(-light.direction));
float epsilon = light.cutoff - light.outerCutoff;
float intensity = clamp((theta - light.outerCutoff) / epsilon, 0.0, 1.0);
// combine results
vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));
vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));
vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));
ambient *= attenuation * intensity;
diffuse *= attenuation * intensity;
specular *= attenuation * intensity;
return (ambient + diffuse + specular);
}
코드 존나 간단해짐!
포인트 라이트에서 배열 선언한거 사용하려면 for루프나 뭐 다른 반복문 돌면됨!
그럼 이제 어떻게 씀?
그냥 쓰던것처럼 쓰면됨
uint32_t loc = glGetUniformLocation(shaderProgram, "someArrayUniform[0].someVariable");
glUniform1f(loc, 1.0f);
그냥 똑같음ㅋㅋ
glm::vec3 pointLightPositions[] =
{
glm::vec3( 0.7f, 0.2f, 2.0f),
glm::vec3( 2.3f, -3.3f, -4.0f),
glm::vec3(-4.0f, 2.0f, -12.0f),
glm::vec3( 0.0f, 0.0f, -3.0f)
};
대충 이런 점광 좌표를 설정해주고
while (!glfwWindowShouldClose(window))
{
// 프레임 보정
// ----------
float currentFrame = static_cast<float>(glfwGetTime());
deltaTime = currentFrame - lastFrame;
lastFrame = currentFrame;
// 인풋
// ----
processInput(window);
// 드로우 시작!
// ----------
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//cubeVAO에 사용할 쉐이더인 normalCubeShader프로그램을 GL에게 쉐이더 상태 등록
normalCubeShader.use();
normalCubeShader.setVec3("viewPos", camera.Position);
normalCubeShader.setFloat("mat.shininess", 64.0f);
//light 수정
// directional light
normalCubeShader.setVec3("dirLight.direction", -0.2f, -1.0f, -0.3f);
normalCubeShader.setVec3("dirLight.ambient", 0.05f, 0.05f, 0.05f);
normalCubeShader.setVec3("dirLight.diffuse", 0.4f, 0.4f, 0.4f);
normalCubeShader.setVec3("dirLight.specular", 0.5f, 0.5f, 0.5f);
// point light 1
normalCubeShader.setVec3("pointLights[0].position", pointLightPositions[0]);
normalCubeShader.setVec3("pointLights[0].ambient", 0.05f, 0.05f, 0.05f);
normalCubeShader.setVec3("pointLights[0].diffuse", 0.8f, 0.8f, 0.8f);
normalCubeShader.setVec3("pointLights[0].specular", 1.0f, 1.0f, 1.0f);
normalCubeShader.setFloat("pointLights[0].constant", 1.0f);
normalCubeShader.setFloat("pointLights[0].linear", 0.09f);
normalCubeShader.setFloat("pointLights[0].quadratic", 0.032f);
// point light 2
normalCubeShader.setVec3("pointLights[1].position", pointLightPositions[1]);
normalCubeShader.setVec3("pointLights[1].ambient", 0.05f, 0.05f, 0.05f);
normalCubeShader.setVec3("pointLights[1].diffuse", 0.8f, 0.8f, 0.8f);
normalCubeShader.setVec3("pointLights[1].specular", 1.0f, 1.0f, 1.0f);
normalCubeShader.setFloat("pointLights[1].constant", 1.0f);
normalCubeShader.setFloat("pointLights[1].linear", 0.09f);
normalCubeShader.setFloat("pointLights[1].quadratic", 0.032f);
// point light 3
normalCubeShader.setVec3("pointLights[2].position", pointLightPositions[2]);
normalCubeShader.setVec3("pointLights[2].ambient", 0.05f, 0.05f, 0.05f);
normalCubeShader.setVec3("pointLights[2].diffuse", 0.8f, 0.8f, 0.8f);
normalCubeShader.setVec3("pointLights[2].specular", 1.0f, 1.0f, 1.0f);
normalCubeShader.setFloat("pointLights[2].constant", 1.0f);
normalCubeShader.setFloat("pointLights[2].linear", 0.09f);
normalCubeShader.setFloat("pointLights[2].quadratic", 0.032f);
// point light 4
normalCubeShader.setVec3("pointLights[3].position", pointLightPositions[3]);
normalCubeShader.setVec3("pointLights[3].ambient", 0.05f, 0.05f, 0.05f);
normalCubeShader.setVec3("pointLights[3].diffuse", 0.8f, 0.8f, 0.8f);
normalCubeShader.setVec3("pointLights[3].specular", 1.0f, 1.0f, 1.0f);
normalCubeShader.setFloat("pointLights[3].constant", 1.0f);
normalCubeShader.setFloat("pointLights[3].linear", 0.09f);
normalCubeShader.setFloat("pointLights[3].quadratic", 0.032f);
// spotLight
normalCubeShader.setVec3("spotLight.position", camera.Position);
normalCubeShader.setVec3("spotLight.direction", camera.Front);
normalCubeShader.setVec3("spotLight.ambient", 0.0f, 0.0f, 0.0f);
normalCubeShader.setVec3("spotLight.diffuse", 1.0f, 1.0f, 1.0f);
normalCubeShader.setVec3("spotLight.specular", 1.0f, 1.0f, 1.0f);
normalCubeShader.setFloat("spotLight.constant", 1.0f);
normalCubeShader.setFloat("spotLight.linear", 0.09f);
normalCubeShader.setFloat("spotLight.quadratic", 0.032f);
normalCubeShader.setFloat("spotLight.cutoff", glm::cos(glm::radians(12.5f)));
normalCubeShader.setFloat("spotLight.outerCutoff", glm::cos(glm::radians(15.0f)));
// world->view로 가는 view변환행렬/view->screen으로 가는 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();
normalCubeShader.setMat4("projection", projection);
normalCubeShader.setMat4("view", view);
//bind texture
//diffuse map
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, diffuseMap);
//specular map
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, specularMap);
glm::mat4 model = glm::mat4(1.0f);
glBindVertexArray(cubeVAO);
for(unsigned int i = 0; i < 10; i++)
{
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));
normalCubeShader.setMat4("model", model);
glDrawArrays(GL_TRIANGLES, 0, 36);
}
//lightCubeVAO에 사용할 쉐이더인 lightCubeShader프로그램을 GL에게 쉐이더 상태 등록
lightCubeShader.use();
//변환행렬들 생성
lightCubeShader.setMat4("projection", projection);
lightCubeShader.setMat4("view", view);
glBindVertexArray(lightCubeVAO);
for (unsigned int i = 0; i < 4; i++)
{
model = glm::mat4(1.0f);
model = glm::translate(model, pointLightPositions[i]);
model = glm::scale(model, glm::vec3(0.2f)); // Make it a smaller cube
lightCubeShader.setMat4("model", model);
glDrawArrays(GL_TRIANGLES, 0, 36);
}
glDrawArrays(GL_TRIANGLES, 0, 36);
// glfw 버퍼 스와핑
// ---------------
glfwSwapBuffers(window);
glfwPollEvents();
}
대충 요로코롬 main에서 사용해주면 됨

이렇게 잘 된당~