Metal片段着色器

时间:2017-11-20 18:39:36

标签: ios random fragment-shader metal distortion

我的圈子变得扭曲,不好。

我正在编写一个片段着色器来进行一些图像变换,我看到一些随机的错误噪音,不知道是什么。

我正在合并两个纹理,一个用.read(gid)读取;并使用采样器读取一个.sample(s,uv);

我在Swift中创建了采样器并将其传递给着色器。

以下是我将纹理传递给着色器的方法:

texture2d<float, access::write> outTexture [[ texture(0) ]],
texture2d<float, access::read> inTextureA [[ texture(1) ]],
texture2d<float, access::sample> inTextureB [[ texture(2) ]]

我得到这样的初始uv坐标:

float u = float(gid.x) / float(inTextureB.get_width());

这就是我所看到的:

Random error noise

任何人都知道发生了什么事?

更新

所以我现在已经从计算着色器切换到带有简单四边形的片段着色器以进行绘制,这是它的设置方式:

Swift - Vertecies:

let a = Vertex(x: -1.0, y: -1.0, s: 0.0, t: 0.0)
let b = Vertex(x: 1.0, y: -1.0, s: 1.0, t: 0.0)
let c = Vertex(x: -1.0, y: 1.0, s: 0.0, t: 1.0)
let d = Vertex(x: 1.0, y: 1.0, s: 1.0, t: 1.0)
let verticesArray: Array<Vertex> = [a,b,c,b,c,d]
var vertexData = Array<Float>()
for vertex in verticesArray {
    vertexData += vertex.floatBuffer()
}
let dataSize = vertexData.count * MemoryLayout.size(ofValue: vertexData[0])
self.vertexBuffer = metalDevice.makeBuffer(bytes: vertexData, length: dataSize, options: [])!

Swift - 管道:

let defaultLibrary = metalDevice.makeDefaultLibrary()!
let vertexProgram = defaultLibrary.makeFunction(name: "basic_vertex")
let fragmentProgram = defaultLibrary.makeFunction(name: "transform")
let pipelineStateDescriptor = MTLRenderPipelineDescriptor()
pipelineStateDescriptor.vertexFunction = vertexProgram
pipelineStateDescriptor.fragmentFunction = fragmentProgram
pipelineStateDescriptor.colorAttachments[0].pixelFormat = .bgra8Unorm
do {
    self.pipelineState = try metalDevice.makeRenderPipelineState(descriptor: pipelineStateDescriptor)
} catch {
    print(error.localizedDescription)
}

Swift - 采样器:

let samplerDescriptor = MTLSamplerDescriptor()
self.samplerState = metalDevice.makeSamplerState(descriptor: samplerDescriptor)!

Swift - 纹理:

let width = CVPixelBufferGetWidth(self.pixelBuffer)
let height = CVPixelBufferGetHeight(self.pixelBuffer)
var cvTextureOut: CVMetalTexture?
CVMetalTextureCacheCreateTextureFromImage(kCFAllocatorDefault, self.textureCache!, self.pixelBuffer, nil, .bgra8Unorm, width, height, 0, &cvTextureOut)
guard let cvTexture = cvTextureOut, let inputTexture = CVMetalTextureGetTexture(cvTexture) else {
    print("Failed to create metal texture")
    return nil
}

Swift - 渲染:

let commandBuffer = self.commandQueue.makeCommandBuffer()!
guard let currenDrawable: CAMetalDrawable = self.currentDrawable else { return }

let renderPassDescriptor = MTLRenderPassDescriptor()
renderPassDescriptor.colorAttachments[0].texture = currenDrawable.texture

let renderCommandEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptor)!

var unifroms: [Float] = ...
let uniformBuffer = self.device?.makeBuffer(length: MemoryLayout<Float>.size * unifroms.count, options: [])
let bufferPointer = uniformBuffer?.contents()
memcpy(bufferPointer, &unifroms, MemoryLayout<Float>.size * unifroms.count)
renderCommandEncoder.setFragmentBuffer(uniformBuffer, offset: 0, index: 0)

renderCommandEncoder.setFragmentTexture(currenDrawable.texture, index: 0)
renderCommandEncoder.setFragmentTexture(inputTexture, index: 1)

renderCommandEncoder.setFragmentSamplerState(self.samplerState, index: 0)


renderCommandEncoder.setRenderPipelineState(self.pipelineState)


renderCommandEncoder.setVertexBuffer(self.vertexBuffer, offset: 0, index: 0)
renderCommandEncoder.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: 6, instanceCount: 2)


renderCommandEncoder.endEncoding()

commandBuffer.present(currenDrawable)
commandBuffer.commit()

金属 - 顶点着色器:

#include <metal_stdlib>
using namespace metal;

struct VertexIn{
    packed_float2 position;
    packed_float2 texCoord;
};

struct VertexOut{
    float4 position [[position]];
    float2 texCoord;
};

vertex VertexOut basic_vertex(const device VertexIn* vertex_array [[ buffer(0) ]],
                              unsigned int vid [[ vertex_id ]]) {

    VertexIn vertexIn = vertex_array[vid];

    VertexOut vertexOut;
    vertexOut.position = float4(vertexIn.position[0], vertexIn.position[1], 0, 1);
    vertexOut.texCoord = vertexIn.texCoord;

    return vertexOut;
}

金属 - 片段着色器:

#include <metal_stdlib>
using namespace metal;

struct VertexOut{
    float4 position [[position]];
    float2 texCoord;
};

struct Uniforms{
    float rot;
    float tx;
    float ty;
    float sx;
    float sy;
};

fragment float4 transform(VertexOut out [[stage_in]],
                          texture2d<float>  inTex [[ texture(0) ]],
                          const device Uniforms& in [[ buffer(0) ]],
                          sampler s [[ sampler(0) ]]) {

    float pi = 3.14159265359;

    float u = out.texCoord[0];
    float v = out.texCoord[1];

    float2 size = float2(in.sx, in.sy);

    float ang = atan2(v - 0.5 - in.ty, u - 0.5 - in.tx) + (-in.rot / 360) * pi * 2;
    float amp = sqrt(pow(u - 0.5 - in.tx, 2) + pow(v - 0.5 - in.ty, 2));
    float2 uv = float2(cos(ang) * amp, sin(ang) * amp) / size + 0.5;
    float4 c = inTex.sample(s, uv);

    float r = c.r;
    float g = c.g;
    float b = c.b;

    return float4(r, g, b, 1.0);
}

这就是一切!

即使我做了这个巨大的重构,我剩下的就是我的圈子以一种新的方式扭曲:

Random error blocks

顺便说一下,我运行了大部分代码两次,一次用于创建圆圈(该着色器有效),两次用于转换圆圈(上面的着色器)。

任何人都看到我正在做的任何错误?

我真的希望我的圈子是圆的。

最终更新:

我明白了!

我试图变得聪明并且两次循环“Swift - Render:”部分,而现在,我现在循环遍历所有内容并将当前可绘制纹理作为纹理输入传递.setFragmentTexture,它很有效现在。所以几乎相同的代码只是一种不同的执行方式。

Round Circle

我的圈子现在已圆了。

0 个答案:

没有答案