ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 2D 평면 물결 효과에 대한 이해
    알고리즘 2019. 9. 5. 09:19

    물결 효과에 대한 공부를 하면서 정리해본다.

    세상에 얼마나 2D Water algorithm 이 존재하는지는 모른다. 구글에서 검색되는 Hugo Elias 의 사이트를 기준으로 이해했고 또 풀어보도록 해보겠다.

    이 알고리즘은 2개의 배열이 필요한데, 하나는 현재의 물결 상태를, 다른 하나는 이전 물결 상태 정보를 저장하고 있다. 시간에 따라 변화하는 물결 상태 정보를 2개의 배열판으로 표현했다고 치면 되겠다.


    (Hugo Elias 그림 참고)

    위 그림에서 버퍼 1은 이전 프레임 버퍼 2과 그 이전 프레임 상태를 기억하는 버퍼 1(그림에는 보이지 않는)의 정보를 토대로 만들어진다. 마찬가지로, 가장 오른쪽에 있는 버퍼 2는 버퍼 1과 그 이전 버퍼 2의 상태를 조합하여 만들어진다. 이렇게 2개의 판을 사용해서 새로운 상태를 표시하기 위해 이전 프레임 정보를 현재 프레임 정보에 조합(?)해 나가는 방식이다. 그렇다면, 배열에는 어떤 정보를 담고 있고, 어떤 조합을 하게 되는 걸까.

     

    물결은 수면의 파동 움직임이고, 또한 수면 높낮이의 변화임을 인식하고 아래 그래프를 참고해본다.

    이 그래프는 수면의 높낮이 변화의1차원 움직임만 표현한 것이다. 이 그래프를 이해한 후 2차원으로 확장시키기만 하면 된다. 물결의 방향이 검은색 화살표와 같을 때 4,3,2,1,0 순으로 물결이 변화한 셈이다. 이 그래프는 상상력이 촘~ 필요하다. 4번 그래프가 0번 쪽으로 횡이동한 것은 아닌데 꼭 그렇게 상상된다. 오로지 수직 움직임만 상상해야 이해가 쉬워진다.

    그리고 수직으로 그은 화살표는 물결의 수직 운동방향이자 세기를 나타내고 있다. 이 그래프에 표시된 운동방향은 모두 아래쪽인데, (다음 물결그래프를 상상해보면) 다음프레임에는 지금보다 수면이 내려가야하기 때문이다. 그리고 수면이 고점과 저점일 때에는 다음 프레임으로 변화하는 양이 작기 때문에 세기가 작고, 중간점으로 갈 수록 변화량이 커지므로 세기가 커지고 있다.

     

    그렇다면 시간(프레임)에 따른 수면의 수직운동을 바로 위의 내용처럼 적용하면 물결이 된다.

    Hugo Elias 는 수직운동량을 두 프레임 이전 그래프에서 찾았다. ②번 그래프의 형태가 0번 그래프의 수직운동세기를 나타내고 있기 때문이다. 이해를 돕기 위해 ②번의 형태를 아래 노란색 그래프로 따로 표시해봤다. 방향이 반대이지만 0번 그래프의 수직방향 운동세기를 그대로 표현하고 있음을 알 수 있다. 따라서 두 프레임 이전 정보와 한 프레임 이전 정보를 알고 있으면 현재 프레임에 정보를 표시할 수 있게 된다. (이전에 높았던 지점은 내려가고 내려간 지점은 올라가는 것은 당연하기에 부호가 반대임) 이제 이 내용을 수식화해보자.

     

    앞에서 말한 2개의 배열(버퍼 1,2)는 수면의 높이값이 저장된다. 버퍼 1은 이전 프레임의 수면정보(①번 그래프)를 갖고 있고, 버퍼 2는 그 이전 프레임의 수면정보(②번 그래프)를 갖고 있다고 가정한다. 그렇다면 현재 수면 그래프의 수직 운동은 아래와 같다. 수직운동은 방향과 세기를 갖기 때문에 속도로 표현한다.

    x 점의 수직 속도 = -(버퍼 2의 x점 속도)

    평면이라면,

    Velocity(x,y) = -Buffer2(x,y)

     

    그래서 현재 수면 그래프는 아래 공식이 될 테다.

    NewHeight(x,y) = Buffer1(x,y) - Buffer2(x,y)

     

    그런데 수면은 사방으로 뻗어나가면서 주변의 영향을 받기 때문에 버퍼의 값은 평균화가 된다.

    Smoothed(x,y) = ((Buffer1(x-1, y) +

                     Buffer1(x+1, y) +

                     Buffer1(x, y-1) +

                     Buffer1(x, y+1)) / 4

     

    Smoothed(x,y) 공식을 테스트 해보면 사방으로 퍼지면서 값이 평균화됨을 알 수 있다.

     

    수면 그래프를 다시 작성하면, 평균화된 이전 버퍼에 속도를 더하면 된다. 게다가 속도에 대한 영향을 줄이기 위해 평균화된 값에 2를 곱한다.

    NewHeight(x,y) = Smoothed(x,y)*2 + Velocity(x,y)

     

    물결은 퍼져나갈 수록 힘을 잃어나간다.

    NewHeight(x,y) = NewHeight(x,y) * damping

     

    공식을 풀어본다.

    NewHeight(x,y) = NewHeight(x,y) - NewHeight(x,y)/n

     

    지금까지 내용을 토대로 기본적인 물결을 스크립트로 작성해보았다. 클릭해보아요~


    
    // Water ripple effect
    
    final int COLUMNS = 40;
    final int ROWS = 40;
    final int BLOCKS = COLUMNS * ROWS;
    final float LEVEL = 50;
    final float HIGH_LEVEL = LEVEL * 2;
    final float HUE = 201;
    final float SATU = 95;
    
    float[] buffer1;
    float[] buffer2;
    float[] temp;
    color[] levelColors;
    PVector[] levels;
    int blockWidth;
    int blockHeight;
    boolean firstClicked = false;
    
    void setup() {
      size(400, 400);
      frameRate(30);
      colorMode(HSB, 360, 100, HIGH_LEVEL);
      
      buffer1 = new float[BLOCKS];
      buffer2 = new float[BLOCKS];
      levels = new PVector[BLOCKS];
      blockWidth = width / COLUMNS;
      blockHeight = height / ROWS;
      levelColors = new color[BLOCKS];
      for (int i=0; i

     

    댓글 4

    • ㅋㅋㅋ지존 2015.11.22 13:32

      저는 스무스 하는 과정에서좀 막히네요 스무스 과정을 한번 했을때 힘을준 좌표의 위아래왼쪽오른쪽의 세기가 똑같아야 하는데
      저같은 경우에는 힘을준 좌표 위쪽왼족이 더 세고 아래쪽 오른쪽이 더작습니다. 이걸 어떻게 해결해야할까요..

    • ㅋㅋㅋ지존 2015.11.23 02:43

      도저히 알고리즘이 이해가 안가서(스무스는 구현햇지만...) 일단 코드를 적으면서 해봤는데
      이게 벽반사는 고려도 안한것 같은데 벽반사가 구현되는 괴현상이... 혹시 부연설명 되나요?

      • Favicon of https://blog.hansoolabs.com BlogIcon hansoo.labs 한수댁 2015.11.23 20:24 신고

        요새 이런 공부 많이 하시나 봐요.^
        제가 스므스 과정이라고 한 부분 때문이에요. 주변 픽셀에 영향을 받기 때문에 구지 벽이라고 확인할 필요없이 자동으로 값이 변하게 되는 겁니다.

Designed by Tistory.