두 챕터 전에, 우리는 씬에 빛을 도입했습니다. 하지만 설정할 수 있는 매개변수를 많이 주지는 않았습니다. 우리가 포함했던 유일한 속성은 빛을 향한 방향이었습니다:
vec3 direction_to_light=normalize(vec3(-1,-1,0));
하지만 빛의 색상이나 밝기를 지정하고 싶다면 어떨까요?
먼저 밝기부터: “밝은 빛”(맑은 날 정오의 햇빛)과 “그리 밝지 않은 빛”(구름 낀 날의 햇빛), 그리고 “아주 약한 빛”(촛불? 달빛?)을 어떻게 구별할 수 있을까요?
순진하게 생각하면, 전력(시간당 에너지, 빛의 경우 “복사 선속(radiant flux)”이라는 이름이 더 적합해 보이며 기호는 Φ)이 좋은 아이디어처럼 들립니다. 햇빛과 전력이 연결되어 있다는 것은 태양광 패널을 통해 알 수 있습니다. 그리고 태양광 패널은 맑은 날과 흐린 날에 서로 다른 양의 전력을 생산합니다.
문제점: 만약 어느 날 우리가 두 번째 태양광 패널을 사용하기로 결정하면, 빛 자체는 전혀 변하지 않았는데도 갑자기 두 배의 전력을 얻게 됩니다.
해결책: 전력을 세는 대신, 단위 면적당 전력을 셉시다. (그러면 두 배의 전력과 두 배의 태양광 패널이 상쇄됩니다.) 이 단위 면적당 전력이라는 양은 방사 조도(irradiance)라고 불리며, 문자 E로 표시되고 제곱미터당 와트(W/m²) 단위로 측정됩니다.
짧은 여담: 면적은 어떻게 계산할까요? 두 개의 동일한 태양광 패널이 있더라도, 하나가 다른 하나보다 더 많은 전력을 생산하도록 배치하는 것은 분명히 가능합니다. 하나를 그늘에 두는 것을 말하는 게 아니라, 단지 방향에 관한 이야기입니다:
따라서 빛에 수직인 면적만 계산하는 것이 좋은 생각이 될 수 있습니다. 즉, “빛의 방향과 표면의 법선 벡터 사이의 각도의 코사인” 계수가 자주 등장하거나, 다른 말로 하면 두 방향의 내적(scalar product)이 자주 등장한다는 뜻입니다 (벡터의 직교 성분과 내적 모두에 대해서는 25장을 참조하세요). 우리는 이것을 “투영된 면적(projected area)”이라고 부를 것입니다.
종종 우리는 한 지점에 도달하는 빛의 총량에는 그다지 관심이 없습니다. 왜냐하면 우리는 그 모든 것을 보지 않기 때문입니다. 우리는 특정 방향(또는 방향의 범위)에서 오는 빛에만 관심이 있습니다. 새로운 양: (투영된) 면적당 그리고 각도당 전력. 잠깐만요: 각도는 평면에서의 방향을 측정하는 단위였죠 (단위 원의 호에서 이 방향들이 차지하는 부분의 길이, 기억나시나요?) — 여기서는 공간에서의 방향을 측정하는 무언가가 필요합니다: 바로 입체각(solid angle)입니다. 우리는 이것을 단위 구의 표면에서 이 방향들이 차지하는 면적으로 정의합니다. 반지름이 1인 구의 표면적은 4π이므로, “모든 방향”의 입체각은 4π이고, 반구(“아래쪽보다 위쪽을 향하는 모든 방향”과 같은)의 입체각은 2π입니다. 우리가 말하는 숫자가 입체각을 나타낸다는 것을 표시하고 싶다면 단위를 붙일 수 있습니다: “스테라디안(steradian)”, sr. 반구의 입체각은 2π sr가 됩니다. (다시 말해, 위 정의에 따르면 “sr”은 그냥 “1”을 멋지게 쓰는 방법일 뿐입니다. 마치 우리가 각도에 대해 이야기하고 있음을 나타내기 위해 °=π/180이라는 숫자를 사용하는 것처럼요 - 더 직접적인 비유는 라디안 “rad”이 될 것이고, 전체 원은 360° = 2π = 2π rad의 각도를 갖게 됩니다.)
그래서, 투영된 면적당 그리고 입체각당 전력. 이것이 바로 복사 휘도(radiance) (L, 제곱미터당 와트 및 스테라디안, W/(m² sr) 단위로 측정됨)입니다. 이 공식에서 “각도”는 입체각의 크기를 의미하며, 어떤 방향이 기여하는지를 의미하지는 않는다는 점에 유의하세요 (물론 L은 여전히 방향에 따라 달라질 수 있습니다).
빛의 광선을 따라 복사 휘도는 어떻게 변할까요? 광선의 시작점에 면적 A₁이 있고 끝점에 A₂가 있으며, 둘 다 광선에 수직이라고 가정합시다 (복사 휘도는 항상 투영된, 즉 수직인 면적을 가정하므로 이는 제한이 아닙니다). 그리고 광선의 길이는 d라고 가정합시다. 그러면 A₁을 떠나는 전력은 A₁ω₁L₁ (여기서 ω₁은 각도, L₁은 시작점의 복사 휘도)이고, 광선 끝의 A₂에 도달하는 전력은 A₂ω₂L₂입니다. 이 두 값은 동일해야 합니다 (에너지, 따라서 빛이 시간에 따라 변하지 않는다면 전력도 보존되며, 빛이 시작점과 끝점 사이에서 무언가(예: 안개)와 상호작용하지 않는 한). 이제 ω₁은 실제로는 A₂/d²의 크기를 가집니다 (엄밀히 말해, 이는 거리가 면적의 크기보다 훨씬 클 때만 매우 좋은 근사치입니다), 그리고 ω₂는 (같은 근사로) A₁/d²와 같습니다. 따라서, L₁A₁A₂/d² = A₁ω₁L₁ = A₂ω₂L₂ = L₂ A₁A₂/d² 이므로, L₁=L₂가 됩니다.
결론: 경로상에 아무것도 없다면, 복사 휘도는 광선을 따라 일정합니다.
방사 조도의 경우, 이것은 성립하지 않습니다.
위에 언급된 것들은 빛을 기술하기에 적합한 물리량입니다. 이와 연관된 다른, 말하자면 인간 인식의 관점인 “측광학(photometry)”에서 온 양(과 단위)들이 있습니다: 방사 조도 대신 조도(illuminance) (여전히 E로 표시되며, W/m² 대신 lm/m², 제곱미터당 루멘으로 측정됨)를 사용하고, 복사 휘도는 휘도(luminance) (W/(m² sr) 대신 lm/(m² sr)로 측정됨)로 대체됩니다. 분명히, 우리는 W와 lm의 차이를 알아내고, 그 다음 복사 휘도와 휘도 (또는 방사 조도와 조도)의 차이를 이해해야 합니다.
그것은 하나의 계수(factor)입니다. 1와트는 683루멘입니다. 이게 다입니다 — 특정 종류의 녹색 빛에 대해서만요. 빛의 색상(또는 파장)이 동일한 전력을 가졌더라도 얼마나 밝게 보이는지에 상당한 영향을 미친다는 것이 밝혀졌습니다. (분명히, 자외선처럼 우리의 밝기 인식에 전혀 기여하지 않는 파장도 있지만, 가시광선 범위 내에서도 상당한 차이가 있습니다.) 우리는 이러한 차이를 무시하고, 복사 휘도와 휘도가 쉽게 상호 교환 가능하다고 가정할 것입니다.
(참고: 단위에 대한 몇 가지 이름들: 칸델라, cd = lm/sr; 럭스: lx = lm/m².)
다른 색상에 관해서는: 뭐, 평소처럼: 우리는 각 구성 요소별 L 값을 다룹니다. 빨간색, 녹색, 그리고 파란색 빛에 대해 각각 하나의 값을 가집니다. 이것이 단순화라는 것을 명심하세요. 실제로는 훨씬 더 많은 다른 색상, 다른 파장의 빛이 있습니다. 그리고 물리적으로, “(단색광의) 노란색 빛”은 “빨간색 빛 더하기 녹색 빛”과 다릅니다. (일부 광학 현상은 관련된 파장에 의존하기 때문에, 이 두 종류의 빛은 다르게 행동할 것입니다; 위에서 와트와 루멘 사이의 변환 계수뿐만 아니라도 말이죠.) 하지만 인간의 인식에 있어서는, 이 두 종류의 빛은 구별할 수 없으며(물론 “올바른” 빨간색과 녹색의 조합을 사용한다면), 우리는 모든 빛이 단지 빨간색, 녹색, 파란색 빛의 혼합이라고 가정합니다. 다시 말하지만: 빛을 보는 데는 이것으로 충분합니다. 다른 모든 색상에 대해 우리는 똑같이 보이는 조합을 찾을 수 있습니다. 빛이, 예를 들어, 올바른(혹은 잘못된?) 재질의 구에 부딪혔을 때, 원래의 빛과 우리의 RGB 대체 빛은 다르게 행동할 수 있습니다. 즉, 구에 의해 반사된 빛(최종적으로 우리 눈에 들어오는)이 원래 빛과 대체 빛 사이에서 달라져서 더 이상 똑같이 보이지 않을 수 있습니다. 우리는 최선을 바라며(즉, 이것이 심각한 정도로 발생하지 않거나, 적어도 많은 중요한 경우에 발생하지 않기를 바라며) — 이것을 무시합니다. (약 400nm에서 700nm 사이의 전체 범위에 대한 값 대신) 3개의 값을 갖는 것이 훨씬 간단하고 빠릅니다.
만약 다른 빛들이 있고 모두 같은 지점에 영향을 미친다면, 우리는 그 값들을 더하고 그들의 복사 휘도(또는 방사 조도)의 합을 고려합니다.
이것은 쉽게 1을 초과하는 값으로 이어질 수 있습니다. (그리고 실제로, 값의 범위는 넓습니다: 햇빛의 복사 휘도는 약 100,000 lm/(m² sr)이고, 별빛은 0.001 lm/(m² sr)입니다.)
이 값들을 어떻게 화면이 이해할 수 있는 색상으로 변환할까요? (결국 우리는 R, G, B에 대해 0과 1 사이의 값이 필요합니다.)
이것을 “톤 매핑(tone mapping)”이라고 합니다. 가장 간단한 방법은 min(1,max(L,0))을 사용하는 것, 즉 값을 0과 1 사이로 제한(clamp)하는 것일 수 있습니다. 이것은 매우 쉽게 완전히 흰색 화면으로 이어질 수 있습니다.
다른 방법은 L/(1+L) (“라인하르트 톤 매핑(Reinhard tone mapping)”)을 사용하는 것입니다. 이것은 전체 범위 [0,∞)를 간격 [0,1)로 압축합니다. L이 0에 가까울 때는 L과 거의 같지만, 큰 값에 대해서는 1 미만으로 유지됩니다. 다른 큰 값들은 다른 색상으로 이어지지만, 그들이 더 밝을수록 이 차이는 더 작아질 것입니다.
(다른 가능성들도 있습니다: 분모의 숫자 “1”이 임의적일 뿐만 아니라 (그리고, 측정 단위를 가져야 합니다), 우리는 물리적 카메라의 동작 등과 관련된 매개변수를 가진 완전히 다른 함수를 사용할 수도 있습니다 — 우리는 지금은 (빨강, 초록, 파랑 각각에 대해) L/(1+L)을 유지할 것입니다.)
아주 대략적으로, 픽셀의 색상은 다음과 같이 계산되어야 합니다:
L=vec3(0);
for each light {
L += 이 빛이 현재 픽셀의 복사 휘도에 기여하는 양
}
colour = vec4(L.r/(1+L.r), L.g/(1+L.g), L.b/(1+L.b), 1.0);
그리고 네, 픽셀 색상으로 변환되어야 하는 것은 방사 조도보다는 복사 휘도입니다. 우리는 위에서 복사 휘도가 본질적으로 광선의 속성임을 보았습니다.
종종 우리가 픽셀에서 보거나 “측정”하는 빛은 광원에서 직접 오는 것이 아니라 광선에 의해 비춰진(조명된) 표면에서 올 것입니다. 도달하는 빛(또는 표면의 방사 조도)을 떠나는 빛(나가는 복사 휘도)의 양으로 바꾸는 방법은 다음 장의 핵심이 될 것입니다.
먼저 원래 질문으로 돌아가 봅시다: 우리의 빛(들)을 숫자로 어떻게 특징지을까요?
우리가 이전에 가졌던 방향성 광원은 현실에 거의 존재하지 않습니다. 하지만 예를 들어 햇빛처럼 광원이 너무 멀리 있어서 광선들이 서로 거의 평행한 것으로 간주할 수 있는 경우에 유용한 근사치입니다.
빛이 오는 방향은 단 하나뿐이므로, 이 빛을 받는 입체각은 항상 0입니다. 따라서 이 빛을 기술하기 위해 복사 휘도를 사용해서는 안 됩니다. 대신 방사 조도를 사용할 수 있습니다. 방사 조도는 빛을 받는 표면의 방향에 따라 달라지므로, 빛의 방향에 수직인 표면에 대한 값을 지정하기로 합니다. 하지만 그 외에도, 보통 방사 조도는 광선을 따라 감소하므로 빛을 특징짓는 숫자로서는 좋지 않은 선택일 수 있습니다 — 하지만 방향성 광원은 모든 지점에서 “무한히 멀리” 있다고 생각되므로, 여기서는 문제가 되지 않으며 방사 조도를 사용할 수 있습니다. 또는 조도를 사용할 수도 있습니다. 조도를 사용합시다.
요약하자면:
매개변수: 방향(규칙상 항상 빛으로의 방향을 사용함), 즉 단위 길이의 vec3, 그리고 방사 조도, 즉 빨강, 초록, 파랑 각 색상에 대해 lm/m² (또는 lx) 단위의 조도를 포함하는 vec3.
몇 가지 값: 위키피디아.
또 다른 종류의 빛: 씬에 위치는 있지만 실제 크기는 없는 빛입니다. 여기서는 방사 조도가 더 이상 좋은 아이디어가 아닙니다. 왜냐하면 위치에 따라 달라지기 때문입니다. 우리는 전력 Φ나 측광학적 аналог인 “광속(luminous flux)”, 즉 루멘(lumen) 단위로 측정되는 양을 사용할 수 있습니다. 거리 d에서, 우리는 Φ/(4πd²)의 조도를 갖게 됩니다 (총 전력은 보존되어야 하고, 그 거리에서 모든 방향을 덮는 투영된 면적은 반지름 d인 구의 면적과 같으므로). 우리는 이 공식을 조금 수정하여 Φ/(4π max(d,0.001)²)를 사용할 수 있습니다. 그렇게 하면 0으로 나누는 것을 피하거나 (그 표현을 선호한다면) 실제 점 광원은 존재하지 않으며 아무것도 광원에 임의로 가까워질 수 없다는 점을 고려할 수 있습니다.
다시 요약하자면:
매개변수: 위치(vec3, 구성 요소는 미터로 해석됨)와 광속(vec3, RGB용) (lm 단위).
다시 몇 가지 값: 위키피디아
이렇게 많은 이론을 다룬 후 (그리고 이 모든 것이 빛을 어떤 숫자로 기술할지 알기 위한 것이었으니), 이제 코드를 작성하고 그림을 만들 시간인 것 같습니다.
방향성 광원으로 시작합시다. 프래그먼트 셰이더를 변경합니다:
#version 450
layout (location=0) out vec4 theColour;
layout (location=0) in vec4 data_from_the_vertexshader;
layout (location=1) in vec3 normal;
void main(){
vec3 L=vec3(0);
vec3 direction_to_light=normalize(vec3(-1,-1,0));
vec3 irradiance=vec3(32000,32000,32000);
L += irradiance*(max(dot(normal,direction_to_light),0))*vec3(data_from_the_vertexshader);
theColour=vec4(L/(1+L),1.0);
}
이것은 햇빛이어야 합니다.

대신에
vec3 irradiance=vec3(0.1,0.1,0.1);
를 사용하면, “달빛 속의 붉은 구” 같은 것이 되어야 합니다:

참고로, 다음 줄에 너무 신경 쓰지 마세요:
L += irradiance*(max(dot(normal,direction_to_light),0))*vec3(data_from_the_vertexshader);
우리는 방사 조도가 면의 방향에 맞게 조정되도록 하고, 그것을 버텍스 셰이더에서 오는 색상 데이터와 (성분별로) 곱합니다. 이 부분은 빛에 대한 작업이 끝나면 처리할 것입니다.
지금은 코드를 약간 재구성해 봅시다:
#version 450
layout (location=0) out vec4 theColour;
layout (location=0) in vec4 colour_in;
layout (location=1) in vec3 normal;
struct DirectionalLight{
vec3 direction_to_light;
vec3 irradiance;
};
void main(){
vec3 L=vec3(0);
DirectionalLight dlight = DirectionalLight(normalize(vec3(-1,-1,0)),vec3(10,10,10));
L += dlight.irradiance*(max(dot(normal,dlight.direction_to_light),0))*vec3(colour_in);
theColour=vec4(L/(1+L),1.0);
}
두 가지가 변경되었습니다: 색상 변수의 이름을 바꾸었고, 빛은 자체 struct가 되었습니다:
struct DirectionalLight{
vec3 direction_to_light;
vec3 irradiance;
};
정의는 충분히 자명해 보입니다. 필드는 dlight.irradiance처럼 .으로 접근합니다. 그리고 하나를 만드는 것은 다음과 같이 작동합니다:
DirectionalLight dlight = DirectionalLight(normalize(vec3(-1,-1,0)),vec3(10,10,10));
생성자는 구조체 정의에 나타나는 순서와 동일한 순서로 인수를 받습니다.
점 광원을 평가하려면, 점의 위치도 분명히 필요합니다. 그리고 그 위치는 투영 행렬이 적용되기 전의 위치여야 합니다. 거리는 조명 계산에 들어가고, 이는 투영에 의해 영향을 받습니다. 월드 좌표를 직접 사용합시다. 이는 버텍스 셰이더에 새로운 출력 변수가 필요하다는 것을 의미합니다. (그리고 이왕 하는 김에, 색상 출력도 vec3로 변경합시다. 어차피 네 번째 구성 요소는 무시하고 있으니까요.)
#version 450
layout (location=0) in vec3 position;
layout (location=1) in vec3 normal;
layout (location=2) in mat4 model_matrix;
layout (location=6) in mat4 inverse_model_matrix;
layout (location=10) in vec3 colour;
layout (set=0, binding=0) uniform UniformBufferObject {
mat4 view_matrix;
mat4 projection_matrix;
} ubo;
layout (location=0) out vec3 colourdata_for_the_fragmentshader;
layout (location=1) out vec3 out_normal;
layout (location=2) out vec4 worldpos;
void main() {
worldpos = model_matrix*vec4(position,1.0);
gl_Position = ubo.projection_matrix*ubo.view_matrix*worldpos;
colourdata_for_the_fragmentshader=colour;
out_normal = transpose(mat3(inverse_model_matrix))*normal;
}
worldpos에 관한 줄들이 변경되었습니다.
프래그먼트 셰이더에서는 입력 변수들이 변경됩니다. (그리고 colour_in에 대한 vec4에서 vec3로의 변환 하나를 제거할 수 있습니다.)
#version 450
layout (location=0) out vec4 theColour;
layout (location=0) in vec3 colour_in;
layout (location=1) in vec3 normal;
layout (location=2) in vec3 worldpos;
struct DirectionalLight{
vec3 direction_to_light;
vec3 irradiance;
};
void main(){
vec3 L=vec3(0);
DirectionalLight dlight=DirectionalLight(normalize(vec3(-1,-1,0)),vec3(10,10,10));
L += dlight.irradiance*(max(dot(normal,dlight.direction_to_light),0))*colour_in;
theColour=vec4(L/(1+L),1.0);
}
이제 점 광원을 추가합니다.
#version 450
layout (location=0) out vec4 theColour;
layout (location=0) in vec3 colour_in;
layout (location=1) in vec3 normal;
layout (location=2) in vec3 worldpos;
struct DirectionalLight{
vec3 direction_to_light;
vec3 irradiance;
};
struct PointLight{
vec3 position;
vec3 luminous_flux;
};
void main(){
vec3 L=vec3(0);
DirectionalLight dlight = DirectionalLight(normalize(vec3(-1,-1,0)),vec3(0.1,0.1,0.1));
L += dlight.irradiance*(max(dot(normal,dlight.direction_to_light),0))*colour_in;
const float PI = 3.14159265358979323846264;
PointLight plight = PointLight(vec3(1.5,0.0,0.0),vec3(10,10,10));
vec3 direction_to_light = normalize(plight.position - worldpos);
float d = length(worldpos - plight.position);
vec3 irradiance = light.luminous_flux/(4*PI*d*d);
L += irradiance*(max(dot(normal,direction_to_light),0))*colour_in;
theColour=vec4(L/(1+L),1.0);
}
숫자는 대략적으로: 방향성 광원은 달, 점 광원은 1m 거리의 촛불입니다. (물론 순수한 흰색은 그에 맞는 색이 아닐 수 있습니다.)
이전과 같은 (기본적이고 순진한) 방식으로 L을 계산하기 전에, 점 광원의 속성과 프래그먼트의 월드 위치로부터 방사 조도와 빛의 방향을 계산합니다.
그리고 두 곳에서 같은 공식으로 복사 휘도를 계산하고 있으므로, 이것을 별도의 함수로 만들어 봅시다:
#version 450
layout (location=0) out vec4 theColour;
layout (location=0) in vec3 colour_in;
layout (location=1) in vec3 normal;
layout (location=2) in vec3 worldpos;
struct DirectionalLight{
vec3 direction_to_light;
vec3 irradiance;
};
struct PointLight{
vec3 position;
vec3 luminous_flux;
};
vec3 compute_radiance(vec3 irradiance, vec3 light_direction, vec3 normal, vec3 surface_colour){
return irradiance*(max(dot(normal,light_direction),0))*surface_colour;
}
void main(){
vec3 L=vec3(0);
DirectionalLight dlight = DirectionalLight(normalize(vec3(-1,-1,0)),vec3(0.1,0.1,0.1));
L += compute_radiance(dlight.irradiance, dlight.direction_to_light, normal, colour_in);
const float PI = 3.14159265358979323846264;
PointLight plight = PointLight(vec3(1.5,0.0,0.0),vec3(10,10,10));
vec3 direction_to_light = normalize(plight.position - worldpos);
float d = length(worldpos - plight.position);
vec3 irradiance = light.luminous_flux/(4*PI*d*d);
L += compute_radiance(irradiance, direction_to_light, normal, colour_in);
theColour=vec4(L/(1+L),1.0);
}
참고로, 함수 정의를 main() 아래가 아닌 위쪽에 두려고 하면 다음과 같은 오류가 발생합니다.
error: 2 compilation errors:
./shaders/shader.frag:25: error: 'compute_radiance' : no matching overloaded function found
./shaders/shader.frag:34: error: 'compute_radiance' : no matching overloaded function found
--> src/renderpass_and_pipeline.rs:88:51
|
88 | .code(vk_shader_macros::include_glsl!("./shaders/shader.frag"));
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
함수는 호출되기 전에 정의(또는 최소한 선언)되어야 합니다.
더 많은 점 광원을 원한다면 어떻게 할까요?
배열 전체를 도입해 봅시다:
const int NUMBER_OF_POINTLIGHTS = 3;
PointLight pointlights [NUMBER_OF_POINTLIGHTS] = {
PointLight(vec3(1.5,0.0,0.0),vec3(10,10,10)),
PointLight(vec3(1.5,0.2,0.0),vec3(5,5,5)),
PointLight(vec3(1.6,-0.2,0.1),vec3(5,5,5))
};
그리고 for 루프에서 이 모든 빛을 사용합니다:
for (int i=0; i<NUMBER_OF_POINTLIGHTS; i++){
이것이 프래그먼트 셰이더의 전체 main 함수입니다:
void main(){
vec3 L=vec3(0);
DirectionalLight dlight = DirectionalLight(normalize(vec3(-1,-1,0)),vec3(0.1,0.1,0.1));
L += compute_radiance(dlight.irradiance, dlight.direction_to_light, normal, colour_in);
const int NUMBER_OF_POINTLIGHTS = 3;
PointLight pointlights [NUMBER_OF_POINTLIGHTS] = {
PointLight(vec3(1.5,0.0,0.0),vec3(10,10,10)),
PointLight(vec3(1.5,0.2,0.0),vec3(5,5,5)),
PointLight(vec3(0.1,-3.0,-3.0),vec3(5,5,5))
};
const float PI = 3.14159265358979323846264;
for (int i=0; i<NUMBER_OF_POINTLIGHTS; i++){
PointLight light = pointlights[i];
vec3 direction_to_light = normalize(light.position - worldpos);
float d = length(worldpos - light.position);
vec3 irradiance = light.luminous_flux/(4*PI*d*d);
L += compute_radiance(irradiance, direction_to_light, normal, colour_in);
};
theColour=vec4(L/(1+L),1.0);
}
아직 빠진 것이 있습니다: 다른 종류의 빛, 그리고 무엇보다도 셰이더에 정적으로 두는 대신 프로그램에서 빛을 생성(하고 이동하고 삭제)하는 기능입니다. 이것들은 나중으로 미루겠습니다. 먼저, compute_radiance 함수를 살펴봐야 합니다.