OpenGL Shader – 1

GLSL을 사용하는 쉐이더 프로그래밍을 알아보자. 쉐이더는 최신 기술이며 3D 게임에서 경이로운 효과를 내는데 사용된다. 이 글을 통해 쉐이더의 세계에 발을 들여놓자.

이 글과 앞으로 진행될 내용을 이해하기 위해서는 OpenGL 프로그래밍에 익숙해야 한다. 또한 쉐이더를 위한 스펙에 대해 알고 싶다면, OpenGL 2.0과 GLSL에 대한 공식문서를 반드시 읽어보길 바란다.

GLSL은 OpenGL 쉐이딩 언어를 의미하며 종종 “glslang”으로 불리며 ARB(Architectural Review Board) OpenGL에 의해 정의된다.

OpenGL과 경쟁하는 Cg, 즉 Nvidia가 나름대로 만든 또 다른 쉐이더 언어와 GLSL을 비교하거나 누가 더 우수한지 따지지는 않겠다. 그 이유는 지금 여기서는 Cg가 아닌 GLSL에 대해서만 살펴볼 것이기 때문이다.

어떤 언어로든 쉐이더를 작성하기 전에, 그래픽 파이프라인에 대한 기본을 이해하는 것이 매우 중요하다. 그래픽 파이프라인은 쉐이더가 무엇을 하는지, 쉐이더의 종류에는 뭐가 있는지와 같은 쉐이더의 핵심을 제공한다. 또한 쉐이더로 할 수 없는것이 무엇인지에 대한 힌트도 제공한다.

파이프라인에 대한 소개 다음에 GLSL을 위한 OpenGL의 설정에 대해서 알아보겠다. OpenGL 어플리케이션에서 쉐이더를 사용하데 필요한 단계에 대해 자세하게 살펴볼 것이다. 최종적으로 OpenGL 어플리케이션이 매우 유연하고 강력한 효과를 낼 수 있는 방법에 대해 살펴볼 것이다.

몇가지 기본적인 개념으로 데이터 타입(data types), 변수(variables), 문장(statements), 함수(function)의 정의에 대해서 알아본다.

이 글은 ARB 확장과 OpenGL 2.0 버전에 대해 다룬다. 후자는 이미 표준이지만 전자는 향후 표준으로 자리잡을 가능성이 있는 것이다. 차이는 작지만 함수의 이름이나 상수 이름에 약간의 차이를 가지고 있다. 이것들을 구분을 돕기위해 코드에 색상을 넣었다. ARB는 회색으로 OpenGL 2.0은 오렌지 색으로 말이다.

이 적용범위는 실수하기 쉽다. 그러니 혹시 이런 표기에 문제가 있다면 언제든지 내게 알려주길 바란다.

이 글은 점진적으로 작성될 것이므로 글과 데모 프로그램에서 문제가 있을 수 있는데, 이것에 대해서 좀 참아주길 바란다. 비록 사소할지라도 문제를 발견하면 알려주길 바란다. 또한 많은 제안을 해주길 바란다. 부디 이 글을 즐겁게 읽어주길 희망하며 첫번째 GLSL, OpenGL Shader의 첫글을 마치도록 하겠다.

The Keyword


간혹, 특히 영업만을 전문으로 하는 분들 중, 기술에 대해 편협하게 생각하는 사람들에게 던지고 싶은 키워드이다. 이런 자들에게 나는 영업에서 기술을 빼면 빈껍데기에 불과하고 때론 “사기”라고 말하고 싶다. 영업뿐만 아니라 디자인의 경우도 마찬가지다.

중요한 것은 기술, 상품으로써의 디자인, 영업.. 이 세박자가 조화롭게 맞물려가야 한다는 것.

스타일 그리고 템플릿(Style, Template) – {3/3}

이제 우리가 진행해야할 것들은 ListBox에 그려진 이미지들의 정렬상태와 크기를 보기 좋게 하는 일과 사용자가 ListBox의 이미지를 선택하는 조작에서 효과를 넣는 일로써, 이 두가지 일을 하나 하나 해보도록 하겠다.

먼저 첫번째 것을 해결해보자. 태크안에 아래의 코드를 추가하자.



두개의 스타일이 지정되어져 있다. 하나는 ListBox에 대한 스타일을 수정하고 있는데, 이미지 항목을 가로로 정렬하고 수평과 수직에 대해서 가운데 정렬을 지정하고 있다. 또한 ListBox의 모서리 부분을 반지름이 6으로 해서 둥그렇게 나타내도록 한다. 그리고 또 하나의 스타일은 ListBox의 항목의 높이 값을 90 픽셀로 지정하고 있어서, 이미지의 크기를 작게 나타내도록 한다. 이렇게 설정된 스타일에 어울리는 ListBox의 높이와 폭을 아래의 코드를 참고해서 기존의 의 코드를 수정하길 바란다.


         Background="DarkGray" Width="630" Height="110"  
         Margin="10" SelectedIndex="0"/>

실행 결과는 아래와 같다.

자, 이제 남은 것은 사용자가 ListBox의 이미지 항목을 선택했을 경우, 그리고 마우스 커서가 이미지 항목에 놓일 때, 떠날 때에 어떤 효과를 줄것이다. 기본적으로 프로그램이 실행되면 모든 이미지 항목에 투명도를 0.4값을 지정해서 흐릿하게 보일 것이다. 이 상태에서 항목을 선택했을때는 선택된 이미지가 선명하게 되면서 커지게 된다. 또한 마우스 커서를 이미지 항목에 놓게 되면 서서히 이미지가 선명하게되고 마우스 커서가 이미지 항목을 떠나게 되면 항목은 다시 서서히 흐릿하게 보이도록 한다. 코드는 바로 앞에서 추가한

부분을 아래로 수정하면 된다. 기본적으로 어떤 이벤트가 발생시에 적절한 효과를 추가하는 것이므로 태그를 사용하고 과 를 써서 정해진 시간에 맞는 에니메이션 기능을 추가했다. 스타일과 템플릿보다는 에니메이션 효과의 내용이 크므로 자세한 설명은 생략하고 코드만 보이도록 하겠다.

 
  
  
    
      
        
        
      
    

    
      
        
          
            
          
        
      
    

    
      
        
          
            
          
        
      
    
  

최종 실행 결과를 살펴보기에 앞서, 가장 처음 모습을 살짝 다시 보면..

이랬던 아이가 어느덧 커서.. 아래와 같은 美人으로 탄생했다. 스타일과 템플릿의 힘으로 말이다.

스타일 그리고 템플릿(Style, Template) – {2/3}

이제 앞에서 작성한 기본 코드에 스타일과 템플릿을 적용해 보도록 하자. 가장 먼저 할 일은 두개의 TextBlock의 스타일을 개선해보도록 하자. 즉, My Photos라는 문자열과 Check out my ew photos!라는 문자열의 모양을 개선해 보자.

먼저 ~ 안에 다음 스타일 지정 태그를 추가하고 실행해보자.

실행 결과를 보면 알겠지만, 먼저 모든 글자에 대한 폰트가 Comic Sans MS로, 크기는 14로 변경되었다. 또한 TextBlock의 경우 수평정렬이 중앙으로 되어 있다. 그런데 의아한 것은 ListBox의 글자까지 변경되었다는 것이다. 이것은 ListBox를 구성하고 아이템들이 TextBlock으로 되어져 있기 때문에 그 영향을 받는 것이다.

이런 결과가 나오게 된 이유는 앞에 추가한 XAML 코드에서 <Style> 태그의 영향 때문이다. Style 태그의 TargetType에 TextBlock로 되어 있으므로 XAML 코드의 해당 TextBlock은 모두 지정한 스타일에 맞춰 그려지게 되는 것이다.

이제 “My Photos”라는 TextBlock의 스타일만을 다르게 지정해보자. 마찬가지로 태크 안에 아래의 코드를 추가한다.

이 <Style> 태크의 속성 중 BaseOn은 앞에서 정의한 모든 TextBlock에 해당하는 스타일의 속성을 기반으로 한다는 의미이다. 그리고 이 스타일의 식별 Key를 “TitleText”라고 지정해 둠으로써 원하는 TextBlock이 이 Key 값을 적용해서 스타일을 바꿀수 있는 것이다. 즉, 기존의 “MyPhotos” 값을 갖는 를 아래처럼 변경해 준 뒤에 실행해보자.

My Photos

자, 이제는 ListBox에 있는 jpg 파일을 파일 경로명이 아닌 이미지로 표현해보도록 하자. 역시 태그 안에 아래의 코드를 추가한다.


  
    
  

이번엔 <Style>이 아닌 <DataTemplate>이다. 데이터 템플릿은 속성인 DataType으로 지정된 데이터에 대해서 어떤식으로 표현할 것인지에 대한 템플릿을 지정하는 것이다. 이 경우 우리가 처음에 만들어 놓은 Photo 클래스에 대한 데이터 템플릿으로 Photo의 ToString으로 얻어온 문자열(jpg 파일명)을 Image로 표현하라는 XAML 코드이다. 실행 결과는 아래와 같다.

이쯤이면 대체로 만족할 만한 결과가 나타나기 시작하는 것이 보이는데, 이제 좀더 세련되게 꾸며 보도록 하자. 이러한 과정이 WPF가 제공하는 매력적인 요소중에 하나이니 말이다.

스타일 그리고 템플릿(Style, Template) – {1/3}

WPF의 스타일과 템플릿 기능을 이용하면 어플리케이션의 외향을 고급스럽고 세련되게 바꿀 수 있고, 사용자에게 좀더 효과적으로 시스템을 이해하고 활용할 수 있는 환경을 제공할수 있다.

먼저 스타일과 템플릿이 전혀 적용되지 않는 것부터 시작해서 단계적으로 하나 하나 적용해 가면서 어떻게 어플리케이션의 외향이 고급스럽게 바뀌어 가는지를 살펴봄으로써 WPF의 스타일과 템플릿을 이해해 보도록 하겠다.

아래의 코드가 처음 단계에 대한 코드이며 이어지는 이미지가 실행결과이다.


  
  
    
  
  
  
    My Photos
    Check out my new photos!
    
    

흔이 우리가 많이 봐왔던 UI인데, 상단에 2개의 TextBlock이 있고 바로 아래에 있는 ListBox에 jpg 이미지에 대한 경로가 나타나 있다.

스타일과 템플릿을 적용해보기에 앞서, 한가지 궁금증을 풀어보도록 하자.

ListBox에 나타난 jpg 이미지 파일의 경로 문자열은 어디서 왔는가? <ListBox>의 속성 중에 ItemsSource가 그 해답으로 가는 길의 시작점이다. ItemSource 속성에 MyPhotos라는 StaticResource를 바인딩하고 있다. 그렇다면 MyPhotos는 무엇인가? <Window.Resources>에 보면 <ObjectDataProvider>를 이용해서 PhotoList라는 클래스를 MyPhotos라는 Key로 생성하고 있다.

이제 PhotoList 클래스에 대해서 살펴보도록 하자. PhotoList는 Photo라는 클래스를 관리해주는 Collection으로써 다음과 같이 정의되어 있다.

public class PhotoList : ObservableCollection
{
    public PhotoList() { }

    public PhotoList(string path) : this(new DirectoryInfo(path)) { }

    public PhotoList(DirectoryInfo directory)
    {
        _directory = directory;
        Update();
    }

    public string Path
    {
        set {
            _directory = new DirectoryInfo(value);
            Update();
        }

        get { return _directory.FullName; }
    }

    public DirectoryInfo Directory
    {
        set
        {
            _directory = value;
            Update();
        }

        get { return _directory; }
    }

    private void Update()
    {
        foreach (FileInfo f in _directory.GetFiles("*.jpg"))
        {
            Add(new Photo(f.FullName));
        }
    }

    DirectoryInfo _directory;
}

먼저, PhotoList는 ObservableCollection<Photo>에서 파생되었는데, ObservableCollection은 콜렉션에서 자신이 관리하고 있는 데이터(여기서는 Photo 클래스의 인스턴스)가 삭제, 추가등과 같은 변경이 있을 경우 통지를 해주는 클래스이며, ListBox 컨트롤의 ItemSource가 될 수 있는 클래스이다. 기본적으로 PhotoList가 하는 일은 특정 폴더경로를 받아서 그 경로에 있는 확장자가 jpg인 파일명을 통해 Photo라는 클래스의 인스턴스를 만들어 준다. 이제 Photo 클래스에 대해서 살펴보자.

public class Photo
{
    public Photo(string path)
    {
        _source = path;
    }

    public override string ToString()
    {
        return Source;
    }

    private string _source;

    public string Source { get { return _source; } }
}

Photo는 무척 간단한 클래스인데, 하는 일은 단지 문자열(여기서는 jpg 파일명)을 속성으로 가지고 있고 ToString을 통해 반환하는 일을 한다.

이제 다시 XAML에서 살펴본 PhotoList 클래스 타입으로써 생성된 MyPhotos라는 Key를 가진 객체에 PhotoList 클래스가 jpg 파일명을 수집할 경로명을 지정해야 하는데, 그것은 해당 XAML에 대한 Code-Behind 코드 안의 WindowLoaded 이벤트에서 이루어지게 된다.

private void WindowLoaded(object sender, RoutedEventArgs e)
{
    Photos = (PhotoList)(this.Resources["MyPhotos"] 
        as ObjectDataProvider).Data;

    Photos.Path = "...\\...\\Images";
}

이제 지금까지의 코드를 기본으로 다양한 스타일과 템플릿을 적용할 준비가 끝났다.