OpenGL Shader – 28

GLSL 예제 : Lighting(Point Light Per Pixel) – 5/6

원문 : http://www.lighthouse3d.com/opengl/glsl/index.php?dirlightpix

이 튜터리얼은 Directional Lights의 코드의 99% 상당 부분에 기반하고 있다. 이 튜토리얼은 Directional Light와 Point Light의 차이점에 대해 기반한다. Directional Light는 무한이 멀리 있다고 가정되므로 빛에 대한 광선이 모든 물체에 도달할때 평행하다.  반면에, Point Light는 위치를 가지고 있으며 모든 방향에서 광선을 보낸다. 더욱이 Point Light에서는, 버텍스와의 거리가 멀어질 수 록 빛의 강도가 감쇄한다.

OpenGL 어플리케이션의 관점으로부터, Directional Light와 Point Light 사이에는 두가지 차이점이 있다.

  • 광원의 위치 필드중 w 요소 : Directional Light에서는 이 값은 0이지만 Point Light에서는 1이다.
  • 빛의 감쇄는 세개의 계수에 기반한다 : 상수항, 1차항, 2차항

계산식 관점에서 이들 차이를 살펴봐야한다. Directional Light의 경우, 빛의 광선의 방향은 모든 버텍스에 대해서 일정하지만, Point Light의 경우, 빛의 위치에서 시작해서 각 버텍스까지의 벡터이다. 그러므로 버텍스 쉐이더에서 변경해야할 것은 빛의 방향을 계산하는 것이다. 빛의 감쇄는 OpenGL에서 다음 공식에 기반하여 계산된다.
k0은 상수항 감쇄이고, k1은 1차항 감쇄, k2는 2차항감쇄이며 d는 빛의 위치와 버텍스 사이의 거리이다.

주의할것은, 빛의 감쇄 모델은 거리에 따른 선형회귀가 아니라는 점이다. 그래서 버텍스에 대해 감쇄를 계산할 수 없으며 프레그먼트 쉐이더에서 보간된 값을 사용할 수 없다. 그러나 우리는 버텍스 쉐이더에서 거리를 계산할 수 있고 프레그먼트 쉐이더에서 보간된 거리를 사용해 감쇄를 계산할 수 있다.

Point Light에 대한 색상에 대한 공식은 다음과 같다.

위의 공식을 보면, ambient항은 반드시 2개로 나눠야 한다: 광원 모델 Ambient 설정을 이용한 전역 Ambient 항과 빛에 대한 일반 Ambient 항. 버텍스 쉐이더는 정확하게 Ambient 항의 계산을 구분해야 한다. 아래는 새로운 버텍스 쉐이더에 대한 코드이다.

varying vec4 diffuse,ambientGlobal,ambient;
varying vec3 normal,lightDir,halfVector;
varying float dist;
	
void main()
{	
    vec4 ecPos;
    vec3 aux;
		
    normal = normalize(gl_NormalMatrix * gl_Normal);
		
    /* these are the new lines of code to compute the light's direction */
    ecPos = gl_ModelViewMatrix * gl_Vertex;
    aux = vec3(gl_LightSource[0].position-ecPos);
    lightDir = normalize(aux);
    dist = length(aux);
	
    halfVector = normalize(gl_LightSource[0].halfVector.xyz);
		
    /* Compute the diffuse, ambient and globalAmbient terms */
    diffuse = gl_FrontMaterial.diffuse * gl_LightSource[0].diffuse;
		
    /* The ambient terms have been separated since one of them */
    /* suffers attenuation */
    ambient = gl_FrontMaterial.ambient * gl_LightSource[0].ambient;
    ambientGlobal = gl_LightModel.ambient * gl_FrontMaterial.ambient;
	
    gl_Position = ftransform();
}

프래그먼트 쉐이더는 감쇄를 계산해야 한다. 해석된 빛의 방향을 정규화도 해야해야 하는데, 방향은 모든 버텍스에 대해서 다르기 때문이다.

varying vec4 diffuse,ambientGlobal, ambient;
varying vec3 normal,lightDir,halfVector;
varying float dist;	
	
void main()
{
    vec3 n,halfV,viewV,ldir;
    float NdotL,NdotHV;
    vec4 color = ambientGlobal;
    float att;

    /* 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 normalized lightdir */
    NdotL = max(dot(n,normalize(lightDir)),0.0);
	
    if (NdotL > 0.0) {
        att = 1.0 / (gl_LightSource[0].constantAttenuation +
              gl_LightSource[0].linearAttenuation * dist +
              gl_LightSource[0].quadraticAttenuation * dist * dist);
        color += att * (diffuse * NdotL + ambient);
		
        halfV = normalize(halfVector);
        NdotHV = max(dot(n,halfV),0.0);
        color += att * gl_FrontMaterial.specular * 
                 gl_LightSource[0].specular * 
                 pow(NdotHV,gl_FrontMaterial.shininess);
    }

    gl_FragColor = color;
}

아래의 이미지는 Point Light와 OpenGL의 고정 기능에 의해 계산된 빛에 대한 차이를 보여준다. 하나는 버텍스에 대한 광원이고 다른 하나는 이 튜터리얼의 쉐이더를 이용한 픽셀에 대한 광원이다.

“OpenGL Shader – 28”에 대한 2개의 댓글

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다