我正在尝试从Unity教程复制splat映射技术。他们使用Texture2DArray
,所以我用以下类型创建了MTLTexture:
private func createTerrainTexture(_ bundle: Bundle) -> MTLTexture {
guard let device = MTLCreateSystemDefaultDevice() else {
fatalError()
}
let names = ["sand", "grass", "earth", "stone", "snow"]
let loader = MTKTextureLoader(device: device)
let array = names.map { name -> MTLTexture in
do {
return try loader.newTexture(name: name, scaleFactor: 1.0, bundle: bundle, options: nil)
} catch {
fatalError()
}
}
guard let queue = device.makeCommandQueue() else {
fatalError()
}
guard let commandBuffer = queue.makeCommandBuffer() else {
fatalError()
}
guard let encoder = commandBuffer.makeBlitCommandEncoder() else {
fatalError()
}
let descriptor = MTLTextureDescriptor()
descriptor.textureType = .type2DArray
descriptor.pixelFormat = array[0].pixelFormat
descriptor.width = array[0].width
descriptor.height = array[0].height
descriptor.mipmapLevelCount = array[0].mipmapLevelCount
descriptor.arrayLength = 5
guard let texture = device.makeTexture(descriptor: descriptor) else {
fatalError()
}
var slice = 0
array.forEach { item in
encoder.copy(from: item,
sourceSlice: 0,
sourceLevel: 0,
sourceOrigin: MTLOrigin(x: 0, y: 0, z: 0),
sourceSize: MTLSize(width: item.width, height: item.height, depth: 1),
to: texture,
destinationSlice: slice,
destinationLevel: 0,
destinationOrigin: MTLOrigin(x: 0, y: 0, z: 0))
slice += 1
}
encoder.endEncoding()
commandBuffer.commit()
commandBuffer.waitUntilCompleted()
return texture
}
这是我的片段着色器功能:
fragment half4 terrainFragment(TerrainVertexOutput in [[stage_in]],
texture2d_array<float> terrainTexture [[texture(0)]])
{
constexpr sampler sampler2d(coord::normalized, filter::linear, address::repeat);
float2 uv = in.position.xz * 0.02;
float4 c1 = terrainTexture.sample(sampler2d, uv, 0);
return half4(c1);
}
这是教程中的Unity着色器:
void surf (Input IN, inout SurfaceOutputStandard o) {
float2 uv = IN.worldPos.xz * 0.02;
fixed4 c = UNITY_SAMPLE_TEX2DARRAY(_MainTex, float3(uv, 0));
Albedo = c.rgb * _Color;
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
o.Alpha = c.a;
}
由于某些原因,当在列中重复纹理时,我得到了错误的结果。
我想要的结果是:
更新。 这是GPU Frame Debugger中的纹理外观:
当我像这样复制mipmap时:
var slice = 0
array.forEach { item in
print(item.width, item.height, item.mipmapLevelCount)
for i in 0..<descriptor.mipmapLevelCount {
encoder.copy(from: item,
sourceSlice: 0,
sourceLevel: i,
sourceOrigin: MTLOrigin(x: 0, y: 0, z: 0),
sourceSize: MTLSize(width: item.width, height: item.height, depth: 1),
to: texture,
destinationSlice: slice,
destinationLevel: i,
destinationOrigin: MTLOrigin(x: 0, y: 0, z: 0))
}
slice += 1
}
我遇到错误:
-[MTLDebugBlitCommandEncoder validateCopyFromTexture:sourceSlice:sourceLevel:sourceOrigin:sourceSize:toTexture:destinationSlice:destinationLevel:destinationOrigin:options:]:254: failed assertion `(sourceOrigin.x + sourceSize.width)(512) must be <= width(256).'
答案 0 :(得分:0)
问题在于片段着色器输入变量的错误移植。在原始输入中使用了worldPos
,但是我使用了float4 position [[position]]
,根据Metal规范,这意味着
描述片段的窗口相对坐标(x,y,z,1 / w)值。
因此位置不正确。正确的片段着色器输入如下所示:
struct TerrainVertexOutput
{
float4 position [[position]];
float3 p;
};
和顶点函数:
vertex TerrainVertexOutput terrainVertex(TerrainVertexInput in [[stage_in]],
constant SCNSceneBuffer& scn_frame [[buffer(0)]],
constant MyNodeBuffer& scn_node [[buffer(1)]])
{
TerrainVertexOutput v;
v.position = scn_node.modelViewProjectionTransform * float4(in.position, 1.0);
v.p = (scn_node.modelTransform * float4(in.position, 1.0)).xyz;
return v;
}