camera projection wip

This commit is contained in:
2025-09-07 12:46:38 -04:00
parent 7d3afc9002
commit 275f4a905e
6 changed files with 100 additions and 41 deletions

View File

@@ -5,7 +5,6 @@
@group(0) @binding(2) var<uniform> config: TracerUniforms; @group(0) @binding(2) var<uniform> config: TracerUniforms;
@group(0) @binding(3) var<uniform> view: ViewUniform;
struct View { struct View {
view_proj: mat4x4<f32>, view_proj: mat4x4<f32>,
@@ -16,25 +15,10 @@ struct View {
inv_projection: mat4x4<f32>, // equic to Unity's _CameraInverseProjection inv_projection: mat4x4<f32>, // equic to Unity's _CameraInverseProjection
}; };
struct ViewUniform {
clip_from_world: mat4x4<f32>,
unjittered_clip_from_world: mat4x4<f32>,
world_from_clip: mat4x4<f32>,
world_from_view: mat4x4<f32>,
view_from_world: mat4x4<f32>,
clip_from_view: mat4x4<f32>,
view_from_clip: mat4x4<f32>,
world_position: vec3<f32>,
exposure: f32,
viewport: vec4<f32>,
frustum: array<vec4<f32>, 6>,
color_grading: vec4<f32>, // simplified for example
mip_bias: f32,
frame_count: u32,
};
struct TracerUniforms { struct TracerUniforms {
sky_color: vec4<f32>, sky_color: vec4<f32>,
world_from_clip: mat4x4<f32>,
world_position: vec3<f32>,
} }
@compute @workgroup_size(8, 8, 1) @compute @workgroup_size(8, 8, 1)
@@ -53,17 +37,33 @@ fn update(@builtin(global_invocation_id) invocation_id: vec3<u32>) {
let size = textureDimensions(output); let size = textureDimensions(output);
let loc = vec2<f32>(f32(invocation_id.x), f32(invocation_id.y)) / vec2<f32>(size.xy); let loc = vec2<f32>(f32(invocation_id.x), f32(invocation_id.y)) / vec2<f32>(size.xy);
let ndc = loc * 2.0f - 1.0f; // let ndc = loc * 2.0f - 1.0f;
var ray = createCameraRay(ndc); // var ray = createCameraRay(ndc);
var result = vec3<f32>(0.0f); // var result = vec3<f32>(0.0f);
var hit = trace(ray); // var hit = trace(ray);
result += ray.energy * shade(&ray, hit); // result += ray.energy * shade(&ray, hit);
// var rayTest = createCameraRay2(ndc);
// let dirColor = (rayTest.direction * 0.5) + vec3<f32>(0.5, 0.5, 0.5);
// let color = vec4(dirColor, 1.0);
let ndc = vec2<f32>(
(f32(invocation_id.x) / f32(size.x)) * 2.0 - 1.0,
(f32(invocation_id.y) / f32(size.y)) * 2.0 - 1.0
);
let clip = vec4<f32>(ndc, 0.0, 1.0);
let world = config.world_from_clip * clip;
let world_pos = world.xyz / world.w;
// map directly to color
let color = vec4<f32>((world_pos * 0.1) + vec3<f32>(0.5), 1.0);
let color = vec4(result , 1.0);
let location = vec2<i32>(i32(invocation_id.x), i32(invocation_id.y)); let location = vec2<i32>(i32(invocation_id.x), i32(invocation_id.y));
textureStore(output, location, color); textureStore(output, location, color);
} }
@@ -113,19 +113,34 @@ fn createRay(origin: vec3<f32>, direction: vec3<f32>) -> Ray
fn createCameraRay(ndc: vec2<f32>) -> Ray { fn createCameraRay(ndc: vec2<f32>) -> Ray {
// let origin = (view.inv_view * vec4<f32>(0.0, 0.0, 0.0, 1.0)).xyz;
// let direction_view = (view.inv_projection * vec4<f32>(uv, 0.0, 1.0)).xyz; let origin = config.world_position;
// let direction = (view.inv_view * vec4<f32>(direction_view, 0.0)).xyz; let target_point = config.world_from_clip * vec4<f32>(ndc, 0.0f, 1.0f);
let origin = view.world_position;
let target_point = view.world_from_clip * vec4<f32>(ndc, 0.0f, 1.0f);
let direction_point = target_point.xyz / target_point.w; let direction_point = target_point.xyz / target_point.w;
let direction = normalize(direction_point - origin); let direction = normalize(direction_point - origin);
return createRay(origin, direction); return createRay(origin, direction);
} }
fn createCameraRay2(ndc: vec2<f32>) -> Ray {
// clip points at near and far
let near_clip = vec4<f32>(ndc, 0.0, 1.0);
let far_clip = vec4<f32>(ndc, 1.0, 1.0);
// project into world space
let near_world4 = config.world_from_clip * near_clip;
let far_world4 = config.world_from_clip * far_clip;
let near_world = near_world4.xyz / near_world4.w;
let far_world = far_world4.xyz / far_world4.w;
// ray starts at near plane, points toward far plane
let origin = near_world;
let direction = normalize(far_world - near_world);
return createRay(origin, direction);
}
fn createSphere(position: vec3<f32>, radius: f32) -> Sphere fn createSphere(position: vec3<f32>, radius: f32) -> Sphere
{ {
var s: Sphere; var s: Sphere;
@@ -139,7 +154,9 @@ fn createSphere(position: vec3<f32>, radius: f32) -> Sphere
fn intersectSphere(ray: Ray, bestHit: ptr<function, RayHit>, sphereIndex: u32) fn intersectSphere(ray: Ray, bestHit: ptr<function, RayHit>, sphereIndex: u32)
{ {
//Sphere sphere = _Spheres[sphereIndex]; //Sphere sphere = _Spheres[sphereIndex];
var sphere = createSphere(vec3<f32>(0.0), 1.0f); var sphere = createSphere(vec3<f32>(0.0f, 0.0f, -10.0f), 1.0f);
var d = ray.origin - sphere.position; var d = ray.origin - sphere.position;
var p1 = -dot(ray.direction, d); var p1 = -dot(ray.direction, d);
var p2sqr = p1 * p1 - dot(d, d) + sphere.radius * sphere.radius; var p2sqr = p1 * p1 - dot(d, d) + sphere.radius * sphere.radius;
@@ -209,7 +226,7 @@ fn shade(ray: ptr<function, Ray>, hit: RayHit) -> vec3<f32>
else else
{ {
(*ray).energy = vec3(0.0f); (*ray).energy = vec3(0.0f);
return vec3<f32>(0.1f); return config.sky_color.xyz;
// var theta = acos(ray.direction.y) / -PI; // var theta = acos(ray.direction.y) / -PI;
// var phi = atan2(ray.direction.x, -ray.direction.z) / -PI * .5f; // var phi = atan2(ray.direction.x, -ray.direction.z) / -PI * .5f;
// return _SkyboxTexture.SampleLevel(sampler_SkyboxTexture, float2(phi, theta), 0).xyz; // return _SkyboxTexture.SampleLevel(sampler_SkyboxTexture, float2(phi, theta), 0).xyz;

View File

@@ -1,12 +1,19 @@
use bevy::{ use bevy::{
asset::RenderAssetUsages, asset::RenderAssetUsages,
math::VectorSpace,
prelude::*, prelude::*,
render::render_resource::{Extent3d, TextureDimension, TextureFormat, TextureUsages}, render::{
render_resource::{Extent3d, TextureDimension, TextureFormat, TextureUsages},
view::RenderLayers,
},
window::PrimaryWindow, window::PrimaryWindow,
}; };
use iyes_perf_ui::prelude::*; use iyes_perf_ui::prelude::*;
use crate::render::pipeline::{TracerPipelinePlugin, TracerRenderTextures, TracerUniforms}; use crate::{
components::rt::RTCamera,
render::pipeline::{TracerPipelinePlugin, TracerRenderTextures, TracerUniforms},
};
pub struct Blackhole; pub struct Blackhole;
@@ -18,6 +25,7 @@ impl Plugin for Blackhole {
app.add_plugins(TracerPipelinePlugin); app.add_plugins(TracerPipelinePlugin);
app.insert_resource(TracerUniforms { app.insert_resource(TracerUniforms {
sky_color: LinearRgba::BLUE, sky_color: LinearRgba::BLUE,
..default()
}); });
//Perf UI //Perf UI
@@ -74,5 +82,16 @@ fn setup(mut commands: Commands, mut images: ResMut<Assets<Image>>, window: Sing
commands.spawn(Camera2d); commands.spawn(Camera2d);
commands.spawn((
// Camera3d::default(),
Projection::Perspective(PerspectiveProjection {
aspect_ratio: size.x as f32 / size.y as f32,
..default()
}),
RTCamera,
Transform::from_xyz(0.0, 0.0, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
Name::new("RT Camera"),
));
commands.insert_resource(TracerRenderTextures(img0, img1)); commands.insert_resource(TracerRenderTextures(img0, img1));
} }

1
src/components/mod.rs Normal file
View File

@@ -0,0 +1 @@
pub mod rt;

4
src/components/rt.rs Normal file
View File

@@ -0,0 +1,4 @@
use bevy::{prelude::*, render::render_resource::encase::private::ShaderType};
#[derive(Component)]
pub struct RTCamera;

View File

@@ -4,6 +4,7 @@ use bevy::window::PresentMode;
use bevy_inspector_egui::{bevy_egui::EguiPlugin, quick::WorldInspectorPlugin}; use bevy_inspector_egui::{bevy_egui::EguiPlugin, quick::WorldInspectorPlugin};
mod app; mod app;
pub mod components;
mod render; mod render;
pub const SHADER_ASSET_PATH: &str = "trace.wgsl"; pub const SHADER_ASSET_PATH: &str = "trace.wgsl";

View File

@@ -4,6 +4,7 @@ use bevy::{
prelude::*, prelude::*,
render::{ render::{
Render, RenderApp, RenderSet, Render, RenderApp, RenderSet,
camera::CameraProjection,
extract_resource::{ExtractResource, ExtractResourcePlugin}, extract_resource::{ExtractResource, ExtractResourcePlugin},
render_asset::RenderAssets, render_asset::RenderAssets,
render_graph::{RenderGraph, RenderLabel}, render_graph::{RenderGraph, RenderLabel},
@@ -15,11 +16,10 @@ use bevy::{
}, },
renderer::{RenderDevice, RenderQueue}, renderer::{RenderDevice, RenderQueue},
texture::GpuImage, texture::GpuImage,
view::{ViewUniform, ViewUniforms},
}, },
}; };
use crate::{SHADER_ASSET_PATH, render::node::TracerNode}; use crate::{SHADER_ASSET_PATH, components::rt::RTCamera, render::node::TracerNode};
#[derive(Debug, Hash, PartialEq, Eq, Clone, RenderLabel)] #[derive(Debug, Hash, PartialEq, Eq, Clone, RenderLabel)]
pub struct TracerLabel; pub struct TracerLabel;
@@ -31,6 +31,8 @@ pub struct TracerRenderTextures(pub Handle<Image>, pub Handle<Image>);
#[derive(Resource, Clone, ExtractResource, ShaderType, Default)] #[derive(Resource, Clone, ExtractResource, ShaderType, Default)]
pub struct TracerUniforms { pub struct TracerUniforms {
pub sky_color: LinearRgba, pub sky_color: LinearRgba,
pub world_from_clip: Mat4,
pub world_position: Vec3,
} }
pub struct TracerPipelinePlugin; pub struct TracerPipelinePlugin;
@@ -43,6 +45,8 @@ impl Plugin for TracerPipelinePlugin {
)); ));
app.init_resource::<TracerUniforms>() app.init_resource::<TracerUniforms>()
.add_systems(Update, switch_textures); .add_systems(Update, switch_textures);
app.add_systems(First, update_tracer_uniforms);
let render_app = app.sub_app_mut(RenderApp); let render_app = app.sub_app_mut(RenderApp);
// render_app.add_systems(Startup, init_pipeline); // render_app.add_systems(Startup, init_pipeline);
@@ -86,7 +90,6 @@ impl FromWorld for TracerPipeline {
texture_storage_2d(TextureFormat::Rgba32Float, StorageTextureAccess::ReadOnly), texture_storage_2d(TextureFormat::Rgba32Float, StorageTextureAccess::ReadOnly),
texture_storage_2d(TextureFormat::Rgba32Float, StorageTextureAccess::WriteOnly), texture_storage_2d(TextureFormat::Rgba32Float, StorageTextureAccess::WriteOnly),
uniform_buffer::<TracerUniforms>(false), uniform_buffer::<TracerUniforms>(false),
uniform_buffer::<ViewUniform>(false),
), ),
), ),
); );
@@ -135,7 +138,6 @@ fn init_pipeline(
texture_storage_2d(TextureFormat::Rgba32Float, StorageTextureAccess::ReadOnly), texture_storage_2d(TextureFormat::Rgba32Float, StorageTextureAccess::ReadOnly),
texture_storage_2d(TextureFormat::Rgba32Float, StorageTextureAccess::WriteOnly), texture_storage_2d(TextureFormat::Rgba32Float, StorageTextureAccess::WriteOnly),
uniform_buffer::<TracerUniforms>(false), uniform_buffer::<TracerUniforms>(false),
uniform_buffer::<ViewUniform>(false),
), ),
), ),
); );
@@ -170,6 +172,24 @@ fn init_pipeline(
#[derive(Resource)] #[derive(Resource)]
pub struct TracerImageBindGroups(pub [BindGroup; 2]); pub struct TracerImageBindGroups(pub [BindGroup; 2]);
fn update_tracer_uniforms(
mut tracer_uniforms: ResMut<TracerUniforms>,
rt_camera: Single<(&GlobalTransform, &Projection), With<RTCamera>>,
) {
let (transform, projection) = rt_camera.into_inner();
let view = transform.compute_matrix().inverse();
let clip_from_view = match projection {
Projection::Perspective(perspective_projection) => perspective_projection.get_clip_from_view(),
_ => unreachable!("This should never happen: Invalid projection type on RT Camera"),
};
let clip_from_world = clip_from_view * view;
let world_from_clip = clip_from_world.inverse();
info_once!("world_from_clip = {:?}", world_from_clip);
tracer_uniforms.world_from_clip = world_from_clip;
tracer_uniforms.world_position = transform.translation();
}
fn prepare_bind_groups( fn prepare_bind_groups(
mut commands: Commands, mut commands: Commands,
pipeline: Res<TracerPipeline>, pipeline: Res<TracerPipeline>,
@@ -178,13 +198,10 @@ fn prepare_bind_groups(
tracer_uniforms: Res<TracerUniforms>, tracer_uniforms: Res<TracerUniforms>,
render_device: Res<RenderDevice>, render_device: Res<RenderDevice>,
queue: Res<RenderQueue>, queue: Res<RenderQueue>,
view_uniforms: Res<ViewUniforms>,
) { ) {
let view_a = gpu_images.get(&tracer_images.0).unwrap(); let view_a = gpu_images.get(&tracer_images.0).unwrap();
let view_b = gpu_images.get(&tracer_images.1).unwrap(); let view_b = gpu_images.get(&tracer_images.1).unwrap();
//Todo: Insert View Uniforms
// Uniform buffer is used here to demonstrate how to set up a uniform in a compute shader // Uniform buffer is used here to demonstrate how to set up a uniform in a compute shader
// Alternatives such as storage buffers or push constants may be more suitable for your use case // Alternatives such as storage buffers or push constants may be more suitable for your use case
let mut uniform_buffer = UniformBuffer::from(tracer_uniforms.into_inner()); let mut uniform_buffer = UniformBuffer::from(tracer_uniforms.into_inner());