728x90
반응형
- 개요
이전과 다르게 두 개의 하트를 그리되,
왼쪽의 하트는 모든 행렬 변환을 수행하고, 오른쪽의 하트는 역행렬을 사용하여 원본을 유지한다
적용하는 행렬 변환은 다음과 같다
- 크기 변환행렬
- 회전 변환행렬
- 전단 변환행렬
- 예제
...
// 게임 로직과 렌더링 로직이 공유하는 변수
// 최종 전단값 / 최종 크기 / 최종 회전각도
float currentShear = 0.f;
float currentScale = 10.f;
float currentDegree = 0.f;
// 매 프레임마다 호출하는 함수
void SoftRenderer::Update2D(float InDeltaSeconds)
{
// 사용자로부터 입력을 받기 위한 레퍼런스
auto& g = Get2DGameEngine();
const InputManager& input = g.GetInputManager();
// 게임 로직의 로컬 변수
// 전단속도 / 최소크기 / 최대크기 / 크기변환속도 / 회전속도
static float shearSpeed = 2.f;
static float scaleMin = 5.f;
static float scaleMax = 20.f;
static float scaleSpeed = 20.f;
static float rotateSpeed = 180.f;
// 사용자로부터 입력값을 받아와 초당 전단값 / 초당 크기변환값 / 초당 회전각을 받아옴
float deltaShear = input.GetAxis(InputAxis::XAxis) * shearSpeed * InDeltaSeconds;
float deltaScale = input.GetAxis(InputAxis::ZAxis) * scaleSpeed * InDeltaSeconds;
float deltaDegree = input.GetAxis(InputAxis::WAxis) * rotateSpeed * InDeltaSeconds;
// 하트의 최종 전단값 / 최종 크기 / 최종 회전각도를 설정
currentShear += deltaShear;
currentScale = Math::Clamp(currentScale + deltaScale, scaleMin, scaleMax);
currentDegree += deltaDegree;
}
// 화면에 렌더링하는 함수
void SoftRenderer::Render2D()
{
// 화면에 렌더하기 위해 가져올 레퍼런스
auto& r = GetRenderer();
const auto& g = Get2DGameEngine();
// 배경에 격자 그리기
DrawGizmo2D();
// 렌더링 로직의 로컬 변수
// 현재 각도 / 각도를 서서히 증가시킬 값 / 하트를 구성하는 점을 저장할 컨테이너 / 현재 각도에 대응하는 색 / 2개의 하트를 그릴 기준점
float rad = 0.f;
static float increment = 0.001f;
static std::vector<Vector2> hearts;
HSVColor hsv(0.f, 1.f, 0.85f);
static Vector2 pivot(200.f, 0.f);
// 컨테이너에 하트를 구성하는 점들을 저장하는 로직
if (hearts.empty())
{
// sin 함수와 cos 함수의 정의역인 0과 2π의 범위만을 순회
for (rad = 0.f; rad < Math::TwoPI; rad += increment)
{
float sin = sinf(rad);
float cos = cosf(rad);
float cos2 = cosf(2 * rad);
float cos3 = cosf(3 * rad);
float cos4 = cosf(4 * rad);
// 하트 방정식 사용
float x = 16.f * sin * sin * sin;
float y = 13 * cos - 5 * cos2 - 2 * cos3 - cos4;
hearts.push_back(Vector2(x, y));
}
}
// 하트의 최종 크기값을 사용하여 크기 변환 행렬의 기저 벡터를 생성
// 생성한 기저 벡터를 통해 크기 변환 행렬 생성
Vector2 sBasis1(currentScale, 0.f);
Vector2 sBasis2(0.f, currentScale);
Matrix2x2 sMatrix(sBasis1, sBasis2);
// 하트의 최종 각도를 사용하여 회전 변환 행렬의 기저 벡터를 생성
// 생성한 기저 벡터를 통해 회전 변환 행렬 생성
float sin, cos;
Math::GetSinCos(sin, cos, currentDegree);
Vector2 rBasis1(cos, sin);
Vector2 rBasis2(-sin, cos);
Matrix2x2 rMatrix(rBasis1, rBasis2);
// 하트의 최종 전단값을 사용하여 전단 변환 행렬의 기저 벡터를 생성
// 생성한 기저 벡터를 통해 전단 변환 행렬 생성
Vector2 shBasis1 = Vector2::UnitX;
Vector2 shBasis2(currentShear, 1.f);
Matrix2x2 shMatrix(shBasis1, shBasis2);
// 생성한 3개의 행렬을 곱하여 합성 행렬
Matrix2x2 cMatrix = shMatrix * rMatrix * sMatrix;
// 하트의 최종 크기값을 사용하여 크기 변환 행렬의 역행렬의 기저 벡터를 생성
// 생성한 기저 벡터를 통해 크기 변환 행렬의 역행렬 생성
float invScale = 1.f / currentScale;
Vector2 isBasis1(invScale, 0.f);
Vector2 isBasis2(0.f, invScale);
Matrix2x2 isMatrix(isBasis1, isBasis2);
// 회전 변환 행렬에 전치 연산을 수행하여 회전 변환 행렬의 역행렬 생성
Matrix2x2 irMatrix = rMatrix.Transpose();
// 하트의 최종 전단값을 사용하여 전단 변환 행렬의 역행렬의 기저 벡터를 생성
// 생성한 기저 벡터를 통래 전단 변환 행렬의 역행렬 생성
Vector2 ishBasis1 = Vector2::UnitX;
Vector2 ishBasis2(-currentShear, 1.f);
Matrix2x2 ishMatrix(ishBasis1, ishBasis2);
// 생성한 3개의 역행렬을 기존의 합성행렬의 순서와 반대로 곱하여 합성 행렬 생성
Matrix2x2 icMatrix = isMatrix * irMatrix * ishMatrix;
// 현재 각도를 초기화한 후 색상을 증가시키면서 점에 대응
rad = 0.f;
for (auto const& v : hearts)
{
// 변환 행렬을 적용한 왼쪽 하트 표현
Vector2 left = cMatrix * v;
r.DrawPoint(left - pivot, hsv.ToLinearColor());
// 왼쪽 하트의 변환 행렬에 역행렬을 적용한 오른쪽 하트 표현
Vector2 right = icMatrix * left;
r.DrawPoint(right + pivot, hsv.ToLinearColor());
hsv.H = rad / Math::TwoPI;
rad += increment;
}
// 최종 전단값 / 최종 크기 / 최종 각도를 화면에 출력
r.PushStatisticText(std::string("Shear : ") + std::to_string(currentShear));
r.PushStatisticText(std::string("Scale : ") + std::to_string(currentScale));
r.PushStatisticText(std::string("Degree : ") + std::to_string(currentDegree));
}
...
예제의 코드는 위와 같다
Update2D 함수에서는 사용자의 입력값을 받아와 하트에 사용할 행렬에 적용할 전단값 / 크기 / 각도를 설정한다
Render2D 함수에서는 Update2D 함수에서 설정한 값들을 사용하여 하트를 렌더링한다
- 왼쪽 하트 : 크기 변환 행렬 * 회전 변환 행렬 * 전단 변환 행렬의 합성 행렬을 적용
- 오른쪽 하트 : 전단 변환 역행렬 * 회전 변환 역행렬 * 크기 변환 역행렬의 합성 행렬을 적용
- 최종 실행 결과

728x90
반응형
'수학 > 이득우의 게임 수학 - 실습 예제' 카테고리의 다른 글
| 스크린 좌표계 구조 (0) | 2025.10.01 |
|---|---|
| 아핀 변환이 적용된 크기 변환 / 회전 변환 / 이동 변환을 구현 (0) | 2025.09.24 |
| 행렬을 사용하여 하트의 크기 변환 & 회전 변환 구현 (0) | 2025.09.10 |
| 극좌표계를 활용한 소용돌이 효과 구현 (0) | 2025.08.27 |
| 삼각함수를 사용한 하트의 회전 (0) | 2025.08.27 |