1. Basic Programming 이해
Basic Programming 파트에서 다루는 핵심은
"Shape를 만들고 → 형태를 정의하고 → 채우고 → 캔버스에 올린다" 로 이해했습니다.
| 목적 | API | 비고 |
|---|---|---|
| Shape 객체 생성 | tvg::Shape::gen() | 모든 도형의 시작점 |
| 사각형(둥근 모서리 포함) | shape->appendRect(x, y, w, h, rx, ry) | rx=ry=0이면 일반 사각형 |
| 원/타원 | shape->appendCircle(cx, cy, rx, ry) | rx≠ry면 타원 |
| 커스텀 패스 | moveTo() / lineTo() / cubicTo() / close() | 직선·베지어 곡선으로 임의 도형 구성 |
| 색 채우기 | shape->fill(r, g, b, a) | 단색 |
| Gradient 채우기 | tvg::LinearGradient / tvg::RadialGradient + colorStops() | shape->fill(gradient) |
| 외곽선(stroke) | strokeWidth() / strokeFill() / strokeCap() / strokeJoin() / strokeDash() | 선 두께·색·점선 패턴 |
| 장면 구성 | tvg::Scene | 여러 Paint를 그룹화, transform 공유 |
| 캔버스에 등록 | canvas->push(shape) | 실제로 그려지는 시점 |
2. 불꽃놀이 파티클 시스템
Basic Programming 파트에서 배운 걸 그대로 사용하기보다 뭔가 움직이는 걸 만들어보고 싶어서 불꽃놀이 파티클을 제작했습니다.
코드 : https://github.com/seobinpark/thorvg.example/commit/2cd0fd7dc216a07548a038c20ca2dc522b6cc6eb
영상
배경 그라디언트
밤하늘 배경은 LinearGradient로 만들었습니다. 화면 전체를 덮는 사각형에 위쪽은 어두운 남색, 아래쪽은 보랏빛이 도는 색으로 두 개의 colorStop으로 구성되어 있습니다.
auto sky = tvg::Shape::gen();
sky->appendRect(0, 0, canvasW, canvasH);
auto grad = tvg::LinearGradient::gen();
grad->linear(0, 0, 0, canvasH);
tvg::Fill::ColorStop stops[2] = {
{0.0f, 8, 10, 35, 255},
{1.0f, 40, 20, 70, 255}
};
grad->colorStops(stops, 2);
sky->fill(grad);
Scene으로 레이어 나누기
도형이 여러개라서 역할별로 Scene을 세 개 만들어서 따로 관리했습니다.
shellScene = tvg::Scene::gen();
glowScene = tvg::Scene::gen();
particleScene = tvg::Scene::gen();
canvas->add(shellScene);
canvas->add(glowScene);
canvas->add(particleScene);
shellScene: 하늘로 쏘아 올라가는 발사체glowScene: 파티클 뒤에 깔리는 빛 번짐용 도형들particleScene: 폭발한 뒤 퍼지는 파티클 본체
이렇게 나눠두면 Scene 단위로 효과를 적용하거나 그리는 순서를 조절하기 편했습니다.
SceneEffect로 블러 넣기
빛나는 느낌을 내려고 glowScene에만 가우시안 블러를를 적용했습니다.
glowScene->add(tvg::SceneEffect::GaussianBlur, 6.0, 0, 0, 100);
Shape 하나하나에 효과를 주는 게 아니라 Scene 전체에 한 번만 걸면 그 안에 들어있는 도형이 전부 블러 처리된다는 점이 신기했습니다.
매 프레임 위치/투명도 갱신
한 번 만든 도형을 새로 만들지 않고, update() 안에서 계속 값만 바꿔가며 재사용했습니다.
it->shape->translate(it->x, it->y);
it->shape->opacity((uint8_t)(it->life * 255));
도형 추가/제거
파티클은 수명이 다하면 지워야 해서 Scene에서 remove()를 사용해서 도형을 제거하는 방식으로 처리했습니다.
particleScene->remove(it->shape);
glowScene->remove(it->glow);
it = particles.erase(it);
update() 루프에서 매 프레임 canvas->update() 호출
translate()나 opacity()로 값이 변경되었을 때 마지막에 canvas->update()를 호출해야 화면에 반영되는 것을 알 수 있었습니다.
canvas->update();
댓글
Discussion 원문