truck-rendimpl 0.2.0

visualization of shape and polygon mesh based on platform
Documentation
mod common;
use common::Plane;
use image::{DynamicImage, ImageBuffer, Rgba};
use std::sync::{Arc, Mutex};
use truck_platform::*;
use truck_rendimpl::*;
use wgpu::*;

const PICTURE_SIZE: (u32, u32) = (1024, 768);

fn test_scene(backend: BackendBit) -> Scene {
    let instance = Instance::new(backend);
    let (device, queue) = common::init_device(&instance);
    let sc_desc = common::swap_chain_descriptor(PICTURE_SIZE);
    let sc_desc = Arc::new(Mutex::new(sc_desc));
    let handler = DeviceHandler::new(device, queue, sc_desc);
    Scene::new(
        handler,
        &SceneDescriptor {
            camera: Camera::perspective_camera(
                Matrix4::look_at_rh(
                    Point3::new(-1.0, 2.5, 2.0),
                    Point3::new(0.25, 0.25, 0.25),
                    Vector3::unit_y(),
                )
                .invert()
                .unwrap(),
                Rad(std::f64::consts::PI / 4.0),
                0.1,
                100.0,
            ),
            lights: vec![Light {
                position: Point3::new(-3.0, 4.0, -2.0),
                color: Vector3::new(1.0, 1.0, 1.0),
                light_type: LightType::Point,
            }],
            ..Default::default()
        },
    )
}

fn shape_cube() -> Solid {
    let s = builder::vertex(Point3::new(0.0, 0.0, 0.0));
    let s = builder::tsweep(&s, Vector3::unit_x());
    let s = builder::tsweep(&s, Vector3::unit_y());
    builder::tsweep(&s, Vector3::unit_z())
}

fn nontex_raymarching(scene: &mut Scene) -> Vec<u8> {
    let (device, sc_desc) = (scene.device(), scene.sc_desc());
    let texture = device.create_texture(&common::texture_descriptor(&sc_desc));
    let mut fragment_shader = "#version 450\n\n".to_string();
    fragment_shader += include_str!("../src/shaders/microfacet-module.frag");
    fragment_shader += include_str!("shaders/nontex-ray-marching.frag");
    let plane = Plane {
        vertex_shader: include_str!("shaders/plane.vert"),
        fragment_shader: &fragment_shader,
        id: RenderID::gen(),
    };
    common::render_one(scene, &texture, &plane);
    common::read_texture(scene.device_handler(), &texture)
}

fn nontex_polygon(scene: &mut Scene, creator: &InstanceCreator) -> Vec<u8> {
    let (device, sc_desc) = (scene.device(), scene.sc_desc());
    let texture = device.create_texture(&common::texture_descriptor(&sc_desc));
    let cube = creator.create_polygon_instance(
        &obj::read(include_bytes!("cube.obj").as_ref()).unwrap(),
        &PolygonInstanceDescriptor {
            instance_state: InstanceState {
                material: Material {
                    albedo: Vector4::new(1.0, 1.0, 1.0, 1.0),
                    roughness: 0.5,
                    reflectance: 0.25,
                    ambient_ratio: 0.02,
                },
                ..Default::default()
            },
        },
    );
    common::render_one(scene, &texture, &cube);
    common::read_texture(scene.device_handler(), &texture)
}

fn nontex_shape(scene: &mut Scene, creator: &InstanceCreator) -> Vec<u8> {
    let (device, sc_desc) = (scene.device(), scene.sc_desc());
    let texture = device.create_texture(&common::texture_descriptor(&sc_desc));
    let cube = creator.create_shape_instance(
        &shape_cube(),
        &ShapeInstanceDescriptor {
            instance_state: InstanceState {
                material: Material {
                    albedo: Vector4::new(1.0, 1.0, 1.0, 1.0),
                    roughness: 0.5,
                    reflectance: 0.25,
                    ambient_ratio: 0.02,
                },
                ..Default::default()
            },
            mesh_precision: 0.01,
        },
    );
    common::render_one(scene, &texture, &cube);
    common::read_texture(scene.device_handler(), &texture)
}

fn exec_nontex_render_test(backend: BackendBit, out_dir: &str) {
    let out_dir = out_dir.to_string();
    std::fs::create_dir_all(&out_dir).unwrap();
    let mut scene = test_scene(backend);
    let creator = scene.instance_creator();
    let buffer0 = nontex_raymarching(&mut scene);
    let buffer1 = nontex_polygon(&mut scene, &creator);
    let buffer2 = nontex_shape(&mut scene, &creator);
    let filename = out_dir.clone() + "nontex-raymarching.png";
    common::save_buffer(filename, &buffer0, PICTURE_SIZE);
    let filename = out_dir.clone() + "nontex-polygon.png";
    common::save_buffer(filename, &buffer1, PICTURE_SIZE);
    common::save_buffer(out_dir.clone() + "nontex-shape.png", &buffer2, PICTURE_SIZE);
    let diff0 = common::count_difference(&buffer0, &buffer1);
    let diff1 = common::count_difference(&buffer1, &buffer2);
    let diff2 = common::count_difference(&buffer2, &buffer0);
    println!("{} pixel difference: ray-marching and polymesh", diff0);
    println!("{} pixel difference: polymesh and shape", diff1);
    println!("{} pixel difference: ray-marching and shape", diff2);
    assert!(diff0 < 10);
    assert!(diff1 == 0);
    assert!(diff2 < 10);
}

#[test]
fn nontex_render_test() { common::os_alt_exec_test(exec_nontex_render_test); }

fn generate_texture(scene: &mut Scene, out_dir: String) -> DynamicImage {
    let texture = common::gradation_texture(scene);
    let buffer = common::read_texture(scene.device_handler(), &texture);
    common::save_buffer(out_dir + "gradation-texture.png", &buffer, PICTURE_SIZE);
    let image_buffer =
        ImageBuffer::<Rgba<_>, _>::from_raw(PICTURE_SIZE.0, PICTURE_SIZE.1, buffer).unwrap();
    DynamicImage::ImageRgba8(image_buffer)
}

fn tex_raymarching(scene: &mut Scene) -> Vec<u8> {
    let (device, sc_desc) = (scene.device(), scene.sc_desc());
    let texture = device.create_texture(&common::texture_descriptor(&sc_desc));
    let mut fragment_shader = "#version 450\n\n".to_string();
    fragment_shader += include_str!("../src/shaders/microfacet-module.frag");
    fragment_shader += include_str!("shaders/tex-ray-marching.frag");
    let plane = Plane {
        vertex_shader: include_str!("shaders/plane.vert"),
        fragment_shader: &fragment_shader,
        id: RenderID::gen(),
    };
    common::render_one(scene, &texture, &plane);
    common::read_texture(scene.device_handler(), &texture)
}

fn tex_polygon(
    scene: &mut Scene,
    creator: &InstanceCreator,
    gradtex: &Arc<DynamicImage>,
) -> Vec<u8> {
    let (device, sc_desc) = (scene.device(), scene.sc_desc());
    let texture = device.create_texture(&common::texture_descriptor(&sc_desc));
    let attach = creator.create_texture(gradtex);
    let cube = creator.create_polygon_instance(
        &obj::read(include_bytes!("cube.obj").as_ref()).unwrap(),
        &PolygonInstanceDescriptor {
            instance_state: InstanceState {
                material: Material {
                    albedo: Vector4::new(1.0, 1.0, 1.0, 1.0),
                    roughness: 0.5,
                    reflectance: 0.25,
                    ambient_ratio: 0.02,
                },
                texture: Some(attach),
                ..Default::default()
            },
        },
    );
    common::render_one(scene, &texture, &cube);
    common::read_texture(scene.device_handler(), &texture)
}

fn tex_shape(scene: &mut Scene, creator: &InstanceCreator, gradtex: &Arc<DynamicImage>) -> Vec<u8> {
    let (device, sc_desc) = (scene.device(), scene.sc_desc());
    let texture = device.create_texture(&common::texture_descriptor(&sc_desc));
    let attach = creator.create_texture(gradtex);
    let cube = creator.create_shape_instance(
        &shape_cube(),
        &ShapeInstanceDescriptor {
            instance_state: InstanceState {
                material: Material {
                    albedo: Vector4::new(1.0, 1.0, 1.0, 1.0),
                    roughness: 0.5,
                    reflectance: 0.25,
                    ambient_ratio: 0.02,
                },
                texture: Some(attach),
                ..Default::default()
            },
            mesh_precision: 0.01,
        },
    );
    common::render_one(scene, &texture, &cube);
    common::read_texture(scene.device_handler(), &texture)
}

fn exec_tex_render_test(backend: BackendBit, out_dir: &str) {
    let out_dir = out_dir.to_string();
    std::fs::create_dir_all(&out_dir).unwrap();
    let mut scene = test_scene(backend);
    let creator = scene.instance_creator();
    let image = Arc::new(generate_texture(&mut scene, out_dir.clone()));
    let anti_buffer = nontex_raymarching(&mut scene);
    let buffer0 = tex_raymarching(&mut scene);
    let buffer1 = tex_polygon(&mut scene, &creator, &image);
    let buffer2 = tex_shape(&mut scene, &creator, &image);
    let filename = out_dir.clone() + "tex-raymarching.png";
    common::save_buffer(filename, &buffer0, PICTURE_SIZE);
    let filename = out_dir.clone() + "tex-polygon.png";
    common::save_buffer(filename, &buffer1, PICTURE_SIZE);
    common::save_buffer(out_dir.clone() + "tex-shape.png", &buffer2, PICTURE_SIZE);
    let diff0 = common::count_difference(&buffer0, &buffer1);
    let diff1 = common::count_difference(&buffer1, &buffer2);
    let diff2 = common::count_difference(&buffer2, &buffer0);
    let anti_diff = common::count_difference(&anti_buffer, &buffer0);
    println!("{} pixel difference: ray-marching and polymesh", diff0);
    println!("{} pixel difference: polymesh and shape", diff1);
    println!("{} pixel difference: ray-marching and shape", diff2);
    assert!(diff0 < 10);
    assert!(diff1 == 0);
    assert!(diff2 < 10);
    assert!(anti_diff > 1000);
}

#[test]
fn tex_render_test() { common::os_alt_exec_test(exec_tex_render_test) }