diff --git a/engine/imgui/src/sgpu/backend_impl.zig b/engine/imgui/src/sgpu/backend_impl.zig index 813069b..e8c65d5 100644 --- a/engine/imgui/src/sgpu/backend_impl.zig +++ b/engine/imgui/src/sgpu/backend_impl.zig @@ -77,6 +77,8 @@ pub const Impl = struct { if (ig.begin("renderer debug", &self.rendererDebug, .{})) { _ = ig.sliderFloat("directional light yaw", &rend.context().directionalLightYaw, 0, 360, null, .{}); + _ = ig.sliderFloat("ortho near", &rend.context().shadowOrthoNear, 0, 200, null, .{}); + _ = ig.sliderFloat("ortho far", &rend.context().shadowOrthoFar, 0, 6000, null, .{}); ig.end(); } } diff --git a/engine/rend/build.zig b/engine/rend/build.zig index d00ab36..c1faebd 100644 --- a/engine/rend/build.zig +++ b/engine/rend/build.zig @@ -39,6 +39,8 @@ pub fn build(b: *std.Build) void { sdl3.shaderDefintion(b, mod, "../../lib/sdl3", optimize, "debug.frag", b.path("shaders/debug.frag.json")); sdl3.shaderDefintion(b, mod, "../../lib/sdl3", optimize, "debug.vert", b.path("shaders/debug.vert.json")); + sdl3.shaderDefintion(b, mod, "../../lib/sdl3", optimize, "depthOnly.frag", b.path("shaders/depthOnly.frag.json")); + // ========== tests ========== const tests = b.addTest(.{ .target = target, diff --git a/engine/rend/shaders/depthOnly.frag.hlsl b/engine/rend/shaders/depthOnly.frag.hlsl new file mode 100644 index 0000000..72c2022 --- /dev/null +++ b/engine/rend/shaders/depthOnly.frag.hlsl @@ -0,0 +1,10 @@ +float4 main( + float2 UV : TEXCOORD0, + float3 WorldPos: TEXCOORD1, + float3 Normal: TEXCOORD2, + float4 DirectionalShadowFragPos: TEXCOORD3 + +) : SV_Target0 +{ + return float4(1.0, 1.0, 1.0, 1.0); +} diff --git a/engine/rend/shaders/depthOnly.frag.json b/engine/rend/shaders/depthOnly.frag.json new file mode 100644 index 0000000..ae8d2c0 --- /dev/null +++ b/engine/rend/shaders/depthOnly.frag.json @@ -0,0 +1,15 @@ +{ + "entryPoints" : [ + { + "name" : "main", + "mode" : "frag" + } + ], + "outputs" : [ + { + "type" : "vec4", + "name" : "out.var.SV_Target0", + "location" : 0 + } + ] +} \ No newline at end of file diff --git a/engine/rend/shaders/lit_mesh.frag.hlsl b/engine/rend/shaders/lit_mesh.frag.hlsl index 3556204..a97b9e1 100644 --- a/engine/rend/shaders/lit_mesh.frag.hlsl +++ b/engine/rend/shaders/lit_mesh.frag.hlsl @@ -1,12 +1,16 @@ Texture2D Texture : register(t0, space2); SamplerState Sampler : register(s0, space2); +Texture2D DirectionalShadowDepthMap : register(t1, space2); +SamplerState DirectionalShadowSampler : register(s1, space2); + cbuffer Uniforms : register(b0, space3) { float4 viewPos; float4 lightPosition; float4 directionalLight; float4 directionalLightColor; + float2 screenSize; float time; }; @@ -68,8 +72,26 @@ float3 BlinnPhong(float3 normal, float3 fragPos, float3 lightPos, float3 lightCo } -float3 DirectionalLight(float3 normal, float3 fragPos) +float shadowCalculation(float3 fragPos, float4 DirectionalShadowFragPos) { + float3 shadowProjection = DirectionalShadowFragPos.xyz / DirectionalShadowFragPos.w; + shadowProjection = shadowProjection * 0.5 + 0.5 ; + shadowProjection.x = shadowProjection.x; + + float2 uv = shadowProjection.xy; + uv.y = -uv.y; + float depthSample = DirectionalShadowDepthMap.Sample(DirectionalShadowSampler, uv); + float fragDepth = DirectionalShadowFragPos.z; + + float bias = 0.005; + float shadow = fragDepth - bias > depthSample ? 1.0 : 0.0; + + return shadow; +} + +float3 DirectionalLight(float3 normal, float3 fragPos, float4 DirectionalShadowFragPos) +{ + float3 lightDir = directionalLight.xyz; float diff = max(dot(lightDir, normal), 0.0); float3 diffuse = diff * directionalLightColor.xyz; @@ -82,13 +104,21 @@ float3 DirectionalLight(float3 normal, float3 fragPos) spec = pow(max(dot(normal, halfwayDir), 0.0), 30.0); float3 specular = spec * directionalLightColor.xyz * 2; + // shadow mapping + + diffuse = diffuse * (1.0 - shadowCalculation(fragPos, DirectionalShadowFragPos)); + return diffuse; } + float4 main( + float4 ScreenPosition: SV_POSITION, + float2 UV : TEXCOORD0, float3 WorldPos: TEXCOORD1, - float3 Normal: TEXCOORD2 + float3 Normal: TEXCOORD2, + float4 DirectionalShadowFragPos: TEXCOORD3 ) : SV_Target0 { float4 s = Texture.Sample(Sampler, UV); @@ -102,14 +132,33 @@ float4 main( float3 lightPos = lightPosition.xyz; //+ float3(0, 20, 0); float3 lightColor = float3(0.8, 0.8, 0.5); - float3 ambient = lerp(float3(0.01, 0.01, 0.003), float3(0.004, 0.004, 0.06), dot(Normal, float3(0.0,1.0,0.0)) ) * 5; + float3 ambient = float3(0.1,0.1,0.1); float3 col = s.xyz; - float3 c2 = col * ambient + col * BlinnPhong(Normal, WorldPos, lightPos, lightColor) + col * DirectionalLight(Normal, WorldPos); + float3 c2 = col * ambient + + col * BlinnPhong(Normal, WorldPos, lightPos, lightColor) + + col * DirectionalLight(Normal, WorldPos, DirectionalShadowFragPos); float4 rv = float4(pow(c2, 1.0 / 2.2), alpha); +// === debug bone zone + // float3 shadowProjection = DirectionalShadowFragPos.xyz / DirectionalShadowFragPos.w; + // shadowProjection = shadowProjection * 0.5 + 0.5 ; + // shadowProjection.x = shadowProjection.x; + + // float2 uv = shadowProjection.xy; + // uv.y = -uv.y; + // float depthSample = DirectionalShadowDepthMap.Sample(DirectionalShadowSampler, uv); + // float fragDepth = shadowProjection.z; + + // float t = DirectionalShadowFragPos.z; + // t = depthSample; + + // rv = float4(t, t, t, 1.0); + + // rv.x = rv.x + lightPos.x * 0.0001; + return rv; } diff --git a/engine/rend/shaders/lit_mesh.frag.json b/engine/rend/shaders/lit_mesh.frag.json index f40582d..982e4b0 100644 --- a/engine/rend/shaders/lit_mesh.frag.json +++ b/engine/rend/shaders/lit_mesh.frag.json @@ -6,7 +6,7 @@ } ], "types" : { - "_11" : { + "_14" : { "name" : "type.Uniforms", "members" : [ { @@ -29,10 +29,15 @@ "type" : "vec4", "offset" : 48 }, + { + "name" : "screenSize", + "type" : "vec2", + "offset" : 64 + }, { "name" : "time", "type" : "float", - "offset" : 64 + "offset" : 72 } ] } @@ -52,6 +57,11 @@ "type" : "vec3", "name" : "in.var.TEXCOORD2", "location" : 2 + }, + { + "type" : "vec4", + "name" : "in.var.TEXCOORD3", + "location" : 3 } ], "outputs" : [ @@ -67,6 +77,12 @@ "name" : "Texture", "set" : 2, "binding" : 0 + }, + { + "type" : "texture2D", + "name" : "DirectionalShadowDepthMap", + "set" : 2, + "binding" : 1 } ], "separate_samplers" : [ @@ -75,13 +91,19 @@ "name" : "Sampler", "set" : 2, "binding" : 0 + }, + { + "type" : "sampler", + "name" : "DirectionalShadowSampler", + "set" : 2, + "binding" : 1 } ], "ubos" : [ { - "type" : "_11", + "type" : "_14", "name" : "type.Uniforms", - "block_size" : 68, + "block_size" : 76, "set" : 3, "binding" : 0 } diff --git a/engine/rend/shaders/meshes.vert.hlsl b/engine/rend/shaders/meshes.vert.hlsl index 4333206..1a59302 100644 --- a/engine/rend/shaders/meshes.vert.hlsl +++ b/engine/rend/shaders/meshes.vert.hlsl @@ -1,3 +1,5 @@ +// I just reuse this one for the shadow mapping right? + struct Input { float3 Position : TEXCOORD0; @@ -15,6 +17,7 @@ struct Output float2 TexCoord : TEXCOORD0; float3 WorldPos : TEXCOORD1; float3 Normal: TEXCOORD2; + float4 DirectionalShadowFragPos: TEXCOORD3; float4 Position : SV_Position; }; @@ -29,6 +32,7 @@ StructuredBuffer scene: register(t0, space0); cbuffer Uniforms : register(b0, space1) { float4x4 ViewProjection; + float4x4 ShadowMapProjection; float time; }; @@ -46,6 +50,7 @@ Output main(Input input) output.Position = mul(ViewProjection, mul(scene[input.Instance].Model, pos)); output.WorldPos = WorldPos.xyz; output.Normal = input.Normal; + output.DirectionalShadowFragPos = mul(ShadowMapProjection, WorldPos); return output; } diff --git a/engine/rend/shaders/meshes.vert.json b/engine/rend/shaders/meshes.vert.json index 223906d..c135b46 100644 --- a/engine/rend/shaders/meshes.vert.json +++ b/engine/rend/shaders/meshes.vert.json @@ -6,7 +6,7 @@ } ], "types" : { - "_11" : { + "_12" : { "name" : "Scene", "members" : [ { @@ -18,12 +18,12 @@ } ] }, - "_10" : { + "_11" : { "name" : "type.StructuredBuffer.Scene", "members" : [ { "name" : "_m0", - "type" : "_11", + "type" : "_12", "array" : [ 0 ], @@ -35,7 +35,7 @@ } ] }, - "_13" : { + "_14" : { "name" : "type.Uniforms", "members" : [ { @@ -45,10 +45,17 @@ "matrix_stride" : 16, "row_major" : true }, + { + "name" : "ShadowMapProjection", + "type" : "mat4", + "offset" : 64, + "matrix_stride" : 16, + "row_major" : true + }, { "name" : "time", "type" : "float", - "offset" : 64 + "offset" : 128 } ] } @@ -85,11 +92,16 @@ "type" : "vec3", "name" : "out.var.TEXCOORD2", "location" : 2 + }, + { + "type" : "vec4", + "name" : "out.var.TEXCOORD3", + "location" : 3 } ], "ssbos" : [ { - "type" : "_10", + "type" : "_11", "name" : "scene", "readonly" : true, "block_size" : 0, @@ -99,9 +111,9 @@ ], "ubos" : [ { - "type" : "_13", + "type" : "_14", "name" : "type.Uniforms", - "block_size" : 68, + "block_size" : 132, "set" : 1, "binding" : 0 } diff --git a/engine/rend/src/sgpu/DebugDrawSystem.zig b/engine/rend/src/sgpu/DebugDrawSystem.zig index 8cf20e4..e0a2bda 100644 --- a/engine/rend/src/sgpu/DebugDrawSystem.zig +++ b/engine/rend/src/sgpu/DebugDrawSystem.zig @@ -193,7 +193,7 @@ pub fn createPipeline(self: *@This()) !*gpu.GPUGraphicsPipeline { pci.depth_stencil_state.write_mask = 0xff; pci.target_info.has_depth_stencil_target = true; - pci.target_info.depth_stencil_format = .textureformatD32Float; + pci.target_info.depth_stencil_format = rend.context().depthFormat; pci.target_info.num_color_targets = 1; pci.target_info.color_target_descriptions = &[_]gpu.GPUColorTargetDescription{ .{ diff --git a/engine/rend/src/sgpu/renderer.zig b/engine/rend/src/sgpu/renderer.zig index 6ba28e5..bc93b8f 100644 --- a/engine/rend/src/sgpu/renderer.zig +++ b/engine/rend/src/sgpu/renderer.zig @@ -37,9 +37,12 @@ pub const Renderer = struct { preDraws: std.ArrayListUnmanaged(struct { ptr: *anyopaque, func: *const fn (*anyopaque, *gpu.GPUCommandBuffer) void }) = .{}, depthTexture: *gpu.GPUTexture = undefined, + depthFormat: gpu.GPUTextureFormat = .textureformatD16Unorm, textureList: *TextureList = undefined, + shadowMapProjection: core.Transform = core.zm.identity(), + blockySampler: *gpu.GPUSampler = undefined, defaultTexture: *rend.Texture = undefined, @@ -47,9 +50,17 @@ pub const Renderer = struct { debugDrawSys: *DebugDrawSystem = undefined, + shadowOrthoNear: f32 = -150, + shadowOrthoFar: f32 = 150, + directionalLightYaw: f32 = 0.0, directionalLightColor: core.colors.Color = .{ .r = 0.8, .g = 0.8, .b = 0.9 }, - directionalLightDir: core.Vectorf = core.Vectorf.new(0.5, 0.25, -1).normalize(), + directionalLightDir: core.Vectorf = core.Vectorf.new(0.5, -0.5, 0.0).normalize(), + + shadowDepthFormat: gpu.GPUTextureFormat = .textureformatD16Unorm, + shadowDepthTexture: *gpu.GPUTexture = undefined, + shadowDepthDebugOutput: *gpu.GPUTexture = undefined, + shadowCastingPipeline: *gpu.GPUGraphicsPipeline = undefined, // transients DO NOT TOUCH swapchainTexture: ?*gpu.GPUTexture = undefined, @@ -118,6 +129,13 @@ pub const Renderer = struct { self.defaultTexture = getTexture(&defaultTextureName).?; self.debugDrawSys = try self.createRendererEngineObject(DebugDrawSystem); + + try self.createDirectionalShadowsPipeline(); + } + + pub fn createDirectionalShadowsPipeline(self: *@This()) !void { + try self.createShadowDepthTexture(); + try self.createShadowCastingPipeline(); } pub fn createSamplers(self: *@This()) !void { @@ -174,11 +192,27 @@ pub const Renderer = struct { return object; } + fn createShadowDepthTexture(self: *@This()) !void { + var gci = std.mem.zeroes(gpu.GPUTextureCreateInfo); + + const shadowMapRes = core.configVar(u32, "renderer.shadowmap.resolution", 2048); + gci.type = .texturetype2d; + gci.format = self.shadowDepthFormat; + gci.width = shadowMapRes; + gci.height = shadowMapRes; + gci.layer_count_or_depth = 1; + gci.num_levels = 1; + gci.sample_count = .samplecount1; + gci.usage = .{ .textureusageDepthStencilTarget = true, .textureusageSampler = true }; + + self.shadowDepthTexture = self.device.createGPUTexture(&gci); + } + fn createDepthTexture(self: *@This()) !void { var gci = std.mem.zeroes(gpu.GPUTextureCreateInfo); gci.type = .texturetype2d; - gci.format = .textureformatD32Float; + gci.format = self.depthFormat; gci.width = @intCast(self.scissor.w); gci.height = @intCast(self.scissor.h); gci.layer_count_or_depth = 1; @@ -212,26 +246,31 @@ pub const Renderer = struct { offset.* = offset.* + size; } - pub fn createMeshPipeline(self: *@This()) !void { + pub fn generateVertexAttributeList(self: *@This()) !std.ArrayList(gpu.GPUVertexAttribute) { + var list = std.ArrayList(gpu.GPUVertexAttribute).init(self.allocator); + var offset: u32 = 0; + { + try addAttribute(&list, &offset, @sizeOf(f32) * 3, .vertexelementformatFloat3); + try addAttribute(&list, &offset, @sizeOf(f32) * 3, .vertexelementformatFloat3); + try addAttribute(&list, &offset, @sizeOf(f32) * 4, .vertexelementformatFloat4); + try addAttribute(&list, &offset, @sizeOf(f32) * 2, .vertexelementformatFloat2); + try addAttribute(&list, &offset, @sizeOf(u32), .vertexelementformatUint); + } + + return list; + } + + pub fn createShadowCastingPipeline(self: *@This()) !void { const vertex = try self.loadShader("meshes.vert", meshes_vert.LoadArgs); - const fragment = try self.loadShader("lit_mesh.frag", lit_mesh_frag.LoadArgs); + const fragment = try self.loadShader("depthOnly.frag", depthOnly.LoadArgs); var pci = std.mem.zeroes(gpu.GPUGraphicsPipelineCreateInfo); pci.vertex_shader = vertex; pci.fragment_shader = fragment; - var attributes = std.ArrayList(gpu.GPUVertexAttribute).init(self.allocator); + var attributes = try self.generateVertexAttributeList(); defer attributes.deinit(); - var offset: u32 = 0; - { - try addAttribute(&attributes, &offset, @sizeOf(f32) * 3, .vertexelementformatFloat3); - try addAttribute(&attributes, &offset, @sizeOf(f32) * 3, .vertexelementformatFloat3); - try addAttribute(&attributes, &offset, @sizeOf(f32) * 4, .vertexelementformatFloat4); - try addAttribute(&attributes, &offset, @sizeOf(f32) * 2, .vertexelementformatFloat2); - try addAttribute(&attributes, &offset, @sizeOf(u32), .vertexelementformatUint); - } - pci.vertex_input_state = .{ .num_vertex_buffers = 1, .vertex_buffer_descriptions = &[_]gpu.GPUVertexBufferDescription{ @@ -247,7 +286,45 @@ pub const Renderer = struct { pci.depth_stencil_state.write_mask = 0xff; pci.target_info.has_depth_stencil_target = true; - pci.target_info.depth_stencil_format = .textureformatD32Float; + pci.target_info.depth_stencil_format = self.shadowDepthFormat; + + pci.target_info.num_color_targets = 0; + + // no color targets + pci.rasterizer_state.cull_mode = .cullmodeNone; + pci.rasterizer_state.fill_mode = .fillmodeFill; + pci.rasterizer_state.enable_depth_bias = true; + + self.shadowCastingPipeline = self.device.createGPUGraphicsPipeline(&pci); + } + + pub fn createMeshPipeline(self: *@This()) !void { + const vertex = try self.loadShader("meshes.vert", meshes_vert.LoadArgs); + const fragment = try self.loadShader("lit_mesh.frag", lit_mesh_frag.LoadArgs); + + var pci = std.mem.zeroes(gpu.GPUGraphicsPipelineCreateInfo); + pci.vertex_shader = vertex; + pci.fragment_shader = fragment; + + var attributes = try self.generateVertexAttributeList(); + defer attributes.deinit(); + + pci.vertex_input_state = .{ + .num_vertex_buffers = 1, + .vertex_buffer_descriptions = &[_]gpu.GPUVertexBufferDescription{ + .{ .slot = 0, .pitch = @sizeOf(rend.MeshVertex), .input_rate = .vertexinputrateVertex, .instance_step_rate = 0 }, + }, + .num_vertex_attributes = @intCast(attributes.items.len), + .vertex_attributes = @ptrCast(attributes.items.ptr), + }; + + pci.depth_stencil_state.compare_op = .compareopLess; + pci.depth_stencil_state.enable_depth_test = true; + pci.depth_stencil_state.enable_depth_write = true; + pci.depth_stencil_state.write_mask = 0xff; + + pci.target_info.has_depth_stencil_target = true; + pci.target_info.depth_stencil_format = self.depthFormat; pci.target_info.num_color_targets = 1; pci.target_info.color_target_descriptions = &[_]gpu.GPUColorTargetDescription{ .{ @@ -357,6 +434,8 @@ pub const Renderer = struct { if (self.activeCamera) |camera| { ptr.ViewProjection = @bitCast(camera.final); } + ptr.ShadowMapProjection = @bitCast(self.shadowMapProjection); + // ptr.ViewProjection = @bitCast(self.shadowMapProjection); ptr.time = @floatCast(self.totalTime); @@ -371,8 +450,9 @@ pub const Renderer = struct { if (self.activeCamera) |cam| { position = cam.finalPos; } - const resolved = core.Rotation.eulerY(core.radians(self.directionalLightYaw)).rotateVector(self.directionalLightDir); + const resolved = core.Rotation.eulerY(core.radians(self.directionalLightYaw)).rotateVector(self.directionalLightDir.fmul(-1)); + ptr.screenSize = @bitCast(platform.context().extent); ptr.viewPos = @bitCast(position.toZm()); ptr.lightPosition = @bitCast(self.lightPosition.toZm()); ptr.directionalLight = @bitCast(resolved.toZm()); @@ -425,32 +505,109 @@ pub const Renderer = struct { const cmd = self.device.acquireGPUCommandBuffer(); self.frameUploads(); - try self.uploadUniforms(cmd); for (self.preDraws.items) |interface| { interface.func(interface.ptr, cmd); } + // mesh pre-iteration + const container = rend.MeshComponent.BaseContainer; + const uploadCount: usize = @min(container.dense.items.len, MaxObjectCount); + + for (0..uploadCount) |i| { + const component = &container.dense.items[i].value; + + if (component.mesh == null) { + component.updateMesh(); + } + + if (component.texture == null) { + component.updateTexture(); + } + } + + self.drawDirectionalShadowMap(cmd); + + self.draw(cmd); + } + + // orthographic view projection from the sun towards the center of the thing + pub fn shadowMapOrtho(self: *@This()) core.Transform { + const ortho = core.zm.orthographicLh(200, 200, self.shadowOrthoNear, self.shadowOrthoFar); + var rotation = core.zm.lookAtLh( + .{ 0, 0, 0, 1 }, + self.directionalLightDir.toZm(), + .{ 0, 1, 0, 1 }, + ); + + rotation = core.zm.mul(core.zm.rotationY(core.radians(self.directionalLightYaw)), rotation); + + return core.zm.mul(rotation, ortho); + } + + pub fn drawDirectionalShadowMap(self: *@This(), cmd: *gpu.GPUCommandBuffer) void { + { + var data = std.mem.zeroes([@sizeOf(meshes_vert.Uniforms) / 8 + 1]usize); + const ptr: *meshes_vert.Uniforms = @ptrCast(@alignCast(&data)); + self.shadowMapProjection = self.shadowMapOrtho(); + ptr.ViewProjection = @bitCast(self.shadowMapProjection); + + cmd.pushGPUVertexUniformData(0, &data, @sizeOf(meshes_vert.Uniforms)); + } + + // can cache this + const depthTarget = std.mem.zeroInit(gpu.GPUDepthStencilTargetInfo, .{ + .texture = self.shadowDepthTexture, + .load_op = .loadopClear, + .store_op = .storeopStore, + .stencil_load_op = .loadopDontCare, + .stencil_store_op = .storeopDontCare, + .clear_depth = 1.0, + .clear_stencil = 0, + }); + + const renderpass = cmd.beginGPURenderPass(null, 0, &depthTarget); + renderpass.bindGPUGraphicsPipeline(self.shadowCastingPipeline); + renderpass.bindGPUVertexStorageBuffers(0, &self.ssboScene, 1); + renderpass.bindGPUVertexBuffers(0, &.{ .buffer = self.meshPool.vertexBuffer, .offset = 0 }, 1); + renderpass.bindGPUIndexBuffer(&.{ .buffer = self.meshPool.indexBuffer, .offset = 0 }, .indexelementsize32bit); + + const container = rend.MeshComponent.BaseContainer; + const uploadCount: usize = @min(container.dense.items.len, MaxObjectCount); + for (0..uploadCount) |i| { + const component = &container.dense.items[i].value; + if (component.mesh) |mesh| { + renderpass.drawGPUIndexedPrimitives(mesh.index.size, 1, mesh.index.start, @intCast(mesh.vertex.start), @intCast(i)); + } + } + renderpass.endGPURenderPass(); + } + + pub fn draw(self: *@This(), cmd: *gpu.GPUCommandBuffer) void { if (cmd.waitAndAcquireGPUSwapchainTexture(self.window, @ptrCast(&self.swapchainTexture), null, null)) { if (self.swapchainTexture == null) { _ = cmd.submitGPUCommandBuffer(); return; } + try self.uploadUniforms(cmd); - var targetInfo: gpu.GPUColorTargetInfo = std.mem.zeroes(gpu.GPUColorTargetInfo); - targetInfo.texture = self.swapchainTexture.?; - targetInfo.clear_color = .{ .r = 0.1, .g = 0.1, .b = 0.1, .a = 1.0 }; - targetInfo.load_op = .loadopClear; - targetInfo.store_op = .storeopStore; + // can cache this + const targetInfo: gpu.GPUColorTargetInfo = std.mem.zeroInit(gpu.GPUColorTargetInfo, .{ + .texture = self.swapchainTexture.?, + .clear_color = .{ .r = 0.1, .g = 0.1, .b = 0.1, .a = 1.0 }, + .load_op = .loadopClear, + .store_op = .storeopStore, + }); - var depthTarget = std.mem.zeroes(gpu.GPUDepthStencilTargetInfo); - depthTarget.texture = self.depthTexture; - depthTarget.load_op = .loadopClear; - depthTarget.store_op = .storeopDontCare; - depthTarget.stencil_load_op = .loadopDontCare; - depthTarget.stencil_store_op = .storeopDontCare; - depthTarget.clear_depth = 1.0; - depthTarget.clear_stencil = 0; + const depthTarget = std.mem.zeroInit(gpu.GPUDepthStencilTargetInfo, .{ + .texture = self.depthTexture, + .load_op = .loadopClear, + .store_op = .storeopDontCare, + .stencil_load_op = .loadopDontCare, + .stencil_store_op = .storeopDontCare, + .clear_depth = 1.0, + .clear_stencil = 0, + }); const renderpass = cmd.beginGPURenderPass(&targetInfo, 1, &depthTarget); //renderpass.bindGPUGraphicsPipeline(self.testPipeline); @@ -460,36 +617,26 @@ pub const Renderer = struct { renderpass.bindGPUVertexStorageBuffers(0, &self.ssboScene, 1); renderpass.bindGPUVertexBuffers(0, &.{ .buffer = self.meshPool.vertexBuffer, .offset = 0 }, 1); renderpass.bindGPUIndexBuffer(&.{ .buffer = self.meshPool.indexBuffer, .offset = 0 }, .indexelementsize32bit); + renderpass.bindGPUFragmentSamplers(1, &.{ .texture = self.shadowDepthTexture, .sampler = self.blockySampler }, 1); // todo move into materials system renderpass.setGPUScissor(&self.scissor); - //SDL_BindGPUFragmentSamplers(renderPass, 0, &(SDL_GPUTextureSamplerBinding){ .texture = Texture, .sampler = Samplers[CurrentSamplerIndex] }, 1); //rendering each mesh { const container = rend.MeshComponent.BaseContainer; const uploadCount: usize = @min(container.dense.items.len, MaxObjectCount); for (0..uploadCount) |i| { - // core.engine_log("pushing draw {d}", .{i}); const component = &container.dense.items[i].value; - // todo move this to slow tick? only update once every 1 second or so? - if (component.mesh == null) { - component.updateMesh(); - } - - if (component.texture == null) { - component.updateTexture(); - } - var t = component.texture; if (t == null) { t = self.defaultTexture; } + renderpass.bindGPUFragmentSamplers(0, &.{ .texture = t.?.texture, .sampler = self.blockySampler }, 1); if (component.mesh) |mesh| { - // core.engine_log("pushing draw for instance {d}", .{i}); renderpass.drawGPUIndexedPrimitives(mesh.index.size, 1, mesh.index.start, @intCast(mesh.vertex.start), @intCast(i)); } } @@ -543,6 +690,7 @@ const gpu = sdl3.gpu; const meshes_vert = @import("meshes.vert"); const lit_mesh_frag = @import("lit_mesh.frag"); +const depthOnly = @import("depthOnly.frag"); const sample_vert = @import("sample.vert"); const MeshVertices = meshes_vert.Scene; diff --git a/extras/readme.md b/extras/readme.md index e69de29..cd306fa 100644 --- a/extras/readme.md +++ b/extras/readme.md @@ -0,0 +1,5 @@ +extras are like engine plugins + +they have access to all engine modules without having to be upstreamed as part of the engine + +and they don't have to be linked into your project unless you want them to be. diff --git a/lib/sdl3/src/gpu.zig b/lib/sdl3/src/gpu.zig index cf696e9..6852595 100644 --- a/lib/sdl3/src/gpu.zig +++ b/lib/sdl3/src/gpu.zig @@ -620,7 +620,7 @@ pub const GPUColorTargetInfo = extern struct { clear_color: FColor, // The color to clear the color target to at the start of the render pass. Ignored if SDL_GPU_LOADOP_CLEAR is not used. load_op: GPULoadOp, // What is done with the contents of the color target at the beginning of the render pass. store_op: GPUStoreOp, // What is done with the results of the render pass. - resolve_texture: *GPUTexture, // The texture that will receive the results of a multisample resolve operation. Ignored if a RESOLVE* store_op is not used. + resolve_texture: ?*GPUTexture, // The texture that will receive the results of a multisample resolve operation. Ignored if a RESOLVE* store_op is not used. resolve_mip_level: u32, // The mip level of the resolve texture to use for the resolve operation. Ignored if a RESOLVE* store_op is not used. resolve_layer: u32, // The layer index of the resolve texture to use for the resolve operation. Ignored if a RESOLVE* store_op is not used. cycle: bool, // true cycles the texture if the texture is bound and load_op is not LOAD diff --git a/projects/content/_shaders/dxil/depthOnly.frag.dxil b/projects/content/_shaders/dxil/depthOnly.frag.dxil new file mode 100644 index 0000000..b03e545 Binary files /dev/null and b/projects/content/_shaders/dxil/depthOnly.frag.dxil differ diff --git a/projects/content/_shaders/dxil/lit_mesh.frag.dxil b/projects/content/_shaders/dxil/lit_mesh.frag.dxil index 87c9092..d7893de 100644 Binary files a/projects/content/_shaders/dxil/lit_mesh.frag.dxil and b/projects/content/_shaders/dxil/lit_mesh.frag.dxil differ diff --git a/projects/content/_shaders/dxil/meshes.vert.dxil b/projects/content/_shaders/dxil/meshes.vert.dxil index ff4f46e..9685fde 100644 Binary files a/projects/content/_shaders/dxil/meshes.vert.dxil and b/projects/content/_shaders/dxil/meshes.vert.dxil differ diff --git a/projects/content/_shaders/msl/depthOnly.frag.msl b/projects/content/_shaders/msl/depthOnly.frag.msl new file mode 100644 index 0000000..e221fab --- /dev/null +++ b/projects/content/_shaders/msl/depthOnly.frag.msl @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 out_var_SV_Target0 [[color(0)]]; +}; + +fragment main0_out main0() +{ + main0_out out = {}; + out.out_var_SV_Target0 = float4(1.0); + return out; +} + diff --git a/projects/content/_shaders/msl/lit_mesh.frag.msl b/projects/content/_shaders/msl/lit_mesh.frag.msl index 3f54b53..5c4c56f 100644 --- a/projects/content/_shaders/msl/lit_mesh.frag.msl +++ b/projects/content/_shaders/msl/lit_mesh.frag.msl @@ -9,9 +9,12 @@ struct type_Uniforms float4 lightPosition; float4 directionalLight; float4 directionalLightColor; + float2 screenSize; float time; }; +constant float3 _56 = {}; + struct main0_out { float4 out_var_SV_Target0 [[color(0)]]; @@ -22,14 +25,15 @@ struct main0_in float2 in_var_TEXCOORD0 [[user(locn0)]]; float3 in_var_TEXCOORD1 [[user(locn1)]]; float3 in_var_TEXCOORD2 [[user(locn2)]]; + float4 in_var_TEXCOORD3 [[user(locn3)]]; }; -fragment main0_out main0(main0_in in [[stage_in]], constant type_Uniforms& Uniforms [[buffer(0)]], texture2d Texture [[texture(0)]], sampler Sampler [[sampler(0)]]) +fragment main0_out main0(main0_in in [[stage_in]], constant type_Uniforms& Uniforms [[buffer(0)]], texture2d Texture [[texture(0)]], texture2d DirectionalShadowDepthMap [[texture(1)]], sampler Sampler [[sampler(0)]], sampler DirectionalShadowSampler [[sampler(1)]]) { main0_out out = {}; - float4 _61 = Texture.sample(Sampler, in.in_var_TEXCOORD0); - float _62 = _61.w; - if (_62 < 0.00999999977648258209228515625) + float4 _65 = Texture.sample(Sampler, in.in_var_TEXCOORD0); + float _66 = _65.w; + if (_66 < 0.00999999977648258209228515625) { discard_fragment(); } @@ -68,7 +72,12 @@ fragment main0_out main0(main0_in in [[stage_in]], constant type_Uniforms& Unifo _119 = ((float3(0.800000011920928955078125, 0.800000011920928955078125, 0.5) * precise::max(dot(_100, in.in_var_TEXCOORD2), 0.0)) * _93) + (((float3(0.800000011920928955078125, 0.800000011920928955078125, 0.5) * powr(precise::max(dot(in.in_var_TEXCOORD2, fast::normalize(_100 + fast::normalize(in.in_var_TEXCOORD1 - Uniforms.viewPos.xyz))), 0.0), 30.0)) * 2.0) * _93); break; } while(false); - out.out_var_SV_Target0 = float4(powr(_61.xyz * (((mix(float3(0.00999999977648258209228515625, 0.00999999977648258209228515625, 0.0030000000260770320892333984375), float3(0.0040000001899898052215576171875, 0.0040000001899898052215576171875, 0.0599999986588954925537109375), float3(in.in_var_TEXCOORD2.y)) * 5.0) + _119) + (Uniforms.directionalLightColor.xyz * precise::max(dot(Uniforms.directionalLight.xyz, in.in_var_TEXCOORD2), 0.0))), float3(0.4545454680919647216796875)), _62); + float3 _136 = ((in.in_var_TEXCOORD3.xyz / float3(in.in_var_TEXCOORD3.w)) * 0.5) + float3(0.5); + float3 _138; + _138.x = _136.x; + float2 _139 = _138.xy; + _139.y = -_136.y; + out.out_var_SV_Target0 = float4(powr(_65.xyz * ((float3(0.100000001490116119384765625) + _119) + ((Uniforms.directionalLightColor.xyz * precise::max(dot(Uniforms.directionalLight.xyz, in.in_var_TEXCOORD2), 0.0)) * (1.0 - float((in.in_var_TEXCOORD3.z - 0.004999999888241291046142578125) > DirectionalShadowDepthMap.sample(DirectionalShadowSampler, _139).x)))), float3(0.4545454680919647216796875)), _66); return out; } diff --git a/projects/content/_shaders/msl/meshes.vert.msl b/projects/content/_shaders/msl/meshes.vert.msl index ec6dfbc..187dcb9 100644 --- a/projects/content/_shaders/msl/meshes.vert.msl +++ b/projects/content/_shaders/msl/meshes.vert.msl @@ -16,6 +16,7 @@ struct type_StructuredBuffer_Scene struct type_Uniforms { float4x4 ViewProjection; + float4x4 ShadowMapProjection; float time; }; @@ -24,6 +25,7 @@ struct main0_out float2 out_var_TEXCOORD0 [[user(locn0)]]; float3 out_var_TEXCOORD1 [[user(locn1)]]; float3 out_var_TEXCOORD2 [[user(locn2)]]; + float4 out_var_TEXCOORD3 [[user(locn3)]]; float4 gl_Position [[position]]; }; @@ -37,11 +39,13 @@ struct main0_in vertex main0_out main0(main0_in in [[stage_in]], constant type_Uniforms& Uniforms [[buffer(0)]], const device type_StructuredBuffer_Scene& scene [[buffer(1)]], uint gl_InstanceIndex [[instance_id]]) { main0_out out = {}; - float4 _44 = float4(in.in_var_TEXCOORD0, 1.0); + float4 _46 = float4(in.in_var_TEXCOORD0, 1.0); + float4 _49 = scene._m0[gl_InstanceIndex].Model * _46; out.out_var_TEXCOORD0 = in.in_var_TEXCOORD3; - out.out_var_TEXCOORD1 = (scene._m0[gl_InstanceIndex].Model * _44).xyz; + out.out_var_TEXCOORD1 = _49.xyz; out.out_var_TEXCOORD2 = in.in_var_TEXCOORD1; - out.gl_Position = Uniforms.ViewProjection * (scene._m0[gl_InstanceIndex].Model * _44); + out.out_var_TEXCOORD3 = Uniforms.ShadowMapProjection * _49; + out.gl_Position = Uniforms.ViewProjection * (scene._m0[gl_InstanceIndex].Model * _46); return out; } diff --git a/projects/content/_shaders/spv/depthOnly.frag.spv b/projects/content/_shaders/spv/depthOnly.frag.spv new file mode 100644 index 0000000..7e413c3 Binary files /dev/null and b/projects/content/_shaders/spv/depthOnly.frag.spv differ diff --git a/projects/content/_shaders/spv/lit_mesh.frag.spv b/projects/content/_shaders/spv/lit_mesh.frag.spv index a5e8b1c..d7c14fa 100644 Binary files a/projects/content/_shaders/spv/lit_mesh.frag.spv and b/projects/content/_shaders/spv/lit_mesh.frag.spv differ diff --git a/projects/content/_shaders/spv/meshes.vert.spv b/projects/content/_shaders/spv/meshes.vert.spv index ac79cd2..66db4e6 100644 Binary files a/projects/content/_shaders/spv/meshes.vert.spv and b/projects/content/_shaders/spv/meshes.vert.spv differ