여기까지의 작업내용: https://github.com/hwi-middle/HimchanSoftwareRenderer/tree/0a71d0b0df0a0bb7648bcd3d6618afa300d0ea9f
상수 추가
static constexpr float PI = 3.1415926535897932f;
static constexpr float TwoPI = 2 * PI;
static constexpr float HalfPI = 0.5 * PI;
static constexpr float InvPI = 1 / PI;
static constexpr float Rad2Deg = PI / 180.f;
static constexpr float Deg2Rad = 180.f / PI;
Math 구조체(구 MathUtil)에 호도법과 관련된 컴파일타임 상수들을 추가했다. PI값은 얼마나 정밀하게(?) 쓸 지 고민하다가 언리얼 엔진의 UnrealMathUtility.h에 정의된 PI값을 가져왔다.
Rad ↔ Deg 변환은 언리얼 엔진에서처럼 함수로 처리할지, 유니티에서처럼 상수 곱셈으로 처리할지 고민했는데 개인적으로 유니티에서 사용했던 방식이 편했어서 유니티의 방식을 가져왔다.
선형보간, SmoothStep
template <class T>
FORCEINLINE static constexpr T Square(const T& InValue)
{
return InValue * InValue;
}
template <class T>
FORCEINLINE static constexpr T Lerp(const T& InFrom, const T& InTo, float InAlpha)
{
InAlpha = Clamp(InAlpha, 0.f, 1.f);
return (1 - InAlpha) * InFrom + InAlpha * InTo;
}
template <class T>
FORCEINLINE static constexpr T SmoothStep(const T& InMin, const T& InMax, float InAlpha)
{
InAlpha = Clamp(InAlpha, 0.f, 1.f);
return Square(InAlpha) * (-2 * InAlpha + 3) * (InMax - InMin) + InMin;
}
template <class T>
FORCEINLINE static constexpr T Clamp(const T& InValue, float InMin, float InMax)
{
return (InValue < InMin) ? (InMin) : (InValue < InMax ? InValue : InMax);
}
https://youtu.be/60VoL-F-jIQ?si=-RivwmBGXAeY2zOa
선형보간과 SmoothStep이다. 사실 원래는 Lerp만 만들었는데 '더 만들거 없나' 싶어서 유니티의 Mathf 레퍼런스 문서를 보면서 함수 쇼핑(?)하다가 SmoothStep도 만들었다. Lerp는 워낙에 간단하니 그렇다치고 SmoothStep은 수식을 외우고 다니지는 않아서 유튜브 영상을 참고했다.
다만 영상에서는 내가 원하는 동작인 그래프를 줄이고 위아래로 이동시키는건 없어서 그건 직접했다. 얼마전에 셰이더 짜면서 삼각함수를 이용한 코드에서 Min값과 Max값의 범위를 갖도록 그래프를 변형시켰던 경험이 있어서 쉽게 구현했다.
template <class T>
FORCEINLINE static constexpr T SmoothStep(const T& InMin, const T& InMax, float InAlpha)
{
InAlpha = Clamp(InAlpha, 0.f, 1.f);
return Lerp(Square(InAlpha), 1 - Square(InAlpha - 1), InAlpha) * (InMax - InMin) + InMin;
}
원래는 이런식으로 x^2 그래프와 1-(x-1)^2의 그래프를 선형보간하는 형태였다가, 영상을 보니 그냥 수식을 정리할 수 있어서 최종적으로는 return Square(InAlpha) * (-2 * InAlpha + 3) * (InMax - InMin) + InMin; 와 같은 형태로 구현했다.
SmoothStep은 코드레벨에서는 달리 참고한 곳이 없어서 나름 테스트를 해봤다. 대충 SmoothStep 그래프의 개형을 화면에 그려보고, 로그도 찍어보면서 정상작동을 확인했다.
삼각함수
FORCEINLINE static float Sin(const float InValue)
{
return sinf(InValue);
}
FORCEINLINE static double Sin(const double InValue)
{
return sin(InValue);
}
FORCEINLINE static float Asin(const float InValue)
{
return asinf(InValue);
}
FORCEINLINE static double Asin(const double InValue)
{
return asin(InValue);
}
FORCEINLINE static float Cos(const float InValue)
{
return cosf(InValue);
}
FORCEINLINE static double Cos(const double InValue)
{
return cos(InValue);
}
FORCEINLINE static float Acos(const float InValue)
{
return acosf(InValue);
}
FORCEINLINE static double Acos(const double InValue)
{
return acos(InValue);
}
FORCEINLINE static float Tan(const float InValue)
{
return tanf(InValue);
}
FORCEINLINE static double Tan(const double InValue)
{
return tan(InValue);
}
FORCEINLINE static float Atan(const float InValue)
{
return atanf(InValue);
}
FORCEINLINE static double Atan(const double InValue)
{
return atan(InValue);
}
FORCEINLINE static float Atan2(const float InY, const float InX)
{
return atan2f(InY, InX);
}
FORCEINLINE static double Atan2(const double InY, const double InX)
{
return atan2(InY, InX);
}
삼각함수 부분은 그냥 표준 라이브러리 코드를 가져다쓰고 있다. 테일러 급수를 더 명확하게 공부해서 삼각함수도 직접 구현해볼까 생각했었는데, 결론적으로 그러지 않게 되었다.
유니티와 언리얼 엔진 모두 언어에서 제공하는 라이브러리를 사용하고 있음을 확인했기 때문이다. 언리얼은 cos, cosf 가져다 쓰고있고 유니티도 Math.Cos를 가져다 쓰고 있었다(참고로 유니티는 오픈소스가 아니긴 하지만 IDE가 디컴파일 해준 코드를 볼 수는 있다).
아마 언어에서 제공하는 삼각함수 관련함수가 충분히 성능이 잘 나오나보다. 특히 STL 다 무시하고 자체구현 컨테이너까지 만드는 언리얼에서도 그냥 가져다 쓸 정도면... 굳이 애써서 더 느린 함수 만들 필요는 없을 것 같아서 이렇게 마무리했다.
https://youtu.be/YHl5jqL-yZA?si=xIvZ31bN4QVvXi-e
테일러 급수에 대해서 빠르고 간단하게 정리된 영상 정도만 훑어보고 넘어갔다.
마무리
이번 범위(?)는 달리 어려운 부분은 없었지만 어떤 함수를 구현할지 고민하고, 상용엔진들과 CK소프트렌더러의 코드를 번갈아가면서 살펴보고, 책을 번갈아가면서 탐독하는 과정이 재미있었다. 직접 라이브러리를 구축한다는게 생각보다 정말 재밌는 일인 것 같다. 구현하면 할 수록 즐겁고 과제나 학기작 등 다른 일 보다 이걸 우선적으로 하고 싶은 충동이 들 정도다.
빨리 종강하고 소프트 렌더러 만들기에 집중하고싶다.
'개발일지 > 소프트 렌더러' 카테고리의 다른 글
소프트웨어 렌더러 만들기 - 6 (다양한 크기의 Vector와 Square Matrix) (0) | 2024.02.20 |
---|---|
소프트웨어 렌더러 만들기 - 5 (Custom Assertion) (0) | 2023.11.11 |
소프트웨어 렌더러 만들기 - 3 (스크린 좌표계, 출력 영역 조정) (0) | 2023.10.30 |
소프트웨어 렌더러 만들기 - 2 (Vector2 클래스 및 프로젝트 설정) (0) | 2023.10.26 |
소프트웨어 렌더러 만들기 - 1 (픽셀 찍기, 콘솔창 활성화) (0) | 2023.10.23 |