Swift SceneView中如何增加线框/线条的厚度?
在Swift的SceneView中如何增加线框/线条的厚度?
在Swift中使用SceneView时,我想要增加线框/线条的厚度,但查阅Apple文档后,没找到任何和线框lineWidth或厚度相关的属性。我尝试用MetalMaterial和自定义Shader来实现,但代码抛出多个错误,至今没有效果。
我的SceneView示例代码如下:
// Create an array of SCNVector3 representing the points var points = [SCNVector3]() for vert in mesh.verticies { if let vert = vert as? Point3D { points.append(SCNVector3(vert.x, vert.y, vert.z)) } } // Create an array of UInt32 representing the face indices var faceIndices = [UInt32]() for face in mesh.faces { if let face = face as? Face3D { faceIndices.append(UInt32(face.a)) faceIndices.append(UInt32(face.b)) faceIndices.append(UInt32(face.c)) } } // Create a geometry source for the points let pointsSource = SCNGeometrySource(vertices: points) // Create a geometry element for the faces let faceElement = SCNGeometryElement(indices: faceIndices, primitiveType: .triangles) let geometry = SCNGeometry(sources: [pointsSource], elements: [faceElement]) let node = SCNNode(geometry: geometry) node.name = "DebugNode" let material = SCNMaterial() material.isDoubleSided = true // Disable backface culling // Set the material on your mesh node.geometry?.materials = [material] node.geometry?.firstMaterial?.diffuse.contents = mesh3D.colour; node.geometry?.firstMaterial?.fillMode = .fill
我尝试了以下MetalMaterial代码,但抛出多个错误:
metalMaterial.shaderModifiers = [.geometry: """ #pragma arguments float lineThickness; #pragma body _geometry.position = _geometry.position; _geometry.lineWidth = lineThickness; """ ]
如前所述,我在SCNNode、SCNGeometry或SCNGeometryElement上找不到任何可直接设置的相关属性。
可行的解决方案
SceneKit原生并不支持直接修改线框模式下的线条厚度,因为底层渲染框架(OpenGL/Metal)的原生线框渲染线宽受平台限制(比如多数平台仅支持1像素线宽)。可以通过以下两种方式实现类似效果:
1. 生成边缘几何体(最可靠)
放弃原生线框,手动为网格的每条边缘创建细圆柱体或长方体,这样可以完全控制线条的厚度、颜色等属性:
// 示例:为两个点之间创建一个圆柱体线条 func createLineNode(from start: SCNVector3, to end: SCNVector3, radius: CGFloat, color: UIColor) -> SCNNode { let direction = end - start let length = direction.length() let lineGeometry = SCNCylinder(radius: radius, height: length) lineGeometry.firstMaterial?.diffuse.contents = color let lineNode = SCNNode(geometry: lineGeometry) // 旋转和定位圆柱体到两点连线 lineNode.position = SCNVector3((start.x + end.x)/2, (start.y + end.y)/2, (start.z + end.z)/2) lineNode.eulerAngles = SCNVector3( Float.pi/2, atan2(direction.z, direction.x), atan2(direction.y, direction.x) ) return lineNode } // 遍历网格的所有边缘,生成线条节点 // 需要注意去重边缘,避免重复创建
2. 使用自定义Metal着色器实现宽线框
原生Shader修改无法直接设置lineWidth,需要在片元着色器中通过重心坐标模拟宽线框效果:
let material = SCNMaterial() material.shaderModifiers = [ .fragment: """ #pragma arguments float lineWidth; float lineSmooth; vec4 lineColor; #pragma body // 获取片元在三角形内的重心坐标 vec3 barycentric = v_barycentricCoord; vec3 d = fwidth(barycentric); vec3 a3 = smoothstep(d * (lineWidth - lineSmooth), d * (lineWidth + lineSmooth), barycentric); float edgeFactor = min(min(a3.x, a3.y), a3.z); // 混合线框颜色和原颜色 _output.color = mix(lineColor, _output.color, edgeFactor); """ ] // 设置参数 material.setValue(2.0, forKey: "lineWidth") material.setValue(0.5, forKey: "lineSmooth") material.setValue(UIColor.red, forKey: "lineColor") // 设置填充模式为填充,因为我们在片元着色器中模拟线框 material.fillMode = .fill
注意:这种方法依赖SceneKit提供的v_barycentricCoord变量,部分自定义几何体可能需要手动添加重心坐标源才能正常工作。
内容的提问来源于stack exchange,提问作者JSW
相关产品推荐
相关产品推荐

