【发布时间】:2019-05-04 04:50:19
【问题描述】:
我正在尝试复制 Unity 教程中的 splat map 技术。他们使用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 帧调试器中的样子:
当我像这样复制 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).'
【问题讨论】:
-
你在 Xcode 的 GPU 帧调试器中查看过纹理吗?
array中的所有源纹理是否具有相同的尺寸?该代码允许这些源纹理具有 mipmap,但没有正确复制它们。您还需要遍历关卡并复制它们。 -
@KenThomases 你能告诉我如何复制 mipmap 吗?我试图做内部循环,但在
copy()方法中失败了。 -
您使用来自
0..<item.mipmapLevelCount的索引进行循环并执行您现在正在执行的复制,但为sourceLevel:和destinationLevel:参数指定该索引。如果您仍然遇到问题,请编辑您的问题以显示新代码以及您遇到的任何错误。 -
@KenThomases 好的,我尝试复制 mipmap 但出现错误。
-
哦,对了。我的错。您需要调整要复制的级别 (
sourceSize:) 的大小,因为每个级别都小于之前的级别。宽度的 C 为max(1, item.width >> level)。身高类似。不确定它在 Swift 中是否有所不同。