← 과제 목록

[과제] 2주차) Basic Programming 박서빈

@seobinpark
  • #과제

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 원문