centralized state
This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
//! Szenen-Pass: zeichnet die 3D-Welt ins interne Target.
|
||||
//!
|
||||
//! Stand Schritt 3: ein hartkodierter Testwürfel mit Vertex-Colors —
|
||||
//! Meshes aus OBJ und Texturen kommen in Schritt 5. Die Shader
|
||||
//! (scene.wgsl) sind dagegen schon die echten PS1-Shader.
|
||||
|
||||
use wgpu::util::DeviceExt;
|
||||
|
||||
use crate::render::math::Mat4;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
|
||||
struct Vertex {
|
||||
pos: [f32; 3],
|
||||
color: [f32; 3],
|
||||
}
|
||||
|
||||
const VERTEX_LAYOUT: wgpu::VertexBufferLayout<'static> = wgpu::VertexBufferLayout {
|
||||
array_stride: size_of::<Vertex>() as u64,
|
||||
step_mode: wgpu::VertexStepMode::Vertex,
|
||||
attributes: &wgpu::vertex_attr_array![0 => Float32x3, 1 => Float32x3],
|
||||
};
|
||||
|
||||
pub struct ScenePass {
|
||||
pipeline: wgpu::RenderPipeline,
|
||||
vbuf: wgpu::Buffer,
|
||||
ibuf: wgpu::Buffer,
|
||||
ubuf: wgpu::Buffer,
|
||||
bind: wgpu::BindGroup,
|
||||
index_count: u32,
|
||||
}
|
||||
|
||||
impl ScenePass {
|
||||
pub fn new(
|
||||
device: &wgpu::Device,
|
||||
color_format: wgpu::TextureFormat,
|
||||
depth_format: wgpu::TextureFormat,
|
||||
) -> Self {
|
||||
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||
label: Some("scene"),
|
||||
source: wgpu::ShaderSource::Wgsl(include_str!("scene.wgsl").into()),
|
||||
});
|
||||
let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||
label: Some("scene"),
|
||||
layout: None,
|
||||
vertex: wgpu::VertexState {
|
||||
module: &shader,
|
||||
entry_point: Some("vs_main"),
|
||||
compilation_options: Default::default(),
|
||||
buffers: &[VERTEX_LAYOUT],
|
||||
},
|
||||
fragment: Some(wgpu::FragmentState {
|
||||
module: &shader,
|
||||
entry_point: Some("fs_main"),
|
||||
compilation_options: Default::default(),
|
||||
targets: &[Some(color_format.into())],
|
||||
}),
|
||||
// Cull aus: der Z-Buffer sortiert auch so korrekt, und die
|
||||
// irl3d-Materialien sind teils two-sided. Entscheidung pro
|
||||
// Material fällt mit dem Szenen-Loader (Schritt 5).
|
||||
primitive: wgpu::PrimitiveState::default(),
|
||||
depth_stencil: Some(wgpu::DepthStencilState {
|
||||
format: depth_format,
|
||||
depth_write_enabled: Some(true),
|
||||
depth_compare: Some(wgpu::CompareFunction::Less),
|
||||
stencil: wgpu::StencilState::default(),
|
||||
bias: wgpu::DepthBiasState::default(),
|
||||
}),
|
||||
multisample: wgpu::MultisampleState::default(),
|
||||
multiview_mask: None,
|
||||
cache: None,
|
||||
});
|
||||
|
||||
let (verts, indices) = cube();
|
||||
let vbuf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
label: Some("cube vertices"),
|
||||
contents: bytemuck::cast_slice(&verts),
|
||||
usage: wgpu::BufferUsages::VERTEX,
|
||||
});
|
||||
let ibuf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
label: Some("cube indices"),
|
||||
contents: bytemuck::cast_slice(&indices),
|
||||
usage: wgpu::BufferUsages::INDEX,
|
||||
});
|
||||
let ubuf = device.create_buffer(&wgpu::BufferDescriptor {
|
||||
label: Some("scene uniforms"),
|
||||
size: size_of::<Mat4>() as u64,
|
||||
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
||||
mapped_at_creation: false,
|
||||
});
|
||||
let bind = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
label: Some("scene"),
|
||||
layout: &pipeline.get_bind_group_layout(0),
|
||||
entries: &[wgpu::BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: ubuf.as_entire_binding(),
|
||||
}],
|
||||
});
|
||||
|
||||
Self { pipeline, vbuf, ibuf, ubuf, bind, index_count: indices.len() as u32 }
|
||||
}
|
||||
|
||||
/// Uniforms für diesen Frame hochladen — vor dem Render-Pass rufen.
|
||||
pub fn prepare(&self, queue: &wgpu::Queue, mvp: &Mat4) {
|
||||
queue.write_buffer(&self.ubuf, 0, bytemuck::bytes_of(mvp));
|
||||
}
|
||||
|
||||
pub fn draw(&self, pass: &mut wgpu::RenderPass) {
|
||||
pass.set_pipeline(&self.pipeline);
|
||||
pass.set_bind_group(0, &self.bind, &[]);
|
||||
pass.set_vertex_buffer(0, self.vbuf.slice(..));
|
||||
pass.set_index_buffer(self.ibuf.slice(..), wgpu::IndexFormat::Uint16);
|
||||
pass.draw_indexed(0..self.index_count, 0, 0..1);
|
||||
}
|
||||
}
|
||||
|
||||
/// Einheitswürfel um den Ursprung, jede Seite eine Farbe. Der
|
||||
/// Helligkeitsverlauf über die Ecken erzeugt Gradienten, an denen
|
||||
/// Dither und affine Interpolation sichtbar werden.
|
||||
fn cube() -> (Vec<Vertex>, Vec<u16>) {
|
||||
const S: f32 = 0.5;
|
||||
let faces: [([f32; 3], [[f32; 3]; 4]); 6] = [
|
||||
([0.9, 0.2, 0.2], [[ S, -S, -S], [ S, S, -S], [ S, S, S], [ S, -S, S]]), // +X
|
||||
([0.2, 0.9, 0.9], [[-S, -S, -S], [-S, S, -S], [-S, S, S], [-S, -S, S]]), // -X
|
||||
([0.2, 0.9, 0.2], [[-S, S, -S], [ S, S, -S], [ S, S, S], [-S, S, S]]), // +Y
|
||||
([0.9, 0.2, 0.9], [[-S, -S, -S], [ S, -S, -S], [ S, -S, S], [-S, -S, S]]), // -Y
|
||||
([0.3, 0.3, 0.9], [[-S, -S, S], [ S, -S, S], [ S, S, S], [-S, S, S]]), // +Z
|
||||
([0.9, 0.8, 0.2], [[-S, -S, -S], [ S, -S, -S], [ S, S, -S], [-S, S, -S]]), // -Z
|
||||
];
|
||||
const SHADE: [f32; 4] = [1.0, 0.65, 0.4, 0.65];
|
||||
|
||||
let mut verts = Vec::with_capacity(24);
|
||||
let mut idx: Vec<u16> = Vec::with_capacity(36);
|
||||
for (base, corners) in faces {
|
||||
let b = verts.len() as u16;
|
||||
for (i, pos) in corners.into_iter().enumerate() {
|
||||
verts.push(Vertex { pos, color: base.map(|c| c * SHADE[i]) });
|
||||
}
|
||||
idx.extend([b, b + 1, b + 2, b, b + 2, b + 3]);
|
||||
}
|
||||
(verts, idx)
|
||||
}
|
||||
Reference in New Issue
Block a user