Рисовать часть MTLBuffer?

Я рендеринг фрагментов из буфера с этим вызовом:

renderEncoder.drawPrimitives(type: .point,
                             vertexStart: 0,
                             vertexCount: 1,
                             instanceCount: emitter.currentParticles)

emitter.currentParticles общее количество частиц в буфере. Можно ли как-то нарисовать только часть буфера?

Я пробовал это, но он рисует первую половину буфера:

renderEncoder.drawPrimitives(type: .point,
                             vertexStart: emitter.currentParticles / 2,
                             vertexCount: 1,
                             instanceCount: emitter.currentParticles / 2)

На самом деле, кажется, что vertexStart не имеет никакого эффекта Кажется, я могу установить любое значение, и оно все еще начинается с 0.

Редактировать:

Конфигурация трубопровода:

private func buildParticlePipelineStates() {
    do {
        guard let library = Renderer.device.makeDefaultLibrary(),
        let function = library.makeFunction(name: "compute") else { return }

        // particle update pipeline state
        particlesPipelineState = try Renderer.device.makeComputePipelineState(function: function)

        // render pipeline state
        let vertexFunction = library.makeFunction(name: "vertex_particle")
        let fragmentFunction = library.makeFunction(name: "fragment_particle")
        let descriptor = MTLRenderPipelineDescriptor()
        descriptor.vertexFunction = vertexFunction
        descriptor.fragmentFunction = fragmentFunction

        descriptor.colorAttachments[0].pixelFormat = renderPixelFormat
        descriptor.colorAttachments[0].isBlendingEnabled = true
        descriptor.colorAttachments[0].rgbBlendOperation = .add
        descriptor.colorAttachments[0].alphaBlendOperation = .add
        descriptor.colorAttachments[0].sourceRGBBlendFactor = .sourceAlpha
        descriptor.colorAttachments[0].sourceAlphaBlendFactor = .sourceAlpha
        descriptor.colorAttachments[0].destinationRGBBlendFactor = .oneMinusSourceAlpha
        descriptor.colorAttachments[0].destinationAlphaBlendFactor = .oneMinusSourceAlpha

        renderPipelineState = try
        Renderer.device.makeRenderPipelineState(descriptor: descriptor)
        renderPipelineState = try Renderer.device.makeRenderPipelineState(descriptor: descriptor)
    } catch let error {
        print(error.localizedDescription)
    }
}

Вершинный шейдер:

struct VertexOut {
    float4 position   [[ position ]];
    float  point_size [[ point_size ]];
    float4 color;
};


vertex VertexOut vertex_particle(constant float2 &size [[buffer(0)]],
                             device Particle *particles [[buffer(1)]],
                             constant float2 &emitterPosition [[ buffer(2) ]],
                             uint instance [[instance_id]])
{
    VertexOut out;

    float2 position = particles[instance].position + emitterPosition;
    out.position.xy = position.xy / size * 2.0 - 1.0;
    out.position.z = 0;
    out.position.w = 1;
    out.point_size = particles[instance].size * particles[instance].scale;
    out.color = particles[instance].color;
    return out;
}

fragment float4 fragment_particle(VertexOut in [[ stage_in ]],
                              texture2d<float> particleTexture [[ texture(0) ]],
                              float2 point [[ point_coord ]]) {
    constexpr sampler default_sampler;

    float4 color = particleTexture.sample(default_sampler, point);

    if ((color.a < 0.01) || (in.color.a < 0.01)) {
        discard_fragment();
    }
    color = float4(in.color.xyz, 0.2 * color.a * in.color.a);
    return color;
}

1 ответ

Решение

Вы не используете дескриптор вершины, ни [[stage_in]] параметр для вашего вершинного шейдера. Итак, Metal не выбирает / собирает данные вершин для вас. Вы просто индексируете в буфер, который размечен вашими данными вершин уже в нужном вам формате. Все в порядке. Смотрите мой ответ здесь для получения дополнительной информации о дескрипторе вершины.

Учитывая, что, тем не менее, vertexStart Параметр вызова draw влияет только на значение параметра вашей вершинной функции с [[vertex_id]] приписывать. Ваша вершинная функция не имеет такого параметра, не говоря уже о его использовании. Вместо этого он использует [[instance_id]] параметр для индексации в буфере данных вершин. Вы можете прочитать еще один из моих ответов здесь для быстрого ознакомления с вызовами отрисовки и с тем, как они приводят к вызовам вашей функции вершинного шейдера.

Есть несколько способов изменить положение вещей, чтобы нарисовать только половину точек. Вы можете изменить используемый вами розыгрыш:

renderEncoder.drawPrimitives(type: .point,
                             vertexStart: 0,
                             vertexCount: 1,
                             instanceCount: emitter.currentParticles / 2,
                             baseInstance: emitter.currentParticles / 2)

Это не потребует никаких изменений в вершинном шейдере. Это просто изменяет диапазон значений, подаваемых на instance параметр. Однако, поскольку это не похоже на случай инстансинга, я рекомендую вам изменить шейдер и ваш вызов отрисовки. Для шейдера переименуйте instance параметр для vertex или же vid и измените его атрибут с [[instance_id]] в [[vertex_id]], Затем измените вызов ничьей на:

renderEncoder.drawPrimitives(type: .point,
                             vertexStart: emitter.currentParticles / 2,
                             vertexCount: emitter.currentParticles / 2)

По правде говоря, они в основном ведут себя одинаково в этом случае, но последний лучше отражает то, что вы делаете (и вызов отрисовки проще, что приятно).

Другие вопросы по тегам