engine/render: start implement multi-texture materials

ecs
q3k 2020-07-13 22:58:24 +02:00
parent c5e6c75f23
commit 0aefcdd347
10 changed files with 240 additions and 76 deletions

View File

@ -11,6 +11,8 @@ rust_binary(
], ],
srcs = [ srcs = [
"src/main.rs", "src/main.rs",
"src/physics/mod.rs",
"src/physics/color.rs",
"src/render/mod.rs", "src/render/mod.rs",
"src/render/renderable.rs", "src/render/renderable.rs",
"src/render/vulkan/data.rs", "src/render/vulkan/data.rs",

View File

@ -3,13 +3,9 @@
#extension GL_ARB_separate_shader_objects : enable #extension GL_ARB_separate_shader_objects : enable
layout(binding = 0) uniform sampler2D texSampler; layout(binding = 0) uniform sampler2D texSampler;
layout(location = 0) in vec2 fragTexCoord;
layout(location = 0) in vec3 fragColor;
layout(location = 1) in vec2 fragTexCoord;
layout(location = 0) out vec4 outColor; layout(location = 0) out vec4 outColor;
void main() { void main() {
//outColor = vec4(fragColor, 1.0);
outColor = texture(texSampler, fragTexCoord); outColor = texture(texSampler, fragTexCoord);
} }

View File

@ -6,12 +6,10 @@ layout(push_constant) uniform UniformBufferObject {
} ubo; } ubo;
layout(location = 0) in vec3 pos; layout(location = 0) in vec3 pos;
layout(location = 1) in vec3 color; layout(location = 1) in mat4 model;
layout(location = 2) in mat4 model; layout(location = 5) in vec2 tex;
layout(location = 6) in vec2 tex;
layout(location = 0) out vec3 fragColor; layout(location = 0) out vec2 fragTexCoord;
layout(location = 1) out vec2 fragTexCoord;
out gl_PerVertex { out gl_PerVertex {
vec4 gl_Position; vec4 gl_Position;
@ -19,7 +17,6 @@ out gl_PerVertex {
void main() { void main() {
gl_Position = ubo.view * model * vec4(pos, 1.0); gl_Position = ubo.view * model * vec4(pos, 1.0);
fragColor = color;
fragTexCoord = tex; fragTexCoord = tex;

View File

@ -7,9 +7,11 @@ use cgmath as cgm;
mod render; mod render;
mod util; mod util;
mod physics;
use render::vulkan::data; use render::vulkan::data;
use render::renderable::{Object, Renderable, Resource, ResourceManager, Texture, Mesh}; use render::renderable::{Object, Renderable, Resource, ResourceManager, Material, Mesh, ImageRefOrColor};
use physics::color;
fn main() { fn main() {
env_logger::init(); env_logger::init();
@ -64,9 +66,10 @@ fn main() {
rm.add(Resource::Mesh(Mesh::new(vertices, indices))) rm.add(Resource::Mesh(Mesh::new(vertices, indices)))
}; };
let path = &crate::util::file::resource_path(String::from("assets/test-128px.png")); let material_cube = rm.add(Resource::Material(Material::new(
let image = Arc::new(image::open(path).unwrap()); ImageRefOrColor::image(String::from("assets/test-128px.png")),
let texture_cube = rm.add(Resource::Texture(Texture::new(image))); ImageRefOrColor::color(color::LinearF32::new(1.0)),
)));
let mut renderer = render::Renderer::initialize(); let mut renderer = render::Renderer::initialize();
@ -78,7 +81,7 @@ fn main() {
let cube = render::renderable::Object { let cube = render::renderable::Object {
mesh: mesh_cube, mesh: mesh_cube,
transform: transform, transform: transform,
texture: texture_cube, material: material_cube,
}; };
cubes.push(Box::new(cube)); cubes.push(Box::new(cube));
} }

View File

@ -0,0 +1,40 @@
#[derive(Copy, Clone, Debug)]
pub struct XYZ {
pub x: f32,
pub y: f32,
pub z: f32,
}
impl XYZ {
pub fn new(x: f32, y: f32, z: f32) -> Self {
Self {
x, y, z,
}
}
}
pub struct sRGB {
pub r: u8,
pub g: u8,
pub b: u8,
}
impl sRGB {
pub fn new(r: u8, g: u8, b: u8) -> Self {
Self {
r, g, b,
}
}
}
pub struct LinearF32 {
pub d: f32,
}
impl LinearF32 {
pub fn new(d: f32) -> Self {
Self {
d,
}
}
}

View File

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

View File

@ -14,22 +14,24 @@ use vulkano::format as vf;
use vulkano::image as vm; use vulkano::image as vm;
use crate::render::vulkan::data; use crate::render::vulkan::data;
use crate::physics::color;
use crate::util::file;
pub struct ResourceManager { pub struct ResourceManager {
meshes: HashMap<u64, Mesh>, meshes: HashMap<u64, Mesh>,
textures: HashMap<u64, Texture>, materials: HashMap<u64, Material>,
} }
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub enum ResourceID { pub enum ResourceID {
Texture(u64), Material(u64),
Mesh(u64), Mesh(u64),
} }
impl hash::Hash for ResourceID { impl hash::Hash for ResourceID {
fn hash<H: hash::Hasher>(&self, state: &mut H) { fn hash<H: hash::Hasher>(&self, state: &mut H) {
match self { match self {
ResourceID::Texture(i) => i.hash(state), ResourceID::Material(i) => i.hash(state),
ResourceID::Mesh(i) => i.hash(state), ResourceID::Mesh(i) => i.hash(state),
} }
} }
@ -38,11 +40,11 @@ impl hash::Hash for ResourceID {
impl PartialEq for ResourceID { impl PartialEq for ResourceID {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
let this = match self { let this = match self {
ResourceID::Texture(i) => i, ResourceID::Material(i) => i,
ResourceID::Mesh(i) => i, ResourceID::Mesh(i) => i,
}; };
let that = match other { let that = match other {
ResourceID::Texture(i) => i, ResourceID::Material(i) => i,
ResourceID::Mesh(i) => i, ResourceID::Mesh(i) => i,
}; };
this == that this == that
@ -52,7 +54,7 @@ impl PartialEq for ResourceID {
impl Eq for ResourceID {} impl Eq for ResourceID {}
pub enum Resource { pub enum Resource {
Texture(Texture), Material(Material),
Mesh(Mesh), Mesh(Mesh),
} }
@ -60,16 +62,16 @@ impl<'a> ResourceManager {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
meshes: HashMap::new(), meshes: HashMap::new(),
textures: HashMap::new(), materials: HashMap::new(),
} }
} }
pub fn add(&mut self, r: Resource) -> ResourceID { pub fn add(&mut self, r: Resource) -> ResourceID {
match r { match r {
Resource::Texture(t) => { Resource::Material(t) => {
let id = t.id; let id = t.id;
self.textures.insert(id, t); self.materials.insert(id, t);
ResourceID::Texture(id) ResourceID::Material(id)
} }
Resource::Mesh(t) => { Resource::Mesh(t) => {
let id = t.id; let id = t.id;
@ -79,9 +81,9 @@ impl<'a> ResourceManager {
} }
} }
pub fn texture(&'a self, id: &ResourceID) -> Option<&'a Texture> { pub fn material(&'a self, id: &ResourceID) -> Option<&'a Material> {
if let ResourceID::Texture(i) = id { if let ResourceID::Material(i) = id {
return Some(self.textures.get(&i).unwrap()); return Some(self.materials.get(&i).unwrap());
} }
return None return None
} }
@ -94,47 +96,169 @@ impl<'a> ResourceManager {
} }
} }
pub struct Texture { pub trait ChannelLayout {
image: Arc<image::DynamicImage>, fn vulkan_from_image(
image: Arc<image::DynamicImage>,
graphics_queue: Arc<vd::Queue>,
) -> Arc<vm::ImmutableImage<vf::Format>>;
fn vulkan_from_value(
&self,
graphics_queue: Arc<vd::Queue>,
) -> Arc<vm::ImmutableImage<vf::Format>>;
}
impl ChannelLayout for color::XYZ {
fn vulkan_from_image(
image: Arc<image::DynamicImage>,
graphics_queue: Arc<vd::Queue>,
) -> Arc<vm::ImmutableImage<vf::Format>> {
let (width, height) = (image.width(), image.height());
let rgba = image.to_rgba();
// TODO(q3k): RGB -> CIE XYZ
let (image_view, future) = vm::ImmutableImage::from_iter(
rgba.into_raw().iter().cloned(),
vm::Dimensions::Dim2d{ width, height },
vf::Format::R8G8B8A8Unorm,
graphics_queue.clone(),
).unwrap();
future.flush().unwrap();
image_view
}
fn vulkan_from_value(
&self,
graphics_queue: Arc<vd::Queue>,
) -> Arc<vm::ImmutableImage<vf::Format>> {
let mut image = image::ImageBuffer::<image::Rgba<f32>, Vec<f32>>::new(1, 1);
image.put_pixel(0, 0, image::Rgba([self.x, self.y, self.z, 0.0]));
let (image_view, future) = vm::ImmutableImage::from_iter(
image.into_raw().iter().cloned(),
vm::Dimensions::Dim2d{ width: 1, height: 1 },
vf::Format::R32G32B32A32Sfloat,
graphics_queue.clone(),
).unwrap();
future.flush().unwrap();
image_view
}
}
impl ChannelLayout for color::LinearF32 {
fn vulkan_from_image(
image: Arc<image::DynamicImage>,
graphics_queue: Arc<vd::Queue>,
) -> Arc<vm::ImmutableImage<vf::Format>> {
let (width, height) = (image.width(), image.height());
assert!(match image.color() {
image::ColorType::L8 => true,
image::ColorType::L16 => true,
_ => false,
}, "linearf32 texture must be 8-bit grayscale");
let gray = image.to_luma();
let (image_view, future) = vm::ImmutableImage::from_iter(
gray.into_raw().iter().cloned(),
vm::Dimensions::Dim2d{ width, height },
vf::Format::R8G8B8A8Unorm,
graphics_queue.clone(),
).unwrap();
future.flush().unwrap();
image_view
}
fn vulkan_from_value(
&self,
graphics_queue: Arc<vd::Queue>,
) -> Arc<vm::ImmutableImage<vf::Format>> {
let mut image = image::ImageBuffer::<image::Luma<f32>, Vec<f32>>::new(1, 1);
image.put_pixel(0, 0, image::Luma([self.d]));
let (image_view, future) = vm::ImmutableImage::from_iter(
image.into_raw().iter().cloned(),
vm::Dimensions::Dim2d{ width: 1, height: 1 },
vf::Format::R32Sfloat,
graphics_queue.clone(),
).unwrap();
future.flush().unwrap();
image_view
}
}
pub enum ImageRefOrColor<T: ChannelLayout> {
Color(T),
ImageRef(ImageRef),
}
impl<T: ChannelLayout> ImageRefOrColor<T> {
fn vulkan_image(&self, graphics_queue: Arc<vd::Queue>) -> Arc<vm::ImmutableImage<vf::Format>> {
match self {
ImageRefOrColor::<T>::Color(c) => c.vulkan_from_value(graphics_queue),
ImageRefOrColor::<T>::ImageRef(r) => T::vulkan_from_image(r.load(), graphics_queue),
}
}
pub fn color(color: T) -> Self {
ImageRefOrColor::<T>::Color(color)
}
pub fn image(name: String) -> Self {
ImageRefOrColor::<T>::ImageRef(ImageRef{ name })
}
}
pub struct ImageRef {
name: String,
}
impl ImageRef {
fn load (&self) -> Arc<image::DynamicImage> {
let path = &file::resource_path(self.name.clone());
Arc::new(image::open(path).unwrap())
}
}
pub struct Material {
diffuse: ImageRefOrColor<color::XYZ>,
roughness: ImageRefOrColor<color::LinearF32>,
id: u64, id: u64,
// vulkan cache // vulkan cache
vulkan: Mutex<Option<Arc<vm::ImmutableImage<vf::Format>>>>, vulkan: Mutex<Option<data::Textures>>,
} }
impl Texture {
impl Material {
pub fn new( pub fn new(
image: Arc<image::DynamicImage>, diffuse: ImageRefOrColor<color::XYZ>,
roughness: ImageRefOrColor<color::LinearF32>,
) -> Self { ) -> Self {
Self { Self {
image, diffuse,
roughness,
// TODO: use a better method // TODO: use a better method
id: time::SystemTime::now().duration_since(time::UNIX_EPOCH).unwrap().as_nanos() as u64, id: time::SystemTime::now().duration_since(time::UNIX_EPOCH).unwrap().as_nanos() as u64,
vulkan: Mutex::new(None), vulkan: Mutex::new(None),
} }
} }
pub fn vulkan_texture(
pub fn vulkan_textures(
&self, &self,
graphics_queue: Arc<vd::Queue>, graphics_queue: Arc<vd::Queue>,
) -> Arc<vm::ImmutableImage<vf::Format>> { ) -> data::Textures {
let mut cache = self.vulkan.lock().unwrap(); let mut cache = self.vulkan.lock().unwrap();
match &mut *cache { match &mut *cache {
Some(data) => data.clone(), Some(data) => data.clone(),
None => { None => {
let width = self.image.width(); let diffuse = self.diffuse.vulkan_image(graphics_queue.clone());
let height = self.image.height(); let roughness = self.roughness.vulkan_image(graphics_queue.clone());
let image_rgba = self.image.to_rgba(); let textures = data::Textures {
let (image_view, future) = vm::ImmutableImage::from_iter( diffuse, roughness,
image_rgba.into_raw().iter().cloned(), };
vm::Dimensions::Dim2d{ width, height }, *cache = Some(textures.clone());
vf::Format::R8G8B8A8Unorm, textures
graphics_queue.clone(),
).unwrap();
future.flush().unwrap();
*cache = Some(image_view.clone());
image_view
}, },
} }
} }
@ -226,12 +350,12 @@ pub trait Renderable {
pub struct Object { pub struct Object {
pub mesh: ResourceID, pub mesh: ResourceID,
pub texture: ResourceID, pub material: ResourceID,
pub transform: cgm::Matrix4<f32>, pub transform: cgm::Matrix4<f32>,
} }
impl Renderable for Object { impl Renderable for Object {
fn render_data(&self) -> Option<(ResourceID, ResourceID, &cgm::Matrix4<f32>)> { fn render_data(&self) -> Option<(ResourceID, ResourceID, &cgm::Matrix4<f32>)> {
Some((self.mesh, self.texture, &self.transform)) Some((self.mesh, self.material, &self.transform))
} }
} }

View File

@ -1,3 +1,8 @@
use std::sync::Arc;
use vulkano::image as vm;
use vulkano::format::Format;
use cgmath as cgm; use cgmath as cgm;
#[derive(Default, Copy, Clone)] #[derive(Default, Copy, Clone)]
@ -35,3 +40,11 @@ vulkano::impl_vertex!(Instance, model);
pub struct UniformBufferObject { pub struct UniformBufferObject {
pub view: cgm::Matrix4<f32>, pub view: cgm::Matrix4<f32>,
} }
#[derive(Clone)]
pub struct Textures {
// diffuse: RGB
pub diffuse: Arc<vm::ImmutableImage<Format>>,
// roughness: R
pub roughness: Arc<vm::ImmutableImage<Format>>,
}

View File

@ -164,7 +164,7 @@ impl<WT: 'static + Send + Sync> Instance<WT> {
let mut buffers: Vec<Box<vc::AutoCommandBuffer>> = vec![]; let mut buffers: Vec<Box<vc::AutoCommandBuffer>> = vec![];
// Sort renderables by mesh and textureid. // Sort renderables by mesh and materialid.
let mut meshes: ResourceMap<(renderable::ResourceID, renderable::ResourceID), &cgm::Matrix4<f32>> = ResourceMap::new(); let mut meshes: ResourceMap<(renderable::ResourceID, renderable::ResourceID), &cgm::Matrix4<f32>> = ResourceMap::new();
let ubo = data::UniformBufferObject { let ubo = data::UniformBufferObject {
@ -173,8 +173,8 @@ impl<WT: 'static + Send + Sync> Instance<WT> {
profiler.end("mgc.prep"); profiler.end("mgc.prep");
for r in renderables { for r in renderables {
if let Some((mesh_id, texture_id, transform)) = r.render_data() { if let Some((mesh_id, material_id, transform)) = r.render_data() {
meshes.add((mesh_id, texture_id), transform); meshes.add((mesh_id, material_id), transform);
} }
} }
profiler.end("mgc.sort"); profiler.end("mgc.sort");
@ -185,9 +185,9 @@ impl<WT: 'static + Send + Sync> Instance<WT> {
let pipeline = self.pipeline.as_ref().unwrap().get_pipeline().clone(); let pipeline = self.pipeline.as_ref().unwrap().get_pipeline().clone();
for ((mesh_id, texture_id), transforms) in meshes.resources { for ((mesh_id, material_id), transforms) in meshes.resources {
let mesh = rm.mesh(&mesh_id).unwrap(); let mesh = rm.mesh(&mesh_id).unwrap();
let texture = rm.texture(&texture_id).unwrap(); let material = rm.material(&material_id).unwrap();
let mut builder = vc::AutoCommandBufferBuilder::secondary_graphics_one_time_submit( let mut builder = vc::AutoCommandBufferBuilder::secondary_graphics_one_time_submit(
device.clone(), queue.family(), vf::Subpass::from(rp.clone(), 0).unwrap()).unwrap(); device.clone(), queue.family(), vf::Subpass::from(rp.clone(), 0).unwrap()).unwrap();
@ -199,7 +199,7 @@ impl<WT: 'static + Send + Sync> Instance<WT> {
).unwrap(); ).unwrap();
future.flush().unwrap(); future.flush().unwrap();
let image = texture.vulkan_texture(queue.clone()); let image = material.vulkan_textures(queue.clone()).diffuse;
let ds = self.pipeline.as_mut().unwrap().make_descriptor_set(image); let ds = self.pipeline.as_mut().unwrap().make_descriptor_set(image);
let (vbuffer, ibuffer) = mesh.vulkan_buffers(queue.clone()); let (vbuffer, ibuffer) = mesh.vulkan_buffers(queue.clone());

View File

@ -38,25 +38,17 @@ impl Forward {
name: Some(Cow::Borrowed("pos")), name: Some(Cow::Borrowed("pos")),
}, },
vps::ShaderInterfaceDefEntry { vps::ShaderInterfaceDefEntry {
location: 1..2, format: Format::R32G32B32Sfloat, location: 1..5, format: Format::R32G32B32A32Sfloat,
name: Some(Cow::Borrowed("color")),
},
vps::ShaderInterfaceDefEntry {
location: 2..6, format: Format::R32G32B32A32Sfloat,
name: Some(Cow::Borrowed("model")), name: Some(Cow::Borrowed("model")),
}, },
vps::ShaderInterfaceDefEntry { vps::ShaderInterfaceDefEntry {
location: 6..7, format: Format::R32G32Sfloat, location: 5..6, format: Format::R32G32Sfloat,
name: Some(Cow::Borrowed("tex")), name: Some(Cow::Borrowed("tex")),
}, },
], ],
outputs: vec![ outputs: vec![
vps::ShaderInterfaceDefEntry { vps::ShaderInterfaceDefEntry {
location: 0..1, format: Format::R32G32B32Sfloat, location: 0..1, format: Format::R32G32Sfloat,
name: Some(Cow::Borrowed("fragColor")),
},
vps::ShaderInterfaceDefEntry {
location: 1..2, format: Format::R32G32Sfloat,
name: Some(Cow::Borrowed("fragTexCoord")), name: Some(Cow::Borrowed("fragTexCoord")),
}, },
], ],
@ -78,11 +70,7 @@ impl Forward {
ty: vps::GraphicsShaderType::Fragment, ty: vps::GraphicsShaderType::Fragment,
inputs: vec![ inputs: vec![
vps::ShaderInterfaceDefEntry { vps::ShaderInterfaceDefEntry {
location: 0..1, format: Format::R32G32B32Sfloat, location: 0..1, format: Format::R32G32Sfloat,
name: Some(Cow::Borrowed("fragColor")),
},
vps::ShaderInterfaceDefEntry {
location: 1..2, format: Format::R32G32Sfloat,
name: Some(Cow::Borrowed("fragTexCoord")), name: Some(Cow::Borrowed("fragTexCoord")),
}, },
], ],