Metal vertex shader draw points of a Texture

牧云@^-^@ 提交于 2019-12-24 11:01:30

问题


I want to execute Metal (or OpenGLES 3.0) shader that draws Points primitive with blending. To do that, I need to pass all the pixel coordinates of the texture to Vertex shader as vertices which computes the position of the vertex to be passed to fragment shader. The fragment shader simply outputs the color for the point with blending enabled. My problem is if there is an efficient was to pass coordinates of vertices to the vertex shader, since there would be too many vertices for 1920x1080 image, and that needs to be done 30 times in a second? Like we do in a compute shader by using dispatchThreadgroups command, except that compute shader can not draw a geometry with blending enabled.

EDIT: This is what I did -

  let vertexFunctionRed = library!.makeFunction(name: "vertexShaderHistogramBlenderRed")

    let fragmentFunctionAccumulator = library!.makeFunction(name: "fragmentShaderHistogramAccumulator")


    let renderPipelineDescriptorRed = MTLRenderPipelineDescriptor()
    renderPipelineDescriptorRed.vertexFunction = vertexFunctionRed
    renderPipelineDescriptorRed.fragmentFunction = fragmentFunctionAccumulator
    renderPipelineDescriptorRed.colorAttachments[0].pixelFormat = .bgra8Unorm
    renderPipelineDescriptorRed.colorAttachments[0].isBlendingEnabled = true
    renderPipelineDescriptorRed.colorAttachments[0].rgbBlendOperation = .add
    renderPipelineDescriptorRed.colorAttachments[0].sourceRGBBlendFactor = .one
    renderPipelineDescriptorRed.colorAttachments[0].destinationRGBBlendFactor = .one

    do {
        histogramPipelineRed = try device.makeRenderPipelineState(descriptor: renderPipelineDescriptorRed)
    } catch {
        print("Unable to compile render pipeline state Histogram Red!")
        return
    }

Drawing code:

  let commandBuffer = commandQueue?.makeCommandBuffer()
        let renderEncoder = commandBuffer?.makeRenderCommandEncoder(descriptor: renderPassDescriptor!)
        renderEncoder?.setRenderPipelineState(histogramPipelineRed!)
        renderEncoder?.setVertexTexture(metalTexture, index: 0)
        renderEncoder?.drawPrimitives(type: .point, vertexStart: 0, vertexCount: 1, instanceCount: metalTexture!.width*metalTexture!.height)
  renderEncoder?.drawPrimitives(type: .point, vertexStart: 0, vertexCount: metalTexture!.width*metalTexture!.height, instanceCount: 1)

and Shaders:

  vertex MappedVertex vertexShaderHistogramBlenderRed (texture2d<float, access::sample> inputTexture [[ texture(0) ]],
                                                 unsigned int vertexId [[vertex_id]])
  {
        MappedVertex out;

constexpr sampler s(s_address::clamp_to_edge, t_address::clamp_to_edge, min_filter::linear, mag_filter::linear, coord::pixel);

ushort width = inputTexture.get_width();
ushort height = inputTexture.get_height();

float X = (vertexId % width)/(1.0*width);
float Y = (vertexId/width)/(1.0*height);

 int red = inputTexture.sample(s, float2(X,Y)).r;

 out.position = float4(-1.0 + (red * 0.0078125), 0.0, 0.0, 1.0);
 out.pointSize = 1.0;
 out.colorFactor = half3(1.0, 0.0, 0.0);

 return out;
 }

   fragment half4 fragmentShaderHistogramAccumulator ( MappedVertex in [[ stage_in ]]
                                              )
 {
    half3 colorFactor = in.colorFactor;
    return half4(colorFactor*(1.0/256.0), 1.0); 
}

回答1:


Maybe you can draw a single point instanced 1920x1080 times. Something like:

vertex float4 my_func(texture2d<float, access::read> image [[texture(0)]],
                      constant uint &width [[buffer(0)]],
                      uint instance_id [[instance_id]])
{
    // decompose the instance ID to a position
    uint2 pos = uint2(instance_id % width, instance_id / width);
    return float4(image.read(pos).r * 255, 0, 0, 0);
}


来源:https://stackoverflow.com/questions/50504961/metal-vertex-shader-draw-points-of-a-texture

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!