Initial commit
This commit is contained in:
commit
f8a1c65b8c
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/target
|
1448
Cargo.lock
generated
Normal file
1448
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
18
Cargo.toml
Normal file
18
Cargo.toml
Normal file
@ -0,0 +1,18 @@
|
||||
[package]
|
||||
name = "yray"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.58"
|
||||
bytemuck = "1.10.0"
|
||||
bytemuck_derive = "1.1.0"
|
||||
log = "0.4.17"
|
||||
nalgebra-glm = "0.17.0"
|
||||
simplelog = "^0.12.0"
|
||||
vulkano = "0.29.0"
|
||||
vulkano-shaders = "0.29.0"
|
||||
vulkano-win = "0.29.0"
|
||||
winit = "^0.26.1"
|
56
src/error.rs
Normal file
56
src/error.rs
Normal file
@ -0,0 +1,56 @@
|
||||
use std::fmt::{Display, Formatter};
|
||||
|
||||
use vulkano::{
|
||||
device::{physical::SurfacePropertiesError, DeviceCreationError},
|
||||
image::view::ImageViewCreationError,
|
||||
instance::InstanceCreationError,
|
||||
render_pass::FramebufferCreationError,
|
||||
swapchain::SwapchainCreationError,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum EngineError {
|
||||
NoPhysicalDeviceFound,
|
||||
SwapchainRecreateFailed,
|
||||
VulkanInstanceError(InstanceCreationError),
|
||||
VulkanDeviceError(DeviceCreationError),
|
||||
WindowError(vulkano_win::CreationError),
|
||||
VulkanSwapchainError(SwapchainCreationError),
|
||||
VulkanSurfacePropertiesError(SurfacePropertiesError),
|
||||
VulkanImageViewError(ImageViewCreationError),
|
||||
VulkanFramebufferError(FramebufferCreationError),
|
||||
}
|
||||
|
||||
impl Display for EngineError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! wrap {
|
||||
($dst:ty, $src:ty, $variant:ident) => {
|
||||
impl From<$src> for $dst {
|
||||
fn from(e: $src) -> Self {
|
||||
Self::$variant(e)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
wrap!(EngineError, DeviceCreationError, VulkanDeviceError);
|
||||
wrap!(EngineError, InstanceCreationError, VulkanInstanceError);
|
||||
wrap!(EngineError, vulkano_win::CreationError, WindowError);
|
||||
wrap!(
|
||||
EngineError,
|
||||
SurfacePropertiesError,
|
||||
VulkanSurfacePropertiesError
|
||||
);
|
||||
wrap!(EngineError, SwapchainCreationError, VulkanSwapchainError);
|
||||
wrap!(EngineError, ImageViewCreationError, VulkanImageViewError);
|
||||
wrap!(
|
||||
EngineError,
|
||||
FramebufferCreationError,
|
||||
VulkanFramebufferError
|
||||
);
|
||||
|
||||
impl std::error::Error for EngineError {}
|
173
src/main.rs
Normal file
173
src/main.rs
Normal file
@ -0,0 +1,173 @@
|
||||
extern crate nalgebra_glm as glm;
|
||||
|
||||
use std::time::Instant;
|
||||
|
||||
use bytemuck_derive::{Pod, Zeroable};
|
||||
use glm::TMat4;
|
||||
use render::event::RenderEvent;
|
||||
use vulkano::{
|
||||
buffer::{BufferUsage, CpuAccessibleBuffer, CpuBufferPool},
|
||||
command_buffer::{AutoCommandBufferBuilder, CommandBufferUsage, SubpassContents},
|
||||
descriptor_set::{PersistentDescriptorSet, WriteDescriptorSet},
|
||||
pipeline::{
|
||||
graphics::{
|
||||
input_assembly::InputAssemblyState, vertex_input::BuffersDefinition,
|
||||
viewport::ViewportState,
|
||||
},
|
||||
GraphicsPipeline, Pipeline, PipelineBindPoint,
|
||||
},
|
||||
render_pass::Subpass,
|
||||
sync::GpuFuture,
|
||||
};
|
||||
use winit::{
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Default, Zeroable, Pod)]
|
||||
struct Vertex {
|
||||
position: [f32; 3],
|
||||
color: [f32; 3],
|
||||
}
|
||||
vulkano::impl_vertex!(Vertex, position, color);
|
||||
|
||||
pub mod error;
|
||||
pub mod render;
|
||||
|
||||
use crate::render::{context::VulkanContext, shaders};
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Default, Clone, Copy)]
|
||||
struct ModelViewProjection {
|
||||
model: TMat4<f32>,
|
||||
view: TMat4<f32>,
|
||||
projection: TMat4<f32>,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut mvp = ModelViewProjection {
|
||||
model: glm::identity(),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let event_loop = EventLoop::new();
|
||||
let vk = VulkanContext::new_windowed(&event_loop, WindowBuilder::new()).unwrap();
|
||||
|
||||
let vs = shaders::vs::load(vk.device().clone()).unwrap();
|
||||
let fs = shaders::fs::load(vk.device().clone()).unwrap();
|
||||
|
||||
let uniform_buffer: CpuBufferPool<shaders::vs::ty::MVP_Data> =
|
||||
CpuBufferPool::uniform_buffer(vk.device().clone());
|
||||
|
||||
let pipeline = GraphicsPipeline::start()
|
||||
.vertex_input_state(BuffersDefinition::new().vertex::<Vertex>())
|
||||
.vertex_shader(vs.entry_point("main").unwrap(), ())
|
||||
.input_assembly_state(InputAssemblyState::new())
|
||||
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
|
||||
.fragment_shader(fs.entry_point("main").unwrap(), ())
|
||||
.render_pass(Subpass::from(vk.render_pass().clone(), 0).unwrap())
|
||||
.build(vk.device().clone())
|
||||
.unwrap();
|
||||
|
||||
let vertex_buffer = CpuAccessibleBuffer::from_iter(
|
||||
vk.device().clone(),
|
||||
BufferUsage::vertex_buffer(),
|
||||
false,
|
||||
[
|
||||
Vertex {
|
||||
position: [-1.0, -1.0, 0.0],
|
||||
color: [1.0, 0.0, 0.0],
|
||||
},
|
||||
Vertex {
|
||||
position: [0.0, 1.0, 0.0],
|
||||
color: [0.0, 1.0, 0.0],
|
||||
},
|
||||
Vertex {
|
||||
position: [1.0, -1.0, 0.0],
|
||||
color: [0.0, 0.0, 1.0],
|
||||
},
|
||||
]
|
||||
.iter()
|
||||
.cloned(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let rotation_start = Instant::now();
|
||||
|
||||
let viewport = vk.viewport();
|
||||
mvp.projection = glm::perspective(
|
||||
viewport.dimensions[0] / viewport.dimensions[1],
|
||||
45.0,
|
||||
0.01,
|
||||
100.0,
|
||||
);
|
||||
|
||||
vk.run(event_loop, move |event, ctl| match event {
|
||||
RenderEvent::CloseRequested => {
|
||||
*ctl = ControlFlow::Exit;
|
||||
}
|
||||
RenderEvent::ViewportResized(size) => {
|
||||
mvp.projection = glm::perspective(
|
||||
size.0 / size.1,
|
||||
45.0,
|
||||
0.01,
|
||||
100.0,
|
||||
);
|
||||
}
|
||||
}, move |vk, future, framebuffer| {
|
||||
let clear_values = vec![[0.0, 0.68, 1.0, 1.0].into()];
|
||||
|
||||
let uniform_buffer_subbuffer = {
|
||||
let elapsed = rotation_start.elapsed().as_secs() as f64
|
||||
+ rotation_start.elapsed().subsec_nanos() as f64 / 1_000_000_000.0;
|
||||
|
||||
mvp.view = glm::look_at(
|
||||
&glm::vec3(elapsed.cos() as f32 * 5.0, 5.0, elapsed.sin() as f32 * 5.0),
|
||||
&glm::vec3(0.0, 0.0, 0.0),
|
||||
&glm::vec3(0.0, 1.0, 0.0),
|
||||
);
|
||||
|
||||
let uniform_data = shaders::vs::ty::MVP_Data {
|
||||
model: mvp.model.into(),
|
||||
view: mvp.view.into(),
|
||||
projection: mvp.projection.into(),
|
||||
};
|
||||
|
||||
uniform_buffer.next(uniform_data).unwrap()
|
||||
};
|
||||
|
||||
let layout = pipeline.layout().set_layouts().get(0).unwrap();
|
||||
let set = PersistentDescriptorSet::new(
|
||||
layout.clone(),
|
||||
[WriteDescriptorSet::buffer(0, uniform_buffer_subbuffer)],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let mut cmd_buffer_builder = AutoCommandBufferBuilder::primary(vk.device().clone(), vk.queue_family(), CommandBufferUsage::OneTimeSubmit)
|
||||
.unwrap();
|
||||
cmd_buffer_builder
|
||||
.begin_render_pass(
|
||||
framebuffer,
|
||||
SubpassContents::Inline,
|
||||
clear_values,
|
||||
)
|
||||
.unwrap()
|
||||
.set_viewport(0, [vk.viewport().clone()])
|
||||
.bind_pipeline_graphics(pipeline.clone())
|
||||
.bind_descriptor_sets(
|
||||
PipelineBindPoint::Graphics,
|
||||
pipeline.layout().clone(),
|
||||
0,
|
||||
set,
|
||||
)
|
||||
.bind_vertex_buffers(0, vertex_buffer.clone())
|
||||
.draw(3, 1, 0, 0)
|
||||
.unwrap()
|
||||
.end_render_pass()
|
||||
.unwrap();
|
||||
let command_buffer = cmd_buffer_builder.build().unwrap();
|
||||
|
||||
future.then_execute(vk.queue().clone(), command_buffer).unwrap()
|
||||
});
|
||||
}
|
328
src/render/context.rs
Normal file
328
src/render/context.rs
Normal file
@ -0,0 +1,328 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use vulkano::{
|
||||
device::{
|
||||
physical::{PhysicalDevice, PhysicalDeviceType, QueueFamily},
|
||||
Device, DeviceCreateInfo, DeviceExtensions, Queue, QueueCreateInfo,
|
||||
},
|
||||
image::{view::ImageView, SwapchainImage},
|
||||
instance::{Instance, InstanceCreateInfo},
|
||||
pipeline::graphics::viewport::Viewport,
|
||||
render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass},
|
||||
swapchain::{
|
||||
self, AcquireError, Surface, Swapchain, SwapchainAcquireFuture, SwapchainCreateInfo,
|
||||
SwapchainCreationError,
|
||||
},
|
||||
sync::{self, FlushError},
|
||||
sync::{GpuFuture, JoinFuture},
|
||||
Version,
|
||||
};
|
||||
use vulkano_win::VkSurfaceBuild;
|
||||
use winit::{
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
window::{Window, WindowBuilder},
|
||||
};
|
||||
|
||||
use crate::error::EngineError;
|
||||
|
||||
use super::event::RenderEvent;
|
||||
|
||||
pub struct VulkanContext {
|
||||
#[allow(dead_code)]
|
||||
instance: Arc<Instance>,
|
||||
device: Arc<Device>,
|
||||
queue: Arc<Queue>,
|
||||
|
||||
swapchain: Arc<Swapchain<Window>>,
|
||||
swapchain_images: Vec<Arc<SwapchainImage<Window>>>,
|
||||
render_pass: Arc<RenderPass>,
|
||||
framebuffers: Vec<Arc<Framebuffer>>,
|
||||
|
||||
surface: Arc<Surface<Window>>,
|
||||
viewport: Viewport,
|
||||
}
|
||||
|
||||
impl VulkanContext {
|
||||
pub fn new_windowed(
|
||||
event_loop: &EventLoop<()>,
|
||||
window_builder: WindowBuilder,
|
||||
) -> Result<Self, EngineError> {
|
||||
let required_extensions = vulkano_win::required_extensions();
|
||||
let device_extensions = DeviceExtensions {
|
||||
khr_swapchain: true,
|
||||
..DeviceExtensions::none()
|
||||
};
|
||||
|
||||
let instance = Instance::new(InstanceCreateInfo {
|
||||
enabled_extensions: required_extensions,
|
||||
max_api_version: Some(Version::V1_1),
|
||||
..Default::default()
|
||||
})?;
|
||||
let surface = window_builder.build_vk_surface(event_loop, instance.clone())?;
|
||||
|
||||
let (physical, queue_family) =
|
||||
Self::select_physical_device(&instance, surface.clone(), device_extensions)
|
||||
.ok_or(EngineError::NoPhysicalDeviceFound)?;
|
||||
|
||||
let (device, mut queues) = Device::new(
|
||||
physical,
|
||||
DeviceCreateInfo {
|
||||
enabled_extensions: physical.required_extensions().union(&device_extensions),
|
||||
|
||||
queue_create_infos: vec![QueueCreateInfo::family(queue_family)],
|
||||
..Default::default()
|
||||
},
|
||||
)?;
|
||||
let queue = queues.next().unwrap();
|
||||
|
||||
let dim = surface.window().inner_size();
|
||||
let (swapchain, swapchain_images) = {
|
||||
let caps = physical
|
||||
.surface_capabilities(&surface, Default::default())
|
||||
.unwrap();
|
||||
let usage = caps.supported_usage_flags;
|
||||
let alpha = caps.supported_composite_alpha.iter().next().unwrap();
|
||||
let image_format = Some(physical.surface_formats(&surface, Default::default())?[0].0);
|
||||
|
||||
Swapchain::new(
|
||||
device.clone(),
|
||||
surface.clone(),
|
||||
SwapchainCreateInfo {
|
||||
min_image_count: caps.min_image_count,
|
||||
image_format,
|
||||
image_extent: dim.into(),
|
||||
image_usage: usage,
|
||||
composite_alpha: alpha,
|
||||
..Default::default()
|
||||
},
|
||||
)?
|
||||
};
|
||||
|
||||
let render_pass = vulkano::single_pass_renderpass!(
|
||||
device.clone(),
|
||||
attachments: {
|
||||
color: {
|
||||
load: Clear,
|
||||
store: Store,
|
||||
format: swapchain.image_format(),
|
||||
samples: 1,
|
||||
}
|
||||
},
|
||||
pass: {
|
||||
color: [color],
|
||||
depth_stencil: {}
|
||||
}
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let framebuffers = Self::create_framebuffers(&swapchain_images, render_pass.clone())?;
|
||||
|
||||
let viewport = Viewport {
|
||||
origin: [0.0, 0.0],
|
||||
dimensions: dim.into(),
|
||||
depth_range: 0.0..1.0,
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
instance,
|
||||
surface,
|
||||
device,
|
||||
queue,
|
||||
swapchain,
|
||||
swapchain_images,
|
||||
render_pass,
|
||||
framebuffers,
|
||||
viewport,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn run<
|
||||
T: GpuFuture + 'static,
|
||||
EventHandler: 'static + FnMut(RenderEvent, &mut ControlFlow),
|
||||
RenderHandler: 'static
|
||||
+ FnMut(
|
||||
&mut VulkanContext,
|
||||
JoinFuture<Box<dyn GpuFuture>, SwapchainAcquireFuture<Window>>,
|
||||
Arc<Framebuffer>,
|
||||
) -> T,
|
||||
>(
|
||||
mut self,
|
||||
event_loop: EventLoop<()>,
|
||||
mut event_handler: EventHandler,
|
||||
mut render_handler: RenderHandler,
|
||||
) -> ! {
|
||||
let mut recreate_swapchain = false;
|
||||
let mut previous_frame_end =
|
||||
Some(Box::new(sync::now(self.device().clone())) as Box<dyn GpuFuture>);
|
||||
|
||||
event_loop.run(move |event, _, ctl| {
|
||||
match event {
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
..
|
||||
} => {
|
||||
event_handler(RenderEvent::CloseRequested, ctl);
|
||||
}
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::Resized(_),
|
||||
..
|
||||
} => {
|
||||
recreate_swapchain = true;
|
||||
}
|
||||
Event::RedrawEventsCleared => {
|
||||
previous_frame_end
|
||||
.as_mut()
|
||||
.take()
|
||||
.unwrap()
|
||||
.cleanup_finished();
|
||||
|
||||
if recreate_swapchain {
|
||||
let size = self.recreate_swapchain().unwrap();
|
||||
event_handler(RenderEvent::ViewportResized(size), ctl);
|
||||
recreate_swapchain = false;
|
||||
}
|
||||
|
||||
let (image_num, suboptimal, acquire_future) =
|
||||
match swapchain::acquire_next_image(self.swapchain.clone(), None) {
|
||||
Ok(r) => r,
|
||||
Err(AcquireError::OutOfDate) => {
|
||||
recreate_swapchain = true;
|
||||
return;
|
||||
}
|
||||
Err(e) => panic!("Failed to acquire next image: {:?}", e),
|
||||
};
|
||||
|
||||
if suboptimal {
|
||||
recreate_swapchain = true;
|
||||
}
|
||||
|
||||
let future = previous_frame_end.take().unwrap().join(acquire_future);
|
||||
let framebuffer = self.framebuffers[image_num].clone();
|
||||
let future = render_handler(&mut self, future, framebuffer)
|
||||
.then_swapchain_present(
|
||||
self.queue.clone(),
|
||||
self.swapchain.clone(),
|
||||
image_num,
|
||||
)
|
||||
.then_signal_semaphore_and_flush();
|
||||
|
||||
match future {
|
||||
Ok(future) => {
|
||||
previous_frame_end = Some(Box::new(future) as Box<_>);
|
||||
}
|
||||
Err(FlushError::OutOfDate) => {
|
||||
recreate_swapchain = true;
|
||||
previous_frame_end =
|
||||
Some(Box::new(sync::now(self.device.clone())) as Box<_>);
|
||||
}
|
||||
Err(e) => {
|
||||
println!("Failed to flush future: {:?}", e);
|
||||
previous_frame_end =
|
||||
Some(Box::new(sync::now(self.device.clone())) as Box<_>);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
fn recreate_swapchain(&mut self) -> Result<(f32, f32), EngineError> {
|
||||
let dim = self.surface.window().inner_size();
|
||||
self.viewport.dimensions = dim.into();
|
||||
|
||||
let (new_swapchain, new_images) = match self.swapchain.recreate(SwapchainCreateInfo {
|
||||
image_extent: dim.into(),
|
||||
..self.swapchain.create_info()
|
||||
}) {
|
||||
Ok(r) => r,
|
||||
Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => {
|
||||
return Err(EngineError::SwapchainRecreateFailed);
|
||||
}
|
||||
Err(e) => panic!("Failed to recreate swapchain: {:?}", e),
|
||||
};
|
||||
|
||||
self.swapchain = new_swapchain;
|
||||
self.framebuffers = Self::create_framebuffers(&new_images, self.render_pass.clone())?;
|
||||
self.swapchain_images = new_images;
|
||||
|
||||
Ok(dim.into())
|
||||
}
|
||||
|
||||
pub fn device(&self) -> &Arc<Device> {
|
||||
&self.device
|
||||
}
|
||||
|
||||
pub fn render_pass(&self) -> &Arc<RenderPass> {
|
||||
&self.render_pass
|
||||
}
|
||||
|
||||
pub fn viewport(&self) -> &Viewport {
|
||||
&self.viewport
|
||||
}
|
||||
|
||||
pub fn swapchain(&self) -> &Arc<Swapchain<Window>> {
|
||||
&self.swapchain
|
||||
}
|
||||
|
||||
pub fn queue_family(&self) -> QueueFamily {
|
||||
self.queue.family()
|
||||
}
|
||||
|
||||
pub fn framebuffers(&self) -> &[Arc<Framebuffer>] {
|
||||
&self.framebuffers
|
||||
}
|
||||
|
||||
pub fn queue(&self) -> &Arc<Queue> {
|
||||
&self.queue
|
||||
}
|
||||
|
||||
fn select_physical_device(
|
||||
instance: &Arc<Instance>,
|
||||
surface: Arc<Surface<Window>>,
|
||||
device_extensions: DeviceExtensions,
|
||||
) -> Option<(PhysicalDevice, QueueFamily)> {
|
||||
PhysicalDevice::enumerate(instance)
|
||||
.filter(|&p| p.supported_extensions().is_superset_of(&device_extensions))
|
||||
.filter_map(|p| {
|
||||
p.queue_families()
|
||||
.find(|&q| {
|
||||
q.supports_graphics() && q.supports_surface(&surface).unwrap_or(false)
|
||||
})
|
||||
.map(|q| (p, q))
|
||||
})
|
||||
.min_by_key(|(p, _)| match p.properties().device_type {
|
||||
PhysicalDeviceType::DiscreteGpu => 0,
|
||||
PhysicalDeviceType::IntegratedGpu => 1,
|
||||
PhysicalDeviceType::VirtualGpu => 2,
|
||||
PhysicalDeviceType::Cpu => 3,
|
||||
PhysicalDeviceType::Other => 4,
|
||||
})
|
||||
}
|
||||
|
||||
fn create_single_framebuffer(
|
||||
image: Arc<SwapchainImage<Window>>,
|
||||
render_pass: Arc<RenderPass>,
|
||||
) -> Result<Arc<Framebuffer>, EngineError> {
|
||||
let view = ImageView::new_default(image)?;
|
||||
let res = Framebuffer::new(
|
||||
render_pass,
|
||||
FramebufferCreateInfo {
|
||||
attachments: vec![view],
|
||||
..Default::default()
|
||||
},
|
||||
)?;
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
fn create_framebuffers(
|
||||
images: &[Arc<SwapchainImage<Window>>],
|
||||
render_pass: Arc<RenderPass>,
|
||||
) -> Result<Vec<Arc<Framebuffer>>, EngineError> {
|
||||
images
|
||||
.iter()
|
||||
.map(|image| Self::create_single_framebuffer(image.clone(), render_pass.clone()))
|
||||
.collect()
|
||||
}
|
||||
}
|
5
src/render/event.rs
Normal file
5
src/render/event.rs
Normal file
@ -0,0 +1,5 @@
|
||||
|
||||
pub enum RenderEvent {
|
||||
ViewportResized((f32, f32)),
|
||||
CloseRequested,
|
||||
}
|
3
src/render/mod.rs
Normal file
3
src/render/mod.rs
Normal file
@ -0,0 +1,3 @@
|
||||
pub mod context;
|
||||
pub mod shaders;
|
||||
pub mod event;
|
45
src/render/shaders.rs
Normal file
45
src/render/shaders.rs
Normal file
@ -0,0 +1,45 @@
|
||||
#![allow(clippy::needless_question_mark)]
|
||||
|
||||
pub mod vs {
|
||||
vulkano_shaders::shader! {
|
||||
ty: "vertex",
|
||||
src: "
|
||||
#version 450
|
||||
|
||||
layout(location = 0) in vec3 position;
|
||||
layout(location = 1) in vec3 color;
|
||||
layout(location = 0) out vec3 out_color;
|
||||
|
||||
layout(set = 0, binding = 0) uniform MVP_Data {
|
||||
mat4 model;
|
||||
mat4 view;
|
||||
mat4 projection;
|
||||
} uniforms;
|
||||
|
||||
void main() {
|
||||
mat4 worldview = uniforms.view * uniforms.model;
|
||||
gl_Position = uniforms.projection * worldview * vec4(position, 1.0);
|
||||
out_color = color;
|
||||
}",
|
||||
types_meta: {
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
|
||||
#[derive(Clone, Copy, Zeroable, Pod)]
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub mod fs {
|
||||
vulkano_shaders::shader! {
|
||||
ty: "fragment",
|
||||
src: "
|
||||
#version 450
|
||||
|
||||
layout(location = 0) in vec3 in_color;
|
||||
layout(location = 0) out vec4 f_color;
|
||||
|
||||
void main() {
|
||||
f_color = vec4(in_color, 1.0);
|
||||
}"
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user