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 = [
"src/main.rs",
"src/physics/mod.rs",
"src/physics/color.rs",
"src/render/mod.rs",
"src/render/renderable.rs",
"src/render/vulkan/data.rs",

View File

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

View File

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

View File

@ -7,9 +7,11 @@ use cgmath as cgm;
mod render;
mod util;
mod physics;
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() {
env_logger::init();
@ -64,9 +66,10 @@ fn main() {
rm.add(Resource::Mesh(Mesh::new(vertices, indices)))
};
let path = &crate::util::file::resource_path(String::from("assets/test-128px.png"));
let image = Arc::new(image::open(path).unwrap());
let texture_cube = rm.add(Resource::Texture(Texture::new(image)));
let material_cube = rm.add(Resource::Material(Material::new(
ImageRefOrColor::image(String::from("assets/test-128px.png")),
ImageRefOrColor::color(color::LinearF32::new(1.0)),
)));
let mut renderer = render::Renderer::initialize();
@ -78,7 +81,7 @@ fn main() {
let cube = render::renderable::Object {
mesh: mesh_cube,
transform: transform,
texture: texture_cube,
material: material_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 crate::render::vulkan::data;
use crate::physics::color;
use crate::util::file;
pub struct ResourceManager {
meshes: HashMap<u64, Mesh>,
textures: HashMap<u64, Texture>,
materials: HashMap<u64, Material>,
}
#[derive(Copy, Clone)]
pub enum ResourceID {
Texture(u64),
Material(u64),
Mesh(u64),
}
impl hash::Hash for ResourceID {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
match self {
ResourceID::Texture(i) => i.hash(state),
ResourceID::Material(i) => i.hash(state),
ResourceID::Mesh(i) => i.hash(state),
}
}
@ -38,11 +40,11 @@ impl hash::Hash for ResourceID {
impl PartialEq for ResourceID {
fn eq(&self, other: &Self) -> bool {
let this = match self {
ResourceID::Texture(i) => i,
ResourceID::Material(i) => i,
ResourceID::Mesh(i) => i,
};
let that = match other {
ResourceID::Texture(i) => i,
ResourceID::Material(i) => i,
ResourceID::Mesh(i) => i,
};
this == that
@ -52,7 +54,7 @@ impl PartialEq for ResourceID {
impl Eq for ResourceID {}
pub enum Resource {
Texture(Texture),
Material(Material),
Mesh(Mesh),
}
@ -60,16 +62,16 @@ impl<'a> ResourceManager {
pub fn new() -> Self {
Self {
meshes: HashMap::new(),
textures: HashMap::new(),
materials: HashMap::new(),
}
}
pub fn add(&mut self, r: Resource) -> ResourceID {
match r {
Resource::Texture(t) => {
Resource::Material(t) => {
let id = t.id;
self.textures.insert(id, t);
ResourceID::Texture(id)
self.materials.insert(id, t);
ResourceID::Material(id)
}
Resource::Mesh(t) => {
let id = t.id;
@ -79,9 +81,9 @@ impl<'a> ResourceManager {
}
}
pub fn texture(&'a self, id: &ResourceID) -> Option<&'a Texture> {
if let ResourceID::Texture(i) = id {
return Some(self.textures.get(&i).unwrap());
pub fn material(&'a self, id: &ResourceID) -> Option<&'a Material> {
if let ResourceID::Material(i) = id {
return Some(self.materials.get(&i).unwrap());
}
return None
}
@ -94,47 +96,169 @@ impl<'a> ResourceManager {
}
}
pub struct Texture {
image: Arc<image::DynamicImage>,
pub trait ChannelLayout {
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,
// vulkan cache
vulkan: Mutex<Option<Arc<vm::ImmutableImage<vf::Format>>>>,
vulkan: Mutex<Option<data::Textures>>,
}
impl Texture {
impl Material {
pub fn new(
image: Arc<image::DynamicImage>,
diffuse: ImageRefOrColor<color::XYZ>,
roughness: ImageRefOrColor<color::LinearF32>,
) -> Self {
Self {
image,
diffuse,
roughness,
// TODO: use a better method
id: time::SystemTime::now().duration_since(time::UNIX_EPOCH).unwrap().as_nanos() as u64,
vulkan: Mutex::new(None),
}
}
pub fn vulkan_texture(
pub fn vulkan_textures(
&self,
graphics_queue: Arc<vd::Queue>,
) -> Arc<vm::ImmutableImage<vf::Format>> {
) -> data::Textures {
let mut cache = self.vulkan.lock().unwrap();
match &mut *cache {
Some(data) => data.clone(),
None => {
let width = self.image.width();
let height = self.image.height();
let image_rgba = self.image.to_rgba();
let (image_view, future) = vm::ImmutableImage::from_iter(
image_rgba.into_raw().iter().cloned(),
vm::Dimensions::Dim2d{ width, height },
vf::Format::R8G8B8A8Unorm,
graphics_queue.clone(),
).unwrap();
future.flush().unwrap();
*cache = Some(image_view.clone());
image_view
let diffuse = self.diffuse.vulkan_image(graphics_queue.clone());
let roughness = self.roughness.vulkan_image(graphics_queue.clone());
let textures = data::Textures {
diffuse, roughness,
};
*cache = Some(textures.clone());
textures
},
}
}
@ -226,12 +350,12 @@ pub trait Renderable {
pub struct Object {
pub mesh: ResourceID,
pub texture: ResourceID,
pub material: ResourceID,
pub transform: cgm::Matrix4<f32>,
}
impl Renderable for Object {
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;
#[derive(Default, Copy, Clone)]
@ -35,3 +40,11 @@ vulkano::impl_vertex!(Instance, model);
pub struct UniformBufferObject {
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![];
// 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 ubo = data::UniformBufferObject {
@ -173,8 +173,8 @@ impl<WT: 'static + Send + Sync> Instance<WT> {
profiler.end("mgc.prep");
for r in renderables {
if let Some((mesh_id, texture_id, transform)) = r.render_data() {
meshes.add((mesh_id, texture_id), transform);
if let Some((mesh_id, material_id, transform)) = r.render_data() {
meshes.add((mesh_id, material_id), transform);
}
}
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();
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 texture = rm.texture(&texture_id).unwrap();
let material = rm.material(&material_id).unwrap();
let mut builder = vc::AutoCommandBufferBuilder::secondary_graphics_one_time_submit(
device.clone(), queue.family(), vf::Subpass::from(rp.clone(), 0).unwrap()).unwrap();
@ -199,7 +199,7 @@ impl<WT: 'static + Send + Sync> Instance<WT> {
).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 (vbuffer, ibuffer) = mesh.vulkan_buffers(queue.clone());

View File

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