Reputation: 23
I am trying to create a sphere with a cross-section view in Swift using ARKit and SceneKit.
When I am clipping/cutting the sphere with two planes, and try to move the device (iPad) around the sphere, the rendering is not as expected. The expected view of the object is a sphere with two planes cut. If I move the device around, I will see the rendering changes as the device moves. The video should show how it behaves when the device moves: https://youtu.be/m-YuHMxJoJk
func applyCrossSection() {
print("Applying cross section")
guard !selectedLines.isEmpty,
let sphereNode = currentSphereNode,
let sphere = sphereNode.geometry as? SCNSphere else {
print("Failed to apply cross section: selectedLines count=\(selectedLines.count), sphereNode=\(String(describing: currentSphereNode))")
return
}
// Determine if we use a horizontal or vertical cut.
let isHorizontal = selectedLines[0].name == "horizontalLine"
// Choose a fixed axis in world space.
let fixedAxis: SCNVector3 = isHorizontal ? SCNVector3(0, 1, 0) : SCNVector3(1, 0, 0)
// Use the sphere's presentation world position to get an up-to-date center.
let sphereCenter = sphereNode.presentation.worldPosition
// Compute the projection (scalar) of each selected line's first dot along the fixed axis.
var projections: [Float] = []
for line in selectedLines {
if let firstDot = line.childNodes.first {
let diff = firstDot.worldPosition - sphereCenter
// Dot product with the fixed axis.
let proj = diff.x * fixedAxis.x + diff.y * fixedAxis.y + diff.z * fixedAxis.z
projections.append(proj)
}
}
projections.sort()
guard let minVal = projections.first, let maxVal = projections.last else {
print("No valid positions found from selected lines")
return
}
print("minVal:", minVal, ", maxVal:", maxVal)
// Convert the fixed axis into the sphere's local coordinate system.
var cutAxisInSphere = sphereNode.convertVector(fixedAxis, from: nil)
let axisLength = sqrt(cutAxisInSphere.x * cutAxisInSphere.x +
cutAxisInSphere.y * cutAxisInSphere.y +
cutAxisInSphere.z * cutAxisInSphere.z)
if axisLength != 0 {
cutAxisInSphere.x /= axisLength
cutAxisInSphere.y /= axisLength
cutAxisInSphere.z /= axisLength
}
print("Cut axis in sphere:", cutAxisInSphere, "min:", minVal, "max:", maxVal)
// Build shader code using the fixed cut axis.
let shaderCode = """
#pragma arguments
float u_min;
float u_max;
float3 u_cutAxis;
#pragma body
{
float d = dot(_surface.position, u_cutAxis);
if (d < u_min || d > u_max) {
discard_fragment();
}
}
"""
sphereNode.geometry?.firstMaterial?.shaderModifiers = [.surface: shaderCode]
sphereNode.geometry?.firstMaterial?.setValue(minVal, forKey: "u_min")
sphereNode.geometry?.firstMaterial?.setValue(maxVal, forKey: "u_max")
sphereNode.geometry?.firstMaterial?.setValue(NSValue(scnVector3: cutAxisInSphere), forKey: "u_cutAxis")
// Optionally update visual cutting planes for debugging.
addVisualPlanesForCrossSection(
sphere: sphere,
sphereNode: sphereNode,
horizontalPositions: isHorizontal ? projections : [],
verticalPositions: isHorizontal ? [] : projections
)
print("Cross section applied.")
}
I am guessing that it is the problem with the discard_fragment() method in metal? Or something related to the transform? I'm not quite sure on what's going on as I am new to SceneKit and ARKit.
Any help would be highly appreciated, thank you!
Upvotes: 0
Views: 23