Выровненный по экрану квадрат в пространстве клипов рисуется поверх более близких элементов

#scenekit #metal

#scenekit #Металлические

Вопрос:

У меня есть текстурированный квадрат, который нужно нарисовать в screenspace, размер / выравнивание по экранному экрану, в SceneKit сцене на дальней плоскости — думайте об этом как о текстурированном квадрате для заливки фона.

Я вручную создал четырехугольник с 4 вершинами в пространстве клипов и написал шейдер вершин / фрагментов для его рисования. Вершины установлены на z = 1, что должно поместить их в дальнюю плоскость.

Однако, хотя квадрат правильно отображается, как и ожидалось, в пространстве экрана (например, он правильно выровнен), он рисуется поверх всего содержимого сцены — при z = 1 для вершин пространства клипов он должен быть позади всего.

Я смог обойти это, установив низкое renderingOrder значение и отключив запись глубины, но это не исправление, это хак.

 // build the plane vertices in clip space
let vertices: [SCNVector3] = [
    SCNVector3(-1, -1,  1),
    SCNVector3( 1, -1,  1),
    SCNVector3( 1,  1,  1),
    SCNVector3(-1,  1,  1),
]

let texCoords: [CGPoint] = [
    CGPoint(x: 0, y: 1),
    CGPoint(x: 1, y: 1),
    CGPoint(x: 1, y: 0),
    CGPoint(x: 0, y: 0),
]

let indices: [UInt16] = [
    0,1,2,
    0,2,3,
]

let vertexSource = SCNGeometrySource(vertices: vertices)
let texCoordSource = SCNGeometrySource(textureCoordinates: texCoords)
let elementSource = SCNGeometryElement(indices: indices, primitiveType: .triangles)
let planeGeometry = SCNGeometry(sources: [vertexSource, texCoordSource], elements: [elementSource])

let program = SCNProgram()
program.library = ShaderUtilities.metalLibrary
program.vertexFunctionName = "plane_vertex"
program.fragmentFunctionName = "plane_fragment"
                
if let material = planeGeometry.firstMaterial {
    material.program = program
    material.setValue(SCNMaterialProperty(contents: backdropImage as Any), forKey: "backgroundTexture")
}

let planeNode = SCNNode(geometry: planeGeometry)
meshCameraNode.addChildNode(planeNode)
 

Соответствующие металлические шейдеры:

 struct PlaneVertexIn {
    float3 position [[attribute(SCNVertexSemanticPosition)]];
    float2 uv [[attribute(SCNVertexSemanticTexcoord0)]];
};

struct PlaneVertexOut {
    float4 position [[position]];
    float2 uv;
};

vertex PlaneVertexOut plane_vertex(PlaneVertexIn in [[ stage_in ]],
                                            constant SCNSceneBufferamp; scn_frame [[buffer(0)]],
                                            constant NodeBufferamp; scn_node [[buffer(1)]]) {
    PlaneVertexOut out;
    // the vertices are already in clip space to form a screen-aligned quad, so no need to apply a transform.
    out.position = float4(in.position.x, in.position.y, in.position.z, 1.0);
    out.uv = in.uv;
    return out;
}

fragment float4 plane_fragment(PlaneVertexOut out [[ stage_in ]],
                               constant SCNSceneBufferamp; scn_frame [[buffer(0)]],
                               texture2d<float, access::sample> backgroundTexture [[texture(0)]]) {
    constexpr sampler textureSampler(coord::normalized, filter::linear, address::repeat);
    return backgroundTexture.sample(textureSampler, out.uv);
}