#ios #swift #graphics #metal
#iOS #swift #графика #Металлические
Вопрос:
Я новичок в Swift и Metal, и я пытаюсь создать простой движок рендеринга для iOS только для учебных целей. Итак, я застрял в рисовании простого эллипса с использованием функции fragment shader. На самом деле я делаю рендеринг квадрата, передаю нормализованные координаты устройства верхней левой и нижней правой вершин этого квадрата и, наконец, в шейдере фрагментов вычисляю, находится ли каждый фрагмент внутри эллипса. Я уже делал это в OpenGL, и все работало нормально. Я не уверен, что это правильный способ визуализации эллипса, но этот подход, похоже, не работает в Metal.
Итак, вот функции шейдеров:
#include <metal_stdlib>
using namespace metal;
struct EllipseIn {
float4 position [[attribute(0)]];
float4 color [[attribute(1)]];
float3 tlPosition [[attribute(2)]];
float3 brPosition [[attribute(3)]];
};
struct EllipseOut {
float4 position [[position]];
float4 color;
float3 tlPosition [[flat]];
float3 brPosition [[flat]];
};
vertex EllipseOut ellipse_vertex(const EllipseIn ellipseIn [[stage_in]]) {
EllipseOut ellipseOut;
ellipseOut.position = ellipseIn.position;
ellipseOut.color = ellipseIn.color;
ellipseOut.tlPosition = ellipseIn.tlPosition;
ellipseOut.brPosition = ellipseIn.brPosition;
return ellipseOut;
}
fragment float4 ellipse_fragment(EllipseOut ellipseIn [[stage_in]]) {
float3 t = (ellipseIn.brPosition - ellipseIn.tlPosition) / 2;
float a = t.x;
float b = t.y;
float x = ellipseIn.position.x;
float y = ellipseIn.position.y;
float xc = ellipseIn.tlPosition.x a;
float yc = ellipseIn.tlPosition.y b;
float x2 = x - xc;
float y2 = y - yc;
float c = x2 / a;
float d = y2 / b;
float res = c * c d * d;
if (res > 1)
discard_fragment();
return ellipseIn.color;
}
И вот код Swift:
import Foundation
import MetalKit
struct EllipseVertexDescriptor {
var position: SIMD4<Float>
var color: SIMD4<Float>
var tlPosition: SIMD3<Float>
var brPosition: SIMD3<Float>
}
class EllipseRenderer : PrimitiveRenderer {
private var pipelineState: MTLRenderPipelineState!
func preparePipelineState(device: MTLDevice, library: MTLLibrary) {
let ellipseVertexFunction = library.makeFunction(name: "ellipse_vertex")
let ellipseFragmentFunction = library.makeFunction(name: "ellipse_fragment")
let ellipsePipelineDescriptor = MTLRenderPipelineDescriptor()
ellipsePipelineDescriptor.vertexFunction = ellipseVertexFunction
ellipsePipelineDescriptor.fragmentFunction = ellipseFragmentFunction
ellipsePipelineDescriptor.colorAttachments[0].pixelFormat = .bgra8Unorm
ellipsePipelineDescriptor.colorAttachments[0].isBlendingEnabled = true
let ellipseVertexDescriptor = MTLVertexDescriptor()
ellipseVertexDescriptor.attributes[0].format = .float4
ellipseVertexDescriptor.attributes[0].offset = 0
ellipseVertexDescriptor.attributes[0].bufferIndex = 0
ellipseVertexDescriptor.attributes[1].format = .float4
ellipseVertexDescriptor.attributes[1].offset = MemoryLayout<SIMD4<Float>>.stride
ellipseVertexDescriptor.attributes[1].bufferIndex = 0
ellipseVertexDescriptor.attributes[2].format = .float3
ellipseVertexDescriptor.attributes[2].offset = MemoryLayout<SIMD4<Float>>.stride MemoryLayout<SIMD4<Float>>.stride
ellipseVertexDescriptor.attributes[2].bufferIndex = 0
ellipseVertexDescriptor.attributes[3].format = .float3
ellipseVertexDescriptor.attributes[3].offset = MemoryLayout<SIMD4<Float>>.stride MemoryLayout<SIMD4<Float>>.stride MemoryLayout<SIMD3<Float>>.stride
ellipseVertexDescriptor.attributes[3].bufferIndex = 0
ellipseVertexDescriptor.layouts[0].stride = MemoryLayout<EllipseVertexDescriptor>.stride
ellipsePipelineDescriptor.vertexDescriptor = ellipseVertexDescriptor
pipelineState = try! device.makeRenderPipelineState(descriptor: ellipsePipelineDescriptor)
}
func render(device: MTLDevice, encoder: MTLRenderCommandEncoder, primitive: MetalPrimitive) {
var mp = primitive
guard let gp = mp.data as? CliGeometryPrimitiveData,
let tlp = gp.tlPositionN, //top left position in normalized device coordinates
let brp = gp.brPositionN, //bottom right position in normalized device coordinates
let fillColor = gp.fillColorN else {return}
if (mp.redraw) {
let vertices: [EllipseVertexDescriptor] = [
EllipseVertexDescriptor(
position: SIMD4<Float>(tlp.x, tlp.y, 0, 1),
color: SIMD4<Float>(fillColor.x, fillColor.y, fillColor.z, fillColor.w),
tlPosition: SIMD3<Float>(tlp.x, tlp.y, 0),
brPosition: SIMD3<Float>(brp.x, brp.y, 0)
),
EllipseVertexDescriptor(
position: SIMD4<Float>(tlp.x, brp.y, 0, 1),
color: SIMD4<Float>(fillColor.x, fillColor.y, fillColor.z, fillColor.w),
tlPosition: SIMD3<Float>(tlp.x, tlp.y, 0),
brPosition: SIMD3<Float>(brp.x, brp.y, 0)
),
EllipseVertexDescriptor(
position: SIMD4<Float>(brp.x, brp.y, 0, 1),
color: SIMD4<Float>(fillColor.x, fillColor.y, fillColor.z, fillColor.w),
tlPosition: SIMD3<Float>(tlp.x, tlp.y, 0),
brPosition: SIMD3<Float>(brp.x, brp.y, 0)
),
EllipseVertexDescriptor(
position: SIMD4<Float>(brp.x, tlp.y, 0, 1),
color: SIMD4<Float>(fillColor.x, fillColor.y, fillColor.z, fillColor.w),
tlPosition: SIMD3<Float>(tlp.x, tlp.y, 0),
brPosition: SIMD3<Float>(brp.x, brp.y, 0)
),
]
let indicies: [UInt16] = [
0, 1, 2,
2, 3, 0,
]
mp.vertexBuffer = device.makeBuffer(bytes: vertices, length: vertices.count * MemoryLayout<EllipseVertexDescriptor>.stride, options: [])
mp.indexBuffer = device.makeBuffer(bytes: indicies, length: indicies.count * MemoryLayout<UInt16>.size, options: [])
}
encoder.setRenderPipelineState(pipelineState)
encoder.setVertexBuffer(mp.vertexBuffer!, offset: 0, index: 0)
encoder.drawIndexedPrimitives(type: .triangle, indexCount: 6, indexType: .uint16, indexBuffer: mp.indexBuffer!, indexBufferOffset: 0)
}
}
Результатом приведенного выше кода является то, что все фрагменты отбрасываются.
Чего мне не хватает? Любая помощь будет оценена.