un-hardcode various render properties (environment, material)

This commit is contained in:
a dinosaur 2024-08-25 14:49:07 +10:00
parent 7a417e0701
commit 428b142bf2
8 changed files with 85 additions and 37 deletions

View File

@ -57,6 +57,8 @@ public class Application {
printErr("Renderer init error: unexpected error")
}
self.del.create(renderer!)
lastCounter = SDL_GetPerformanceCounter()
return .running
}

View File

@ -26,6 +26,11 @@ add_executable(Voxelotl MACOSX_BUNDLE
# Resource classes
NSImageLoader.swift
# Renderer classes
Renderer/Material.swift
Renderer/Environment.swift
Renderer/Renderer.swift
# Input wrappers
Input/Keyboard.swift
Input/GameController.swift
@ -34,7 +39,6 @@ add_executable(Voxelotl MACOSX_BUNDLE
# Core utility classes
Color.swift
Camera.swift
Renderer.swift
FPSCalculator.swift
GameDelegate.swift
Application.swift
@ -85,3 +89,4 @@ source_group("Source Files" REGULAR_EXPRESSION "\\.(swift|metal)$")
source_group("Source Files\\Random" REGULAR_EXPRESSION "Random/")
source_group("Source Files\\Math" REGULAR_EXPRESSION "Math/")
source_group("Source Files\\Input" REGULAR_EXPRESSION "Input/")
source_group("Source Files\\Renderer" REGULAR_EXPRESSION "Renderer/")

View File

@ -26,9 +26,11 @@ class Game: GameDelegate {
var projection: matrix_float4x4 = .identity
var chunk = Chunk(position: .zero)
init() {
func create(_ renderer: Renderer) {
self.resetPlayer()
self.generateWorld()
renderer.clearColor = Color<Double>.black.mix(.white, 0.1).linear
}
private func resetPlayer() {
@ -103,6 +105,15 @@ class Game: GameDelegate {
func draw(_ renderer: Renderer, _ time: GameTime) {
let totalTime = Float(time.total.asFloat)
let env = Environment(
cullFace: .back,
lightDirection: .init(0.75, -1, 0.5))
let material = Material(
ambient: Color(rgba8888: 0x4F4F4F00).linear,
diffuse: Color(rgba8888: 0xDFDFDF00).linear,
specular: Color(rgba8888: 0x2F2F2F00).linear,
gloss: 75)
var instances = chunk.compactMap { block, position in
if case let .solid(color) = block.type {
Instance(
@ -121,7 +132,7 @@ class Game: GameDelegate {
.init(angle: totalTime * 0.7, axis: .init(0, 0, 1)),
color: .init(r: 0.5, g: 0.5, b: 1).linear))
if !instances.isEmpty {
renderer.batch(instances: instances, camera: self.camera)
renderer.batch(instances: instances, material: material, environment: env, camera: self.camera)
}
}

View File

@ -1,4 +1,5 @@
public protocol GameDelegate {
func create(_ renderer: Renderer)
func fixedUpdate(_ time: GameTime)
func update(_ time: GameTime)
func draw(_ renderer: Renderer, _ time: GameTime)

View File

@ -203,7 +203,7 @@ struct Player {
chunk: chunk,
origin: self.eyePosition,
direction: .forward * simd_matrix3x3(self.eyeRotation),
maxDistance: 3.666
maxDistance: 3.8
) {
self.rayhitPos = hit.position
if destroy {

View File

@ -0,0 +1,6 @@
public struct Environment {
public var cullFace: Face
public var lightDirection: SIMD3<Float>
public enum Face { case none, front, back }
}

View File

@ -0,0 +1,7 @@
public struct Material {
public var ambient: Color<Float16>
public var diffuse: Color<Float16>
public var specular: Color<Float16>
public var gloss: Float
}

View File

@ -5,30 +5,30 @@ import simd
import ShaderTypes
fileprivate let cubeVertices: [ShaderVertex] = [
.init(position: .init(-1, -1, 1, 1), normal: .init( 0, 0, 1, 0), texCoord: .init(0, 0)),
.init(position: .init( 1, -1, 1, 1), normal: .init( 0, 0, 1, 0), texCoord: .init(1, 0)),
.init(position: .init(-1, 1, 1, 1), normal: .init( 0, 0, 1, 0), texCoord: .init(0, 1)),
.init(position: .init( 1, 1, 1, 1), normal: .init( 0, 0, 1, 0), texCoord: .init(1, 1)),
.init(position: .init( 1, -1, 1, 1), normal: .init( 1, 0, 0, 0), texCoord: .init(0, 0)),
.init(position: .init( 1, -1, -1, 1), normal: .init( 1, 0, 0, 0), texCoord: .init(1, 0)),
.init(position: .init( 1, 1, 1, 1), normal: .init( 1, 0, 0, 0), texCoord: .init(0, 1)),
.init(position: .init( 1, 1, -1, 1), normal: .init( 1, 0, 0, 0), texCoord: .init(1, 1)),
.init(position: .init( 1, -1, -1, 1), normal: .init( 0, 0, -1, 0), texCoord: .init(0, 0)),
.init(position: .init(-1, -1, -1, 1), normal: .init( 0, 0, -1, 0), texCoord: .init(1, 0)),
.init(position: .init( 1, 1, -1, 1), normal: .init( 0, 0, -1, 0), texCoord: .init(0, 1)),
.init(position: .init(-1, 1, -1, 1), normal: .init( 0, 0, -1, 0), texCoord: .init(1, 1)),
.init(position: .init(-1, -1, -1, 1), normal: .init(-1, 0, 0, 0), texCoord: .init(0, 0)),
.init(position: .init(-1, -1, 1, 1), normal: .init(-1, 0, 0, 0), texCoord: .init(1, 0)),
.init(position: .init(-1, 1, -1, 1), normal: .init(-1, 0, 0, 0), texCoord: .init(0, 1)),
.init(position: .init(-1, 1, 1, 1), normal: .init(-1, 0, 0, 0), texCoord: .init(1, 1)),
.init(position: .init(-1, -1, -1, 1), normal: .init( 0, -1, 0, 0), texCoord: .init(0, 0)),
.init(position: .init( 1, -1, -1, 1), normal: .init( 0, -1, 0, 0), texCoord: .init(1, 0)),
.init(position: .init(-1, -1, 1, 1), normal: .init( 0, -1, 0, 0), texCoord: .init(0, 1)),
.init(position: .init( 1, -1, 1, 1), normal: .init( 0, -1, 0, 0), texCoord: .init(1, 1)),
.init(position: .init(-1, 1, 1, 1), normal: .init( 0, 1, 0, 0), texCoord: .init(0, 0)),
.init(position: .init( 1, 1, 1, 1), normal: .init( 0, 1, 0, 0), texCoord: .init(1, 0)),
.init(position: .init(-1, 1, -1, 1), normal: .init( 0, 1, 0, 0), texCoord: .init(0, 1)),
.init(position: .init( 1, 1, -1, 1), normal: .init( 0, 1, 0, 0), texCoord: .init(1, 1)),
.init(position: .init(-1, -1, 1, 1), normal: .init(.back, 0), texCoord: .init(0, 0)),
.init(position: .init( 1, -1, 1, 1), normal: .init(.back, 0), texCoord: .init(1, 0)),
.init(position: .init(-1, 1, 1, 1), normal: .init(.back, 0), texCoord: .init(0, 1)),
.init(position: .init( 1, 1, 1, 1), normal: .init(.back, 0), texCoord: .init(1, 1)),
.init(position: .init( 1, -1, 1, 1), normal: .init(.right, 0), texCoord: .init(0, 0)),
.init(position: .init( 1, -1, -1, 1), normal: .init(.right, 0), texCoord: .init(1, 0)),
.init(position: .init( 1, 1, 1, 1), normal: .init(.right, 0), texCoord: .init(0, 1)),
.init(position: .init( 1, 1, -1, 1), normal: .init(.right, 0), texCoord: .init(1, 1)),
.init(position: .init( 1, -1, -1, 1), normal: .init(.forward, 0), texCoord: .init(0, 0)),
.init(position: .init(-1, -1, -1, 1), normal: .init(.forward, 0), texCoord: .init(1, 0)),
.init(position: .init( 1, 1, -1, 1), normal: .init(.forward, 0), texCoord: .init(0, 1)),
.init(position: .init(-1, 1, -1, 1), normal: .init(.forward, 0), texCoord: .init(1, 1)),
.init(position: .init(-1, -1, -1, 1), normal: .init(.left, 0), texCoord: .init(0, 0)),
.init(position: .init(-1, -1, 1, 1), normal: .init(.left, 0), texCoord: .init(1, 0)),
.init(position: .init(-1, 1, -1, 1), normal: .init(.left, 0), texCoord: .init(0, 1)),
.init(position: .init(-1, 1, 1, 1), normal: .init(.left, 0), texCoord: .init(1, 1)),
.init(position: .init(-1, -1, -1, 1), normal: .init(.down, 0), texCoord: .init(0, 0)),
.init(position: .init( 1, -1, -1, 1), normal: .init(.down, 0), texCoord: .init(1, 0)),
.init(position: .init(-1, -1, 1, 1), normal: .init(.down, 0), texCoord: .init(0, 1)),
.init(position: .init( 1, -1, 1, 1), normal: .init(.down, 0), texCoord: .init(1, 1)),
.init(position: .init(-1, 1, 1, 1), normal: .init(.up, 0), texCoord: .init(0, 0)),
.init(position: .init( 1, 1, 1, 1), normal: .init(.up, 0), texCoord: .init(1, 0)),
.init(position: .init(-1, 1, -1, 1), normal: .init(.up, 0), texCoord: .init(0, 1)),
.init(position: .init( 1, 1, -1, 1), normal: .init(.up, 0), texCoord: .init(1, 1)),
]
fileprivate let cubeIndices: [UInt16] = [
@ -43,12 +43,12 @@ fileprivate let cubeIndices: [UInt16] = [
fileprivate let numFramesInFlight: Int = 3
fileprivate let colorFormat: MTLPixelFormat = .bgra8Unorm_srgb
fileprivate let depthFormat: MTLPixelFormat = .depth32Float
fileprivate let clearColor: Color<Double> = .black.mix(.white, 0.1).linear
public class Renderer {
private var device: MTLDevice
private var layer: CAMetalLayer
private var backBufferSize: Size<Int>
private var _clearColor: Color<Double>
private var _aspectRatio: Float
private var queue: MTLCommandQueue
private var lib: MTLLibrary
@ -70,6 +70,10 @@ public class Renderer {
var frame: Rect<Int> { .init(origin: .zero, size: self.backBufferSize) }
var aspectRatio: Float { self._aspectRatio }
var clearColor: Color<Double> {
get { self._clearColor }
set { self._clearColor = newValue }
}
fileprivate static func createMetalDevice() -> MTLDevice? {
MTLCopyAllDevices().reduce(nil, { best, dev in
@ -100,10 +104,10 @@ public class Renderer {
self.backBufferSize = size
self._aspectRatio = Float(self.backBufferSize.w) / Float(self.backBufferSize.w)
self._clearColor = .black
passDescription.colorAttachments[0].loadAction = .clear
passDescription.colorAttachments[0].storeAction = .store
passDescription.colorAttachments[0].clearColor = MTLClearColor(clearColor)
passDescription.depthAttachment.loadAction = .clear
passDescription.depthAttachment.storeAction = .dontCare
passDescription.depthAttachment.clearDepth = 1.0
@ -302,6 +306,7 @@ public class Renderer {
throw RendererError.drawFailure("Failed to get next drawable render target")
}
passDescription.colorAttachments[0].clearColor = MTLClearColor(self._clearColor)
passDescription.colorAttachments[0].texture = rt.texture
passDescription.depthAttachment.texture = self.depthTextures[self.currentFrame]
@ -319,7 +324,6 @@ public class Renderer {
throw RendererError.drawFailure("Failed to make render encoder from command buffer")
}
encoder.setCullMode(.back)
encoder.setFrontFacing(.counterClockwise) // OpenGL default
encoder.setViewport(Self.makeViewport(rect: self.frame))
encoder.setRenderPipelineState(pso)
@ -342,17 +346,17 @@ public class Renderer {
}
}
func batch(instances: [Instance], camera: Camera) {
func batch(instances: [Instance], material: Material, environment: Environment, camera: Camera) {
assert(self._encoder != nil, "batch can't be called outside of a frame being rendered")
var vertUniforms = VertexShaderUniforms(projView: camera.viewProjection)
var fragUniforms = FragmentShaderUniforms(
cameraPosition: camera.position,
directionalLight: normalize(.init(0.75, -1, 0.5)),
ambientColor: SIMD4(Color(rgba8888: 0x1F1F1F00).linear),
diffuseColor: SIMD4(Color(rgba8888: 0xEFEFEF00).linear),
specularColor: SIMD4(Color(rgba8888: 0x7F7F7F00).linear),
specularIntensity: 50)
directionalLight: normalize(environment.lightDirection),
ambientColor: SIMD4(Color<Float>(material.ambient)),
diffuseColor: SIMD4(Color<Float>(material.diffuse)),
specularColor: SIMD4(Color<Float>(material.specular)),
specularIntensity: material.gloss)
let numInstances = instances.count
let instancesBytes = numInstances * MemoryLayout<VertexShaderInstance>.stride
@ -385,6 +389,8 @@ public class Renderer {
}
instanceBuffer.didModifyRange(0..<instancesBytes)
self._encoder.setCullMode(.init(environment.cullFace))
self._encoder.setVertexBuffer(instanceBuffer,
offset: 0,
index: VertexShaderInputIdx.instance.rawValue)
@ -412,6 +418,16 @@ extension MTLClearColor {
}
}
fileprivate extension MTLCullMode {
init(_ face: Environment.Face) {
self = switch face {
case .none: .none
case .front: .front
case .back: .back
}
}
}
enum RendererError: Error {
case initFailure(_ message: String)
case loadFailure(_ message: String)