2차원 데이터를 이미지(Image)의 형태로 표출하면서 광원(Light Source)을 추가적으로 사용하여 명암 및 그림자 효과를 연출하는 것이 필요할 경우가 있습니다. 예를 들면 고도(Elevation) 값들로 구성된 지형 데이터를 이미지로 표출한 다음 태양의 위치에 따른 그림자의 변화를 보고 싶은 경우를 가정해볼 수 있습니다. 이러한 방식의 연출이 IDL에서 가능할까요? 물론 가능합니다. 다만 이를 위해서는 2차원 데이터를 이미지(Image) 대신 서피스(Surface)의 형태로 표출해야 합니다. 그런 다음에는 광원(Light Source)을 별도로 추가하여 그 위치에 따라 그림자가 형성되도록 해야 합니다. 서피스(Surface)는 X, Y, Z 성분이 모두 존재하는 3차원적인 표출 방식이므로 광원의 위치에 따라 빛이 통과하거나 가려지는 부분들이 발생하도록 하는 것이 가능하기 때문입니다. 사실 이 글의 제목에서는 "이미지"에 대하여 광원 효과를 적용한다고 했지만, 실제로는 서피스의 형태로 표출을 하고 광원을 적용하면서 시선 방향을 적절히 조절하여 마치 "이미지처럼" 보이도록 하는 작업이라고 보시면 됩니다.
그런데 IDL에서 이러한 작업을 어떻게 할 것인가를 IDL의 그래픽 체계들(DG, NG, OG) 각각에 대하여 확인해봅시다. 일단 2차원 데이터를 서피스의 형태로 표출하는 것은 모든 그래픽 체계들에서 가능합니다. 즉 DG(Direct Graphics) 체계의 SURFACE 프로시저, NG(New Graphics) 체계의 SURFACE 함수, OG(Object Graphics) 체계의 IDLgrSurface 클래스 모두 이러한 기능을 담당합니다. 또한 이러한 기능들을 사용하여 표출된 서피스 그림을 보면 기본적인 광원 처리는 지원이 됩니다. 하지만 DG 및 NG 체계의 경우는 광원의 위치가 고정적이며 유저가 따로 제어할 수는 없습니다. 반면 OG 체계에서는 광원에 해당되는 IDLgrLight 클래스를 이용하여 광원을 생성하고 제어하는 것이 가능합니다. 즉 유저가 광원을 직접 생성하고 제어할 수 있는 기능은 오직 OG(Object Graphics) 체계에서만 지원된다는 것을 유념해야 합니다. 따라서 우리의 목적을 위해서는 OG 체계 기반으로 서피스 및 광원 객체를 구현하는 방식으로 작업을 해야 한다는 얘기이고, 이를 위해서는 오브젝트 프로그래밍(Object Programming) 기술을 기반으로 하여 OG 체계에 맞는 코딩이 필요합니다. 사실 OG 체계는 일반적으로 IDL 유저들 사이에서도 그리 자주 사용되지는 않는 편이며, 저 역시도 이 블로그에서 관련 내용을 그리 자주 다루지는 않았습니다. 하지만 어차피 이 방법 뿐이기 때문에 이번 기회에 관련 내용을 한번 소개해보고자 합니다.
먼저 예제로 사용할 2차원 데이터를 정의하고 표출하는 작업부터 시작합니다. 여기서는 마치 물결이 원형으로 퍼지는듯한 패턴을 갖는 가상의 2차원 데이터를 생성하고 사용해보기로 하겠습니다. 예제 데이터를 생성하고 일단 그 형태를 살펴보기 위하여 NG 체계에서 이미지 및 서피스의 형태로 기본적인 표출만 먼저 해본다면 그 과정은 다음과 같습니다.
dd = 0.02
x = [-5:5:dd]#MAKE_ARRAY(10/dd+1, VALUE=1)
y = [-5:5:dd]##MAKE_ARRAY(10/dd+1, VALUE=1)
z = 5*((x^2+y^2)^0.3+SIN((x^2+y^2)*0.5))
HELP, z
PRINT, MIN(z), MAX(z)
win_i = WINDOW(DIMENSIONS=[500, 500], /NO_TOOLBAR)
i = IMAGE(z, RGB_TABLE=0, MARGIN=0, /CURRENT)
win_s = WINDOW(DIMENSIONS=[600, 600], /NO_TOOLBAR)
s = SURFACE(z, COLOR='gray', MARGIN=0.15, /CURRENT)
여기서 생성된 2차원 데이터는 501x501의 구조를 가지며 값들의 범위는 대략 0~20 정도가 됩니다. 이 데이터를 이미지 및 서피스 형태로 표출한 그림들을 보면 다음과 같습니다.


이제 이러한 형태의 데이터임을 염두에 두고 이제 본격적인 작업으로 들어가봅시다. 지금부터는 NG가 아닌 OG 체계 기반으로 코딩 작업을 하게 됩니다. OG 체계 기반으로 위의 데이터를 서피스 객체(Surface Object)의 형태로 표출하는 과정을 보면 대략 다음과 같습니다.
oWin = OBJ_NEW('IDLgrWindow', DIMENSIONS=[500, 500])
oView = OBJ_NEW('IDLgrView', VIEWPLANE_RECT=[-0.5, -0.5, 1, 1])
oModel = OBJ_NEW('IDLgrModel')
oSurface = OBJ_NEW('IDLgrSurface', z, $
STYLE=2, SHADING=1, COLOR=[0, 255, 255])
oModel -> Add, oSurface
oSurface -> GetProperty, XRANGE=xrn, YRANGE=yrn, ZRANGE=zrn
xs = NORM_COORD(xrn)
xs[0] = xs[0]-0.5
ys = NORM_COORD(yrn)
ys[0] = ys[0]-0.5
zs = NORM_COORD(zrn)
zs[0] = zs[0]-0.5
oSurface -> SetProperty, XCOORD_CONV=xs, $
YCOORD_CONV=ys, ZCOORD_CONV=zs
oLight = OBJ_NEW('IDLgrLight', TYPE=2, LOCATION=[0, 0, 10])
oModel-> Add, oLight
oView -> Add, oModel
oWin -> Draw, oView
사실 위의 내용에 관하여 제대로 이해하려면 IDL의 객체 지향 프로그래밍 기법에 대한 지식이 필요하지만 어차피 그 내용을 여기서 전부 다룰 수는 없기 때문에 자세한 설명은 생략하도록 하겠습니다. 다만 핵심적인 부분들 몇가지만 언급한다면, 전반적으로는 그래픽 창을 띄우고 2차원 데이터 z를 서피스 객체의 형태로 표출하는 과정입니다. 이 때 IDLgrSurface 클래스의 서피스 객체를 생성하는데 있어서 데이터 z를 투입하고 타입, 색상 등 몇가지 세부 특성들을 설정하였습니다. 특히 여기서는 STYLE 속성을 2로 설정하고 SHADING 속성을 1로 설정하여 매끈한 표면의 서피스 개체가 되도록 하였음을 유의하면 됩니다. 그 다음에는 IDLgrLight 클래스의 광원 객체를 생성하였음을 주목해야 합니다. 서피스 객체는 이러한 광원이 함께 있어야 명암 및 그림자가 구현되어 우리에게도 입체적으로 보이게 됩니다. 특히 여기서는 TYPE 속성을 2로 설정하여 평행광선(Parallel Light Ray)의 특성을 갖는 Directional Light가 되도록 하였습니다. 참고로 TYPE 속성을 1로 설정할 경우에는 발산광선(Divergent Light Ray)의 특성을 갖는 Positional Light가 됩니다. 어쨌든 여기서는 일단 2로 설정해보았습니다. 그리고 LOCATION 속성에 대하여 주어진 값인 [0, 0, 10]은 광선이 출발하는 지점의 좌표입니다. 기본적으로는 LOCATION 속성에 주어진 좌표에서 출발하여 원점인 [0, 0, 0]으로 향하는 광선이 됩니다. 따라서 위와 같이 설정하면 XY 평면의 원점(그림의 정가운데) 위치의 상공으로부터 Z축의 (-) 방향 즉 연직 아래 방향으로 평행광이 내려쬐는 형태가 됩니다. 그 외에도 그래픽 창을 꽉 채워서 표출되도록 하기 위한 좌표계 설정 작업들도 추가되었다는 정도까지만 언급하기로 합니다. 어쨌든 위의 내용을 실행하면 표출 결과는 다음 그림과 같습니다.

그런데 이 그림을 보면 우리가 서피스(Surface)라는 그래픽 표출 방식에서 통상적으로 보게 되는 약간 대각선 방향의 위쪽에서 아래로 내려다보는 모습과는 전혀 다릅니다. 즉 앞서 NG 체계의 SURFACE 함수로 표출되었던 서피스 그림과 방금 OG 체계 기반으로 표출된 서피스 그림의 모습은 서로 다릅니다. 실제로 OG 체계 기반으로 표출된 그림은 서피스 객체를 상공에서 90도의 기울기로 내려다본 모습입니다. 이와 같이 원래 OG 체계에서 구현된 서피스 객체는 디폴트 시선 방향이 Z축의 (-) 방향으로 내려다보는 방향이 됩니다. 다만 이후에 각 축에 대하여 적절한 회전(Rotation) 처리를 추가적으로 실행해야 비로소 우리에게 익숙한 모습이 됩니다. 어쨌든 여기서는 그냥 이와 같이 상공에서 내려다본 디폴트 시선 방향으로 그대로 둬서 마치 이미지처럼 보이게 하면 우리가 구현하고자 하는 방향과 맞을 것 같습니다. 물론 이것은 본질 자체는 서피스(Surface)이고 단지 위에서 내려다보기 때문에 이미지처럼 보일 뿐 이미지(Image) 자체가 아님을 유의해야 합니다. 따라서 일반적으로 이미지에 대하여 적용하는 컬러테이블의 설정 등을 하지 않고, 그 대신 서피스라는 특성에 맞게 표면 전체를 단일 색상으로 처리하면서 쉐이딩(Shading) 효과를 적용하고 이 상태에서 광원의 위치에 따라 명암 및 그림자 효과가 반영되도록 하였다는 점을 염두에 두면 됩니다.
앞서 IDLgrLight 클래스의 광원 객체를 생성할 때 위치 좌표에 해당되는 LOCATION 속성을 [0, 0, 10]으로 설정하였는데, 이것은 광원이 XY 평면상으로는 원점에 있으면서 Z축 방향으로 10만큼 올라간 상공에 위치한다는 의미입니다. 만약 광원의 위치를 변경하면 그림의 모습도 달라지게 됩니다. 그러면 이번에는 다음과 같이 LOCATION 속성의 값을 변경하여 광원 객체의 위치를 변화시키고 전체 내용을 다시 실행해봅시다.
oLight = OBJ_NEW('IDLgrLight', TYPE=2, LOCATION=[10, 0, 10])
이와 같이 광원의 위치를 중심으로부터 X축의 (+) 방향으로 더 이동시키면 그 결과는 다음 그림과 같습니다. 여기서는 빛이 오른쪽에서 왼쪽으로 향하는 방향으로 비추기 때문에 그림자가 왼쪽 방향으로 형성되는 것을 볼 수 있습니다.

비슷한 요령으로 이번에는 LOCATION 속성을 다음과 같이 설정하여 중심으로부터 Y축의 (+) 방향으로 더 이동시킬 경우에는 그 결과는 다음 그림과 같습니다.
oLight = OBJ_NEW('IDLgrLight', TYPE=2, LOCATION=[0, 10, 10])

여기서는 빛이 위쪽에서 아래쪽으로 향하는 방향으로 비추기 때문에 그림자가 아래쪽 방향으로 형성되는 것을 볼 수 있습니다. 그 외에도 광원의 위치를 다양하게 옮겨가면서 그림자가 어떤 방향으로 형성되는가를 확인해보는 것도 가능합니다.
어쨌든 위와 같은 방식으로 광원 객체를 생성하여 적절한 위치에 놓으면 서피스 객체의 형태에 따라 명암 및 그림자 효과가 반영되어 표출된다는 것을 확인할 수 있습니다. 어쨌든 이러한 결과들은 OG 체계에서 구현된 서피스 객체이긴 하지만 위에서 내려다봄으로써 마치 이미지처럼 보이는 상태임을 유념해야 합니다. 어차피 2차원 데이터를 표출하면서 광원 효과를 적용하려면 서피스의 형태가 되어야하기 때문입니다. 그러면 이어질 내용은 다음 회차에서 계속 소개하기로 합니다.
* 이 글이 도움이 되었다면 게시물에 대하여 공감 버튼(하트 모양) 클릭 및 블로그 구독도 해주시면 더 큰 힘이 됩니다. 감사합니다.
'IDL > Object Graphics' 카테고리의 다른 글
| Wire Frame 3D Sphere의 구현 [2] (0) | 2016.10.24 |
|---|---|
| Wire Frame 3D Sphere의 구현 [1] (0) | 2016.10.21 |
| 3D 폴리곤 가시화 예제 (Stanford bunny) (0) | 2015.06.01 |
| 고리형(Ring Shaped) 폴리곤을 구현해봅시다 (0) | 2014.08.29 |
| Mac OS Mountain Lion 10.8에서 Object Graphics 구현시의 문제 해결법 (0) | 2012.10.31 |