물체의 3차원 회전을 풀그림 할 때 텍스쳐 맵핑도 고려해야 한다. 면에 그려진 텍스쳐도 회전과 함께 같이 돌아야 한다. 여기에 특별한 기법이 따로 있는 것이 아니다. 모든 텍스쳐를 벡터화해서 그 데이터를 함께 회전 행렬을 써서 변환시키면 그 뿐이다. 그러나 <그림1>과 같이 정교한 텍스쳐는 CPU 에 매우 큰 짐을 안겨 줌으로 최적화를 위하여 노력해야 한다. 

 

        <그림1>의 원테의 그라데이션을 회전시키기 위해서는 360개의 3차원 점을 회전시켜야 한다. 따라서 이런 경우에는 그림을 그릴 필요가 있는지 없는지를 먼저 판별한 다음 회전시켜야 한다. 미리 모든 데이터점을 회전시킬 필요가 없다. 한편 <그림3>과 같은 간단한 영자 Z 또는 N자 텍스쳐는 단 10 개의 3차원 점을 회전시키면 된다. CPU 에 큰 부담을 주지 않는다.  

 





무비 1  텍스쳐 맵핑 데모 무비



          오일러각 제어 스라디더를 움직이면서 실험해 보기 바란다.



 



그림 1 면이 회전하면 면위의 그림도 따라 돈다.




그림 2 정교한 맵핑일수록 CPU에 부담을 준다.




그림3 글자와 같은 것은 작은 벡터데이타로 그릴수 있다.

 



그림4 영자 Z(N?)는 10개의 3차원 점으로 처리했다.





______________________________________________


(각주)  2006년 Actionscript3.0 이 나오면서 텍스쳐매핑은 아주 빨라졌다.  2006년 내가 플래시 클럽강좌에 AS3.0 으로 텍스쳐매핑하는 법을 올려 놨다.   그때 만든 데모 플래시 무비를 아래에 첨부한다.



 


 

 



 

Posted by 샛솔 :


         박스 그리기(주사위5)에서 보여 준 정6면체는 반투명 유리 박스로 6면이 모두 보였다.  불투명 박스를 그리기 위해서는 박스의 안쪽 면은 그리지 말아야 한다.  다면체의 안쪽 면은 항상 가려져 보이지 않는다. 


         사실 3차원 입체를 그리기 위해서는 가려진 면을 감추는 것이 매우 중요한 과제가 된다. 그런데 뽈록 입체의 경우 입체의 안쪽면만 제거하면  보이는 모든 것을 그린 셈이 된다.  뽈록 하기 때문에 겉면이 입체의 다른 부분에 의해서 가려지는 경우는 없기 때문이다. 오목 입체의 경우에는 보다 복잡한 알고리즘을 사용하여야 한다. 다행이 주사위는 뽈록 입체이므로 앞뒷면 가리기만 해 주어 겉면만 그리게 되며 그것으로 그리기는 끝난다. 


          3차원 공간에서 평면의 앞뒤면 가리기에는 두 가지 방법이 있다.  하나는 강체를 이루는 표면 다각형의 법선을 먼저 구해 두고 그 법선도 강체와 함께 변환 행렬을 적용한다. 그런 다음 그 법선의 Z성분이 양수냐 음수냐를 가려서 다각형 면이 앞면을 보이고 있나 뒷면을 보이고 있는지를 판별한다. 이 방법은 입체를 변환시킬 때 입체를 구성하는 꼭지점 이외에 각 구성면의 법선까지 함께 변환시켜 주어야 하는 계산상 짐이 생긴다. 

 
          두 번째 방법은 다각형의 꼭지점으로부터 법선의 Z성분을 직접 셈하는 방법이다.  <무비1>은 두가지 방법을 보이기 위하여 만든 무른모 무비이다.   아래에 기술하는 설명을 직접 실험허 보기 바란다.






무비 1




 

           먼저 다각형의 꼭지점의 인덱스는 항상 일관성 있게 정의한다. 박스 그리기 인덱스 배열을 만들 때 이미 언급하였다. 첫 번째 방법으로 미리 셈한 법선을 청회색으로 표시 하였다. 두 번째 방법은 법선을 미리 구하지 않고  다각형의 두 모서리(변)를  벡터로 잡고 이 벡터의 벡터곱을 셈하고 이 벡터곱의 Z성분으로 면의 앞뒷면 가리기로 사용하는 것이다. 

         <무비1> 초기 화면에서 평면(세모꼴)은 Z-Y면에 있는데 인덱스 0,1,2 가 붙어 있다.   미리 구한 법선은 인덱스 0에서 공간 X축을 따라 길이 side인 벡터이다.  이 무른모의 코드 일부를 <표1>에  올려  놓았다.  



1

var  dpt:Number = -1000;

2

var side :Number = 100;

3

 

4

//....

5

 

6

var triDat:Array = new Array([0,0,0],[0,side,0],[0,side/2,side*Math.sqrt(3)/2]);

7

var normal :Array =new Array(side, 0, 0);

8

var rot:Rotation3D = new Rotation3D();

9

var triCur:Array = new Array([0,0,0],[0,side,0],[0,side/2,side*Math.sqrt(3)/2]);

10

var normalCur:Array = new Array(side,0,0);

11

 

12

//.....

13

 

14

for (var i = 0; i <triCur.length ;  i++)

15

triCur[i] = rot.transform3DBody(triDat[i]);

16

normalCur = rot.transform3DBody(normal);

17

//*****

18

 

19

triB.mcmc.lineStyle(1, ColorName.magenta, 100);

20

triB.mcmc.moveTo(0, 0);

21

triB.mcmc.lineTo(Isom.mTSB(normal)[0], Isom.mTSB(normal)[1]);

22

triB.mcmc.moveTo(0, 0);

23

triB.mcmc.lineStyle(3, ColorName.cyan, 100);

24

triB.mcmc.lineTo(Isom.mTSB(normalCur)[0], Isom.mTSB(normalCur)[1]);

25

x01 = Isom.mTSB(triCur[0])[0] - Isom.mTSB(triCur[1])[0] ;

26

        y01 = Isom.mTSB(triCur[0])[1] - Isom.mTSB(triCur[1])[1] ;

27

        z01 = Isom.mTSB(triCur[0])[2] - Isom.mTSB(triCur[1])[2] ;

28

        x21 = Isom.mTSB(triCur[2])[0] - Isom.mTSB(triCur[1])[0] ;

29

        y21 =  Isom.mTSB(triCur[2])[1] - Isom.mTSB(triCur[1])[1] ;

30

        z21 =  Isom.mTSB(triCur[2])[2] - Isom.mTSB(triCur[1])[2] ;

31

        xx = (y21*z01-z21*y01)/side;         

32

        yy = (z21*x01-x21*z01)/side;         

33

        zz = (x21*y01-y21*x01)/side;

34

triB.mcmc.lineStyle(3, colArr[2], 100);

35

triB.mcmc.moveTo(Isom.mTSB(triCur[1])[0], Isom.mTSB(triCur[1])[1]);

36

triB.mcmc.lineTo(Isom.mTSB(triCur[1])[0]+xx, Isom.mTSB(triCur[1])[1]+yy);

37

triB.mcmc.lineStyle(1, ColorUtil.cyan, 100);

38

triB.mcmc.moveTo(0, 0);

39

triB.mcmc.lineTo(Isom.mTSB(triCur)[0], Isom.mTSB(triCur)[1]);

40

 

41

        nz =   Isom.mTSB(normalCur)[2];

42

txtt.write(dpt4, " nz = "+ NumU.doubleFormat(nz, 3),  40, 160, 170, 30,  ColorUtil.cyan, "_sans",  18, true);

43

txtt.write(dpt5," zz = "+ NumU.doubleFormat(zz, 3), 40, 180, 170, 30,

colArr[2], "_sans",  18, true);

44

                        if (zz>0)  colCur = colArr[0];

45

                        else colCur = colArr[1]; 

46

                        triB.mcmc.lineStyle(0, colCur, 85);

47

                        triB.mcmc.beginFill(colCur, 85);

48

        triB.mcmc.moveTo(Isom.mTSB(triCur[0])[0], Isom.mTSB(triCur[0])[1]);

49

for (var i = 1; i<triCur.length; i++)

50

triB.mcmc.lineTo(Isom.mTSB(triCur[i])[0], Isom.mTSB(triCur[i])[1]);

51

triB.mcmc.lineTo(Isom.mTSB(triCur[0])[0], Isom.mTSB(triCur[0])[1]);

52

triB.mcmc.endFill();             

53

        if(zz<0){

54

                                triB.mcmc.lineStyle(3, colArr[2], 100);

55

triB.mcmc.moveTo(Isom.mTSB(triCur[1])[0], Isom.mTSB(triCur[1])[1]);

56

triB.mcmc.lineTo(Isom.mTSB(triCur[1])[0]+xx, Isom.mTSB(triCur[1])[1]+yy);

57

triB.mcmc.lineStyle(3, ColorName.cyan, 100);

58

triB.mcmc.moveTo(0, 0);

59

triB.mcmc.lineTo(Isom.mTSB(normalCur)[0], Isom.mTSB(normalCur)[1]);

60

        }



표1 평면의 앞뒷면 가리기 코드의 일부 리스팅

 


            줄7에 법선을 미리 정의하고  줄16에서 변환된 법선벡타를 줄10의 배열에 담는다.  이 변환된 벡터를 아이소메트릭 공간에 투영했을 때 그 3번째 성분인 Z성분을 줄41에서 nz라 정의하고 화면에 출력한다.  또 다른 법선 산출방법은 다각형의 첫 3꼭지점으로 부터 두개의 벡터,  (1→0)  와 (1→2)  를 구성하여 그 벡터곱을 셈한다. 그런 다음 그 곱의 Z성분을 줄25에서 줄30까지 셈하여 이것을 zz 로 정의하여 화면에 출력한다.   평면이 스크린에 수직이 되면 평면의 법선은 스크린과 평행이 되므로 이 nz 또는 zz 값은 0이 된다. 따라서 이 nz 또는 zz 값을 가지고 평면의 앞뒷면을 가릴 수가 있다. 


 


그림1



그림2



그림3

              <그림1>, <그림2>, <그림3>은 평면이 앞면을 보일 때 nz 또는 zz 값이 음수를 보이다가 평면이 스크린과 수직이 되면 이들 값은 정확히 0을 갖는다.  다시 평면이 뒷면을 보이면 이들 값은 양수가 되는 것을 보이고 있다. 

무비 1을 직접 실험해 보기 바란다.

Posted by 샛솔 :


오일러각
 
        직교축에 대한 회전각으로 강체의 지향을 정하는 것이 부적당하다면 그 대안은 무엇인가?  18세기의 수학의 거장이며 물리학에도 혁혁한 공적을 남긴 오일러(Leonard Euler)가 제안한 오일러각이라는 것이 있다. 

       
        이 것은 특별한 3축에 대한 회전각으로 정의되는데 서로 직교하는 축은 아니다.  이 오일러각으로 강체의 지향을  정하는 것이 물리적으로 가장 좋은 방법인데 이 회전각 묶음을 주면 유일하게 강체의 지향이 결정된다.  뿐만 아니라 이 변수는 강체의 운동방정식을 간단하게 세울 수 있다는 이점이 있다.

        오일러각은 Φ(phi),   θ(theta),   Ψ(psi)로 부르는 세 각의 세트로 표시한다. 

 

       앞 강좌 (박스 그리기)에 보여 준 <표2>의 Rotation3D 클래스의 메쏘드 함수 48줄의 eulerAngles는 이 3개의 각, 즉 Φ(파이),   θ(쎄타),   Ψ(싸이, 또는 프싸이)를 인수로 주면  강체의 지향을 유일하게 결정해 주는 직교 회전 행렬 값을 돌려주는 함수이다. 


        이 튜토리얼에서는 이 함수를 주로 써서 강체의 지향과 회전운동을 기술할 것이다. 또 물리 모델이 도입되어 강체의 회전을 자연스레 기술하자면 역학 방정식을 풀어야 하고 그러기 위해서는 오일러 각을 쓸 수밖에 없다. 트위닝을 위하여는 쿼터니온이  쓰이기도 하지만 운동방정식을 풀기에는 부적당한 변수다.  오일러각이 유일한 대안이다.   앞 강좌 <표2>의 48줄의 수식은 복잡하므로 여기서는 생략하고 관심 있는 독자는 대학 2년차 이상의 역학 교과서를 참조할 것을 권한다. 

       아래의 플래시 무비는 오일러각을 설명하기 위해 만든 것이다.   스라이더를 움직이며 오일러각의 변동과 원테의 지향을 비교해 보기 바란다. 






무비1  오일러각을 설명하기 위한 플래시 무비 
스라이더를 움직이며 오일러각들이
강체의 지향을 어떻게 기술하나 이해할 수 있다.

  

       이제 색채를 띤 원테를 강체로 잡고 오일러각을  설명하기로 한다.  이 원테에 붙어 있는 몸통 직교 자리표는  X축을 노랑색, Z축을 청회색(cyan)으로 표시해 보였다.  몸통 Y축은 그림이 혼잡해지는 것을 피하기 위해 생략한다.  X-Z축이 결정되면  Y축은 오른손 나사법칙에 따라 유일하게 결정되기 때문이다. 








그림1 오일러각  Φ은 공간 Z축에 대한 회전각이다.




그림2 마지막으로 몸통Z축(청회색)을 축으로 Ψ 만큼회전시킨다.


 

     첫째 회전각은 공간 Z축에 대한 회전 각이다. 이 때  공간 X축과 겹쳐 있던 몸통 X축은 공간 X축과 Φ만큼 회전한다. <그림 1>이 이를 보여 주고 있다. 이 때 몸통 X축을 마디선이라 한다. 현재는 몸통 X축과 겹쳐 있지만 일반적으로는 몸통 X축과는 따로 떨어져  항상 공간 X-Y면에 놓이게 된다.(<그림3>참조)  여기서는 녹색으로 표시한다.  다음은 이 마디선을 축으로 하여 θ큼 회전시킨다.  <그림2>가 이 결과를 보여 주고 있다. <그림2>가 보여 주듯 이젠 몸통 Z축은 공간 Z축과 분리되어 θ각을 이룬다. 



 



그림3) 마디선을 축으로 하여 θ 각 회전시킨 결과.

        마지막으로 Ψ는 이젠 겹침이 풀린 몸통 Z축을 중심으로 회전한 각이다. <그림3>이 이것을 보여 준다. 이때 마디선은  몸통 X축과 겹침이 풀려 그 모습을 드러내었다.     다시 한번 종합해 보면 마디선은 공간 X-Y평면과 몸통 X-Y평면이 교차하는 선이고 Φ는 이 마디선이 공간 X축과 이루는 각이다. 그리고 θ는 이 몸통 X-Y면이 공간 X-Y면과 이루는 각으로 몸통 Z축이 공간 Z축이 만드는 각이다.  Ψ는 몸통 X축이 마디선과 이루는 각이다.        

        오일러각은 어떤 각을 먼저 변화시키건 상관없이 같은 직교변환행렬을 만들고 따라서 강체의 지향이 일의적으로
정해진다.  이 무비는 앞 강좌 <표2>의 Rotation3D 클래스의 줄 48에 정의된 메쏘드 함수 eulerAngles를 써서 제어한 것이다.
      

      앞으로는 강체의 지향은 모두 이 오일러각을 써서 제어하기로 한다. 이 방법만이 강체의 지향을 일의적으로 정하
고 또 강체의 회전 운동을 가장 잘 기술할 수 있기 때문이다.
 
 
 
 

Posted by 샛솔 :