金属片段着色器 - 如何根据可以通过绘制功能调整的制服为每个像素分配颜色?
Metal fragment shader - How do I assign each pixel a color based on the uniforms that I can adjust via the draw function?
我有一个 MTKView,它正在渲染一个立方体,其大小使其其中一个面填满整个屏幕。
我将一个包含随机浮点数的结构变量(称为 theuniforms)传递给片段着色器,我想根据浮点数的值为屏幕上的每个像素着色不同的颜色。
现在,它根据浮点值将整个四边形着色为红色或蓝色,这在绘图函数中是随机的。
我认为我的设置是错误的,因为在任何帧我都只能访问一个随机浮点数,并且在该帧期间所有像素都将被着色(我不会有一组随机浮点数来确定像素颜色)。但是,我想我可以简单地向结构变量添加更多变量来解决这个问题。
主要问题是通过片段函数访问单个像素...如果它的性能足以确定屏幕上每个像素的颜色。
import UIKit
import MetalKit
class ViewController: UIViewController, MTKViewDelegate {
var metaldevice: MTLDevice!
var metalview: MTKView!
var metallibrary: MTLLibrary!
var metalqueue: MTLCommandQueue!
var metalpipelinestate: MTLRenderPipelineState!
var metalmesh: MTKMesh!
var theuniforms = Uniforms(color: 1)
struct Uniforms {
var color = Float(1)
}
override var prefersStatusBarHidden: Bool { return true }
override var prefersHomeIndicatorAutoHidden: Bool { return true }
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = .black
self.metaldevice = MTLCreateSystemDefaultDevice()
self.metalview = MTKView()
self.metalview.frame = UIScreen.main.bounds
self.metalview.clearColor = MTLClearColor(red: 0, green: 0, blue: 0, alpha: 1)
self.metalview.delegate = self
self.metalview.device = self.metaldevice
self.view.addSubview(self.metalview)
self.metallibrary = self.metaldevice.makeDefaultLibrary()
self.metalqueue = self.metaldevice.makeCommandQueue()
self.metalmesh = self.returncube()
let vfunc = self.metallibrary.makeFunction(name: "vertex_main")
let ffunc = self.metallibrary.makeFunction(name: "fragment_main")
let descriptor = MTLRenderPipelineDescriptor()
descriptor.colorAttachments[0].pixelFormat = .bgra8Unorm
descriptor.vertexFunction = vfunc
descriptor.fragmentFunction = ffunc
descriptor.vertexDescriptor = MTKMetalVertexDescriptorFromModelIO(self.metalmesh.vertexDescriptor)
self.metalpipelinestate = try! self.metaldevice.makeRenderPipelineState(descriptor: descriptor)
}
func returncube() -> MTKMesh {
let allocator = MTKMeshBufferAllocator(device: self.metaldevice)
let model = MDLMesh(boxWithExtent: [2, 2, 2], segments: [1, 1, 1], inwardNormals: false, geometryType: .triangles, allocator: allocator)
let mesh = try! MTKMesh(mesh: model, device: self.metaldevice)
return mesh
}
func draw(in view: MTKView) {
let descriptor = self.metalview.currentRenderPassDescriptor
let buffer = self.metalqueue.makeCommandBuffer()
let encoder = buffer?.makeRenderCommandEncoder(descriptor: descriptor!)
encoder?.setRenderPipelineState(self.metalpipelinestate)
self.theuniforms.color = Float.random(in: 1.0...2.0)
encoder?.setVertexBytes(&theuniforms, length: MemoryLayout<Uniforms>.stride, index: 1)
encoder?.setVertexBuffer(self.metalmesh.vertexBuffers[0].buffer, offset: 0, index: 0)
for mesh in self.metalmesh.submeshes {
encoder?.drawIndexedPrimitives(type: .triangle, indexCount: mesh.indexCount, indexType: mesh.indexType, indexBuffer: mesh.indexBuffer.buffer, indexBufferOffset: mesh.indexBuffer.offset)
}
encoder?.endEncoding()
let drawable = self.metalview.currentDrawable
buffer?.present(drawable!)
buffer?.commit()
}
func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {
}
}
这是着色器代码
#include <metal_stdlib>
using namespace metal;
#include <simd/simd.h>
struct VertexIn {
float4 position [[attribute(0)]];
};
struct Uniforms {
float color;
};
struct VertexOut {
float4 position [[position]];
Uniforms uniforms;
};
vertex VertexOut vertex_main(const VertexIn in [[stage_in]],
constant Uniforms &uniforms [[buffer(1)]]
)
{
VertexOut out;
out.position = in.position;
out.uniforms = uniforms;
return out;
}
fragment float4 fragment_main( VertexOut in [[stage_in]])
{
float4 color;
if (in.uniforms.color > 1.5){ color = float4(1, 0, 0, 1); }
else { color = float4(0, 0, 1, 1); }
return color;
}
首先,您不应该使用制服来更改您重载 CPU 的每个像素的颜色,但这是一项 GPU 任务,因此您应该在着色器中使用一些函数。如果你想让制服相应地改变片段的颜色,你必须将它传递给片段函数 setFragmentBytes(&theuniforms, length: MemoryLayout<Uniforms>.stride, index: 1)
并在片段函数中调用它。如果我没看错,你想要立方体上的白噪声之类的东西,所以这里是着色器:
#include <metal_stdlib>
using namespace metal;
#include <simd/simd.h>
struct VertexIn {
float4 position [[attribute(0)]];
};
struct Uniforms {
float color;
};
struct VertexOut {
float4 position [[position]];
};
vertex VertexOut vertex_main(const VertexIn in [[stage_in]])
{
VertexOut out;
out.position = in.position;
return out;
}
float rand(float2 co)
{
return fract(sin(dot(co.xy ,float2(12.9898,78.236))) * 43758.5453);
}
fragment float4 fragment_main( VertexOut in [[stage_in]],constant Uniforms &uniforms [[buffer(1)]])
{
float2 pos = in.position.xy;
float4 color = float4(rand(pos));
//if (in.uniforms.color > 1.5){ color = float4(1, 0, 0, 1); }
//else { color = float4(0, 0, 1, 1); }
return color;
}
我有一个 MTKView,它正在渲染一个立方体,其大小使其其中一个面填满整个屏幕。
我将一个包含随机浮点数的结构变量(称为 theuniforms)传递给片段着色器,我想根据浮点数的值为屏幕上的每个像素着色不同的颜色。
现在,它根据浮点值将整个四边形着色为红色或蓝色,这在绘图函数中是随机的。
我认为我的设置是错误的,因为在任何帧我都只能访问一个随机浮点数,并且在该帧期间所有像素都将被着色(我不会有一组随机浮点数来确定像素颜色)。但是,我想我可以简单地向结构变量添加更多变量来解决这个问题。
主要问题是通过片段函数访问单个像素...如果它的性能足以确定屏幕上每个像素的颜色。
import UIKit
import MetalKit
class ViewController: UIViewController, MTKViewDelegate {
var metaldevice: MTLDevice!
var metalview: MTKView!
var metallibrary: MTLLibrary!
var metalqueue: MTLCommandQueue!
var metalpipelinestate: MTLRenderPipelineState!
var metalmesh: MTKMesh!
var theuniforms = Uniforms(color: 1)
struct Uniforms {
var color = Float(1)
}
override var prefersStatusBarHidden: Bool { return true }
override var prefersHomeIndicatorAutoHidden: Bool { return true }
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = .black
self.metaldevice = MTLCreateSystemDefaultDevice()
self.metalview = MTKView()
self.metalview.frame = UIScreen.main.bounds
self.metalview.clearColor = MTLClearColor(red: 0, green: 0, blue: 0, alpha: 1)
self.metalview.delegate = self
self.metalview.device = self.metaldevice
self.view.addSubview(self.metalview)
self.metallibrary = self.metaldevice.makeDefaultLibrary()
self.metalqueue = self.metaldevice.makeCommandQueue()
self.metalmesh = self.returncube()
let vfunc = self.metallibrary.makeFunction(name: "vertex_main")
let ffunc = self.metallibrary.makeFunction(name: "fragment_main")
let descriptor = MTLRenderPipelineDescriptor()
descriptor.colorAttachments[0].pixelFormat = .bgra8Unorm
descriptor.vertexFunction = vfunc
descriptor.fragmentFunction = ffunc
descriptor.vertexDescriptor = MTKMetalVertexDescriptorFromModelIO(self.metalmesh.vertexDescriptor)
self.metalpipelinestate = try! self.metaldevice.makeRenderPipelineState(descriptor: descriptor)
}
func returncube() -> MTKMesh {
let allocator = MTKMeshBufferAllocator(device: self.metaldevice)
let model = MDLMesh(boxWithExtent: [2, 2, 2], segments: [1, 1, 1], inwardNormals: false, geometryType: .triangles, allocator: allocator)
let mesh = try! MTKMesh(mesh: model, device: self.metaldevice)
return mesh
}
func draw(in view: MTKView) {
let descriptor = self.metalview.currentRenderPassDescriptor
let buffer = self.metalqueue.makeCommandBuffer()
let encoder = buffer?.makeRenderCommandEncoder(descriptor: descriptor!)
encoder?.setRenderPipelineState(self.metalpipelinestate)
self.theuniforms.color = Float.random(in: 1.0...2.0)
encoder?.setVertexBytes(&theuniforms, length: MemoryLayout<Uniforms>.stride, index: 1)
encoder?.setVertexBuffer(self.metalmesh.vertexBuffers[0].buffer, offset: 0, index: 0)
for mesh in self.metalmesh.submeshes {
encoder?.drawIndexedPrimitives(type: .triangle, indexCount: mesh.indexCount, indexType: mesh.indexType, indexBuffer: mesh.indexBuffer.buffer, indexBufferOffset: mesh.indexBuffer.offset)
}
encoder?.endEncoding()
let drawable = self.metalview.currentDrawable
buffer?.present(drawable!)
buffer?.commit()
}
func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {
}
}
这是着色器代码
#include <metal_stdlib>
using namespace metal;
#include <simd/simd.h>
struct VertexIn {
float4 position [[attribute(0)]];
};
struct Uniforms {
float color;
};
struct VertexOut {
float4 position [[position]];
Uniforms uniforms;
};
vertex VertexOut vertex_main(const VertexIn in [[stage_in]],
constant Uniforms &uniforms [[buffer(1)]]
)
{
VertexOut out;
out.position = in.position;
out.uniforms = uniforms;
return out;
}
fragment float4 fragment_main( VertexOut in [[stage_in]])
{
float4 color;
if (in.uniforms.color > 1.5){ color = float4(1, 0, 0, 1); }
else { color = float4(0, 0, 1, 1); }
return color;
}
首先,您不应该使用制服来更改您重载 CPU 的每个像素的颜色,但这是一项 GPU 任务,因此您应该在着色器中使用一些函数。如果你想让制服相应地改变片段的颜色,你必须将它传递给片段函数 setFragmentBytes(&theuniforms, length: MemoryLayout<Uniforms>.stride, index: 1)
并在片段函数中调用它。如果我没看错,你想要立方体上的白噪声之类的东西,所以这里是着色器:
#include <metal_stdlib>
using namespace metal;
#include <simd/simd.h>
struct VertexIn {
float4 position [[attribute(0)]];
};
struct Uniforms {
float color;
};
struct VertexOut {
float4 position [[position]];
};
vertex VertexOut vertex_main(const VertexIn in [[stage_in]])
{
VertexOut out;
out.position = in.position;
return out;
}
float rand(float2 co)
{
return fract(sin(dot(co.xy ,float2(12.9898,78.236))) * 43758.5453);
}
fragment float4 fragment_main( VertexOut in [[stage_in]],constant Uniforms &uniforms [[buffer(1)]])
{
float2 pos = in.position.xy;
float4 color = float4(rand(pos));
//if (in.uniforms.color > 1.5){ color = float4(1, 0, 0, 1); }
//else { color = float4(0, 0, 1, 1); }
return color;
}