Home [learn-opengl] Lighting: Multiple lights
Post
Cancel

[learn-opengl] Lighting: Multiple lights

Multiple lights

  • 이번에는 6개의 광원들을 생성한다.

    • directional light로 태양과 같은 빛을 시뮬레이션
    • 4개의 point light를 사용하여 scene 전체에 빛을 산란
    • flashlight 추가
  • 하나 이상의 광원을 scene에 사용하기 위해, lighting 계산을 GLSL의 함수로 작성할 것이다.

    • main에 다 계산하면 코드 읽기 불편하다.
    • glsl의 함수는 C와 비슷(프로토타입, 리턴, 이름 등)
  • 여러가지 light를 사용할 때 접근 방식은 일반적으로 다음과 같이 한다.

    • fragment의 출력 칼라를 나타내는 하나의 컬러 벡터.
    • 각 light들을 위해 이 fragment에 light가 기여하는 컬러를 이 fragment의 출력 컬러에 더함.
    • scene의 각 light는 fragment에 미치는 효과를 계산하고 최종 출력 컬러에 기여하게됨.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
out vec4 FragColor;

void main()
{
  // define an output color value
  vec3 output = vec3(0.0);
  // add the directional light's contribution to the output
  output += someFunctionToCalculateDirectionalLight();
  // do the same for all point lights
  for(int i = 0; i < nr_of_point_lights; i++)
  	output += someFunctionToCalculatePointLight();
  // and add others lights as well (like spotlights)
  output += someFunctionToCalculateSpotLight();

  FragColor = vec4(output, 1.0);
}
  • 실제 코드는 구현에 따라 다르지만 일반적인 구조는 동일함

    • 광원에 대한 효과를 계산하는 여러가지 함수들을 정의하고, 그 결과 컬러를 출력 컬러 벡터에 더한다.

Directional light

  • fragment shader에 함수를 정의하는 것

    • 이 함수는 해당 fragment에 대한 directional light의 기여도를 계산한다.
    • 몇 가지의 파라미터를 받고 계산된 directional lighting를 리턴함
  • 먼저 필요한 변수들을 설정

    • DirLight 구조체에 필요한 변수들을 담고, uniform으로 선언
1
2
3
4
5
6
7
8
struct DirLight {
    vec3 direction;

    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
};
uniform DirLight dirLight;
  • 그런다음 dirLight 를 함수에 넘겨준다.
1
2
vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir);

  • C언어처럼 프로토타입을 main 함수 위에 두고 main 밑에 함수 작성 가능

  • 이 함수는 DirLight 타입과 두개의 벡터를 필요로한다.

    • 이 함수의 내용은 다음과 같다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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);
}
  • 이전에 작성한 코드를 복사한것.
    • 하나의 컬러 벡터로 리턴

Point light

  • 마찬가지로 attenuation과 함께 함수로 정의
1
2
3
4
5
6
7
8
9
10
11
12
13
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];
  • scene에 배치할 point light의 갯수를 GLSL에서 전처리기로 선언한 것을 볼 수 있다.

  • 프로토 타입은 다음과 같다.

    • 이 함수는 필요한 모든 데이터를 파라미터로 받고, fragment에 대한 특정한 point light의 기여 컬러를 나타내는 vec3을 리턴한다.
1
vec3 CalcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir);
  • 아래와 같은 함수를 생성할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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);
}
  • 이 함수를 반복문으로 호출하여 여러개의 point light를 계산할 수 있다.

Putting it all together

  • 이제 다음과 같이 계산할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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);
}

여기서 함수안에 많은 중복된 계산들이 있다.(reflect vecto, specular, diffuse, sampling the material textures). 이런 코드들이 최적화할 수 있는 부분이다.

  • 모든 광원이 처리될 때까지 각 light 타입은 그들의 기여도를 최종 출력 컬러에 더한다.

    • 최종 컬러는 scene의 모든 광원들의 컬러 효과를 포함하고 있다.
  • point light uniform 은 배열이기 때문에 여기서 잠깐 설명한다.

    • 하나의 struct uniform을 설정하는 것과 비슷하다.
    1
    
    lightingShader.setFloat("pointLights[0].constant", 1.0f);
    
    • 하지만 이렇게 하면 코드가 길어지니 추상화할 수 있지만, 결국에는 모든 light들의 uniform을 설정해야함.
  • 또한 point light들의 위치 벡터를 정의해서 scene에 배치할 수 있다.

1
2
3
4
5
6
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)
};
  • 이제 잡다한 코드들을 작성하면 다음과 같은 결과가 나온다.

  • 전체적으로 밝은것을 볼 수 있고, 빛을 산란하는 4개의 light, flashlight는 플레이어 시점을 기준으로 보이는것을 볼 수 있다.

전체 코드

출처

Multiple-lights

Multiple-lights

This post is licensed under CC BY 4.0 by the author.

[shader] 노트

[learn-opengl] Model Loading: Assimp