원문 : http://www.lighthouse3d.com/opengl/glsl/index.php?dirlightpix
이 션섹에서는 이전 셕센에서의 쉐이더를 Directional Light를 픽셀 마다 계산하도록 수정할 것이다.
먼저 버텍스 당 우리가 받는 정보를 살펴보면, …
- 법선벡터
- Half 벡터
- 빛의 방향
법선벡터를 카메라 공간 좌표계로 변환하고 정규화해야한다. 또한 이미 카메라 공간 좌표계인 Half 벡터와 빛의 방향 벡터 역시 정규화해야 한다. 이들 정규화된 벡터는 보간되어질 것이고 프레그먼트 쉐이더로 보내지는데, 이를 위해서 정규화된 벡터를 유지하기 위해서 varying 변수를 선언할 필요가 있다.
버텍스 쉐이더에서는 광원설정값과 재질을 조합하는 몇가지 연산을 수행할 수 있다.
아래는 버텍스 쉐이더의 코드이다.
varying vec4 diffuse,ambient;
varying vec3 normal,lightDir,halfVector;
void main()
{
/* first transform the normal into eye space and
normalize the result */
normal = normalize(gl_NormalMatrix * gl_Normal);
/* now normalize the light's direction. Note that
according to the OpenGL specification, the light
is stored in eye space. Also since we're talking about
a directional light, the position field is actually direction */
lightDir = normalize(vec3(gl_LightSource[0].position));
/* Normalize the halfVector to pass it to the fragment shader */
halfVector = normalize(gl_LightSource[0].halfVector.xyz);
/* Compute the diffuse, ambient and globalAmbient terms */
diffuse = gl_FrontMaterial.diffuse * gl_LightSource[0].diffuse;
ambient = gl_FrontMaterial.ambient * gl_LightSource[0].ambient;
ambient += gl_LightModel.ambient * gl_FrontMaterial.ambient;
gl_Position = ftransform();
}
이제 프레그먼트 쉐이더에 대해서 살펴보자. 동일한 Varying 변수가 선언되어야 한다. 법선벡터를 다시 정규화해야한다. 하지만 빛의 방향벡터는 다시 정규화할 필요가 없다. 우리는 Directional Light에 대해 이야기 하고 있으므로 이 마지막 벡터는 모든 버텍스에 공통이다(쌩뚱맞은 말같은데……. =_=). 두개의 동일한 벡터 사이의 보간에 대한 결과는 같은 벡터이므로, 다시 정규화할 필요가 없는 것이다. 다음으로 우리는 보간되고 정규화된 법선벡터와 빛의 방향 벡터를 내적계산한다. 아래가 여기서 언급한 프레그먼트 쉐이더에 대한 시작 부분에 대한 코드이다.
varying vec4 diffuse,ambient;
varying vec3 normal,lightDir,halfVector;
void main()
{
vec3 n,halfV;
float NdotL,NdotHV;
/* The ambient term will always be present */
vec4 color = ambient;
/* a fragment shader can't write a varying variable, hence we need
a new variable to store the normalized interpolated normal */
n = normalize(normal);
/* compute the dot product between normal and ldir */
NdotL = max(dot(n,lightDir),0.0);
....
}
만약 NdotL이 0보다 크다면, Diffuse 요소를 계산해야 하는데, 버텍스 쉐이더로부터 받은 Diffuse 설정값은 내적값으로 곱해진다. Specular 요소도 반드시 계산해야 한다. Specular 요소를 계산하기 위해서는 먼저 버텍스 쉐이더로부터 받은 halfVector를 정규화해야하고, halfVector와 normal 간의 내적 계산을 한다.
....
if (NdotL > 0.0) {
color += diffuse * NdotL;
halfV = normalize(halfVector);
NdotHV = max(dot(n,halfV),0.0);
color += gl_FrontMaterial.specular *
gl_LightSource[0].specular *
pow(NdotHV, gl_FrontMaterial.shininess);
}
gl_FragColor = color;
}
다음 이미지는 Per Pixel과 Per Vertex 광원에 대한 결과화면이다.