From 296af144ce65f56948e42a6b88706fb667e5e7ae Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Tue, 20 Nov 2018 17:13:31 -0700 Subject: [PATCH] Get face normals rendering --- Terrain2/Renderer.swift | 25 ++++++++++++---- Terrain2/ShaderTypes.h | 12 ++++++-- Terrain2/Shaders/Shaders.metal | 35 +++++++++++++---------- Terrain2/Shaders/TerrainAlgorithms.metal | 23 +++++++++------ Terrain2/Terrain.swift | 36 ++++++++++++++++++------ 5 files changed, 90 insertions(+), 41 deletions(-) diff --git a/Terrain2/Renderer.swift b/Terrain2/Renderer.swift index b1ff7f0..60a65b5 100644 --- a/Terrain2/Renderer.swift +++ b/Terrain2/Renderer.swift @@ -320,21 +320,34 @@ class Renderer: NSObject, MTKViewDelegate { 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.pushDebugGroup("Draw Vertex Normals") - renderEncoder.setVertexBuffer(dynamicUniformBuffer, offset:uniformBufferOffset, index: BufferIndex.uniforms.rawValue) + let vertexBuffer = terrain.mesh.vertexBuffers[BufferIndex.meshPositions.rawValue] + let normalBuffer = terrain.mesh.vertexBuffers[BufferIndex.normals.rawValue] + + renderEncoder.setVertexBuffer(vertexBuffer.buffer, offset: vertexBuffer.offset, index: NormalBufferIndex.points.rawValue) + renderEncoder.setVertexBuffer(normalBuffer.buffer, offset: normalBuffer.offset, index: NormalBufferIndex.normals.rawValue) + renderEncoder.setVertexBuffer(dynamicUniformBuffer, offset: uniformBufferOffset, index: NormalBufferIndex.uniforms.rawValue) renderEncoder.drawPrimitives(type: .line, vertexStart: 0, vertexCount: 2, instanceCount: terrain.mesh.vertexCount) renderEncoder.popDebugGroup() + + renderEncoder.pushDebugGroup("Draw Face Normals") + + let faceMidpointsBuffer = terrain.faceMidpointsBuffer + let faceNormalsBuffer = terrain.faceNormalsBuffer + + renderEncoder.setVertexBuffer(faceMidpointsBuffer, offset: 0, index: NormalBufferIndex.points.rawValue) + renderEncoder.setVertexBuffer(faceNormalsBuffer, offset: 0, index: NormalBufferIndex.normals.rawValue) + renderEncoder.setVertexBuffer(dynamicUniformBuffer, offset:uniformBufferOffset, index: NormalBufferIndex.uniforms.rawValue) + renderEncoder.drawPrimitives(type: .line, vertexStart: 0, vertexCount: 2, instanceCount: 2 * Int(terrain.segments.x * terrain.segments.y)) + renderEncoder.popDebugGroup() + renderEncoder.endEncoding() } diff --git a/Terrain2/ShaderTypes.h b/Terrain2/ShaderTypes.h index 4e99efa..2bc734c 100644 --- a/Terrain2/ShaderTypes.h +++ b/Terrain2/ShaderTypes.h @@ -21,8 +21,7 @@ #include -typedef NS_ENUM(NSInteger, BufferIndex) -{ +typedef NS_ENUM(NSInteger, BufferIndex) { BufferIndexMeshPositions = 0, BufferIndexNormals = 1, BufferIndexMeshGenerics = 2, @@ -30,6 +29,12 @@ typedef NS_ENUM(NSInteger, BufferIndex) BufferIndexUniforms = 4, }; +typedef NS_ENUM(NSInteger, NormalBufferIndex) { + NormalBufferIndexPoints = 0, + NormalBufferIndexNormals = 1, + NormalBufferIndexUniforms = 2, +}; + typedef NS_ENUM(NSInteger, VertexAttribute) { VertexAttributePosition = 0, @@ -48,7 +53,8 @@ typedef NS_ENUM(NSInteger, GeneratorBufferIndex) { GeneratorBufferIndexIndexes = 2, GeneratorBufferIndexNormals = 3, GeneratorBufferIndexFaceNormals = 4, - GeneratorBufferIndexUniforms = 5, + GeneratorBufferIndexFaceMidpoints = 5, + GeneratorBufferIndexUniforms = 6, }; typedef NS_ENUM(NSInteger, GeneratorTextureIndex) { diff --git a/Terrain2/Shaders/Shaders.metal b/Terrain2/Shaders/Shaders.metal index 941f154..921dd2c 100644 --- a/Terrain2/Shaders/Shaders.metal +++ b/Terrain2/Shaders/Shaders.metal @@ -27,19 +27,30 @@ typedef struct { float4 position [[position]]; float3 normal; + float4 color; float2 texCoord; } ColorInOut; #pragma mark - Geometry Shaders vertex ColorInOut vertexShader(Vertex in [[stage_in]], - constant packed_float3 *faceNormals [[buffer(BufferIndexFaceNormals)]], - constant Uniforms &uniforms [[buffer(BufferIndexUniforms)]]) + constant float3 *faceNormals [[buffer(BufferIndexFaceNormals)]], + constant Uniforms &uniforms [[buffer(BufferIndexUniforms)]], + uint vid [[vertex_id]]) { ColorInOut out; - out.position = uniforms.projectionMatrix * uniforms.modelViewMatrix * float4(in.position, 1.0); - out.normal = uniforms.normalMatrix * in.normal; + float4 vertexCoords = float4(in.position, 1.0); + float4 eyeCoords = uniforms.modelViewMatrix * vertexCoords; + out.position = uniforms.projectionMatrix * eyeCoords; + + float3 normal = normalize(uniforms.normalMatrix * in.normal); + out.normal = normal; + + float3 lightDirection = -eyeCoords.xyz; + float lightDotNormal = dot(normal, lightDirection); + out.color = float4(abs(lightDotNormal) * float3(0.3), 1.0); + out.texCoord = in.texCoord; return out; @@ -49,20 +60,14 @@ fragment float4 fragmentShader(ColorInOut in [[stage_in]], constant Uniforms & uniforms [[ buffer(BufferIndexUniforms) ]], texture2d colorMap [[ texture(TextureIndexColor) ]]) { - constexpr sampler colorSampler(mip_filter::linear, - mag_filter::linear, - min_filter::linear); - - half4 colorSample = colorMap.sample(colorSampler, in.texCoord.xy); - - return float4(1.0); + return in.color; } #pragma mark - Normal Shaders -vertex float4 normalVertexShader(constant packed_float3 *positions [[buffer(BufferIndexMeshPositions)]], - constant packed_float3 *normals [[buffer(BufferIndexNormals)]], - constant Uniforms &uniforms [[buffer(BufferIndexUniforms)]], +vertex float4 normalVertexShader(constant packed_float3 *positions [[buffer(NormalBufferIndexPoints)]], + constant packed_float3 *normals [[buffer(NormalBufferIndexNormals)]], + constant Uniforms &uniforms [[buffer(NormalBufferIndexUniforms)]], uint instID [[instance_id]], uint vertID [[vertex_id]]) { @@ -71,7 +76,7 @@ vertex float4 normalVertexShader(constant packed_float3 *positions [[buffer(Buff { v += 0.25 * normals[instID]; } - float4 out = uniforms.projectionMatrix * uniforms.modelViewMatrix * float4(v, 1.0); + float4 out = uniforms.projectionMatrix * uniforms.modelViewMatrix * float4(v, 1); return out; } diff --git a/Terrain2/Shaders/TerrainAlgorithms.metal b/Terrain2/Shaders/TerrainAlgorithms.metal index 2456946..108757b 100644 --- a/Terrain2/Shaders/TerrainAlgorithms.metal +++ b/Terrain2/Shaders/TerrainAlgorithms.metal @@ -61,16 +61,23 @@ kernel void updateGeometryHeights(texture2d texture [[texture(GeneratorTe vertexes[vIdx].y = height.r; } -kernel void updateGeometryNormals(constant float3 *vertexes [[buffer(GeneratorBufferIndexMeshPositions)]], - constant packed_uint3 *indexes [[buffer(GeneratorBufferIndexIndexes)]], - device packed_float3 *normals [[buffer(GeneratorBufferIndexFaceNormals)]], +kernel void updateGeometryNormals(constant packed_float3 *meshPositions [[buffer(GeneratorBufferIndexMeshPositions)]], + constant packed_ushort3 *indexes [[buffer(GeneratorBufferIndexIndexes)]], + device packed_float3 *faceNormals [[buffer(GeneratorBufferIndexFaceNormals)]], + device packed_float3 *faceMidpoints [[buffer(GeneratorBufferIndexFaceMidpoints)]], uint tid [[thread_position_in_grid]]) { - const uint3 triIdx = indexes[tid]; - float3 side1(vertexes[triIdx.y] - vertexes[triIdx.x]); - float3 side2(vertexes[triIdx.y] - vertexes[triIdx.z]); - float3 normal(normalize(cross(side1, side2))); - normals[tid] = normal; + const ushort3 triangleIndex = indexes[tid]; + + const float3 v1 = meshPositions[triangleIndex.x]; + const float3 v2 = meshPositions[triangleIndex.y]; + const float3 v3 = meshPositions[triangleIndex.z]; + + float3 side1 = v1 - v2; + float3 side2 = v1 - v3; + float3 normal = normalize(cross(side1, side2)); + faceNormals[tid] = normal; + faceMidpoints[tid] = 0.3333333333 * (v1 + v2 + v3); } kernel void updateGeometryVertexNormals() diff --git a/Terrain2/Terrain.swift b/Terrain2/Terrain.swift index 7e03d16..47fb40a 100644 --- a/Terrain2/Terrain.swift +++ b/Terrain2/Terrain.swift @@ -88,6 +88,7 @@ class Terrain: NSObject { let vertexDescriptor: MTLVertexDescriptor let mesh: MTKMesh let faceNormalsBuffer: MTLBuffer + let faceMidpointsBuffer: MTLBuffer var generator: TerrainGenerator @@ -118,15 +119,23 @@ class Terrain: NSObject { return nil } - // A normal is a float 3. Two triangles per segment, x * t segments. - let faceNormalsLength = MemoryLayout.stride * 2 * Int(segments.x * segments.y) - guard let faceNormalsBuf = device.makeBuffer(length: faceNormalsLength, options: .storageModePrivate) else { + // A normal is a float3. Two triangles per segment, x * t segments. + let faceDataLength = 12 * 2 * Int(segments.x * segments.y) + guard let faceNormalsBuf = device.makeBuffer(length: faceDataLength, options: .storageModeShared) else { print("Couldn't create buffer for face normals") return nil } faceNormalsBuffer = faceNormalsBuf + guard let faceMidpointsBuf = device.makeBuffer(length: faceDataLength, options: .storageModeShared) else { + print("Couldn't create buffer for face normals") + return nil + } + faceMidpointsBuffer = faceMidpointsBuf + super.init() + + populateInitialFaceNormals() } func generate(completion: @escaping () -> Void) -> Progress { @@ -134,7 +143,7 @@ class Terrain: NSObject { generatorQueue.async { progress.becomeCurrent(withPendingUnitCount: 1) - let _ = self.generator.render(progress: progress) + let heights = self.generator.render(progress: progress) progress.completedUnitCount += 1 // TODO: Store heights @@ -149,7 +158,7 @@ class Terrain: NSObject { func scheduleGeometryUpdates(inCommandBuffer commandBuffer: MTLCommandBuffer, uniforms: MTLBuffer, uniformsOffset: Int) { if let computeEncoder = commandBuffer.makeComputeCommandEncoder() { - print("Scheduling update geometry heights") + //print("Scheduling update geometry heights") computeEncoder.label = "Geometry Heights Encoder" computeEncoder.pushDebugGroup("Update Geometry: Heights") computeEncoder.setComputePipelineState(updateHeightsPipeline) @@ -165,18 +174,27 @@ class Terrain: NSObject { } if let computeEncoder = commandBuffer.makeComputeCommandEncoder() { - print("Scheduling update geometry normals") + //print("Scheduling update geometry normals") computeEncoder.label = "Surface Normals Encoder" computeEncoder.pushDebugGroup("Update Geometry: Surface Normals") computeEncoder.setComputePipelineState(updateSurfaceNormalsPipeline) let indexBuffer = mesh.submeshes[0].indexBuffer - 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: GeneratorBufferIndex.indexes.rawValue) + computeEncoder.setBuffer(indexBuffer.buffer, offset: indexBuffer.offset, index: GeneratorBufferIndex.indexes.rawValue) + let positionsBuffer = mesh.vertexBuffers[BufferIndex.meshPositions.rawValue] + computeEncoder.setBuffer(positionsBuffer.buffer, offset: positionsBuffer.offset, index: GeneratorBufferIndex.meshPositions.rawValue) computeEncoder.setBuffer(faceNormalsBuffer, offset: 0, index: GeneratorBufferIndex.faceNormals.rawValue) + computeEncoder.setBuffer(faceMidpointsBuffer, offset: 0, index: GeneratorBufferIndex.faceMidpoints.rawValue) computeEncoder.dispatchThreads(MTLSize(width: 2 * Int(segments.x * segments.y), height: 1, depth: 1), threadsPerThreadgroup: MTLSize(width: 64, height: 1, depth: 1)) computeEncoder.popDebugGroup() computeEncoder.endEncoding() } } + + private func populateInitialFaceNormals() { + let normalsCount = 2 * Int(segments.x * segments.y) + let faceNormals = UnsafeMutableRawPointer(faceNormalsBuffer.contents()).bindMemory(to: float3.self, capacity: normalsCount) + for i in 0..