본문 바로가기
셰이더 (Shader)/The Renderman Shading Language Guide

[RSL] Part2 : Trigonometry, Vector Math (123p ~ 134p)

by Minkyu Lee 2023. 5. 16.

 

Trigonometry

수학의 난이도를 한 단계 올려보자.

삼각함수는 어려울 수 있지만, 기억해야 할 공식과 성질은 손가락에 꼽을 정도로 적다.

나머지는 삼각함수 책이나 웹사이트에서 언제든 찾아보면 된다.

삼각함수는 모두 삼각형과 관련이 있다.

따라서 삼각형의 중요한 성질들을 살펴보자.

 

삼각형은 세 개의 변으로 이루어져있다.

들이 서로 만나는 부분에는 각도가 있다.

삼각형의 각도의 합은 항상 180도, 혹은 라디안으로 표현하면 pi / 2 이다.

 

삼각형은 많은 종류가 있지만, 컴퓨터 그래픽스의 공부에서 가장 흔한 것은 직각삼각형이다.

직각삼각형은 한 개의 90도 각을 가지고 있으며, 다른 두 각은 90도를 합친 값이 된다.

삼각형의 가장 긴 변은 (언제나 90도 각도의 맞은편에 위치함) 빗변(hypotenuse)이라고 한다.

 

피타고라스의 정리는 다음과 같은 공식으로 빗변을 계산한다.

 

이 공식은 직각삼각형의 두 변의 길이를 알고 있을 때,

다른 하나의 길이를 계산하는데 사용된다.


이제 삼각함수의 다른 특징을 알아보자.

사인(sin), 코사인(cos), 탄젠트(tan)는 각각 삼각형의 특정 영역을 측정하는 데 사용된다.

각각의 값은 다음과 같이 변의 길이를 사용하여 계산된다.

sin=opp/hyp
cos=adj/hyp
tan=opp/adj

 

이러한 공식에서, opp는 계산하려는 각도에 대한 대변(주어진 각과 마주보는 각)이 되는 변을 나타내며, 

adj는 각도에 인접한 변(각도에 닿은 변)을 나타내고, 

hyp는 다시 말해 직각삼각형의 가장 긴 변인 빗변을 나타낸다. 

 

간단한 예를 살펴보자.

다음 공식에서, 우리는 먼저 빗변을 계산하고, 그 다음으로 A 각도사인, 코사인, 탄젠트를 계산할 것이다.

마지막으로, 우리는 각 A를 계산할 것이다.

Figure 5.19는 직각삼각형을 보여준다.


 

위 공식은 자주 사용할 기본 삼각함수 연산이다. 

 

이제 사인, 코사인, 탄젠트의 속성을 살펴보자.

사인과 코사인의 값은 항상 -1에서 1까지의 범위다.

각도나 라디안이 증가하거나 감소함에 따라 파동 형태를 띈다. 

또한 간격이 같다.

따라서 sin(x)이 1일때, cos(x)는 0이 된다.

 

탄젠트는 x = 0일 때 sin(x) 곡선과 접선에 머물며 -무한대에서 무한대까지 이동한다.

곡선의 접선은 곡선에 닿는 선이며, 곡선을 교차하지 않는다.

 

사인과 코사인의 반복적인 성질은 텍스처 생성에 유용하다.

 

Figure 5.20은 사인, 코사인, 탄젠트의 그래프를 보여주고, 

Figure 5.21과 5.22는 사인이 텍스처 생성에 어떻게 사용될 수 있는지 보여준다.

 

사용 예시를 보자.

2차원 텍스처 좌표에 회전을 적용한다.

다음 전처리 매크로를 사용하면, 피벗 지점으로 ox와 oy를 사용하여, 점 x와 y를 회전시킬 수 있다.

주어진 각도(rad)만큼 회전하는 것이다.

회전 결과는 rx와 ry 변수에 저장된다.

/* rotate2d()
* from rmannotes.sl
* 2D rotation of point (x,y) about origin (ox,oy) by an angle rad.
* The resulting point is (rx, ry).
*
*/
#define rotate2d(x,y,rad,ox,oy,rx,ry) \
rx = ((x) - (ox)) * cos(rad) - ((y) - (oy)) * sin(rad) + (ox); \
ry = ((x) - (ox)) * sin(rad) + ((y) - (oy)) * cos(rad) + (oy)

Vector Math

벡터와 행렬컴퓨터 그래픽스에서 가장 많이 사용되는 수학적 개념 중 하나이다.

행렬은 이동, 회전 및 스케일링과 같은 변환을 나타내는 데 사용된다.

벡터는 위치, 방향, 법선 및 때로는 색상을 나타내는 데 사용된다.

 

우리는 대부분 벡터에 집중할 것이다.

벡터가 셰이더 작성자에게 더욱 유용하기 때문이다.

 

벡터는 모든 TD에게 중요한 개념이지만, 셰이더 개발자에게는 필수적이다.

이 장에서 완전히 이해해야 할 핵심이 바로 벡터이다.

 

벡터는 세 개의 요소로 이루어진 1차원 배열로 표현된다.

벡터의 세 값은 x, y, z 값을 나타냅니다.

RenderMan은 매우 유사한 세 요소(points, vectors, normals)를 가지고 있지만 서로 다른 정보를 나타낸다.

 

점(point)의 경우, 세 값은 3D 공간에서의 위치를 나타낸다.

벡터(vector)의 경우, 현재 좌표 시스템의 원점에서 특정지점까지 이동하는 선으로 정의된 방향을 나타낸다.

법선(normals)은 방향을 나타내지만, 원점은 좌표 시스템의 원점이 아닌 표면 위치이다.

 

또한 벡터는 원점에서 벡터 위치까지의 거리인, 크기를 포함한다.

 

이제 벡터의 가장 중요한 속성과 가장 유용한 벡터 연산을 살펴보자.

 

Vector Additions, Subtractions, and Scaling

덧셈과 뺄셈을 살펴보자.

벡터 덧셈과 뺄셈은 원소별로 수행된다.

벡터 A와 B의 덧셈은 다음과 같다.

.

시각화를 위해, 2차원 벡터 두 개를 더하는 방법을 살펴보자.

벡터 Q = [2 2]와 T = [-3 1]를 더해본다.

Figure 5.23를 보면 이러한 덧셈을 그림으로 설명하고 있다.

 

벡터 뺄셈도 원소별로 수행된다.
시각적으로 벡터 뺄셈은 원점에서부터 나오는 두 벡터의 끝점을 연결한다. 

Figure 5.24는 같은 Q와 T 벡터의 뺄셈을 보여줍니다.

벡터의 길이를 스케일링하는 것은, 정수 또는 실수 값을 벡터와 곱하는 것으로 이루어진다.

벡터의 원소별로 독립적으로 곱해진다.

 

벡터 스칼라 곱셈뿐만 아니라, 벡터 덧셈도 교환 법칙을 가진다.

Q를 i배 하면 iQ와 같다.

벡터 Q=[2 3 1]에 정수 i=2를 곱하는 것은, 그림 5.25와 같이 결과를 얻는다.

Vector Length and Normalize

벡터는 방향과 길이(또는 크기)로 정의된다.

벡터의 길이를 계산하는 것은 매우 유용한 연산이다.

 

그 중 하나로 벡터의 정규화(normalization)가 있다.

이를 통해 벡터의 길이를 1로 만들어 ,유닛 벡터(unit vector) 또는 정규화된 벡터(normalized vector)가 된다.

정규화에 대해서는 나중에 더 자세히 설명할 것이다.

벡터의 길이를 계산하려면 다음 공식을 사용한다.

벡터 Q=[2 4 1]와 벡터 T=[6 16 3]의 길이는 다음과 같다.

RSL은 벡터의 길이를 계산하는 내장 함수를 제공한다.

이 함수는 ln = length(vector V)를 따른다.

내부적으로 다음과 같이 계산됩니다.

ln = sqrt(V.V);

 

벡터를 정규화(normalizing)하는 것은 임의의 길이를 가진 벡터를 단위 벡터(unit vector)로 변환하는 작업이다.

 

셰이더 프로그래밍에서 단위 벡터는 매우 유용하다.

벡터의 길이를 정규화하면 여러 수학적 연산이 간소화된다.

RSL이를 고려하여 일부 내장 함수를 매우 최적화된 방식으로 구현한다. (예: reflect() 함수).

 

그러나 함수에 제공하는 벡터가 정규화되지 않은 경우 결과가 잘못될 수 있다.

벡터를 정규화하려면 각 구성 요소의 값을 벡터의 길이로 나누면 된다.

공식은 다음과 같다.

만약 벡터 T = [6 16 3]를 정규화하면 다음과 같이 됩니다.

이것은 [6 16 3]과 정확히 같은 방향을 가진 벡터이다.

하지만 길이는 0에서 1 범위 이내이다.

 

RSL은 정규화를 매우 효율적으로 처리하는 내장 함수를 제공한다.

이 함수는 normalize(vector V)로 사용한다.

내부적으로는 다음과 같이 계산된다.

Dot and Cross Product

유용한 다른 수식들로는, 두 벡터 사이의 각도를 측정할 수 있는 내적(dot product)과,

두 벡터에 수직인 벡터를 만들 수 있는 연산이다.

 

이 중 가장 유용한 것은 내적이다.

내적 계산법은 다음과 같다.

내적은 코사인 법칙(cosine law)에 따라 다음과 같이 정의될 수도 있다.

벡터의 내적은 두 벡터의 길이를 코사인 값으로 곱한 것과 같다.

대부분의 경우 정규화된 벡터, 즉 길이가 1인 벡터를 다룰 것이기 때문에 A.B = cos(angle)이라고 말할 수 있다.

 

내적은 또한 두 벡터 사이의 각도의 코사인 값을 반환한다.

두 벡터가 서로 평행하면 각도는 0이며, 내적 값은 1이다. (cosine of 0 – 1). 

두 벡터가 서로 수직으로 90도의 각도를 이룰 때, 내적 값은 0이다.(cosine of 90 = 0). 

 

또한 반대 방향을 향할수록 값이 감소한다.

두 벡터가 서로 평행하지만 다른 방향을 가리킬 때 -1이 된다. 

이러한 이유로, 내적은 두 벡터의 방향을 비교할 때 사용된다.

 

이러한 이유로 이 연산은 램버시안 확산(diffuse) 모델과 같은 조명 함수에서 사용된다.
표면 법선 N과 라이트 벡터 L의 내적으로 빛을 계산한다.
기여도를 계산하기 때문입니다. 

 

그림 5.26은 이 개념을 설명한다.

내적의 또 다른 특징은 단일 벡터 A.A에 적용할 때, 결과가 항상 해당 벡터의 길이의 제곱인 것이다.

이러한 이유로 RSL은, length() 함수의 길이 방정식에서 length(vector V) 대신 A.A를 사용한다.

다음은 예제이다.

T = [2 3 2].T.T = 22+33+22
T.T = 17

벡터의 길이를 계산하려면 식을 풀면 된다.

 

내적에 대한 마지막 응용으로는, 두 벡터 사이의 각도를 계산할 수 있다는 것이다.

벡터의 위치는 중요하지 않다. 벡터는 방향만 가지고 있기 때문이다.

따라서 위치와 상관없이 두 벡터 사이의 각도를 측정할 수 있다.

 

다만, 두 벡터가 동일한 좌표계에서 정의되어야한다.

이제 두 벡터 사이의 각도를 구하는 공식은 다음과 같다.


외적 (cross product)은 두 개의 벡터로 수행할 수 있는 또 다른 연산이다.

외적을 수행하면 (RSL에서 ^로 표현), 결과는 수직인 벡터가 된다.

크기는 이들이 이루는 평행사변형의 면적과 같다.

 

두 벡터가 서로 평행하지 않은 경우,

외적을 적용하면 두 벡터로 설명되는 평면에 평행한 벡터를 얻을 수 있다는 것이다.

 

외적은 표면의 렌더링 노멀(법선)을 찾는 것과 공간에서 주어진 임의의 평면에 대한 노멀을 찾는 것에 주로 쓰인다.

외적 공식은 다음과 같다.

이 연산이 어떻게 표면 법선을 얻는 데 적용될까?

평면 다각형(또는 NURBS) 그리드가 있다고 가정해보자.

이 그리드는 XY 평면 위에 평면 상태로 있다.

이 그리드가 원점과 다른 꼭지점이 (1,1,0)인 크기가 1인 그리드라면, 이는 두 벡터 Sx(1,0,0)와 Sy(0,1,0)로 정의된 평면에 놓여져 있음을 알 수 있다.

이 두 벡터를 이용해 외적을 해보자.

 

위에서 보듯, XY 평면에 놓인 평면 그리드의 표면 법선은 항상 Z를 향한다.

그림 5.27은 외적 결과를 보여준다.

Reflections and Refractions

마지막으로 반사 굴절에 대해 알아보자.

 

반사는 관찰 지점에 따라 튕겨져 나가는 광선이다.

반사의 예로는 빛나는 표면, 거울 및 광택이 있는 금속이 있다.

 

표면을 통과하면서 벡터의 방향이 바뀌는 광선은 굴절이다.

굴절의 예로는 물 컵과 크리스털 조각이 있다.


반사는 다음과 같은 공식을 사용하여 계산됩니다:

I는 시선에서 표면 지점으로 이동하는 입사 벡터이다. N은 정규화된 표면 법선이다.

RSL에서는 다음과 같이 정의된 내장 함수 reflect()가 있다.

vector reflect( vector I,N)
{
	return I – 2 (I.N)*N;
}

reflect()의 결과를 environment()로 환경 텍스처를 가져올 때 사용하거나,

trace()로 레이트레이스된 반사를 찾는 등 다양한 용도로 사용할 수 있다.

 

Figure 5.28은 reflect()를 사용한 구의 예시를 보여준다.

 

굴절(refraction) 벡터는 유리로 만들어진 물체나 물잔과 같은 효과를 시뮬레이션하는 데 필요하다.

 

굴절 벡터는 transmission 벡터로도 불린다.

대부분의 RenderMan 셰이더에서 변수를 식별하는 데 문자 T를 사용한다.

 

굴절 벡터는 반사보다 계산하기가 조금 더 어렵다. 하지만 간단한 수학으로 계산할 수도 있다.

굴절의 공식은 다음과 같다.

 

 

이를 각 항목을 변수 집합으로 계산함으로써 간소화할 수 있다.

RSL은 굴절을 계산하는 refract() 내장 함수를 제공한다.

굴절된 광선은 환경 맵 검출이나 레이 트레이싱 함수 등에서 사용할 수 있다.

refract() 함수의 구문은 다음과 같다.

vector refract( vector I, N; float eta )
{
	float IdotN = I.N; float k = 1 - eta*eta*(1 - IdotN*IdotN);
	return k < 0 ? (0,0,0) : eta*I - (eta*IdotN + sqrt(k))*N;
}

I입사 벡터(incidence vector)이다.

N 표면 법선(surface normal)이다. 

 

eta는 입사 벡터가 있는 매질의 굴절률과 진입하는 매질의 굴절률의 비율이다.

이 값은 광선이 렌더링 표면을 통과할 때 얼마나 굴절될지를 결정한다.

 

Figure 5.29는 refract()의 결과이다.

다음은 일반적인 재질의 굴절률이다.

■ Vacuum 1.00000 (exactly)
■ Air (STP) 1.00029
■ Acetone 1.36
■ Alcohol 1.329
■ Crown Glass 1.52
■ Crystal 2.00
■ Diamond 2.417
■ Emerald 1.57
■ Ethyl Alcohol 1.36
■ Flourite 1.434
■ Fused Quartz 1.46
■ Heaviest Flint Glass 1.89
■ Heavy Flint Glass 1.65
■ Glass 1.5
■ Ice 1.309
■ Iodine Crystal 3.34
■ Light Flint Glass 1.575
■ Liquid Carbon Dioxide 1.20
■ Polystyrene 1.55
■ Quartz 1 1.644
■ Quartz 2 1.553
■ Ruby 1.77
■ Sapphire 1.77
■ Sodium Chloride (Salt) 1 1.544
■ Sodium Chloride (Salt) 2 1.644
■ Sugar Solution(30%) 1.38
■ Sugar Solution (80%) 1.49
■ Topaz 1.61
■ Water (20 C) 1.333

 

올바른 eta 값을 얻으려면 1.00029 (공기의 굴절률)을 물질의 굴절률로 나누면 된다.

따라서 유리의 eta 값은 0.66686이다.

 

굴절된 광선을 계산하는 또 다른 방법은 fresnel() 함수를 사용하는 것이다.

이 함수는 광택 있는 플라스틱과 같은 특정 재료의 외관을 재현하는 데 필수적이다. 

 

fresnel() 함수는 viewing(시점)광선이 표면 법선을 향해 수직일수록 표면을 더욱 반사적으로 만든다. 

fresnel() 함수의 구문은 다음과 같다.

void fresnel( vector I, N; float eta, Kr, Kt [; output vector R, T] )

 

I는 입사 벡터, N은 표면 법선, eta는 광선에 영향을 미치는 굴절률을 나타내는 float값이다.

Kr과 Kt은 반사(Kr)와 굴절(Kt)이 벡터 I가 표면을 수직 각도로 도달할 때 얼마나 증가하는지를 제어하는 값이다.

선택적인 벡터 매개변수 R과 T는 fresnel() 함수가 반사 벡터(R)와 굴절 또는 전파 벡터(T)를 반환할 수 있도록 한다.

 

그림 5.30과 5.31은 fresnel() 함수의 효과를 보여준다.

 

댓글