엔진프로그래밍

UV좌표와 뷰 좌표계

a파이 2020. 6. 24. 22:57

UV 좌표계

UV 좌표계는 3차원 공간에 폴리곤에 텍스쳐를 입히기 위한 기준이 되는 2차원 좌표계입니다.

UV 좌표는 최소0 최대1의 좌표를 가지고 있습니다.

UV 좌표는 0에서 1의 좌표를 가지고 있으며 1을 넘어가거나 0미만이 될 경우 다시 텍스쳐가 반복되어 나옵니다.

마인크래프트에서의 UV좌표

위 텍스쳐는 마인크래프트에서의 캐릭터 텍스쳐입니다.

텍스쳐의 얼굴부분만 프로젝트에서 출력해 보겠습니다.

마인크래프트 스킨텍스쳐의 사이즈는 64px * 64px입니다.

위 텍스쳐에서 얼굴 부분의 UV좌표는 다음과 같습니다.

결과 화면

Source Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
Vertex2 vertices[vertexCount] = {
    Vertex2(Vector2(-squareHalfSize, -squareHalfSize), LinearColor::Red, Vector2(0.125f, 0.75f)),
    Vertex2(Vector2(-squareHalfSize, squareHalfSize), LinearColor::Green, Vector2(0.125f, 0.875f)),
    Vertex2(Vector2(squareHalfSize, squareHalfSize), LinearColor::Blue, Vector2(0.25f, 0.875f)),
    Vertex2(Vector2(squareHalfSize, -squareHalfSize), LinearColor::Magenta, Vector2(0.25f, 0.75f))
};
 
// 화면상의 점 구하기
ScreenPoint lowerLeftPoint = ScreenPoint::ToScreenCoordinate(_ScreenSize, minPos);
ScreenPoint upperRightPoint = ScreenPoint::ToScreenCoordinate(_ScreenSize, maxPos);
for (int x = lowerLeftPoint.X; x <= upperRightPoint.X; ++x)
{
    for (int y = upperRightPoint.Y; y <= lowerLeftPoint.Y; ++y)
    {
        ScreenPoint fragment = ScreenPoint(x, y);
        Vector2 pointToTest = fragment.ToVectorCoordinate(_ScreenSize);
        Vector2 w = pointToTest - triangleVertices[0].Position;
            float dotWU = w.Dot(u);
        float dotWV = w.Dot(v);
        float s = (dotWU * dotVV - dotWV * dotUV) * invDenominator;
        float t = (dotWV * dotUU - dotWU * dotUV) * invDenominator;
        float oneMinusST = 1.- s - t;
        if (((s >= 0.f) && (s <= 1.f)) && ((t >= 0.f) && (t <= 1.f)) && ((oneMinusST >= 0.f) && (oneMinusST <= 1.f)))
        {
            Vector2 fragmentUV = triangleVertices[0].UV * oneMinusST + triangleVertices[1].UV * s + triangleVertices[2].UV * t;
            int x = Math::FloorToInt(fragmentUV.X * textureWidth);
            int y = Math::FloorToInt(fragmentUV.Y * textureHeight);
            x %= textureWidth;
            y %= textureHeight;
 
            LinearColor c = textureBuffer[textureWidth * (textureHeight - (1 + y)) + x];
            _RSI->DrawPoint(fragment, c);
        }
    }
}
cs

뷰 좌표계

뷰 좌표계는 월드를 보고있는 카메라 좌표계입니다.

월드 좌표계에서 (4, 4)에 원이 있고, 카메라는 (12, 8)에 위치해 있다고 할 때,

카메라 좌표계에서 원의 위치는 (-8,4)가 나오게 됩니다.

이를 구하는 방법은 카메라의 이동 행렬의 역행렬에 오브젝트의 위치를 곱해주면 됩니다.

카메라가 회전한 경우도 동일하게 회전 행렬의 역행렬을 곱해주면 됩니다.

회전행렬의 역행렬은 전치행렬이니 다음과 같이 나옵니다.

내적

벡터의 내적은 다음과 같습니다.

내적의 특징

1. 벡터의 내적은 두 벡터로 연산하지만 나오는 값은 스칼라 값이기 때문에 내적은 연속해서 계산할 수 없습니다.

2. a,b 벡터가 평행하면 두 벡터가 이루는 각은 0도 또는 180도가 되기 때문에 코사인 세타는  1또는 -1이됩니다.

3. a,b 벡터가 수직하면 두 벡터가 이루는 각은 90도가 되기 때문에 코사인 세타는 0이 됩니다. 따라서 두 벡터가 수직을 이루면 내적은 0입니다.

4. 벡터의 내적은 교환법칙, 분배법칙이 성립합니다.

내적을 유용하게 사용할 수 있는 상황

1. 쉐이더의 조명 모델에서 Specular의 반사값인 cos값과 sin값을 빠르게 구할 수 있습니다.

2. 두 벡터의 성분을 알고 있다면, 크기를 알 수 있고 내적을 이용해 아크코사인 세타를 통해 구할 수 있습니다.

3. 시야각 내의 적 판별

나의 시야각을 세타라고 하면

정면을 바라고 있는 벡터와 적의 위치에서 나의 위치를 뺀 벡터 간의 내적을 통해 나오는 각도 값이 2분의 세타를 넘지 않아야 시야각 내의 존재한다는 것을 판별할 수 있습니다.