JavaFX 그래픽 특수 효과

JavaFX에는 그래픽 작업용 기능이 여러가지 제공되어 있다. 표시 위치나 방향 등을 변환하거나 모양을 투과하거나 흐림과 그림자 효과 등을 적용해 보고, 이러한 특수 효과에 대해 정리하고 설명한다.

트랜스포메이션(Transformation)

모양의 도형은 기본 형상은 매우 간단하다. 예를 들어 사각형이라면, 가로, 세로, 수평, 수직 선뿐이다. “조금 기울어 진 사각형"같은 컨포넌트는 없다. 그렇다면 이러한 것은 어떻게 만들면 될까?

그런 경우에는 “트랜스포메이션(transformation)“을 이용하면 된다. 이것은 모양의 표시를 이동, 회전, 확대, 축소하는 기능이다. 모양의 슈퍼 클래스에 해당하는 Node 클래스에는이를 위한 메소드가 다음과 같이 제공되고 있다.

평행 이동

"Node".setTranslateX(이동률);
"Node".setTranslateY(이동률);
"Node".setTranslateZ(이동률);

회전

"Node".setRotate(회전각도);

확대 축소

"Node".setScaleX(확대율);
"Node".setScaleY(확대율);
"Node".setScaleY(확대율);

평행 이동은 X, Y, Z 축이 각각의 방향으로 평행 이동하는 것이다 (왜 Z 축까지 있지? 라고 생각 하겠지만, 사실 JavaFX는 3D 그래픽 기능도 포함되어 있기 때문이다).

회전은 회전 각도를 지정한다. “역 회전은?“인 경우에는 마이너스의 각도를 지정하면 된다.

확대 축소도 역시 X, Y, Z 축이 각각의 방향으로 준비되어 있다. 이것은 확대율이므로, 1.0보다 크면 확대, 작으면 축소한다.

간단한 사용 예제는 다음과 같다. 지난번 만든 모양 생성 createShape 방법을 재작성하는 형태로 작성되어 있다.

public void createShape(Pane root){
    for(int i = 0;i < 100;i++){
        Rectangle r = new Rectangle(10, 10, 25, 25);
        r.setFill(Color.YELLOW);
        r.setStroke(Color.GREEN);
        r.setStrokeWidth(3);
        r.setRotate(10 * i);
        r.setTranslateX(5 * i);
        r.setTranslateY(2.5 * i);
        r.setScaleX(1 + 0.05 * i);
        root.getChildren().add(r);
    }
}

실행하면 사각형 모양이 조금씩 변화하면서 그려진다 것을 알 수 있다.

Rectangle를 작성하고 평행 이동, 회전, 확대하여 도형의 표시를 조금씩 밀어간다. new Rectangle로 만든 도형의 위치와 크기는 모두 동일하지만 표시는 조금씩 변화 해 나가는 것이다.

도형의 투과

지금까지의 도형은 모든 도형의 내부를 정해진 색으로 채워진 형태로 되어 있었다. 도형을 포개면, 물론 아래에 있는 도형은 숨겨져 보이지 않게 되었다. 그럼 도형을 반투명하고 아래의 것이 비쳐 보이는 용의 표시를하고 싶다면 어떻게 하면 될까?

그런 경우에는 setOpacity라는 메소드를 사용한다. 이것은 도형의 투과율을 설정하는 메소드로 다음과 같이 사용한다.

"Node".setOpacity(투과율);

인수는 0 ~ 1 사이의 실수(double)를 지정한다. 제로라면 투명하게, 1이면 불투명하다.

이것으로 도형을 반투명하게 하는 것은 간단하게 할 수 있다. 다만, 실제로 해보면 알 수 있지만, 이 방법은 “윤곽선만 그려서 도형의 내부는 반투명하게 한다"는 것은 되지 않는다. 이것은 도형 전체를 동일하게 투과하는 것이다. 내부뿐만 아니라 윤곽선도 마찬가지로 투명하게 한다.

도형의 내부를 투과하고 싶은 경우는, setFill을 사용하면 된다. 이것으로 Color.TRANSPARENT를 지정하면 내부가 투명하게 윤곽선만의 도형이 그려진다.

아래 소스 코드는 사용 예제 소스 코드이다.

public void createShape(Pane root){
    for(int i = 0;i < 20;i++){
        Rectangle r = new Rectangle(10, 10, 50, 50);
        r.setFill(Color.BLUE);
        r.setStroke(Color.WHITE);
        r.setTranslateX(20 * i);
        r.setTranslateY(10 * i);
        r.setOpacity(1 - 0.05 * i);
        root.getChildren().add(r);
        Rectangle r2 = new Rectangle(10, 10, 50, 50);
        r2.setStroke(Color.RED);
        r2.setFill(Color.TRANSPARENT);
        r2.setTranslateX(20 * i);
        r2.setTranslateY(10 * i);
        root.getChildren().add(r2);
    }
}

실행하면 사각형이 가로로 나란히 표시된다. 윤곽선만은 빨간색으로 표시되고 그 내부는 조금씩 투명하게 바뀐다.

여기에서는 setOpacity 전체를 투과한 도형과 setFill(Color.TRANSPARENT) 내부를 투과한 도형을포개어서 “윤곽선은 그대로 두고, 내부만 조금씩 투과하는 모양"을 표현하고 있다.

흐림(Blur) 효과

셰이프에는 시각 효과를 위한 기능도 포함되어 있다. 시각 효과라고 하면 무슨 뜻인지 알기 어렵겠지만, 예를 들면 ‘흐림’등이 대표적이다.

시각 효과는 shape(Node의 서브 클래스)에 포함되어 있는 “setEffect"라는 메소드를 사용하여 설정한다. 이것은 다음과 같이 실행한다.

"Node".setEffect("Effect");

인수에는 시각 효과의 내용을 나타내는 Effect라는 클래스 (또는 그 서브 클래스)의 인스턴스를 지정한다. 시각 효과는 많은 것이 제공되어 있으며, 그것들은 모두 Effect 클래스의 서브 클래스로 사용할 수 있다.

흐림 관계된 시각 효과 클래스로는 다음과 같은 것들이 있다. 또한 생성자는 시각 효과를 표현하는데 필요한 설정 값 인수에 대해 설명하겠다.

Gaussian Blur

new GaussianBlur(반경)

Gaussian Blur는 가우스 곡선을 이용하여 흐림 효과를 내는 것이다. 넓은 범위에 흐림을 매끄럽게 적용할 시에 사용된다. 인수에는 흐름 반경이 되는 실수를 지정한다.

Motion Blur

new MotionBlur(각도, 반경)

Motion Blur는 고속으로 이동하는 모습을 촬영한 것 같은 흐림 효과를 내는 것이다. 인수에는 흐림 방향을 나타내는 각도와 흐림의 강도를 나타내는 반경을 각각 실수로 지정한다.

Box Blur

new BoxBlur(, 높이, 반복)

Box Blur는 인접 픽셀의 평균값을 바탕으로 흐림 효과를 내는 것이다. 인수는 박스의 가로 폭과 높이, 시각 효과의 반복 수를 지정한다. 폭과 높이는 double 값, 반복 수는 int 값이다.

간단한 사용 예는 다음과 같다.

public void createShape(Pane root){
    for(int i = 0;i < 10;i++){
        Rectangle r = new Rectangle(20, 20, 50, 50);
        r.setTranslateX(25 * i);
        r.setTranslateY(15 * i);
        r.setFill(Color.BLUE);
        r.setStroke(Color.RED);
        r.setStrokeWidth(5);
        r.setEffect(new GaussianBlur(2.0 * i));
        root.getChildren().add(r);
    }
}

소스 코드를 실행해 보면 조금씩 Gaussian Blur으로 흐림 효과를 강화하게 사각형을 그려간다.

그림자 효과와 반사

흐림 효과와 함께 많이 사용되는 시각 효과라고 하면 “그림자” 효과일거다. 그림자에는 몇 가지 종류가 있다. 여기서 간단하게 정리해 보겠다.

드롭 섀도우

new DropShadow(반경, 가로 오프셋, 수직 오프셋, 색상);

도형의 그림자가 그 아래에 떨어지는 효과를 “드롭 섀도우"라고 한다. 이것은 “DropShadow” 클래스로 제공된다. 생성자에는 여러 가지가 있지만, 가장 자주 사용되는 것은 그림자가 적용되는 반경 (그림자의 폭), 그림자의 가로 세로 방향의 편차 폭 (오프셋), 그림자 색 (Color 인스턴스)을 대충 인수로 지정하여 작성하는 것이 일반적이다.

내부의 섀도우

new InnerShadow(반경, 가로 오프셋, 수직 오프셋, 색상);

도형의 내부가 움푹 패인 것처럼 안쪽에 그림자를 그리는 것이다. 이것도 DropShadow와 마찬가지로 인수에는 반경, 가로 세로 오프셋, 그림자 색을 지정한다.

반사

new Reflection(상단 오프셋,  프랙션(fraction),  상부 투과폭,  바닥 투과폭);

이것은 그림자와는 조금 다르지만, 그림자처럼 빛의 효과로 표현한다. 리플렉션(reflection)은 얼음이나 물 등 위에 도형이 있듯이, 그 아래에 반전된 이미지를 표시하는 것이다. 인수에는 그 도형과 반사 도형과의 간격, 반사 도형이 그려진 비율 (프랙션, 0 ~ 1의 실수), 도형의 위와 아래의 지정된 비율을 투과하기 위한 설정 (0 ~ 1의 실수)등을 지정한다.

실제 사용 예제는 아래와 같다.

public void createShape(Pane root){
    Rectangle r = new Rectangle(20, 20, 100, 50);
    r.setFill(Color.BLUE);
    r.setStroke(Color.RED);
    r.setStrokeWidth(5);
    r.setEffect(new DropShadow(20.0, 10.0, 5.0, Color.BLACK));
    root.getChildren().add(r);
     
    Ellipse e = new Ellipse(220, 50, 70, 30);
    e.setFill(Color.YELLOW);
    e.setStroke(Color.GREEN);
    e.setStrokeWidth(5);
    e.setEffect(new Reflection(5.0, 2.0, 0.5, 0));
    root.getChildren().add(e);
     
    Text t = new Text(50, 250, "Hello!");
    t.setFont(new Font(80));
    t.setFill(Color.YELLOW);
    t.setStroke(Color.GREEN);
    t.setStrokeWidth(1);
    t.setEffect(new InnerShadow(2.0, 2.0, 1.0, Color.BLACK));
    root.getChildren().add(t);
}

DropShadowInnerShadow은 그 만큼 어려워 아니라고 생각한다. Reflection는 반사 도형의 표시 폭과 상하가 스치는 느낌 등을 설정할 수 있기 때문에, 각각의 인자의 역할을 알면 재미있는 효과를 얻을 수 있게 된다.

효과 체인 여러 시각 효과를 적용

다양한 시각 효과를 소개했지만, 이 모든 NodesetEffect로 설정을 했었다. 결국은 동시에 여러 Effect를 설정할 수 없다는 것이다.

하지만 실제에 여러 시각 효과를 동시에 사용하고 싶은 경우가 있다. 이런 경우는 어떻게 해야 할까?

이것은 좀 발상을 바꾸어 생각해야할 필요가 한다. setEffect에서 설정할 수 있는 Effect는 1개뿐이다. 이것은 바꾸려는 것이 아니다. 주목해야 하는 것은 적용하는 Node가 아니라 시각적 효과가 될 Effect 클래스이다.

Effect 클래스는 입력된 이미지에 효과를 적용하여 출력하는 역할을 한다. 즉 “입력 이미지"을 변환해서 “출력 이미지"를 생성하여 전달하는 것이다. 결국은 어떤 시각 효과의 출력을 다른 시각 효과의 입력에 전달할 수 있다면, 여러 시각 효과를 사용할 수있을 것이다.

즉, “시각 효과 A” → “시각 효과 B"→ “시각 효과 C”……로 차례로 시각 효과를 적용해 가고, 최종 결과를 setEffect로 설정하는 것이다. 이것을 “효과 체인(effect chain)“이라고 한다. 이 효과 체인은 “setInput “라는 메소드를 이용하여 설정할 수 있다.

"Effect".setInput("Effect");

setInput는 시각 효과가 있는 Effect 클래스의 메소드이다. 인수에는 다른 Effect 인스턴스를 지정한다. 따라서 인수로 설정된 Effect의 결과를 바탕으로 더욱 시각적 효과를 설정할 수 있다.

실제 사용 예제는 아래와 같다.

public void createShape(Pane root){
    InnerShadow is = new InnerShadow(10.0, 2.0, 2.0, Color.rgb(0, 100, 100));
    DropShadow ds = new DropShadow(20.0, 10.0, 5.0, Color.BLACK);
    ds.setInput(is);
    Reflection rf = new Reflection(0.0, 2.0, 0.5, 0);
    rf.setInput(ds);
 
    Rectangle r = new Rectangle(20, 20, 100, 50);
    r.setFill(Color.CYAN);
    r.setEffect(rf);
    root.getChildren().add(r);
}

여기서 InnerShadow, DropShadow, Reflection 3가지 EffectsetInput에 이어 시각 효과를 생성한다. 효과 체인 사용법을 알면 얼마든지 시각 효과를 조합해 사용할 수 있다.




최종 수정 : 2017-09-19