diff --git a/Terrain2/Renderer.swift b/Terrain2/Renderer.swift index 8e4e06b..12ce84e 100644 --- a/Terrain2/Renderer.swift +++ b/Terrain2/Renderer.swift @@ -32,6 +32,7 @@ class Renderer: NSObject, MTKViewDelegate { let commandQueue: MTLCommandQueue var dynamicUniformBuffer: MTLBuffer var pipelineState: MTLRenderPipelineState + var normalPipelineState: MTLRenderPipelineState var depthState: MTLDepthStencilState var colorMap: MTLTexture @@ -82,6 +83,7 @@ class Renderer: NSObject, MTKViewDelegate { library: library, metalKitView: metalKitView, mtlVertexDescriptor: terrain.vertexDescriptor) + normalPipelineState = try Renderer.buildNormalRenderPipeline(withDevice: device, library: library, view: metalKitView) } catch { print("Unable to compile render pipeline state. Error info: \(error)") return nil @@ -112,7 +114,7 @@ class Renderer: NSObject, MTKViewDelegate { let fragmentFunction = library.makeFunction(name: "fragmentShader") let pipelineDescriptor = MTLRenderPipelineDescriptor() - pipelineDescriptor.label = "RenderPipeline" + pipelineDescriptor.label = "Geometry Render Pipeline" pipelineDescriptor.sampleCount = metalKitView.sampleCount pipelineDescriptor.vertexFunction = vertexFunction pipelineDescriptor.fragmentFunction = fragmentFunction @@ -125,6 +127,23 @@ class Renderer: NSObject, MTKViewDelegate { return try device.makeRenderPipelineState(descriptor: pipelineDescriptor) } + class func buildNormalRenderPipeline(withDevice device: MTLDevice, library: MTLLibrary, view: MTKView) throws -> MTLRenderPipelineState { + let vertexFunction = library.makeFunction(name: "normalVertexShader") + let fragmentFunction = library.makeFunction(name: "normalFragmentShader") + + let pipelineDescriptor = MTLRenderPipelineDescriptor() + pipelineDescriptor.label = "Normal Render Pipeline" + pipelineDescriptor.sampleCount = view.sampleCount + pipelineDescriptor.vertexFunction = vertexFunction + pipelineDescriptor.fragmentFunction = fragmentFunction + + pipelineDescriptor.colorAttachments[0].pixelFormat = view.colorPixelFormat + pipelineDescriptor.depthAttachmentPixelFormat = view.depthStencilPixelFormat + pipelineDescriptor.stencilAttachmentPixelFormat = view.depthStencilPixelFormat + + return try device.makeRenderPipelineState(descriptor: pipelineDescriptor) + } + class func buildUpdateGeometryNormalsPipeline(withDevice device: MTLDevice, library: MTLLibrary) throws -> MTLComputePipelineState { guard let computeFunction = library.makeFunction(name: "updateGeometryNormals") else { throw RendererError.badComputeFunction @@ -242,7 +261,6 @@ class Renderer: NSObject, MTKViewDelegate { /// Final pass rendering code here if let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptor) { renderEncoder.label = "Primary Render Encoder" - renderEncoder.pushDebugGroup("Draw Plane") renderEncoder.setCullMode(.none) @@ -281,12 +299,35 @@ class Renderer: NSObject, MTKViewDelegate { } renderEncoder.popDebugGroup() - renderEncoder.endEncoding() - - if let drawable = view.currentDrawable { - commandBuffer.present(drawable) - } + } + + let normalsRenderPassDescriptor = renderPassDescriptor.copy() as! MTLRenderPassDescriptor + normalsRenderPassDescriptor.colorAttachments[0].loadAction = .load + normalsRenderPassDescriptor.colorAttachments[0].storeAction = .store + + if let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: normalsRenderPassDescriptor) { + renderEncoder.label = "Normals Render Encoder" + renderEncoder.pushDebugGroup("Draw Normals") + + renderEncoder.setRenderPipelineState(normalPipelineState) + renderEncoder.setDepthStencilState(depthState) + + let vertexBuffer = terrain.mesh.vertexBuffers[BufferIndex.meshPositions.rawValue] + renderEncoder.setVertexBuffer(vertexBuffer.buffer, offset: vertexBuffer.offset, index: BufferIndex.meshPositions.rawValue) + let normalBuffer = terrain.mesh.vertexBuffers[BufferIndex.normals.rawValue] + renderEncoder.setVertexBuffer(normalBuffer.buffer, offset: normalBuffer.offset, index: BufferIndex.normals.rawValue) + + renderEncoder.setVertexBuffer(dynamicUniformBuffer, offset:uniformBufferOffset, index: BufferIndex.uniforms.rawValue) + + renderEncoder.drawPrimitives(type: .line, vertexStart: 0, vertexCount: 2, instanceCount: terrain.mesh.vertexCount) + + renderEncoder.popDebugGroup() + renderEncoder.endEncoding() + } + + if let drawable = view.currentDrawable { + commandBuffer.present(drawable) } } diff --git a/Terrain2/ShaderTypes.h b/Terrain2/ShaderTypes.h index fee7472..c7433bb 100644 --- a/Terrain2/ShaderTypes.h +++ b/Terrain2/ShaderTypes.h @@ -33,7 +33,7 @@ typedef NS_ENUM(NSInteger, VertexAttribute) { VertexAttributePosition = 0, VertexAttributeNormal = 1, - VertexAttributeTexcoord = 2, + VertexAttributeTexCoord = 2, }; typedef NS_ENUM(NSInteger, TextureIndex) @@ -42,7 +42,7 @@ typedef NS_ENUM(NSInteger, TextureIndex) }; typedef NS_ENUM(NSInteger, GeneratorBufferIndex) { - GeneratorBufferIndexVertexes = 0, + GeneratorBufferIndexMeshPositions = 0, GeneratorBufferIndexTexCoords = 1, GeneratorBufferIndexIndexes = 2, GeneratorBufferIndexNormals = 3, diff --git a/Terrain2/Shaders/Shaders.metal b/Terrain2/Shaders/Shaders.metal index 4eb4fe0..4afb2a2 100644 --- a/Terrain2/Shaders/Shaders.metal +++ b/Terrain2/Shaders/Shaders.metal @@ -20,7 +20,7 @@ typedef struct { float3 position [[attribute(VertexAttributePosition)]]; float3 normal [[attribute(VertexAttributeNormal)]]; - float2 texCoord [[attribute(VertexAttributeTexcoord)]]; + float2 texCoord [[attribute(VertexAttributeTexCoord)]]; } Vertex; typedef struct @@ -65,17 +65,19 @@ fragment float4 fragmentShader(ColorInOut in [[stage_in]], #pragma mark - Normal Shaders -vertex float4 normalVertexShader(constant float3 *positions [[buffer(BufferIndexMeshPositions)]], - constant float3 *normals [[buffer(BufferIndexNormals)]], +vertex float4 normalVertexShader(constant packed_float3 *positions [[buffer(BufferIndexMeshPositions)]], + constant packed_float3 *normals [[buffer(BufferIndexNormals)]], + constant Uniforms &uniforms [[buffer(BufferIndexUniforms)]], uint instID [[instance_id]], uint vertID [[vertex_id]]) { - float3 out = positions[instID]; + float3 v = positions[instID]; if ( vertID == 1 ) { - out += normals[instID]; + v += normals[instID]; } - return float4(out, 1.0); + float4 out = uniforms.projectionMatrix * uniforms.modelViewMatrix * float4(v, 1.0); + return out; } fragment half4 normalFragmentShader() diff --git a/Terrain2/Terrain.swift b/Terrain2/Terrain.swift index 92b7a7b..74333d8 100644 --- a/Terrain2/Terrain.swift +++ b/Terrain2/Terrain.swift @@ -23,9 +23,9 @@ class Terrain: NSObject { desc.attributes[VertexAttribute.normal.rawValue].offset = 0 desc.attributes[VertexAttribute.normal.rawValue].bufferIndex = BufferIndex.normals.rawValue - desc.attributes[VertexAttribute.texcoord.rawValue].format = .float2 - desc.attributes[VertexAttribute.texcoord.rawValue].offset = 0 - desc.attributes[VertexAttribute.texcoord.rawValue].bufferIndex = BufferIndex.meshGenerics.rawValue + desc.attributes[VertexAttribute.texCoord.rawValue].format = .float2 + desc.attributes[VertexAttribute.texCoord.rawValue].offset = 0 + desc.attributes[VertexAttribute.texCoord.rawValue].bufferIndex = BufferIndex.meshGenerics.rawValue desc.layouts[BufferIndex.meshPositions.rawValue].stride = 12 desc.layouts[BufferIndex.meshPositions.rawValue].stepRate = 1 @@ -63,7 +63,7 @@ class Terrain: NSObject { } attributes[VertexAttribute.position.rawValue].name = MDLVertexAttributePosition attributes[VertexAttribute.normal.rawValue].name = MDLVertexAttributeNormal - attributes[VertexAttribute.texcoord.rawValue].name = MDLVertexAttributeTextureCoordinate + attributes[VertexAttribute.texCoord.rawValue].name = MDLVertexAttributeTextureCoordinate plane.vertexDescriptor = mdlVertexDescriptor @@ -82,8 +82,7 @@ class Terrain: NSObject { private let updateHeightsPipeline: MTLComputePipelineState private let updateSurfaceNormalsPipeline: MTLComputePipelineState - private let updateVertexNormalsPipeline: MTLComputePipelineState - + let dimensions: float2 let segments: uint2 let vertexDescriptor: MTLVertexDescriptor @@ -113,7 +112,6 @@ class Terrain: NSObject { do { updateHeightsPipeline = try Terrain.computePipeline(withFunctionNamed: "updateGeometryHeights", device: device, library: library) updateSurfaceNormalsPipeline = try Terrain.computePipeline(withFunctionNamed: "updateGeometryNormals", device: device, library: library) -// updateVertexNormalsPipeline = try Terrain.computePipeline(withFunctionNamed: "updateGeometryVertexNormals", device: device, library: library) } catch { print("Unable to create compute pipelines for terrain geometry updates. Error: \(error)") return nil @@ -148,10 +146,10 @@ class Terrain: NSObject { computeEncoder.setComputePipelineState(updateHeightsPipeline) computeEncoder.setTexture(generator.outTexture, index: GeneratorTextureIndex.in.rawValue) let vertexBuffer = mesh.vertexBuffers[BufferIndex.meshPositions.rawValue] - computeEncoder.setBuffer(vertexBuffer.buffer, offset: vertexBuffer.offset, index: BufferIndex.meshPositions.rawValue) + computeEncoder.setBuffer(vertexBuffer.buffer, offset: vertexBuffer.offset, index: GeneratorBufferIndex.meshPositions.rawValue) let texCoordBuffer = mesh.vertexBuffers[BufferIndex.meshGenerics.rawValue] - computeEncoder.setBuffer(texCoordBuffer.buffer, offset: texCoordBuffer.offset, index: BufferIndex.texCoords.rawValue) - computeEncoder.setBuffer(uniforms, offset: uniformsOffset, index: BufferIndex.uniforms.rawValue) + computeEncoder.setBuffer(texCoordBuffer.buffer, offset: texCoordBuffer.offset, index: GeneratorBufferIndex.texCoords.rawValue) + computeEncoder.setBuffer(uniforms, offset: uniformsOffset, index: GeneratorBufferIndex.uniforms.rawValue) computeEncoder.dispatchThreads(MTLSize(width: Int(segments.x + 1), height: Int(segments.y + 1), depth: 1), threadsPerThreadgroup: MTLSize(width: 8, height: 8, depth: 1)) computeEncoder.popDebugGroup() computeEncoder.endEncoding() @@ -163,11 +161,11 @@ class Terrain: NSObject { computeEncoder.pushDebugGroup("Update Geometry: Surface Normals") computeEncoder.setComputePipelineState(updateSurfaceNormalsPipeline) let indexBuffer = mesh.submeshes[0].indexBuffer - computeEncoder.setBuffer(indexBuffer.buffer, offset: indexBuffer.offset, index: BufferIndex.meshPositions.rawValue) + computeEncoder.setBuffer(indexBuffer.buffer, offset: indexBuffer.offset, index: GeneratorBufferIndex.meshPositions.rawValue) let vertexBuffer = mesh.vertexBuffers[BufferIndex.meshPositions.rawValue] - computeEncoder.setBuffer(vertexBuffer.buffer, offset: vertexBuffer.offset, index: BufferIndex.meshPositions.rawValue) - let normalBuffer = mesh.vertexBuffers[BufferIndex.faceNormals.rawValue] - computeEncoder.setBuffer(normalBuffer.buffer, offset: normalBuffer.offset, index: BufferIndex.faceNormals.rawValue) + computeEncoder.setBuffer(vertexBuffer.buffer, offset: vertexBuffer.offset, index: GeneratorBufferIndex.indexes.rawValue) +// let normalBuffer = mesh.vertexBuffers[BufferIndex.faceNormals.rawValue] +// computeEncoder.setBuffer(normalBuffer.buffer, offset: normalBuffer.offset, index: BufferIndex.faceNormals.rawValue) computeEncoder.dispatchThreads(MTLSize(width: mesh.vertexCount, height: 1, depth: 1), threadsPerThreadgroup: MTLSize(width: 64, height: 1, depth: 1)) computeEncoder.popDebugGroup() computeEncoder.endEncoding()