From 5a7b53833d78a1d68313a59943aa11c5a8029973 Mon Sep 17 00:00:00 2001 From: a dinosaur Date: Mon, 19 Aug 2024 00:05:53 +1000 Subject: [PATCH] specular highlights (blinn-phong) --- Sources/Voxelotl/Game.swift | 5 +++-- Sources/Voxelotl/Renderer.swift | 6 +++++- Sources/Voxelotl/shader.metal | 16 ++++++++++++---- Sources/Voxelotl/shadertypes.h | 4 +++- 4 files changed, 23 insertions(+), 8 deletions(-) diff --git a/Sources/Voxelotl/Game.swift b/Sources/Voxelotl/Game.swift index a0976f9..4d39b07 100644 --- a/Sources/Voxelotl/Game.swift +++ b/Sources/Voxelotl/Game.swift @@ -64,6 +64,7 @@ class Game: GameDelegate { func draw(_ renderer: Renderer, _ time: GameTime) { let totalTime = Float(time.total.asFloat) + let cubeSpeedMul: Float = 0.1 var instances: [Instance] = boxes.map { Instance( @@ -73,9 +74,9 @@ class Game: GameDelegate { } instances.append( Instance( - position: .init(0, sin(totalTime * 1.5) * 0.5, -2) * 2, + position: .init(0, sin(totalTime * 1.5 * cubeSpeedMul) * 0.5, -2) * 2, scale: .init(repeating: 0.5), - rotation: .init(angle: totalTime * 3.0, axis: .init(0, 1, 0)), + rotation: .init(angle: totalTime * 3.0 * cubeSpeedMul, axis: .init(0, 1, 0)), color: .init(r: 0.5, g: 0.5, b: 1).linear)) renderer.batch(instances: instances, camera: self.camera) } diff --git a/Sources/Voxelotl/Renderer.swift b/Sources/Voxelotl/Renderer.swift index 01b886f..278f255 100644 --- a/Sources/Voxelotl/Renderer.swift +++ b/Sources/Voxelotl/Renderer.swift @@ -343,7 +343,11 @@ public class Renderer { assert(instances.count < 28) var vertUniforms = VertexShaderUniforms(projView: camera.viewProjection) - var fragUniforms = FragmentShaderUniforms(directionalLight: normalize(.init(0.75, -1, 0.5))) + var fragUniforms = FragmentShaderUniforms( + cameraPosition: camera.position, + directionalLight: normalize(.init(0.75, -1, 0.5)), + specularColor: SIMD4(Color(rgba8888: 0x7F7F7F00).linear), + specularIntensity: 50) let instances = instances.map { (instance: Instance) -> VertexShaderInstance in let model = .translate(instance.position) * diff --git a/Sources/Voxelotl/shader.metal b/Sources/Voxelotl/shader.metal index de9d58e..30ba338 100644 --- a/Sources/Voxelotl/shader.metal +++ b/Sources/Voxelotl/shader.metal @@ -4,6 +4,7 @@ struct FragmentInput { float4 position [[position]]; + float3 world; float3 normal; float2 texCoord; half4 color; @@ -18,10 +19,10 @@ vertex FragmentInput vertexMain( ) { auto position = vtx[vertexID].position; auto world = i[instanceID].model * position; - auto ndc = u.projView * world; FragmentInput out; - out.position = ndc; + out.position = u.projView * world; + out.world = world.xyz; out.color = half4(i[instanceID].color) / 255.0; out.normal = (i[instanceID].normalModel * vtx[vertexID].normal).xyz; out.texCoord = vtx[vertexID].texCoord; @@ -36,11 +37,18 @@ fragment half4 fragmentMain( constexpr metal::sampler sampler(metal::address::repeat, metal::filter::nearest); auto normal = metal::normalize(in.normal); - float lambert = metal::dot(normal, -u.directionalLight); + auto lightVec = -u.directionalLight; + float lambert = metal::dot(normal, lightVec); float diffuse = metal::max(0.0, lambert); + auto eyeVector = metal::normalize(u.cameraPosition - in.world); + auto halfDir = metal::normalize(lightVec + eyeVector); + float specularAngle = metal::max(0.0, metal::dot(halfDir, normal)); + float specularTerm = metal::pow(specularAngle, u.specularIntensity); + half4 specularAmount = specularTerm * metal::smoothstep(0, 2, lambert * u.specularIntensity); + half4 albedo = texture.sample(sampler, in.texCoord); albedo *= in.color; - return albedo * diffuse; + return albedo * diffuse + half4(u.specularColor) * specularAmount; } diff --git a/Sources/Voxelotl/shadertypes.h b/Sources/Voxelotl/shadertypes.h index 1448c47..e84a541 100644 --- a/Sources/Voxelotl/shadertypes.h +++ b/Sources/Voxelotl/shadertypes.h @@ -37,7 +37,9 @@ typedef NS_ENUM(NSInteger, FragmentShaderInputIdx) { }; typedef struct { - vector_float3 directionalLight; + vector_float3 cameraPosition, directionalLight; + vector_float4 specularColor; + float specularIntensity; } FragmentShaderUniforms; #endif//SHADERTYPES_H