Compare commits

...

7 Commits

16 changed files with 79 additions and 75 deletions

View File

@ -23,7 +23,7 @@ slab = "^0.4"
smallmap = "^1.0" smallmap = "^1.0"
smallvec = "^1.0" smallvec = "^1.0"
strum = { version = "0.24", features = ["derive"] } strum = { version = "0.24", features = ["derive"] }
wgpu = "^0.12" wgpu = "^0.13"
winit = "0.26" winit = "0.26"
[dependencies.legion] [dependencies.legion]
@ -31,7 +31,7 @@ version = "^0.4"
optional = true optional = true
[dependencies.naga] [dependencies.naga]
version = "0.8.5" version = "0.9"
features = ["wgsl-in", "glsl-in", "wgsl-out", "serialize", "deserialize"] features = ["wgsl-in", "glsl-in", "wgsl-out", "serialize", "deserialize"]
[[bin]] [[bin]]

View File

@ -7,7 +7,7 @@
> A repulsive reaper of all of human consciousness -- but mostly SIGGRAPH presentations. > A repulsive reaper of all of human consciousness -- but mostly SIGGRAPH presentations.
> H.R Giger's irreconcilable reality... if H.R. Giger was a computer programmer. > H.R Giger's irreconcilable reality... if H.R. Giger was a computer programmer.
Cyborg is a free-form rendering engine that we mash whatever we feel like into. Cyborg is an experimental, GPU-driven rendering engine using Rust and wgpu.
It's a test bed for all sorts of modern rendering technology. Our goal is to take It's a test bed for all sorts of modern rendering technology. Our goal is to take
techniques and features from modern game engines and reimplement them on our own, for techniques and features from modern game engines and reimplement them on our own, for
the sake of education, performance, and reusability. We wanna *make shit work.* the sake of education, performance, and reusability. We wanna *make shit work.*
@ -16,16 +16,15 @@ We also want to give artists a playground for generating all sorts of unique
3D visuals -- custom shaders, procedurally generated meshes and textures, 3D visuals -- custom shaders, procedurally generated meshes and textures,
and a focus on *interesting* visuals. and a focus on *interesting* visuals.
Realism is a non-goal! Realism is a non-goal!
Make shrooms obsolete.
Go wild. Go wild.
Modularity and reusability are important. We wanna be able to unplug certain Modularity and reusability are important. We want to be able to unplug certain
parts of the rendering pipeline, upgrade them, fix them, document them, commit parts of the rendering pipeline, upgrade them, fix them, document them, commit
them, tag them, etc., then stick them back in in a different place to end up them, tag them, etc., then stick them back in in a different place to end up
with a different resulting image. with a different resulting image.
Cyborg is licensed under the GPL-3.0. Yes, this does mean that Bevy games using Cyborg is licensed under the GPL-3.0. Yes, this does mean that any software using
Cyborg will be required to make their source code public. But it also means that Cyborg will be required to make its source code public. But it also means that
new visuals added to Cyborg will become available to all other developers and new visuals added to Cyborg will become available to all other developers and
artists to use. Hopefully, this will expand the collective knowledge of how different artists to use. Hopefully, this will expand the collective knowledge of how different
rendering effects work, and decrease the pressure on graphics programmers to rendering effects work, and decrease the pressure on graphics programmers to
@ -37,10 +36,3 @@ gathered from countless programmers and individual projects.
> Resistance is futile. > Resistance is futile.
> >
> -- The Borg > -- The Borg
Cyborg is based on [Bevy](https://bevyengine.org/), a "refreshingly simple"
game engine written in Rust. Bevy provides a lot of useful utilities like
ECS-based data parallelization and a modular plugin system. Cyborg is integrated
into the rest of the Bevy ecosystem, and can also work as a replacement Bevy's
provided rendering engine, giving Bevy games GPU-powered, procedurally-generated
content and access to modern rendering optimizations.

View File

@ -7,16 +7,16 @@ edition = "2021"
bytemuck = "^1.0" bytemuck = "^1.0"
crossbeam-channel = "^0.5" crossbeam-channel = "^0.5"
cyborg = { path = "../", features = ["legion"] } cyborg = { path = "../", features = ["legion"] }
egui = "0.17.0" egui = "0.18"
egui-winit = "0.17.0" egui-winit = "0.18.0"
egui_wgpu_backend = "0.17.0" egui_wgpu_backend = "0.18.0"
glam = { version = "0.20", features = ["serde"] } glam = { version = "0.20", features = ["serde"] }
gltf = { version = "1.0", features = ["utils"] } gltf = { version = "1.0", features = ["utils"] }
legion = "^0.4" legion = "^0.4"
parking_lot = "^0.11" parking_lot = "^0.11"
pollster = "0.2" pollster = "0.2"
puffin = "^0.13" puffin = "^0.13"
puffin_egui = "0.14.0" puffin_egui = "0.16.0"
rfd = "^0.8" rfd = "^0.8"
stl = "0.2.1" stl = "0.2.1"

View File

@ -60,10 +60,10 @@ impl Application {
.unwrap(); .unwrap();
let config = wgpu::SurfaceConfiguration { let config = wgpu::SurfaceConfiguration {
usage: wgpu::TextureUsages::RENDER_ATTACHMENT, usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
format: surface.get_preferred_format(&adapter).unwrap(), format: *surface.get_supported_formats(&adapter).first().unwrap(),
width: size.width, width: size.width,
height: size.height, height: size.height,
present_mode: wgpu::PresentMode::Mailbox, present_mode: wgpu::PresentMode::Fifo,
}; };
surface.configure(&device, &config); surface.configure(&device, &config);

View File

@ -204,7 +204,7 @@ impl RenderState {
let render_schedule = render_schedule.build(); let render_schedule = render_schedule.build();
// make debug draw grid // make debug draw grid
let grid_size = 10; let grid_size = 100;
let grid_color = [0.0, 1.0, 0.0]; let grid_color = [0.0, 1.0, 0.0];
let mut grid_vertices = Vec::new(); let mut grid_vertices = Vec::new();

View File

@ -15,7 +15,10 @@ pub struct ScriptData {
#[derive(Copy, Clone, Debug, Deserialize, Serialize)] #[derive(Copy, Clone, Debug, Deserialize, Serialize)]
pub struct Transform { pub struct Transform {
#[serde(default)]
pub position: glam::Vec3, pub position: glam::Vec3,
#[serde(default)]
pub orientation: glam::Quat, pub orientation: glam::Quat,
} }

View File

@ -1,25 +1,30 @@
struct CameraUniform { struct CameraUniform {
eye: vec4<f32>; eye: vec4<f32>,
vp: mat4x4<f32>; vp: mat4x4<f32>,
}; };
struct VertexInput { struct VertexInput {
[[location(0)]] position: vec3<f32>; @location(0) position: vec3<f32>,
[[location(1)]] tan_frame: u32; @location(1) tan_frame: u32,
}; };
struct VertexOutput { struct VertexOutput {
[[builtin(position)]] clip_position: vec4<f32>; @builtin(position) clip_position: vec4<f32>,
[[location(0)]] position: vec3<f32>; @location(0) position: vec3<f32>,
@location(1) color: vec3<f32>,
}; };
[[group(0), binding(0)]] @group(0) @binding(0)
var<uniform> camera: CameraUniform; var<uniform> camera: CameraUniform;
[[stage(vertex)]] fn random(seed: u32, salt: f32) -> f32 {
return abs(sin((f32(seed & u32(0x11111)) * 0.7071 + salt) * 78.233));
}
@vertex
fn vs_main( fn vs_main(
[[builtin(instance_index)]] mesh_idx: u32, @builtin(instance_index) mesh_idx: u32,
[[builtin(vertex_index)]] vertex_idx: u32, @builtin(vertex_index) vertex_idx: u32,
vertex: VertexInput, vertex: VertexInput,
) -> VertexOutput { ) -> VertexOutput {
let world_pos = vertex.position; let world_pos = vertex.position;
@ -27,12 +32,15 @@ fn vs_main(
var out: VertexOutput; var out: VertexOutput;
out.clip_position = camera.vp * vec4<f32>(world_pos, 1.0); out.clip_position = camera.vp * vec4<f32>(world_pos, 1.0);
out.position = world_pos; out.position = world_pos;
out.color.r = random(vertex_idx, f32(1.0) + world_pos.x);
out.color.g = random(vertex_idx, f32(2.0) + world_pos.y);
out.color.b = random(vertex_idx, f32(3.0) + world_pos.z);
return out; return out;
} }
[[stage(fragment)]] @fragment
fn fs_main( fn fs_main(
frag: VertexOutput, frag: VertexOutput,
) -> [[location(0)]] vec4<f32> { ) -> @location(0) vec4<f32> {
return vec4<f32>(sin(frag.position) * vec3<f32>(0.5) + vec3<f32>(0.5), 1.0); return vec4<f32>(frag.color, 1.0);
} }

View File

@ -2,32 +2,33 @@
#include skin.wgsl #include skin.wgsl
struct SkinningUniform { struct SkinningUniform {
transform: mat4x4<f32>; transform: mat4x4<f32>,
src_offset: u32; src_offset: u32,
dst_offset: u32; dst_offset: u32,
count: u32; count: u32,
}; };
[[group(0), binding(0)]] @group(0) @binding(0)
var<storage,read> skinning_ubo: SkinningUniform; var<storage,read> skinning_ubo: SkinningUniform;
[[group(0), binding(1)]] @group(0) @binding(1)
var<storage,write> dst_vertices: SkinnedVertexArray; var<storage,write> dst_vertices: SkinnedVertexArray;
[[group(0), binding(2)]] @group(0) @binding(2)
var<storage,read> src_vertices: SkinnedVertexArray; var<storage,read> src_vertices: SkinnedVertexArray;
[[stage(compute), workgroup_size(64)]] @compute
@workgroup_size(64)
fn cs_main( fn cs_main(
[[builtin(global_invocation_id)]] global_invocation_id: vec3<u32>, @builtin(global_invocation_id) global_invocation_id: vec3<u32>,
) { ) {
let vertex_index = global_invocation_id.x; let vertex_index = global_invocation_id.x;
if (vertex_index >= skinning_ubo.count) { if (vertex_index >= skinning_ubo.count) {
return; return;
} }
let src_index = skinning_ubo.src_offset + vertex_index; let src_index = skinning_ubo.src_offset + vertex_index;
let dst_index = skinning_ubo.dst_offset + vertex_index; let dst_index = skinning_ubo.dst_offset + vertex_index;
let ptf = src_vertices.data[src_index].ptf; let ptf = src_vertices.data[src_index].ptf;
let position = ptf.xyz; let position = ptf.xyz;

View File

@ -1,6 +1,6 @@
struct TangentFrame { struct TangentFrame {
normal: vec3<f32>; normal: vec3<f32>,
tangent: vec3<f32>; tangent: vec3<f32>
}; };
// https://knarkowicz.wordpress.com/2014/04/16/octahedron-normal-vector-encoding/?like_comment=12 // https://knarkowicz.wordpress.com/2014/04/16/octahedron-normal-vector-encoding/?like_comment=12

View File

@ -1,7 +1,7 @@
struct SkinnedVertex { struct SkinnedVertex {
ptf: vec4<f32>; ptf: vec4<f32>
}; };
struct SkinnedVertexArray { struct SkinnedVertexArray {
data: array<SkinnedVertex>; data: array<SkinnedVertex>
}; };

View File

@ -268,14 +268,14 @@ impl Renderer {
let mut rp = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { let mut rp = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("Render Pass"), label: Some("Render Pass"),
color_attachments: &[wgpu::RenderPassColorAttachment { color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: target_views.output, view: target_views.output,
resolve_target: None, resolve_target: None,
ops: wgpu::Operations { ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color::BLACK), load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
store: true, store: true,
}, },
}], })],
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment { depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
view: target_views.depth, view: target_views.depth,
depth_ops: Some(wgpu::Operations { depth_ops: Some(wgpu::Operations {

View File

@ -24,7 +24,7 @@ impl DebugPass {
target_info: ViewportInfo, target_info: ViewportInfo,
) -> Self { ) -> Self {
// TODO hook into ShaderStore system // TODO hook into ShaderStore system
let shader = device.create_shader_module(&wgpu::include_wgsl!("debug_shader.wgsl")); let shader = device.create_shader_module(wgpu::include_wgsl!("debug_shader.wgsl"));
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("DebugPass Pipeline Layout"), label: Some("DebugPass Pipeline Layout"),
@ -43,11 +43,11 @@ impl DebugPass {
fragment: Some(wgpu::FragmentState { fragment: Some(wgpu::FragmentState {
module: &shader, module: &shader,
entry_point: "fs_main", entry_point: "fs_main",
targets: &[wgpu::ColorTargetState { targets: &[Some(wgpu::ColorTargetState {
format: target_info.output_format, format: target_info.output_format,
blend: Some(wgpu::BlendState::REPLACE), blend: Some(wgpu::BlendState::REPLACE),
write_mask: wgpu::ColorWrites::ALL, write_mask: wgpu::ColorWrites::ALL,
}], })],
}), }),
primitive: wgpu::PrimitiveState { primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::LineList, topology: wgpu::PrimitiveTopology::LineList,
@ -130,7 +130,7 @@ impl RenderPass for DebugPass {
self.device self.device
.create_render_bundle_encoder(&wgpu::RenderBundleEncoderDescriptor { .create_render_bundle_encoder(&wgpu::RenderBundleEncoderDescriptor {
label: Some("DebugPass Render Bundle"), label: Some("DebugPass Render Bundle"),
color_formats: &[self.target_info.output_format], color_formats: &[Some(self.target_info.output_format)],
depth_stencil: Some(wgpu::RenderBundleDepthStencil { depth_stencil: Some(wgpu::RenderBundleDepthStencil {
format: self.target_info.depth_format, format: self.target_info.depth_format,
depth_read_only: false, // TODO optimize? depth_read_only: false, // TODO optimize?

View File

@ -1,26 +1,26 @@
struct CameraUniform { struct CameraUniform {
eye: vec4<f32>; eye: vec4<f32>,
vp: mat4x4<f32>; vp: mat4x4<f32>,
}; };
struct VertexInput { struct VertexInput {
[[location(0)]] position: vec3<f32>; @location(0) position: vec3<f32>,
[[location(1)]] color: vec3<f32>; @location(1) color: vec3<f32>
}; };
struct VertexOutput { struct VertexOutput {
[[builtin(position)]] clip_position: vec4<f32>; @builtin(position) clip_position: vec4<f32>,
[[location(0)]] position: vec3<f32>; @location(0) position: vec3<f32>,
[[location(1)]] color: vec3<f32>; @location(1) color: vec3<f32>
}; };
[[group(0), binding(0)]] @group(0) @binding(0)
var<uniform> camera: CameraUniform; var<uniform> camera: CameraUniform;
[[stage(vertex)]] @vertex
fn vs_main( fn vs_main(
[[builtin(instance_index)]] mesh_idx: u32, @builtin(instance_index) mesh_idx: u32,
[[builtin(vertex_index)]] vertex_idx: u32, @builtin(vertex_index) vertex_idx: u32,
vertex: VertexInput, vertex: VertexInput,
) -> VertexOutput { ) -> VertexOutput {
let world_pos = vertex.position; let world_pos = vertex.position;
@ -32,9 +32,9 @@ fn vs_main(
return out; return out;
} }
[[stage(fragment)]] @fragment
fn fs_main( fn fs_main(
frag: VertexOutput, frag: VertexOutput,
) -> [[location(0)]] vec4<f32> { ) -> @location(0) vec4<f32> {
return vec4<f32>(frag.color, 1.0); return vec4<f32>(frag.color, 1.0);
} }

View File

@ -142,11 +142,11 @@ impl MeshPass {
let shader = shader_info.store.get(&shader_info.forward).unwrap(); let shader = shader_info.store.get(&shader_info.forward).unwrap();
let targets = &[wgpu::ColorTargetState { let targets = &[Some(wgpu::ColorTargetState {
format: target_info.output_format, format: target_info.output_format,
blend: Some(wgpu::BlendState::REPLACE), blend: Some(wgpu::BlendState::REPLACE),
write_mask: wgpu::ColorWrites::ALL, write_mask: wgpu::ColorWrites::ALL,
}]; })];
let mut pipeline_desc = wgpu::RenderPipelineDescriptor { let mut pipeline_desc = wgpu::RenderPipelineDescriptor {
label: Some("Opaque MeshPass Pipeline"), label: Some("Opaque MeshPass Pipeline"),
@ -449,7 +449,7 @@ impl RenderPass for MeshPass {
mesh.vertex_count / 64 + 1 mesh.vertex_count / 64 + 1
}; };
cmds.dispatch(workgroup_num as u32, 1, 1); cmds.dispatch_workgroups(workgroup_num as u32, 1, 1);
} }
} }
} }
@ -465,7 +465,7 @@ impl RenderPass for MeshPass {
self.device self.device
.create_render_bundle_encoder(&wgpu::RenderBundleEncoderDescriptor { .create_render_bundle_encoder(&wgpu::RenderBundleEncoderDescriptor {
label: Some("Opaque Pass Render Bundle"), label: Some("Opaque Pass Render Bundle"),
color_formats: &[self.target_info.output_format], color_formats: &[Some(self.target_info.output_format)],
depth_stencil: Some(wgpu::RenderBundleDepthStencil { depth_stencil: Some(wgpu::RenderBundleDepthStencil {
format: self.target_info.depth_format, format: self.target_info.depth_format,
depth_read_only: false, // TODO optimize? depth_read_only: false, // TODO optimize?

View File

@ -83,7 +83,7 @@ impl ShaderStore {
fn load_wgsl(&self, wgsl_source: String) -> Result<wgpu::ShaderModule, ShaderError> { fn load_wgsl(&self, wgsl_source: String) -> Result<wgpu::ShaderModule, ShaderError> {
let shader = self let shader = self
.device .device
.create_shader_module(&wgpu::ShaderModuleDescriptor { .create_shader_module(wgpu::ShaderModuleDescriptor {
label: None, label: None,
source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Owned(wgsl_source)), source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Owned(wgsl_source)),
}); });

View File

@ -58,10 +58,10 @@ impl WinitViewport {
.unwrap(); .unwrap();
let config = wgpu::SurfaceConfiguration { let config = wgpu::SurfaceConfiguration {
usage: wgpu::TextureUsages::RENDER_ATTACHMENT, usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
format: surface.get_preferred_format(&adapter).unwrap(), format: *surface.get_supported_formats(&adapter).first().unwrap(),
width: size.width, width: size.width,
height: size.height, height: size.height,
present_mode: wgpu::PresentMode::Mailbox, present_mode: wgpu::PresentMode::Fifo,
}; };
surface.configure(&device, &config); surface.configure(&device, &config);