2D рендеринг, вращение и соотношение сторон металла - PullRequest
0 голосов
/ 11 сентября 2018

Я глубоко погрузился в изучение Metal, имея очень мало знаний о мире рендеринга в целом, поэтому извините за потенциально новый вопрос.

У меня есть следующее:

  1. Целевая текстура произвольного размера, определенного пользователем.
  2. Текстура, полученная из изображения, предоставленного пользователем.
  3. Масштаб, перемещение и вращение, предоставляемые пользователем 0.0 ... 1.0

Я хотел бы поставить изображение на текстуре.У меня это работает на 90%, но, конечно, меня сбивают с толку матрицы.Я делаю следующее:

  1. Вычисление матрицы масштабирования для приведения текстуры изображения в целевую текстуру с правильным соотношением сторон.
  2. Настройка матрицы масштабирования, чтобы также принять вдля учета дополнительной шкалы, предоставленной пользователем.
  3. Расчет матрицы перемещения и поворота на основе предоставленных пользователем значений.
  4. Применение матриц масштабирования, поворота и перевода для установки штампа "на место""через вершинный шейдер.
  5. Render!

Конечно, это прекрасно работает, если целевая текстура имеет квадратную форму, но когда это не так, поворот вызывает искажение изображения.Я понимаю, почему это происходит, я просто не понимаю правильные матрицы, которые могли бы это исправить, или я просто делаю это совершенно неправильно.


Код рендеринга:

let renderPassDescriptor = MTLRenderPassDescriptor()
renderPassDescriptor.colorAttachments[0].texture = drawable.texture
renderPassDescriptor.colorAttachments[0].loadAction = .clear
renderPassDescriptor.colorAttachments[0].clearColor = clearColor
renderPassDescriptor.colorAttachments[0].storeAction = .store

let commandBuffer = commandQueue.makeCommandBuffer()!
let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptor)!

let vertices: [AspectVertex] = [
    AspectVertex(position: float2(-1.0,  1.0), texturePosition: float2(0.0, 0.0)),
    AspectVertex(position: float2( 1.0,  1.0), texturePosition: float2(1.0, 0.0)),
    AspectVertex(position: float2(-1.0, -1.0), texturePosition: float2(0.0, 1.0)),
    AspectVertex(position: float2( 1.0, -1.0), texturePosition: float2(1.0, 1.0))
]

let vertexBuffer = metalDevice.makeBuffer(bytes: vertices, length: MemoryLayout<AspectVertex>.size * vertices.count, options: [])!

let availableSize = sceneSize * userScale

let delta = availableSize / imageSize
let factor = delta.min()!

let scaledSize = imageSize * factor
let normalizedSize = scaledSize / sceneSize

var vertexUniform = AspectVertexUniform(
    rotation: float4x4.zRotation(by: Float.pi * rotation),
    scaling: float4x4.scale(by: normalizedSize.x, y: normalizedSize.y, z: 1.0),
)

let vertexUniformBuffer = metalDevice.makeBuffer(bytes: &vertexUniform, length: MemoryLayout<AspectVertexUniform>.size, options: [])!

encoder.setRenderPipelineState(pipelineState)
encoder.setVertexBuffer(vertexBuffer, offset: 0, index: 0)
encoder.setVertexBuffer(vertexUniformBuffer, offset: 0, index: 1)
encoder.setFragmentTexture(texture, index: 0)
encoder.drawPrimitives(type: .triangleStrip, vertexStart: 0, vertexCount: 4)

renderEncoder.endEncoding()

commandBuffer.present(drawable)
commandBuffer.commit()

Вершинный шейдер: (перевод пока отсутствует)

vertex AspectVertexOut aspect_vertex(const device AspectVertexIn* vertices [[ buffer(0) ]],
                                     const device AspectVertexUniform& uniform [[ buffer(1) ]],
                                     unsigned int vid [[ vertex_id ]])
{
    AspectVertexIn vertexIn = vertices[vid];

    AspectVertexOut vertexOut;
    vertexOut.position = uniform.rotationMatrix * uniform.scalingMatrix * float4(vertexIn.position, 0.0, 1.0);
    vertexOut.texturePosition = vertexIn.texturePosition;

    return vertexOut;
}
...