【问题标题】:How to use different fragment shaders in one Metal API scene?如何在一个 Metal API 场景中使用不同的片段着色器?
【发布时间】:2016-08-17 20:36:00
【问题描述】:

我最近一直在用 Apple 的 Metal API 做一些实验,现在我来到了主题问题 - 如何在一个 Metal API 场景中使用不同的片段着色器?有可能吗?

背景:整个几何图元由一个简单的顶点片段链渲染,内部定义/计算颜色(假设我们有一个立方体,它的所有面都使用描述的方法渲染)。接下来,需要用纹理额外渲染图元的一部分(仅向其中一个面添加一些图片)。

我们是否需要使用不同的片段着色器来实现这一点?我想可以在第一步使用一些默认纹理,这将提供一些解决方案。

你会推荐什么?

//============= 编辑的部分更进一步==========//

按照 Warren 的建议,我尝试使用具有两对不同渲染函数的 MTLRenderPipelineState 的两个不同对象。拥有以下代码我没有得到想要的结果。每一个 states 在单独完成时都按照他们应该的方式渲染,但是一起完成只会给我们第一个 one 被渲染。

创作

id <MTLFunction> fragmentProgram = [_defaultLibrary newFunctionWithName:@"color_fragment"];

// Load the vertex program into the library
id <MTLFunction> vertexProgram = [_defaultLibrary newFunctionWithName:@"lighting_vertex"];

// Create a vertex descriptor from the MTKMesh
MTLVertexDescriptor *vertexDescriptor = MTKMetalVertexDescriptorFromModelIO(_boxMesh.vertexDescriptor);
vertexDescriptor.layouts[0].stepRate = 1;
vertexDescriptor.layouts[0].stepFunction = MTLVertexStepFunctionPerVertex;

// Create a reusable pipeline state
MTLRenderPipelineDescriptor *pipelineStateDescriptor = [[MTLRenderPipelineDescriptor alloc] init];
pipelineStateDescriptor.label = @"MyPipeline";
pipelineStateDescriptor.sampleCount = _view.sampleCount;
pipelineStateDescriptor.vertexFunction = vertexProgram;
pipelineStateDescriptor.fragmentFunction = fragmentProgram;
pipelineStateDescriptor.vertexDescriptor = vertexDescriptor;
pipelineStateDescriptor.colorAttachments[0].pixelFormat = _view.colorPixelFormat;
pipelineStateDescriptor.depthAttachmentPixelFormat = _view.depthStencilPixelFormat;
pipelineStateDescriptor.stencilAttachmentPixelFormat = _view.depthStencilPixelFormat;

NSError *error = NULL;
_pipelineStateColor = [_device newRenderPipelineStateWithDescriptor:pipelineStateDescriptor error:&error];
if (!_pipelineStateColor) {
    NSLog(@"Failed to created pipeline state, error %@", error);
}

pipelineStateDescriptor.fragmentFunction = [_defaultLibrary newFunctionWithName:@"lighting_fragment"];
_pipelineStateTexture = [_device newRenderPipelineStateWithDescriptor:pipelineStateDescriptor error:&error];
if (!_pipelineStateTexture) {
    NSLog(@"Failed to created pipeline state, error %@", error);
}

渲染

 - (void)renderInto:(id <MTLRenderCommandEncoder>)renderEncoder
 WithPipelineState:(id<MTLRenderPipelineState>)pipelineState
{
    [renderEncoder setRenderPipelineState:pipelineState];
    [renderEncoder setVertexBuffer:_boxMesh.vertexBuffers[0].buffer offset:_boxMesh.vertexBuffers[0].offset atIndex:0 ];
    [renderEncoder setVertexBuffer:_dynamicConstantBuffer offset:(sizeof(uniforms_t) * _constantDataBufferIndex) atIndex:1 ];
    [renderEncoder setVertexBuffer:_textureBuffer offset:0 atIndex:2];
    [renderEncoder setFragmentTexture:_textureData atIndex:0];

    MTKSubmesh* submesh = _boxMesh.submeshes[0];

    [renderEncoder drawIndexedPrimitives:submesh.primitiveType
                              indexCount:submesh.indexCount
                               indexType:submesh.indexType
                             indexBuffer:submesh.indexBuffer.buffer
                       indexBufferOffset:submesh.indexBuffer.offset];
}

- (void)_render
{
    dispatch_semaphore_wait(_inflight_semaphore, DISPATCH_TIME_FOREVER);

    [self _update];

    id <MTLCommandBuffer> commandBuffer = [_commandQueue commandBuffer];

    __block dispatch_semaphore_t block_sema = _inflight_semaphore;
    [commandBuffer addCompletedHandler:^(id<MTLCommandBuffer> buffer) {
        dispatch_semaphore_signal(block_sema);
    }];

    MTLRenderPassDescriptor* renderPassDescriptor = _view.currentRenderPassDescriptor;

    if(renderPassDescriptor != nil)
    {
        id <MTLRenderCommandEncoder> renderEncoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];

        renderEncoder.label = @"MyRenderEncoder";

        [renderEncoder setDepthStencilState:_depthState];

        [self renderInto:renderEncoder WithPipelineState:_pipelineStateColor];

        [self renderInto:renderEncoder WithPipelineState:_pipelineStateTexture];

        [renderEncoder endEncoding];

        [commandBuffer presentDrawable:_view.currentDrawable];
    }

    _constantDataBufferIndex = (_constantDataBufferIndex + 1) % kMaxInflightBuffers;

    [commandBuffer commit];
}

最后是片段着色器

fragment float4 color_fragment(ColorInOut  in [[stage_in]])
{
    return float4(0.8f, 0.f, 0.1f, 0.5f);
}

fragment float4 texture_fragment(ColorInOut                       in [[stage_in]],
                                 texture2d<float, access::sample> texture [[texture(0)]])
{
    constexpr sampler s(coord::normalized,
                        address::clamp_to_zero,
                        filter::linear);

    return texture.sample(s, in.texture_coordinate);
}

【问题讨论】:

    标签: graphics textures rendering metal


    【解决方案1】:

    您可以通过创建多个渲染管道状态在单个帧/通道中使用多个片段着色器。只需为每个顶点/片段函数对创建管道状态,并在您的渲染命令编码器上调用 setRenderPipelineState: 以在发出绘图调用之前设置适当的管道状态。您将需要编写单独的片段着色器函数来进行颜色传递和纹理采样。

    【讨论】:

    • 感谢沃伦的评论!对我来说,可以使用多个管道状态并不明显......我会尝试然后接受答案:)
    • 我试过了,但似乎我遗漏了一些东西......我用我使用的实际代码扩展了这个问题。如果你看看它,将不胜感激。谢谢
    • 我没有复制粘贴您的代码,但令人惊讶的是,当我自己尝试此操作时,我犯了与您相同的 exact 错误:您的第二个片段函数名为 @ 987654322@,但是您在构建第二个管道时要求名为lighting_fragment 的函数。这将返回nil,并且管道的光栅化将被禁用,导致它不绘制任何东西。如果你使用正确的函数名,一切都应该是花花公子。
    • 不幸的是,事实并非如此......我在编辑问题的那一刻重命名了着色器,所以这个愚蠢的错误只发生在这里。正如我所说的那样——两个状态单独工作,但由于某种原因不能同时工作。
    • 您是否使用每个着色器在同一个地方绘制相同的几何图形?我不希望这会奏效。您只需要在各自的绘制调用中渲染应该受每个着色器影响的几何图形。否则,根据您的深度状态配置,一个总是会覆盖另一个。
    猜你喜欢
    • 1970-01-01
    • 2020-03-29
    • 2019-01-24
    • 2015-08-30
    • 2018-04-02
    • 1970-01-01
    • 2016-05-08
    • 2021-04-21
    • 2021-01-02
    相关资源
    最近更新 更多