如何解决金属反馈回路伪影
您好,我正在尝试为我正在开发的应用程序创建一些金属反馈循环。但是我得到了一些奇怪的文物。我怀疑这是由于我的管道中的某些东西,或者我如何对前一帧进行采样。在代码中,为第二个缓冲区设置了额外的管道,因为最终我将需要它来制作图灵模式。但是现在我已经尽可能地减少了代码以隔离问题。
现在它只是在左上角画一个小圆圈。一旦被绘制,缓冲区一个片段以一个像素的位移对自身进行采样。这会导致圆圈在每次循环时沿着屏幕向下延伸。问题是我在拉伸圆圈之前出现了彩色线条。当我尝试进行递归模糊或其他类型的反馈循环时,会出现相同的工件。主缓冲区仅用于在进程结束时混合两个缓冲区。我认为可以忽略它来解决这个问题。以前有人遇到过这种情况吗?
此处的视频:https://photos.app.goo.gl/ThRJHddo2xKke6CN7
此处的屏幕截图:Picture of artifact
在我的 viewController 中调用金属控制器
'''
override func viewDidLoad() {
super.viewDidLoad()
Metal.initMetalLayer(mView: MetalView) // make Metal layer
Metal.initMetalPipeline()//make Metal pipeline
Metal.commandQueue = Metal.device.makeCommandQueue()//make Metal command Queue
timer = CAdisplayLink(target: self,selector: #selector(gameloop)) //make the timer to trigger the render call
timer.add(to: RunLoop.main,forMode: .default) //ties the screen refresh to the render call
}
@objc func gameloop() {
autoreleasepool {
Metal.currentTime = Float(CACurrentMediaTime() - Metal.startTime)
//print(Metal.currentTime)
// Metal.bufferBRender()
Metal.bufferARender()
Metal.mainRender() //TURNS MetaL RENDER OFF OR ON
}
}
'''
金属声明
'''
//
// MTL.swift
// Habituate
//
// Created by Brendan Tipney on 2021-01-14.
//
import Foundation
import Metal
import UIKit
import MetalKit
struct MTL {
var device: MTLDevice!
var MetalLayer: cametallayer!
let vertexData: [Float] = [
-1.0,1.0,0.0,-1.0,0.0
]
var vertexBuffer: MTLBuffer!
var textureA: MTLTexture!
var textureB: MTLTexture!
//MARK: - AUX data
var layoutData = simd_float4(390,727,0.75,1.0)
var habitData = [vector_float4(0.25,1.0),vector_float4(0.1,0.3,0.4)]
var touchData = vector_float4(0,0)
var startTime = CACurrentMediaTime()
var currentTime = Float(CACurrentMediaTime())
//MARK: - RCTDIFData
var RCTDIFData = vector_float4(0.055,0.064,2.0,1.0)
var pipelinestate: MTLRenderPipelinestate! //DO I need more then one pipeline state?
var pipelinestateB: MTLRenderPipelinestate!
var pipelinestateA: MTLRenderPipelinestate!
var commandQueue: MTLCommandQueue!
var timer: CAdisplayLink!
mutating func initMetalLayer(mView : MTKView) {
device = MTLCreateSystemDefaultDevice()
MetalLayer = cametallayer()
MetalLayer.device = device
MetalLayer.pixelFormat = .bgra8Unorm
MetalLayer.framebufferOnly = false// Set to false as I intend to read from the frame buffer for Feedback loops. Set to true if not needed
MetalLayer.frame = mView.layer.frame
mView.layer.addSublayer(MetalLayer)
let viewSize = simd_float2(Float(mView.layer.frame.size.width),Float(mView.layer.frame.size.height))
layoutData = simd_float4(viewSize.x,viewSize.y,1.0) //Trying to load the size data.
print("view = \(viewSize)")
let dataSize = vertexData.count * MemoryLayout.size(ofValue: vertexData[0])
vertexBuffer = device.makeBuffer(bytes: vertexData,length: dataSize,options: []) //makes the buffer with the vertex data.
}
mutating func initMetalPipeline(){
let defaultLibrary = device.makeDefaultLibrary()!
let vertexProgram = defaultLibrary.makeFunction(name: "vertex_main")
let fragmentProgram = defaultLibrary.makeFunction(name: "fragment_main")
let fragmentProgramBuffA = defaultLibrary.makeFunction(name: "fragment_BuffA")
let fragmentProgramBuffB = defaultLibrary.makeFunction(name: "fragment_BuffB")
//MARK: - VertexDescriptor
let vertexDescriptor = MTLVertexDescriptor()//I think this is where the buffers are stored
vertexDescriptor.attributes[0].format = .float3
vertexDescriptor.attributes[0].bufferIndex = 0
vertexDescriptor.attributes[0].offset = 0
vertexDescriptor.layouts[0].stride = MemoryLayout<float3>.stride
//MARK: - Texture Descriptor
let textureDescriptor = MTLTextureDescriptor() //None of this textureDescriptors may be needed
textureDescriptor.textureType = MTLTextureType.type2D
textureDescriptor.width = Int(layoutData.x)
textureDescriptor.height = Int(layoutData.y)
textureDescriptor.pixelFormat = MTLPixelFormat.bgra8Unorm
textureDescriptor.usage = [.rendertarget,.shaderRead]
textureA = device.makeTexture(descriptor: textureDescriptor)
textureB = device.makeTexture(descriptor: textureDescriptor)
//MARK: - Pipeline Descriptor
let pipelinestateDescriptor = MTLRenderPipelineDescriptor()
pipelinestateDescriptor.vertexFunction = vertexProgram
pipelinestateDescriptor.fragmentFunction = fragmentProgram
pipelinestateDescriptor.colorAttachments[0].pixelFormat = .bgra8Unorm
pipelinestateDescriptor.vertexDescriptor = vertexDescriptor
pipelinestate = try! device.makeRenderPipelinestate(descriptor: pipelinestateDescriptor)
//MARK: - BufferA Pipeline Descriptor
pipelinestateDescriptor.fragmentFunction = fragmentProgramBuffA
pipelinestateA = try! device.makeRenderPipelinestate(descriptor: pipelinestateDescriptor)
//MARK: - BufferB Pipeline Descriptor
pipelinestateDescriptor.fragmentFunction = fragmentProgramBuffB
pipelinestateB = try! device.makeRenderPipelinestate(descriptor: pipelinestateDescriptor)
}
//MARK: - Main Render
mutating func mainRender() {
let commandBuffer = commandQueue.makeCommandBuffer()!
guard let drawable = MetalLayer?.nextDrawable() else { return }
let renderPassDescriptor = MTLRenderPassDescriptor()
renderPassDescriptor.colorAttachments[0].texture = drawable.texture
renderPassDescriptor.colorAttachments[0].loadAction = .clear //PERHAPS SETTING THIS TO LOAD CAN MAKE A FB LOOP
renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColor(red: 0.0,green: 1.0,blue: 0.0,alpha: 0.0)
let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptor)!
renderEncoder.label = "Main"
renderEncoder.setRenderPipelinestate(pipelinestate)
renderEncoder.setVertexBuffer(vertexBuffer,offset: 0,index: 0)//this is where the vertex buffer is loaded
renderEncoder.setFragmentTexture(textureA,index: 0)
renderEncoder.setFragmentTexture(textureB,index: 1)
renderEncoder.setFragmentBytes(&layoutData,length: MemoryLayout.size(ofValue: layoutData),index: 1) //this sends the dimentions to the shader
renderEncoder.setFragmentBytes(&habitData,length: 4 * MemoryLayout.size(ofValue: habitData),index: 2)
renderEncoder.setFragmentBytes(&touchData,length: MemoryLayout.size(ofValue: touchData),index: 3)
renderEncoder.drawPrimitives(type: .triangleStrip,vertexStart: 0,vertexCount: 4,instanceCount: 1)
renderEncoder.endEncoding()
commandBuffer.present(drawable)
commandBuffer.commit()
}
//MARK: - Buffer B Render
mutating func bufferARender(){
let commandBuffer = commandQueue.makeCommandBuffer()!
let renderPassDescriptorA = MTLRenderPassDescriptor()
renderPassDescriptorA.colorAttachments[0].texture = textureA //set back to this after testing
renderPassDescriptorA.colorAttachments[0].loadAction = .load
renderPassDescriptorA.colorAttachments[0].storeAction = .store
renderPassDescriptorA.colorAttachments[0].clearColor = MTLClearColor(red: 0.0,alpha: 1.0)
let renderEncoderA = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptorA)!
renderEncoderA.label = "BufferA"
renderEncoderA.setRenderPipelinestate(pipelinestateA)
renderEncoderA.setVertexBuffer(vertexBuffer,index: 0)//this is where the vertex buffer is loaded
renderEncoderA.setFragmentTexture(textureA,index: 0)
renderEncoderA.setFragmentTexture(textureB,index: 1)
renderEncoderA.setFragmentBytes(&layoutData,index: 1) //this sends the dimentions to the shader
renderEncoderA.setFragmentBytes(&habitData,index: 2)
renderEncoderA.setFragmentBytes(&touchData,index: 3)
renderEncoderA.setFragmentBytes(&RCTDIFData,length: MemoryLayout.size(ofValue: RCTDIFData),index: 4)
renderEncoderA.setFragmentBytes(¤tTime,length: MemoryLayout.size(ofValue: currentTime),index: 5)
renderEncoderA.drawPrimitives(type: .triangleStrip,instanceCount: 1)
renderEncoderA.endEncoding()
//commandBuffer.present(drawable)
commandBuffer.commit()
}
mutating func bufferBRender(){
let commandBuffer = commandQueue.makeCommandBuffer()!
let renderPassDescriptorB = MTLRenderPassDescriptor()
//renderPassDescriptorB.colorAttachments[0].texture = drawable.texture
renderPassDescriptorB.colorAttachments[0].texture = textureB //set back to this after testing
renderPassDescriptorB.colorAttachments[0].loadAction = .load
renderPassDescriptorB.colorAttachments[0].storeAction = .store
renderPassDescriptorB.colorAttachments[0].clearColor = MTLClearColor(red: 0.0,green: 0.0,blue: 1.0,alpha: 1.0)
let renderEncoderB = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptorB)!
renderEncoderB.label = "BufferB"
renderEncoderB.setRenderPipelinestate(pipelinestateB)
renderEncoderB.setVertexBuffer(vertexBuffer,index: 0)//this is where the vertex buffer is loaded
renderEncoderB.setFragmentTexture(textureA,index: 0)
renderEncoderB.setFragmentTexture(textureB,index: 1)
renderEncoderB.setFragmentBytes(&layoutData,index: 1) //this sends the dimentions to the shader
renderEncoderB.setFragmentBytes(&habitData,index: 2)
renderEncoderB.setFragmentBytes(&touchData,index: 3)
renderEncoderB.setFragmentBytes(&RCTDIFData,index: 4)
renderEncoderB.setFragmentBytes(¤tTime,index: 5)
renderEncoderB.drawPrimitives(type: .triangleStrip,instanceCount: 1)
renderEncoderB.endEncoding()
//commandBuffer.present(drawable)
commandBuffer.commit()
}
mutating func getDeviceDetails(){
let window = UIApplication.shared.windows[0]
let safeViewTop = window.safeAreaInsets.top
}
mutating func getHabitDetails(_ habits: HabitData){
}
}
'''
顶点和片段着色器
'''
#include <Metal_stdlib>
using namespace Metal;
struct VertexIn {
float3 position [[attribute(0)]];
};
struct VertexOut {
float4 position [[position]];
};
vertex VertexOut vertex_main(VertexIn in [[stage_in]],const device packed_float3* vertex_array [[buffer(0)]],unsigned int vid [[ vertex_id ]]) {
VertexOut out;
out.position = float4(vertex_array[vid].xy,1.0);
//out.data = float4(in.data,1);
return out;
}
fragment float4 fragment_main( VertexOut in [[stage_in]],constant float4 &layoutData [[ buffer(1) ]],constant array<float4,2> &habitData [[ buffer(2) ]],constant float4 &touchData [[ buffer(3) ]],texture2d<float,access::sample> BuffA [[ texture(0) ]],access::sample> BuffB [[ texture(1) ]]) {
float4 fragIn = in.position;
float2 uv = float2((fragIn.x/layoutData.x),(fragIn.y/layoutData.y));
float4 fragColor = float4(0.0,1.0);
constexpr sampler s(coord::normalized,filter::bicubic,address::clamp_to_edge,compare_func:: less);
float4 texA = BuffA.sample(s,uv);
fragColor = texA;
fragColor.w = 1.0;
return fragColor;
}
fragment float4 fragment_BuffA( VertexOut in [[stage_in]],constant float4 &RCTDIFData [[ buffer(4) ]],constant float ¤tTime [[ buffer(5)]],access::sample> BuffB [[ texture(1) ]]) {
float4 fragIn = in.position;
float2 uv = float2((fragIn.x/layoutData.x),(fragIn.y/layoutData.y));//Need to load the screen size here or it will only look right on my phone.
float2 pxlRatio = float2(1)/layoutData.xy;
float4 fragColor = float4(0.0,1.0);
constexpr sampler s(coord::normalized,filter::nearest,address::clamp_to_zero);
float cornerCircle = step(distance(float2(0,0),uv),0.1);
float4 texA = BuffA.sample(s,uv+float2(-1,-1)*pxlRatio);
fragColor = float4(cornerCircle);
fragColor += texA;
fragColor.w = 1.;
return fragColor;
}
'''
解决方法
似乎我能够通过将 .shadeWrite 添加到我的纹理描述符使用数组来解决这个问题。
'''textureDescriptor.usage = [.renderTarget,.shaderRead,.shaderWrite]'''
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。