momodudu.zip
Metal 스터디(7) - Matrix 적용해보기 ( Projection Transform ) 본문
이전의 Model Transform이 매쉬를 이루는 vertex들을 Local Cooridnate -> World Coordinate로 바꾸는 변환이었다면, Projection Transform은 camera coordinate -> normalized coordinate로 바꾸어주는 변환이다.
projection type에는 두가지가 있는데, orthogonal과 perspective 변환이 있다. orthogonal은 말그대로 직교 변환이고, perspective는 원근법을 구사한 변환이다.
앞전에 생성했던 Cube에 perspective projection을 적용해보자.
var matrix: Matrix4
var projectionMatrix: Matrix4
projectionMatrix를 저장해둘 변수를 하나 만들고,
// uniform buffer 고정길이이므로, 미리 생성만 해둔다.
uniformBuffer = device.makeBuffer(length: MemoryLayout<Float>.size * Matrix4.numberOfElements() * 2, options: [])!
앞전에 생성했던 uniform buffer를 생성할때 길이를 * 2를 해준다.
왜냐하면.. 이 유니폼 버퍼에 앞 포스팅에서 생성했던 model transform matrix와 projection matrix를 함께 넣어줄것이기 때문이다.
그래서 uniform buffer를 반환할때도
public var uniformBufferData: MTLBuffer {
get {
let bufferPointer = uniformBuffer.contents()
let size = MemoryLayout<Float>.size * Matrix4.numberOfElements()
memcpy(bufferPointer, matrix.raw(), size * 2)
memcpy(bufferPointer + size, projectionMatrix.raw(), size)
return uniformBuffer
}
}
이렇게 두번 copy가 필요하다.
최초로는 model transformation, 그 뒤에 바로 붙여서 projection matrix를 카피한다.
그리고 쉐이더도 살짝 수정해주자.
struct Uniforms {
float4x4 modelMatrix; // 매트릭스를 전달받을 타입 선언
float4x4 projMatrix; // projection Matrix
};
vertex VertexOut vertex_main(
const device VertexIn* vertex_array [[buffer(0)]],
const device Uniforms& uniforms [[buffer(1)]],
unsigned int vid [[vertex_id]] ) {
float4x4 mvMat = uniforms.modelMatrix;
float4x4 projMat = uniforms.projMatrix;
VertexIn vertexIn = vertex_array[vid];
VertexOut vertexOut;
vertexOut.position = projMat * mvMat * float4(vertexIn.position, 1.0);
vertexOut.color = vertexIn.color;
return vertexOut;
}
Uniforms 구조체에 projection matrix가 추가됐고, vertexOut에도 projection 매트릭스를 곱해준다.
이렇게하면 projection을 구현할 준비가 완료됐다.
extension Renderable {
public func translate(x: Float, y: Float, z: Float) {
matrix.translate(x, y: y, z: z)
}
public func rotate(x: Float, y: Float, z: Float) {
matrix.rotateAroundX(x, y: y, z: z)
}
public func scale(_ scale: Float) {
matrix.scale(scale, y: scale, z: scale)
}
public func makePerspectiveViewAngle(degree: Float, viewRatio: Float, nearZ: Float, farZ: Float) {
projectionMatrix = Matrix4.makePerspectiveViewAngle(Matrix4.degrees(toRad: degree), aspectRatio: viewRatio, nearZ: nearZ, farZ: farZ)
}
}
그리고 마지막으로 Renderable에 perspective view를 생성하는 함수도 추가해준다.
인자에 해당하는 persepctive view를 생성하는 함수인데, degree는 fovy를 degree로 받고, viewRatio는 aspectRatio 즉 뷰의 종횡비를 넣어주면 된다. 마지막 nearZ와 farZ는 projection plan의 Z값을 설정한다.
이렇게 작성을 완료했으면, 큐브를 여러가지 변환을 이용해서 표현을 해볼건데
renderable?.translate(x: 0.0, y: 0.0, z: -2.0)
renderable?.rotate(x: 0.0, y: 0.0, z: Matrix4.degrees(toRad: 45))
renderable?.scale(0.5)
renderable?.makePerspectiveViewAngle(degree: 30, viewRatio: viewRatio, nearZ: 0.01, farZ: 100.0)
위와 같이 변환이 적용되었을 때 결과값이 어떻게 될지 예측해보자.
먼저, 큐브의 좌표들은 아래와 같다.
let A = Vertex(x: -1.0, y: 1.0, z: 1.0, color: NSColor.red)
let B = Vertex(x: -1.0, y: -1.0, z: 1.0, color: NSColor.green)
let C = Vertex(x: 1.0, y: -1.0, z: 1.0, color: NSColor.blue)
let D = Vertex(x: 1.0, y: 1.0, z: 1.0, color: NSColor.brown)
let Q = Vertex(x: -1.0, y: 1.0, z: -1.0, color: NSColor.red)
let R = Vertex(x: 1.0, y: 1.0, z: -1.0, color: NSColor.green)
let S = Vertex(x: -1.0, y: -1.0, z: -1.0, color: NSColor.blue)
let T = Vertex(x: 1.0, y: -1.0, z: -1.0, color: NSColor.blue)
이걸 그림으로 표현해보면 아래와 같다. 알기 쉽게 그림으로 표현한것이지만, 실제로는 perspective projection을 할 것이므로 앞면은 작게, 뒷면은 더 크게 나온다.
먼저 전체 점이 z축으로 -2만큼 이동변환을 했으니 뒤로 간다. 이걸 뒤로 이동시키는 이유는...
정의한 projection matrix는 먼저 near가 0.1 이고 far가 100이다. 카메라의 z값이 0에 위치해있고, projection의 view frustum은 -Z축을 향하고 있다. 그래서 저 좌표 그대로 transformation없이 z가 [1,0~-1.0]인걸 띄워버리면, view frustum영역 밖으로 벗어나게 되어버린다.
그래서 z축으로 -2.0만큼 이동시켜준것이다. 뷰 프러스텀 내에 큐브가 들어올 수 있도록..
그리고 z축에 대해 45도만큼 돌린다.
그래서 45도만큼 기울어진 , 정면에서 봣을 때 마름모와 비슷한 형태를 가지게 되고, scaling도 곱해줬으니 사이즈가 좀 더 작아진다.
projection matrix는 fovy가 degree로 85.0, nearZ = 0.1, farZ = 100.0인 projection 매트릭스가 완성되어 결과물은 아래와 같이 표현된다.
응? 큐브가 아닌데..? 싶겠지만 원근법이 적용되었고, 큐브 내부가 안그릴부분은 안그려서 모양이 조금 이상하다.
'ios > Metal' 카테고리의 다른 글
Metal 스터디 (8) - View Transformation, Viewport transformation (0) | 2022.03.19 |
---|---|
Metal 스터디(6) - matrix 적용해보기 ( Model Transformation ) (0) | 2022.02.10 |
Metal 스터디(5) - MSL에서 packed_float 과 non-packed_float의 차이 (0) | 2022.02.09 |
Metal 스터디 (4) - MSL (0) | 2022.02.08 |
Metal 스터디 (3) - Rendering Pipeline (0) | 2021.11.26 |