refactor chunk rendering and fix chunk rendering precision loss at large positions

This commit is contained in:
2024-09-09 02:20:31 +10:00
parent 6e99401474
commit 935aa3e765
8 changed files with 158 additions and 65 deletions

View File

@ -0,0 +1,42 @@
import simd
public struct ChunkRenderer {
private weak var _renderer: Renderer?
private var _renderChunks = [ChunkID: RendererMesh]()
public var material: Material
public init(renderer: Renderer) {
self._renderer = renderer
self.material = .init(ambient: .black, diffuse: .white, specular: .white, gloss: 20.0)
}
public mutating func draw(environment: Environment, camera globalCamera: Camera) {
let fChunkSz = Float(Chunk.size), divisor = 1 / fChunkSz
let origin = SIMD3<Int>(floor(globalCamera.position * divisor), rounding: .down)
let localCamera = Camera(globalCamera)
localCamera.position = globalCamera.position - SIMD3<Float>(origin) * fChunkSz
self._renderer!.setupBatch(environment: environment, camera: localCamera)
for (chunkID, mesh) in self._renderChunks {
let drawPos = SIMD3<Float>(SIMD3<Int>(chunkID) &- origin) * fChunkSz
self._renderer!.submit(
mesh: mesh,
instance: .init(world: .translate(drawPos)),
material: self.material)
}
}
public mutating func addChunk(id chunkID: ChunkID, mesh: RendererMesh) {
self._renderChunks.updateValue(mesh, forKey: chunkID)
}
public mutating func removeChunk(id chunkID: ChunkID) {
self._renderChunks.removeValue(forKey: chunkID)
}
public mutating func removeAll() {
self._renderChunks.removeAll()
}
}

View File

@ -18,12 +18,12 @@ public struct ModelBatch {
self._cam = camera
self._env = environment
self._prev = nil
self._renderer.setupBatch(material: Game.material, environment: environment, camera: camera)
self._renderer.setupBatch(environment: environment, camera: camera)
}
private mutating func flush() {
assert(self._instances.count > 0)
self._renderer.submitBatch(mesh: self._prev.mesh, instances: self._instances)
self._renderer.submitBatch(mesh: self._prev.mesh, instances: self._instances, material: self._prev.material)
self._instances.removeAll(keepingCapacity: true)
self._prev = nil
}

View File

@ -23,6 +23,7 @@ public class Renderer {
private var depthTextures: [MTLTexture]
//private var _instances: [MTLBuffer?]
private var _cameraPos: SIMD3<Float> = .zero, _directionalDir: SIMD3<Float> = .zero
private var _encoder: MTLRenderCommandEncoder! = nil
@ -381,17 +382,13 @@ public class Renderer {
return ModelBatch(self)
}
internal func setupBatch(material: Material, environment: Environment, camera: Camera) {
internal func setupBatch(environment: Environment, camera: Camera) {
assert(self._encoder != nil, "startBatch can't be called outside of a frame being rendered")
var vertUniforms = VertexShaderUniforms(projView: camera.viewProjection)
var fragUniforms = FragmentShaderUniforms(
cameraPosition: camera.position,
directionalLight: normalize(environment.lightDirection),
ambientColor: SIMD4(material.ambient),
diffuseColor: SIMD4(material.diffuse),
specularColor: SIMD4(material.specular),
specularIntensity: material.gloss)
self._cameraPos = camera.position
self._directionalDir = simd_normalize(environment.lightDirection)
self._encoder.setCullMode(.init(environment.cullFace))
@ -399,12 +396,40 @@ public class Renderer {
self._encoder.setVertexBytes(&vertUniforms,
length: MemoryLayout<VertexShaderUniforms>.stride,
index: VertexShaderInputIdx.uniforms.rawValue)
}
internal func submit(mesh: RendererMesh, instance: ModelBatch.Instance, material: Material) {
assert(self._encoder != nil, "submit can't be called outside of a frame being rendered")
var instanceData = VertexShaderInstance(
model: instance.world,
normalModel: instance.world.inverse.transpose,
color: SIMD4(instance.color))
var fragUniforms = FragmentShaderUniforms(
cameraPosition: self._cameraPos,
directionalLight: self._directionalDir,
ambientColor: SIMD4(material.ambient),
diffuseColor: SIMD4(material.diffuse),
specularColor: SIMD4(material.specular),
specularIntensity: material.gloss)
self._encoder.setVertexBuffer(mesh._vertBuf, offset: 0, index: VertexShaderInputIdx.vertices.rawValue)
// Ideal as long as our uniforms total 4 KB or less
self._encoder.setVertexBytes(&instanceData,
length: MemoryLayout<VertexShaderInstance>.stride,
index: VertexShaderInputIdx.instance.rawValue)
self._encoder.setFragmentBytes(&fragUniforms,
length: MemoryLayout<FragmentShaderUniforms>.stride,
index: FragmentShaderInputIdx.uniforms.rawValue)
self._encoder.drawIndexedPrimitives(
type: .triangle,
indexCount: mesh.numIndices,
indexType: .uint16,
indexBuffer: mesh._idxBuf,
indexBufferOffset: 0)
}
internal func submitBatch(mesh: RendererMesh, instances: [ModelBatch.Instance]) {
internal func submitBatch(mesh: RendererMesh, instances: [ModelBatch.Instance], material: Material) {
assert(self._encoder != nil, "submitBatch can't be called outside of a frame being rendered")
let numInstances = instances.count
assert(numInstances > 0, "submitBatch called with zero instances")
@ -451,12 +476,22 @@ public class Renderer {
normalModel: instance.world.inverse.transpose,
color: SIMD4(instance.color))
}
var fragUniforms = FragmentShaderUniforms(
cameraPosition: self._cameraPos,
directionalLight: self._directionalDir,
ambientColor: SIMD4(material.ambient),
diffuseColor: SIMD4(material.diffuse),
specularColor: SIMD4(material.specular),
specularIntensity: material.gloss)
self._encoder.setVertexBuffer(mesh._vertBuf, offset: 0, index: VertexShaderInputIdx.vertices.rawValue)
// Ideal as long as our uniforms total 4 KB or less
self._encoder.setVertexBytes(instanceData,
length: numInstances * MemoryLayout<VertexShaderInstance>.stride,
index: VertexShaderInputIdx.instance.rawValue)
self._encoder.setFragmentBytes(&fragUniforms,
length: MemoryLayout<FragmentShaderUniforms>.stride,
index: FragmentShaderInputIdx.uniforms.rawValue)
self._encoder.drawIndexedPrimitives(
type: .triangle,