보통은 물체마다 '고유한'색이 있다고 생각한다.
하지만 엄밀히 물체는 광원으로부터 빛을 반사할 뿐이고 그 반사된 빛이 적색계열에 민감한 L형, 녹색계열에 민감한 M형, 청색계열에 민감한 S형 원추세포들의 반응에 따라 색을 구분한다. 그래서 광원의 세기, 색 등에 따라 물체의 색은 달라질수있다.
컴퓨터에서도 원추세포들처럼 R, G, B값들의 조합에 따라 색을 표현한다. 빛을 컴퓨터상에서 표현할때 주로 rasterization과 ray tracing방식을 쓴다. ray tracing방식은 계산비용이 커서 게임과 같은 실시간 렌더링에서는 쓰지 않는다.
(하드웨어의 발전으로 요새는 차차 쓰인다고 한다.)
그래서 rasterization방식을 이용하여 빛을 그리고 색을 표현해볼 것이다.
빛의 종류
빛은 크게 3가지로 추상화할 수 있다.
Ambient Light (주변광) : 수많은 반사를 거쳐서 광원이 불분명한 빛이다. 물체를 덮고 있는 빛이며 일정한 밝기와 색으로 표현된다.
Diffuse Light (분산광) : 물체의 표면에서 분산되어 눈으로 바로 들어오는 빛이다. 각도에 따라 밝기가 다르다.
Specular Light (반사광) : 분산광과 달리 한방향으로 완전히 반사되는 빛이다. 반사되는 부분은 흰색의 광으로 보인다.
Ambient Light
주변광의 경우 각도나 세기에 상관없이 물체나 배경에 '스며들어'있어 일정하다.
이렇게 생각하면 계산이 쉽다.
final color = material color * ambient light color
(반사계수. 즉 물체 색) (주변광의 세기)
만약 물체의 색이 빨간색이고 주변광의 세기가 0.1이면
최종 물체의 색은 어둑어둑한 붉은색이 될 것이다.
final color = {1, 0, 0} * {0.1, 0.1, 0.1} = {0.1, 0.0, 0.0}
Diffuse Light
분산광의 경우 램버트 코사인 법칙을 이용한다. 먼저 표면 위치 벡터에서 빛 위치 벡터를 빼서 빛에서 표면으로 가는 벡터를 구하고 입사각의 코사인값을 구하기 위해 법선 벡터(Normal Vector)와 빛 벡터의 내적을 이용한다.
light vector = light position - object position
cosine = dot product(object normal, normalize(light vector))
lambert factor = max(cosine, 0)
한 점에서 빛은 멀어질수록 같은 면적에따른 빛의 세기가 약해진다. (역제곱 법칙)
luminosity = 1 / (distance * distance)
거리가 1보다 작을때를 위해 식을 수정하면
luminosity = 1 / (1 + (distance * distance))
최종적으로 분산광에 의한 물체 색이다.
final color = material color * (light color * lambert factor * luminosity)
램버트 코사인 법칙에 의한 분산광과 주변광에 의한 라이팅을 램버트 조명모델이라고 한다.
final color = material color * (ambient light color + diffuse light color)
Shader
// vertex를 카메라 공간으로
vec3 modelViewVertex = vec3(u_MVMatrix * a_Position);
//법선 벡터를 카메라 공간으로
vec3 modelViewNormal = vec3(u_MVMatrix * vec4(a_Normal, 0.0));
// 감쇠효과에 사용됨.
float distance = length(u_LightPos - modelViewVertex);
// 빛 방향 벡터 얻음.
vec3 lightVector = normalize(u_LightPos - modelViewVertex);
// 입사각의 코사인값 얻음.
float diffuse = max(dot(modelViewNormal, lightVector), 0.1);
// 거리에 따른 감쇠효과 적용 (0.08은 감쇠를 작게 하기 위함).
diffuse = diffuse * (1.0 / (1.0 + (0.08 * distance * distance)));
// 주변광 세기
float ambient = 0.05;
// fragmentShader에 넘길 색상값(최종색상).
v_Color = a_Color * (ambient + diffuse);
// 최종 위치
gl_Position = u_MVPMatrix * a_Position;
빛 적용 전
적용 후
출처 : Ambient and Diffuse Lighting :: 고귀양이의 노트. (tistory.com)
'그래픽스 > 공통' 카테고리의 다른 글
병목 (Bottleneck) (0) | 2024.01.21 |
---|---|
곡선 (Curve) & 스플라인 (Spline) (0) | 2024.01.21 |
드로우 콜과 배칭 개념 간단 정리 (0) | 2023.05.22 |
그래픽스 파이프라인 (게임 엔진 관점) (0) | 2022.10.31 |
셰이더란? (Shader) (0) | 2022.10.30 |