diff --git a/Terrain2/Renderer.swift b/Terrain2/Renderer.swift index 566b421..271dc58 100644 --- a/Terrain2/Renderer.swift +++ b/Terrain2/Renderer.swift @@ -42,6 +42,11 @@ class Renderer: NSObject, MTKViewDelegate { var uniformBufferIndex = 0 var uniforms: UnsafeMutablePointer + var lightsBuffer: MTLBuffer + var lights: UnsafeMutablePointer + var materialBuffer: MTLBuffer + var material: UnsafeMutablePointer + var projectionMatrix: matrix_float4x4 = matrix_float4x4() var rotation: Float = 0 @@ -103,7 +108,18 @@ class Renderer: NSObject, MTKViewDelegate { depthStateDesciptor.isDepthWriteEnabled = true self.depthState = device.makeDepthStencilState(descriptor:depthStateDesciptor)! + let lightsBufferLength = MemoryLayout.size * 4 + self.lightsBuffer = self.device.makeBuffer(length: lightsBufferLength, options: .storageModeShared)! + self.lights = UnsafeMutableRawPointer(lightsBuffer.contents()).bindMemory(to: Light.self, capacity: 4) + + let materialBufferLength = MemoryLayout.size + self.materialBuffer = self.device.makeBuffer(length: materialBufferLength, options: .storageModeShared)! + self.material = UnsafeMutableRawPointer(materialBuffer.contents()).bindMemory(to: Material.self, capacity: 1) + super.init() + + populateLights() + populateMaterials() } class func buildRenderPipelineWithDevice(device: MTLDevice, @@ -185,6 +201,22 @@ class Renderer: NSObject, MTKViewDelegate { return progress } + private func populateLights() { + for i in 0..<4 { + lights[i].enabled = false + } + + lights[0].enabled = true + lights[0].position = simd_float4(x: 2, y: 10, z: 5, w: 1) + lights[0].color = simd_float3(0.5, 0.5, 0) + } + + private func populateMaterials() { + material[0].diffuseColor = simd_float3(0.8) + material[0].specularColor = simd_float3(1) + material[0].specularExponent = 10 + } + private func updateDynamicBufferState() { /// Update the state of our uniform buffers before rendering @@ -281,6 +313,9 @@ class Renderer: NSObject, MTKViewDelegate { renderEncoder.setVertexBuffer(terrain.faceNormalsBuffer, offset: 0, index: BufferIndex.faceNormals.rawValue) renderEncoder.setVertexBuffer(dynamicUniformBuffer, offset:uniformBufferOffset, index: BufferIndex.uniforms.rawValue) renderEncoder.setFragmentBuffer(dynamicUniformBuffer, offset:uniformBufferOffset, index: BufferIndex.uniforms.rawValue) + + renderEncoder.setFragmentBuffer(lightsBuffer, offset: 0, index: BufferIndex.lights.rawValue) + renderEncoder.setFragmentBuffer(materialBuffer, offset: 0, index: BufferIndex.materials.rawValue) for (index, element) in terrain.mesh.vertexDescriptor.layouts.enumerated() { guard let layout = element as? MDLVertexBufferLayout else { diff --git a/Terrain2/Shaders/ShaderTypes.h b/Terrain2/Shaders/ShaderTypes.h index 3ea3f57..d42bea6 100644 --- a/Terrain2/Shaders/ShaderTypes.h +++ b/Terrain2/Shaders/ShaderTypes.h @@ -22,11 +22,13 @@ #include typedef NS_ENUM(NSInteger, BufferIndex) { - BufferIndexMeshPositions = 0, - BufferIndexNormals = 1, - BufferIndexMeshGenerics = 2, - BufferIndexFaceNormals = 3, - BufferIndexUniforms = 4, + BufferIndexMeshPositions = 0, + BufferIndexNormals = 1, + BufferIndexMeshGenerics = 2, + BufferIndexFaceNormals = 3, + BufferIndexUniforms = 4, + BufferIndexLights = 5, + BufferIndexMaterials = 6, }; typedef NS_ENUM(NSInteger, NormalBufferIndex) { diff --git a/Terrain2/Shaders/Shaders.metal b/Terrain2/Shaders/Shaders.metal index 20bda79..0ab93e7 100644 --- a/Terrain2/Shaders/Shaders.metal +++ b/Terrain2/Shaders/Shaders.metal @@ -24,7 +24,7 @@ typedef struct { typedef struct { float4 position [[position]]; - float4 eyeCoords; + float3 eyeCoords; float3 normal; float2 texCoord; } ColorInOut; @@ -42,7 +42,7 @@ vertex ColorInOut vertexShader(Vertex in [[stage_in]], float4 eyeCoords = uniforms.modelViewMatrix * vertexCoords; out.position = uniforms.projectionMatrix * eyeCoords; - out.eyeCoords = eyeCoords; + out.eyeCoords = eyeCoords.xyz / eyeCoords.w; // TODO: Use the face normal. out.normal = normalize(in.normal); out.texCoord = in.texCoord; @@ -51,23 +51,41 @@ vertex ColorInOut vertexShader(Vertex in [[stage_in]], } fragment float4 fragmentShader(ColorInOut in [[stage_in]], + constant Light *lights [[buffer(BufferIndexLights)]], + constant Material *materials [[buffer(BufferIndexMaterials)]], constant Uniforms &uniforms [[buffer(BufferIndexUniforms)]]) { + // Compute the normal at this position. float3 normal = normalize(uniforms.normalMatrix * in.normal); - float3 lightDirection = normalize(float3(-8, 8, 1) - in.eyeCoords.xyz); + + // Compute the vector pointing to the light from this position. + float3 lightDirection; + constant Light &light = lights[0]; + if (light.position.w == 0.0) { + lightDirection = normalize(light.position.xyz); + } else { + lightDirection = normalize(light.position.xyz / light.position.w - in.eyeCoords); + } + + // Compute the direction of the viewer from this position. float3 viewDirection = normalize(-in.eyeCoords.xyz); - float3 reflection = -reflect(lightDirection, normal); float4 out; - float lightDotNormal = dot(normal, lightDirection); - if (lightDotNormal <= 0.0) { + float lightDirDotNormal = dot(lightDirection, normal); + if (lightDirDotNormal <= 0.0) { // No color contribution to this pixel. - out = float4(); + out = float4(0); } else { - float3 color = 0.8 * lightDotNormal * float3(0.6); - float reflectDotViewDirection = dot(reflection, viewDirection); - if (reflectDotViewDirection > 0.0) { - color += 0.4 * pow(reflectDotViewDirection, 10) * float3(0, 0, 1); + constant Material &material = materials[0]; + + // Comput the direction of reflection given the light direction and the normal at this point. Negate it because the vector returned points from the light to this position and we want it from this position toward the light. + float3 reflection = -reflect(lightDirection, normal); + + float3 color = lightDirDotNormal * light.color * material.diffuseColor; + float reflectDotViewDir = dot(reflection, viewDirection); + if (reflectDotViewDir > 0.0) { + float factor = pow(reflectDotViewDir, material.specularExponent); + color += factor * material.specularColor * light.color; } out = float4(color, 1); }