Я глубоко погрузился в изучение Metal, имея очень мало знаний о мире рендеринга в целом, поэтому извините за потенциально новый вопрос.
У меня есть следующее:
- Целевая текстура произвольного размера, определенного пользователем.
- Текстура, полученная из изображения, предоставленного пользователем.
- Масштаб, перемещение и вращение, предоставляемые пользователем
0.0 ... 1.0
Я хотел бы поставить изображение на текстуре.У меня это работает на 90%, но, конечно, меня сбивают с толку матрицы.Я делаю следующее:
- Вычисление матрицы масштабирования для приведения текстуры изображения в целевую текстуру с правильным соотношением сторон.
- Настройка матрицы масштабирования, чтобы также принять вдля учета дополнительной шкалы, предоставленной пользователем.
- Расчет матрицы перемещения и поворота на основе предоставленных пользователем значений.
- Применение матриц масштабирования, поворота и перевода для установки штампа "на место""через вершинный шейдер.
- 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;
}