본문 바로가기
공허의 유산/표현의 자유

[opengl].[#2.GLSL] 14. Positional light

by 바른생활머시마 2023. 2. 8.
728x90
반응형

앞에서 Ambient+Specular light의 구현을 해보았습니다.

https://learn-and-give.tistory.com/61

 

[opengl].[#2.GLSL] 13. ambient 조명과 specular 조명

앞에서 Diffuse 조명과 재질을 쉐이더로 구현하는 방법을 살펴봤습니다. [opengl].[#2.GLSL] 12. 조명 정보 구조체와 재질 정보 구조체 (tistory.com) [opengl].[#2.GLSL] 12. 조명 정보 구조체와 재질 정보 구조체

learn-and-give.tistory.com

 

이상의 내용은 모두 방향광(directional light)에 대한 내용이었습니다. 방향광은 태양처럼 아주 멀리 있는 광원을 가정한 것으로 모든 지점에서 입사광의 방향이 같고, 거리에 따른 밝기 차기가 없다는 특징이 있습니다. 이제 Positional light를 구현 해 볼텐데요, 쉽게 이해 하자면 점광원(Point light)라고 생각하면 됩니다.

 

 아래 그림은 지도에서 건물들이 있는 사진인데, 그림자가 드리워진 모습을 보면 방향도 같고 밝기도 거의 같아 보입니다. 실제로 태양과의 거리는 다 다르고 방향도 다 다르지만 거리가 멀어서 그 차이가 매우 적어서 거의 같다고 볼 수 있습니다.

 

 

반면 Positional light는 근접한 거리에 조명이 있어, 피사체의 지점마다 조사되는 빛의 방향이 다른 모델입니다. 아래 사진의 그림자를 보면, 윤곽이 서로 다른 방향으로 뻣어나가기 때문에 점점 커지는 경향을 볼 수 있습니다.

                 

빛을 파장으로 보느냐 입자로 보느냐, 혹은 뭐 다른 어려운 특성을 가지고 이야기하기 시작하면, 이러한 조명 모델은 실제와 다르게 매우 단순화 한 모델입니다. 그럼에도 불구하고, 사람들의 눈에는 그 미묘한 차이가 별로 느껴지지 않기 때문에 충분히 이렇게 단순화 하여도 상관이 없죠. 태양도 엄밀히 말하면 한 점으로 이루어진 광원이 아니고 큰 가스 덩어리가 불타고 있는, 불꽃들의 집합이죠.

 

 태양을 하나의 점광원이 아니라 점광원의 집합으로 봐야 쉽게 설명되는 현상들이 많이 있습니다. 일식이 일어날 때 태양이 점광원이라면 언제나 달에 가려져 개기 일식이 일어나야 할 것입니다. 아래 그림을 보면, 개기 일식이 일어나는 곳과 그렇지 않은 곳의 차이를 이해 할 수 있습니다.

 

사실적인 그래픽스에서는 실제 물리에 가까운 모델을 모의하지만, 적어도 이제 쉐이더를 배우는 단계에서는 단순한 조명 모델로 학습을 하는 것이 좋겠다. 일단, 지금은 그럴듯하게 보이면 되니까~

 

 

Positional Light 모델

방향광과 달라지는 점을 한번 정리 해 보겠습니다.

 

1. 각 vertex에서 조명의 방향이 달라진다.

   조명의 입사 방향 = vertex 위치 조명의 위치

2. 거리가 멀면 빛의 강도가 약해진다.

   동일한 광량으로 채워야 할 면적이 넓어지니 당연하다.

 

거리에 따라 조명이 어두워지는 것은, 거리에 따라 빛이 비추는 면적이 거리의 제곱에 반비례하는 것으로 이해하면 되는데, OpenGL에서는 이 감쇄 모델을 가지고 있으며, 적절한 파라미터만 설정 해 주면 된다. OpenGL에서 사용하는 감쇄 모델의 수식은 다음과 같다.

감쇄 성분을 적용한 색상은 아래 식으로 계산 된다.

이런 내용을 고려한 Vertex shader는 아래와 같다.

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);
	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);
	diffuse = gl_FrontMaterial.diffuse * gl_LightSource[0].diffuse;
	ambient = gl_FrontMaterial.ambient * gl_LightSource[0].ambient;
	ambientGlobal = gl_LightModel.ambient * gl_FrontMaterial.ambient;
	
	gl_Position = ftransform();
}

 

Fragment Shader

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;
	
	n = normalize(normal);
	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;
}

 

거리에 따른 감쇄 효과를 확인하기 위해, 구의 앞쪽에 조명을 위치 시키고, 좌우로 먼 거리를 왕복하도록 하였습니다. 결과를 한번 보겠습니다.

 

거리가 멀어져도 별로 감쇄 효과가 나타나지 않습니다. 착시 현상이 있나 싶어서 자세히 봐도 거의 변화가 없는 것으로 보여서 수식을 찬찬히 살펴보니 Attenuation 계수를 조명으로부터 가져오는데, 이 값은 제가 설정 해 주지 않았습니다. 그래서, 관련 된 내용을 좀 찾아보니, 따로 설정하지 않으면 Attenuation 효과가 발생하지 않는 값으로 초기 값이 설정되어 있다고 하네요.

 

https://learn.microsoft.com/en-us/windows/win32/opengl/gllightfv

 

glLightfv function (Gl.h) - Win32 apps

The glLightfv function returns light source parameter values.

learn.microsoft.com

 

가장 마지막 문장을 보면 그 사실을 확인 할 수 있습니다.

 

그럼 Attenuation 파라미터를 한번 설정 해 보겠습니다..

void LightInit()
{
	glClearColor(0.0f, .0f, .0f, 0.0f);
	glClearDepth(1.0f);
	glEnable(GL_DEPTH_TEST);


	float dif[4] = { 0.8f, 0.8f, 0.8f, 1.f };
	float amb[4] = { 0.5f, 0.f, 0.f, 1.f };
	float spc[4] = { 1.f, 1.f, 1.f, 1.f };

	glLightfv(GL_LIGHT0, GL_POSITION, lpos);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
	glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
	glLightfv(GL_LIGHT0, GL_SPECULAR, spc);

	float material_amb_diffuse[4] = { 0.2f, 0.2f, 1.f, 1.f };
	glMaterialfv(GL_FRONT, GL_DIFFUSE, material_amb_diffuse);
	glMaterialfv(GL_FRONT, GL_AMBIENT, material_amb_diffuse);
	glMaterialfv(GL_FRONT, GL_SPECULAR, spc);
	glMateriali(GL_FRONT, GL_SHININESS, 128);


	// Set attenuation
	glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, 0.8);
	glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, 0.001);
	//glLightf(GL_LIGHT0, GL_QUADRATIC_ATTENUATION, 0.9);
}

QUADRIC은 설정하니 global ambient 성분만 남아서 attenuation 느낌이 좀 나는 것 같아 보이는 경우를 한번 찾아봤습니다.

눈에 띄는 차이가 잘 나지는 않지만, Specular 부분을 보니, 값을 조정을 한 경우 멀어질 때 Specular의 크기나 밝기가 조금 작아지고 어두워지는 것 같습니다.

https://www.youtube.com/shorts/1fH3D4I5_ew

 

 

 

감쇄 효과가 잘 보이려면 좀 여러가지를 고래 해서 준비 해야겠네요.

일단 요기까지 한 것을 attenuation을 고려한 positional light의 대략적인 내용을 살펴 보았습니다.

다음에는 spot light를 살펴보겠습니다.

https://learn-and-give.tistory.com/63

 

[opengl].[#2.GLSL] 15. Spot light

Directional light를 살펴보다가 Positional light, 즉, 점광원을 살펴봤습니다. https://learn-and-give.tistory.com/62 [opengl].[#2.GLSL] 14. Positional light 앞에서 Ambient+Specular light의 구현을 해보았습니다. https://learn-and-g

learn-and-give.tistory.com

 

728x90
반응형

댓글