본문 바로가기

DX

조명, 노멀 벡터 변환 (GLM)

GPU의 장점은 단순한 작업을 여러 개 많이 할 때 빠르다는 것이다. CPU 에서 GPU 로 값을 보내는 것이 느리니 가급적 단순하게 정리 되어있는 데이터를 보내줘야한다.

 

실제 게임에서의 캐릭터 움직임은 굉장히 복잡하다. Transformation 이 복잡하게 shader 에서 사용되어지면 그때는 오히려 gpu 의 장점이 사라지기 때문에 가장 간단한 하나의 행렬 형태 즉, 모든 움직임을 조합한 modelMatrix 를 이용해 shader 에서 작업하도록 만드는 실습을 한다.

// 쉐이더에서 공통적으로 사용하는 상수들
// 예) 모든 버텍스들을 같은 비율로 스케일
struct Constants {

    // GPU로 Transformation 대신에 matrix 하나만 보냅니다.
    // Transformation transformation;
    mat4 modelMatrix;  // worldMatrix라고 부르기도 합니다.
    mat4 invTranspose; // Normal에 적용합니다.
    // 그 외에 viewMatrix, projectionMatrix 등도 사용합니다.

    Material material;
    Light light;
    int lightType = 0;
} constants;

 

 

 

레스터라이저 단계에서 행렬로 srt 를 표현한 방식이다. 다만, column major 니까 trs 순으로 행렬을 곱해준다.

 

normal 을 구할 때는 연산이 좀 다른데 이유는 아래 그림과 같이 설명한다.

인덱스 3번을 vec4 로 초기화 하는 것은 수치 에러가 나기 때문에 필연적으로 초기화한다. 이유는 normal 은 이동이 필요 없기 때문이다.

 // 여기서 GPU에게 보내줄 변환 행렬을 만들어줘야 합니다.
 // 순서 주의 (GLM은 column major 사용)
 constants.modelMatrix = glm::translate(mesh->transformation.translation)*
     glm::rotate(mesh->transformation.rotationX, vec3(1.f,0,0))*
     glm::rotate(mesh->transformation.rotationY, vec3(0,1.f,0))*
     glm::scale(mesh->transformation.scale);

 // Non-uniform scale인 경우에만 필요
 constants.invTranspose = constants.modelMatrix;
 constants.invTranspose[3] = vec4(0,0,0,1);
 constants.invTranspose = glm::inverseTranspose(constants.invTranspose);

 

 

 

역행렬의 전치해서 곱해주면 된다. 이유는 스케일링 시에는 아래 그림과 같은 문제가 생길 수 있기 때문에 그냥 일반적인 srt 를 normal 에 적용해서는 안되는 것이다. 따라서 srt 의 역행렬의 전치 행렬을 normal 에 곱해줘야한다.

 

 

 

 

 

이제 쉐이더 쪽 코드를 보면

 

vec3 를 받고 vec4 로 만들어준다. 이유는 cpu 에서 gpu 데이터 보내는게 느리니 vec3 로 보낸다음 간단하게 vec4 로 gpu 에서 만들어주어 속도를 조금이라도 올리는 것이다.

// 마지막에 1.0f 추가
vec4 point =
    vec4(vsInput.position.x, vsInput.position.y, vsInput.position.z, 1.0f);

 

 

이 코드에서 봐야할것은 2가지인데 첫째 Column major 라서 point 연산이 뒤에 있다.

둘 째 물체의 srt 적용 시 normal 은 별도로 적용해줘야한다. 다만 아래에서는 별도로 적용하고 온걸 가져온거임!

// 여기서 여러가지 변환 가능
// vsOutput.position =
//    RotateAboutX(
//        RotateAboutY(vsInput.position * constants.transformation.scale,
//                     constants.transformation.rotationY),
//        constants.transformation.rotationX) +
//    constants.transformation.translation;

// 마지막에 1.0f 추가
vec4 point =
    vec4(vsInput.position.x, vsInput.position.y, vsInput.position.z, 1.0f);

point = constants.modelMatrix * point; // 주의: column-major

vsOutput.position = vec3(point.x, point.y, point.z);

// 주의: 노멀 벡터도 물체와 같이 회전시켜줘야 합니다.
// 더 편하게 구현할 방법은 없을까요?
// vsOutput.normal = RotateAboutX(
//    RotateAboutX(
//        RotateAboutY(vsInput.normal, constants.transformation.rotationY),
//        constants.transformation.rotationX),
//    constants.transformation.rotationX);

// 마지막에 0.0f 추가
vec4 normal =
    vec4(vsInput.normal.x, vsInput.normal.y, vsInput.normal.z, 0.0f);
// Unon-uniform transformation인 경우에는 보정 필요
normal = glm::normalize(constants.invTranspose * normal); // 주의: column-major

'DX' 카테고리의 다른 글

COM 과 D3D  (0) 2025.06.15
DirectX Math  (0) 2025.06.06
GLM  (0) 2025.06.06
좌표계 변환  (0) 2025.05.11
Affine Transformation  (0) 2025.05.11