Last active
April 28, 2025 21:57
-
-
Save chunkyguy/f01b4b72eac9b85c37bc74488aa8f7fa to your computer and use it in GitHub Desktop.
Cornell Box with SceneKit, which is probably the hello world of Computer Graphics.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
import SceneKit | |
import GLKit | |
class World { | |
let scene = SCNScene() | |
func setUp() { | |
let worldNode = scene.rootNode | |
addLights(to: worldNode) | |
addCamera(to: worldNode) | |
addScene(to: worldNode) | |
} | |
} | |
private extension World { | |
private func addCamera(to parent: SCNNode) { | |
let cameraNode = SCNNode() | |
cameraNode.camera = SCNCamera() | |
cameraNode.position = SCNVector3(0, 0, 1) | |
cameraNode.look(at: SCNVector3(0, 0, 0)) | |
parent.addChildNode(cameraNode) | |
} | |
} | |
private extension World { | |
private func addLights(to parent: SCNNode) { | |
addRoomLight(to: parent) | |
addSpotLight(to: parent) | |
} | |
private func addRoomLight(to parent: SCNNode) { | |
let light = SCNLight() | |
light.type = .omni | |
light.color = UIColor(white: 0.5, alpha: 1).cgColor | |
let node = SCNNode() | |
node.light = light | |
node.position = SCNVector3(0, 0, 1) | |
parent.addChildNode(node) | |
} | |
private func addSpotLight(to parent: SCNNode) { | |
let light = SCNLight() | |
light.type = .spot | |
light.color = UIColor.white.cgColor | |
light.castsShadow = true | |
let node = SCNNode() | |
node.light = light | |
node.position = SCNVector3(0, 1, 0) | |
node.look(at: SCNVector3(0, 0, 0)) | |
parent.addChildNode(node) | |
} | |
} | |
private extension World { | |
private func addScene(to parent: SCNNode) { | |
addWalls(to: parent) | |
addCubes(to: parent) | |
addLightSource(to: parent) | |
} | |
} | |
private extension World { | |
private func addWalls(to parent: SCNNode) { | |
let wallColor = UIColor(white: 0.8, alpha: 1) | |
addWall( | |
to: parent, | |
color: wallColor, | |
position: simd_float3(0, 0, -0.5) | |
) | |
addWall( | |
to: parent, | |
color: .green, | |
position: simd_float3(-0.5, 0, 0), | |
rotation: simd_float4(0, 1, 0, GLKMathDegreesToRadians(90)) | |
) | |
addWall( | |
to: parent, | |
color: .red, | |
position: simd_float3(0.5, 0, 0), | |
rotation: simd_float4(0, 1, 0, GLKMathDegreesToRadians(270)) | |
) | |
addWall( | |
to: parent, | |
color: wallColor, | |
position: simd_float3(0, 0.5, 0), | |
rotation: simd_float4(1, 0, 0,GLKMathDegreesToRadians(270)) | |
) | |
addWall( | |
to: parent, | |
color: wallColor, | |
position: simd_float3(0, -0.5, 0), | |
rotation: simd_float4(1, 0, 0, GLKMathDegreesToRadians(90)) | |
) | |
} | |
private func addWall(to parent: SCNNode, color: UIColor, position: simd_float3, rotation: simd_float4 = simd_float4.zero) { | |
let geometry = SCNPlane(width: 1, height: 1) | |
let material = SCNMaterial() | |
material.isDoubleSided = true | |
material.diffuse.contents = color.cgColor | |
geometry.materials = [material] | |
let wall = SCNNode(geometry: geometry) | |
wall.simdPosition = position | |
wall.simdRotation = rotation | |
parent.addChildNode(wall) | |
} | |
} | |
private extension World { | |
private func addCubes(to parent: SCNNode) { | |
addCube( | |
to: parent, | |
height: 0.6, | |
positionX: -0.2, | |
positionZ: -0.5, | |
rotation: 20 | |
) | |
addCube( | |
to: parent, | |
height: 0.3, | |
positionX: 0.1, | |
positionZ: -0.25, | |
rotation: -25 | |
) | |
} | |
private func addCube(to parent: SCNNode, height: CGFloat, positionX: CGFloat, positionZ: CGFloat, rotation: CGFloat) { | |
let cubeGeo = SCNBox(width: 0.3, height: height, length: 0.3, chamferRadius: 0) | |
let cubeMat = SCNMaterial() | |
cubeMat.diffuse.contents = UIColor.lightGray.cgColor | |
cubeGeo.materials = [cubeMat] | |
let cube = SCNNode(geometry: cubeGeo) | |
cube.position = SCNVector3(positionX, -0.5 + height/2.0, positionZ) | |
cube.rotation = SCNVector4(0, 1, 0, GLKMathDegreesToRadians(Float(rotation))) | |
parent.addChildNode(cube) | |
} | |
} | |
private extension World { | |
private func addLightSource(to parent: SCNNode) { | |
let geometry = SCNPlane(width: 0.05, height: 0.05) | |
let material = SCNMaterial() | |
material.isDoubleSided = true | |
material.emission.contents = UIColor.white.cgColor | |
geometry.materials = [material] | |
let node = SCNNode(geometry: geometry) | |
node.position = SCNVector3(0, 0.49, 0) | |
node.rotation = SCNVector4(1, 0, 0, GLKMathDegreesToRadians(270)) | |
parent.addChildNode(node) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment