I have a working skybox now

This commit is contained in:
Peter Li 2025-05-01 22:20:32 -07:00
parent fa1f9c6808
commit 274255556d
21 changed files with 432 additions and 12 deletions

View File

@ -41,6 +41,9 @@ pub fn build(b: *std.Build) void {
sdl3.shaderDefintion(b, mod, "../../lib/sdl3", optimize, "depthOnly.frag", b.path("shaders/depthOnly.frag.json"));
sdl3.shaderDefintion(b, mod, "../../lib/sdl3", optimize, "skybox.frag", b.path("shaders/skybox.frag.json"));
sdl3.shaderDefintion(b, mod, "../../lib/sdl3", optimize, "skybox.vert", b.path("shaders/skybox.vert.json"));
// ========== tests ==========
const tests = b.addTest(.{
.target = target,

View File

@ -4,6 +4,7 @@ SamplerState Sampler : register(s0, space2);
Texture2D<float> DirectionalShadowDepthMap : register(t1, space2);
SamplerState DirectionalShadowSampler : register(s1, space2);
cbuffer Uniforms : register(b0, space3)
{
float4 viewPos;
@ -111,7 +112,7 @@ float shadowCalculationPCF(float3 fragPos, float4 DirectionalShadowFragPos)
uv.y = -uv.y;
float fragDepth = DirectionalShadowFragPos.z;
float combinedShadow = 0.0;
float bias = 0.001;
float bias = 0.005;
for (int y = -range ; y <= range ; y++) {
for (int x = -range ; x <= range ; x++) {
@ -124,8 +125,8 @@ float shadowCalculationPCF(float3 fragPos, float4 DirectionalShadowFragPos)
}
}
combinedDepth = combinedDepth / count;
combinedShadow = combinedShadow / count;
combinedDepth = combinedDepth / (count);
combinedShadow = combinedShadow / (count);
// float fragDepth = DirectionalShadowFragPos.z;
// float shadow = fragDepth - bias > combinedDepth ? 1.0 : 0.0;

View File

@ -0,0 +1,8 @@
TextureCube<float4> SkyboxTexture : register(t0, space2);
SamplerState SkyboxSampler : register(s0, space2);
float4 main(float3 TexCoord : TEXCOORD0) : SV_Target0
{
return pow(SkyboxTexture.Sample(SkyboxSampler, TexCoord), 1.0/2.2);
}

View File

@ -0,0 +1,38 @@
{
"entryPoints" : [
{
"name" : "main",
"mode" : "frag"
}
],
"inputs" : [
{
"type" : "vec3",
"name" : "in.var.TEXCOORD0",
"location" : 0
}
],
"outputs" : [
{
"type" : "vec4",
"name" : "out.var.SV_Target0",
"location" : 0
}
],
"separate_images" : [
{
"type" : "textureCube",
"name" : "SkyboxTexture",
"set" : 2,
"binding" : 0
}
],
"separate_samplers" : [
{
"type" : "sampler",
"name" : "SkyboxSampler",
"set" : 2,
"binding" : 0
}
]
}

View File

@ -0,0 +1,27 @@
struct Input
{
float3 Position : TEXCOORD0;
uint Instance : SV_InstanceID;
};
cbuffer Uniforms : register(b0, space1)
{
float4x4 ViewProjection : packoffset(c0);
};
struct Output
{
float3 UVW : TEXCOORD0;
float4 Position : SV_Position;
};
Output main(Input input)
{
Output output;
float4 pos = float4(input.Position, 1.0);
output.Position = mul(ViewProjection, pos);
output.UVW = input.Position;
return output;
}

View File

@ -0,0 +1,45 @@
{
"entryPoints" : [
{
"name" : "main",
"mode" : "vert"
}
],
"types" : {
"_5" : {
"name" : "type.Uniforms",
"members" : [
{
"name" : "ViewProjection",
"type" : "mat4",
"offset" : 0,
"matrix_stride" : 16,
"row_major" : true
}
]
}
},
"inputs" : [
{
"type" : "vec3",
"name" : "in.var.TEXCOORD0",
"location" : 0
}
],
"outputs" : [
{
"type" : "vec3",
"name" : "out.var.TEXCOORD0",
"location" : 0
}
],
"ubos" : [
{
"type" : "_5",
"name" : "type.Uniforms",
"block_size" : 64,
"set" : 1,
"binding" : 0
}
]
}

View File

@ -26,6 +26,7 @@ roll: f32 = 0,
finalPos: core.Vectorf = .{},
final: Mat = zm.identity(),
finalAlt: Mat = zm.identity(),
noTranslate: Mat = zm.identity(),
entity: core.Entity = undefined,
@ -90,6 +91,9 @@ pub fn resolve(self: *@This()) void {
// self.transform = core.zm.identity();
self.transform = mul(core.zm.matFromQuat(rotation.quat), self.transform);
self.noTranslate = mul(self.transform, self.projection);
self.transform = mul(core.zm.translationV(position.toZm()), self.transform);
self.final = mul(self.transform, self.projection);
// core.engine_log("{any}", .{self.final});

View File

@ -34,6 +34,8 @@ pub const getTexture = renderer.getTexture;
pub const texture = @import("texture/texture.zig");
pub const Texture = texture.Texture;
pub const setSkyboxTexture = renderer.setSkyboxTexture;
var rendAllocator: std.mem.Allocator = undefined;
pub fn getAllocator() std.mem.Allocator {
return rendAllocator;

View File

@ -33,11 +33,132 @@ pub fn loadAsset(self: *@This(), assetRef: assets.AssetRef, propertiesBag: ?asse
var name = assetRef.name;
core.engine_log("loading texture {s}", .{name.utf8()});
if (propertiesBag) |bag| {
const installed = self.uploadTextureFromPath(name, bag.path) catch return error.UnableToLoad;
var installed: *Texture = undefined;
if (bag.textureCube) {
const pathList = bag.textureList.?;
core.engine_log("loading textures as a cubemap\n {s}\n {s}\n {s}\n {s}\n {s}\n {s}", .{
pathList[0],
pathList[1],
pathList[2],
pathList[3],
pathList[4],
pathList[5],
});
installed = self.uploadCubeFromPaths(name, pathList) catch return error.UnableToLoad;
} else {
installed = self.uploadTextureFromPath(name, bag.path) catch return error.UnableToLoad;
}
self.map.put(self.allocator, name.handle(), installed) catch return error.UnableToLoad;
}
}
pub fn uploadCubeFromPaths(self: *@This(), name: core.Name, paths: []const []const u8) !*Texture {
if (paths.len != 6) {
return error.CubeNeedsSixFaces;
}
var cubeList = std.ArrayList(core.png.PngContents).init(self.allocator);
defer cubeList.deinit();
var gpuTexture: *gpu.GPUTexture = undefined;
var first: bool = true;
var textureSize: u32 = 0;
for (paths) |path| {
try cubeList.append(try core.png.PngContents.initFromFS(core.fs(), self.allocator, path));
const png = &cubeList.items[cubeList.items.len - 1];
const cmd = self.device.acquireGPUCommandBuffer();
const copyPass = cmd.beginGPUCopyPass();
if (first) {
first = false;
textureSize = cubeList.items[0].size.x;
var tci: gpu.GPUTextureCreateInfo = std.mem.zeroInit(gpu.GPUTextureCreateInfo, .{
.type = .texturetypeCube,
.format = .textureformatR8g8b8a8UnormSrgb,
.usage = .{ .textureusageSampler = true },
.width = textureSize,
.height = textureSize,
.layer_count_or_depth = 6,
.num_levels = 1,
});
gpuTexture = self.device.createGPUTexture(&tci);
try core.assert(cubeList.items[0].size.x == cubeList.items[0].size.y);
}
const transferBuffer = self.device.createGPUTransferBuffer(&.{
.usage = .transferbufferusageUpload,
.size = textureSize * textureSize * @sizeOf(u32),
.props = 0,
});
defer self.device.releaseGPUTransferBuffer(transferBuffer);
const gtti: gpu.GPUTextureTransferInfo = .{
.transfer_buffer = transferBuffer,
.offset = 0,
.pixels_per_row = textureSize,
.rows_per_layer = textureSize,
};
const data: [*]u8 = @ptrCast(self.device.mapGPUTransferBuffer(transferBuffer, false));
for (png.pixels, 0..) |pixel, i| {
data[i] = pixel;
}
// todo
// TextureCube<float4> SkyboxTexture : register(t0, space2);
// SamplerState SkyboxSampler : register(s0, space2);
const textureRegion = std.mem.zeroInit(gpu.GPUTextureRegion, .{
.texture = gpuTexture,
.w = textureSize,
.h = textureSize,
.d = 1,
.layer = @as(u32, @intCast(cubeList.items.len - 1)),
});
copyPass.uploadToGPUTexture(&gtti, &textureRegion, false);
copyPass.endGPUCopyPass();
_ = cmd.submitGPUCommandBuffer();
}
// what the fuck do we even do here.
// cubemapfacePositivex, right
// cubemapfaceNegativex, left
// cubemapfacePositivey, up
// cubemapfaceNegativey, down
// cubemapfacePositivez, forward
// cubemapfaceNegativez, backward
for (cubeList.items) |*i| {
i.deinit();
}
const tex = try self.allocator.create(Texture);
tex.* = .{
.id = 0,
.format = .f32_rgba,
.usage = .{ .cube = true },
.size = .{ .x = textureSize, .y = textureSize },
.name = name,
.texture = gpuTexture,
};
return tex;
}
pub fn uploadTextureFromPath(self: *@This(), name: core.Name, path: []const u8) !*Texture {
var png = try core.png.PngContents.initFromFS(core.fs(), self.allocator, path);
defer png.deinit();

View File

@ -44,6 +44,7 @@ pub const Renderer = struct {
shadowMapProjection: core.Transform = core.zm.identity(),
blockySampler: *gpu.GPUSampler = undefined,
cubeSampler: *gpu.GPUSampler = undefined,
defaultTexture: *rend.Texture = undefined,
lightPosition: core.Vectorf = .{},
@ -65,6 +66,14 @@ pub const Renderer = struct {
// transients DO NOT TOUCH
swapchainTexture: ?*gpu.GPUTexture = undefined,
skyboxMesh: ?rend.IndexedMesh = null,
skyboxTexture: ?*rend.Texture = null,
skyboxTextureName: ?core.Name = null,
skyboxPipeline: *gpu.GPUGraphicsPipeline = undefined,
skyboxColor: core.colors.Color = .{ .r = 71.0 / 256.0, .g = 200.0 / 256.0, .b = 1.0, .a = 1.0 },
skyboxMeshName: core.Name = core.DefineName("m_skybox"),
pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This());
pub const MaxObjectCount = 50000;
@ -124,13 +133,12 @@ pub const Renderer = struct {
.{ .path = "meshes/primitive_box.obj" },
),
);
var defaultTextureName = core.MakeName("t_default");
self.defaultTexture = getTexture(&defaultTextureName).?;
self.debugDrawSys = try self.createRendererEngineObject(DebugDrawSystem);
try self.createDirectionalShadowsPipeline();
try self.setupSkybox();
}
pub fn createDirectionalShadowsPipeline(self: *@This()) !void {
@ -138,6 +146,50 @@ pub const Renderer = struct {
try self.createShadowCastingPipeline();
}
pub fn setupSkybox(self: *@This()) !void {
try assets.load(
assets.MakeImportRefOptions(
"Mesh",
"m_skybox",
.{ .path = "meshes/skybox.obj" },
),
);
try self.createSkyboxPipeline();
}
pub fn createSkyboxPipeline(self: *@This()) !void {
const vertex = try self.loadShader("skybox.vert", skybox_vert.LoadArgs);
const fragment = try self.loadShader("skybox.frag", skybox_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.target_info.num_color_targets = 1;
pci.target_info.color_target_descriptions = &[_]gpu.GPUColorTargetDescription{
.{
.format = self.device.getGPUSwapchainTextureFormat(self.window),
.blend_state = std.mem.zeroes(gpu.GPUColorTargetBlendState),
},
};
pci.rasterizer_state.fill_mode = .fillmodeFill;
self.skyboxPipeline = self.device.createGPUGraphicsPipeline(&pci);
}
pub fn createSamplers(self: *@This()) !void {
core.engine_log("creating blocky sampler", .{});
self.blockySampler = self.device.createGPUSampler(&std.mem.zeroInit(gpu.GPUSamplerCreateInfo, .{
@ -427,6 +479,17 @@ pub const Renderer = struct {
return rv;
}
pub fn uploadSkyboxUniforms(self: *@This(), cmd: *gpu.GPUCommandBuffer) void {
var data = std.mem.zeroes([@sizeOf(skybox_vert.Uniforms) / 8 + 1]usize);
const ptr: *skybox_vert.Uniforms = @ptrCast(@alignCast(&data));
if (self.activeCamera) |camera| {
ptr.ViewProjection = @bitCast(camera.noTranslate);
}
cmd.pushGPUVertexUniformData(0, &data, @sizeOf(skybox_vert.Uniforms));
}
pub fn uploadUniforms(self: *@This(), cmd: *gpu.GPUCommandBuffer) !void {
{
var data = std.mem.zeroes([@sizeOf(meshes_vert.Uniforms) / 8 + 1]usize);
@ -441,6 +504,7 @@ pub const Renderer = struct {
cmd.pushGPUVertexUniformData(0, &data, @sizeOf(meshes_vert.Uniforms));
}
{
var data = std.mem.zeroes([@sizeOf(lit_mesh_frag.Uniforms) / 8 + 1]usize);
const ptr: *lit_mesh_frag.Uniforms = @ptrCast(@alignCast(&data));
@ -589,13 +653,42 @@ pub const Renderer = struct {
_ = cmd.submitGPUCommandBuffer();
return;
}
try self.uploadUniforms(cmd);
// render the skybox
{
if (self.skyboxMesh == null) {
self.skyboxMesh = getMeshByName(&self.skyboxMeshName);
}
if (self.skyboxTexture == null) {
if (self.skyboxTextureName) |*name| {
self.skyboxTexture = getTexture(name);
}
}
if (self.skyboxTexture) |skyboxTexture| {
if (self.skyboxMesh) |skyboxMesh| {
const targetInfo: gpu.GPUColorTargetInfo = std.mem.zeroInit(gpu.GPUColorTargetInfo, .{
.texture = self.swapchainTexture.?,
.clear_color = self.skyboxColor,
.load_op = .loadopClear,
.store_op = .storeopStore,
});
const renderpass = cmd.beginGPURenderPass(&targetInfo, 1, null);
self.uploadSkyboxUniforms(cmd);
renderpass.bindGPUGraphicsPipeline(self.skyboxPipeline);
renderpass.bindGPUFragmentSamplers(0, &.{ .texture = skyboxTexture.texture, .sampler = self.blockySampler }, 1);
renderpass.drawGPUIndexedPrimitives(skyboxMesh.index.size, 1, skyboxMesh.index.start, @intCast(skyboxMesh.vertex.start), 0);
renderpass.endGPURenderPass();
}
}
}
// can cache this
const targetInfo: gpu.GPUColorTargetInfo = std.mem.zeroInit(gpu.GPUColorTargetInfo, .{
.texture = self.swapchainTexture.?,
.clear_color = .{ .r = 71.0 / 256.0, .g = 200.0 / 256.0, .b = 1.0, .a = 1.0 },
.load_op = .loadopClear,
.clear_color = self.skyboxColor,
.load_op = .loadopLoad,
.store_op = .storeopStore,
});
@ -612,16 +705,18 @@ pub const Renderer = struct {
const renderpass = cmd.beginGPURenderPass(&targetInfo, 1, &depthTarget);
//renderpass.bindGPUGraphicsPipeline(self.testPipeline);
renderpass.bindGPUGraphicsPipeline(self.meshPipe);
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);
// render the meshes
renderpass.bindGPUGraphicsPipeline(self.meshPipe);
renderpass.bindGPUFragmentSamplers(1, &.{ .texture = self.shadowDepthTexture, .sampler = self.blockySampler }, 1);
// todo move into materials system
renderpass.setGPUScissor(&self.scissor);
try self.uploadUniforms(cmd);
//rendering each mesh
{
const container = rend.MeshComponent.BaseContainer;
@ -693,6 +788,9 @@ const lit_mesh_frag = @import("lit_mesh.frag");
const depthOnly = @import("depthOnly.frag");
const sample_vert = @import("sample.vert");
const skybox_vert = @import("skybox.vert");
const skybox_frag = @import("skybox.frag");
const MeshVertices = meshes_vert.Scene;
const MeshUniforms = meshes_vert.Uniforms;
@ -743,6 +841,10 @@ pub fn registerRendererObject(comptime T: type, object: *anyopaque) !void {
return try gRenderer.registerRendererObject(T, object);
}
pub fn setSkyboxTexture(name: []const u8) void {
gRenderer.skyboxTextureName = core.MakeName(name);
}
pub const getMesh = mesh_pool.getMesh;
pub const getMeshByName = mesh_pool.getMeshByName;
pub const pushMeshUpdate = mesh_pool.pushMeshUpdate;

View File

@ -4,6 +4,7 @@ pub const TextureFormat = enum(u8) {
};
pub const TextureUsage = struct {
mesh: bool = false,
cube: bool = false,
};
// handle to a texture resource in the engine.

Binary file not shown.

Binary file not shown.

View File

@ -92,7 +92,7 @@ fragment main0_out main0(main0_in in [[stage_in]], constant type_Uniforms& Unifo
for (int _163 = -4; _163 <= 4; )
{
_153++;
_150 += float((in.in_var_TEXCOORD3.z - 0.001000000047497451305389404296875) > DirectionalShadowDepthMap.sample(DirectionalShadowSampler, (_142 + float2(float(_163) * 0.000244140625, float(_154) * 0.000244140625))).x);
_150 += float((in.in_var_TEXCOORD3.z - 0.004999999888241291046142578125) > DirectionalShadowDepthMap.sample(DirectionalShadowSampler, (_142 + float2(float(_163) * 0.000244140625, float(_154) * 0.000244140625))).x);
_163++;
continue;
}

View File

@ -0,0 +1,22 @@
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
struct main0_out
{
float4 out_var_SV_Target0 [[color(0)]];
};
struct main0_in
{
float3 in_var_TEXCOORD0 [[user(locn0)]];
};
fragment main0_out main0(main0_in in [[stage_in]], texturecube<float> SkyboxTexture [[texture(0)]], sampler SkyboxSampler [[sampler(0)]])
{
main0_out out = {};
out.out_var_SV_Target0 = powr(SkyboxTexture.sample(SkyboxSampler, in.in_var_TEXCOORD0), float4(0.4545454680919647216796875));
return out;
}

View File

@ -0,0 +1,29 @@
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
struct type_Uniforms
{
float4x4 ViewProjection;
};
struct main0_out
{
float3 out_var_TEXCOORD0 [[user(locn0)]];
float4 gl_Position [[position]];
};
struct main0_in
{
float3 in_var_TEXCOORD0 [[attribute(0)]];
};
vertex main0_out main0(main0_in in [[stage_in]], constant type_Uniforms& Uniforms [[buffer(0)]])
{
main0_out out = {};
out.out_var_TEXCOORD0 = in.in_var_TEXCOORD0;
out.gl_Position = Uniforms.ViewProjection * float4(in.in_var_TEXCOORD0, 1.0);
return out;
}

Binary file not shown.

Binary file not shown.

View File

@ -51,6 +51,21 @@ const assetReferences = [_]assets.AssetImportReference{
"t_empire",
.{ .path = "textures/lost_empire-RGBA.png" },
),
assets.MakeImportRefOptions(
"Texture",
"t_skybox",
.{
.textureCube = true,
.textureList = &.{
"sky_air/cube_right.png", // cubemapfacePositivex, right
"sky_air/cube_left.png", // cubemapfacePositivex, right
"sky_air/cube_up.png", // cubemapfacePositivex, right
"sky_air/cube_down.png", // cubemapfacePositivex, right
"sky_air/cube_back.png", // cubemapfacePositivex, right
"sky_air/cube_front.png", // cubemapfacePositivex, right
},
},
),
};
pub fn prepare(self: *@This()) !void {
@ -63,6 +78,8 @@ pub fn prepare(self: *@This()) !void {
try assets.loadList(assetReferences);
rend.setSkyboxTexture("t_skybox");
const exitInput = try core.ActionBinding.create(core.MakeName("exit"));
exitInput.addKey(.escape, .keyDown);
_ = exitInput.data.addListener(null, onExit);