specular metallic materials

This commit is contained in:
CDaut 2023-11-30 22:10:27 +01:00
parent e1c61ef944
commit 1a355f81ca
15 changed files with 533 additions and 27 deletions

BIN
__output_image.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 658 KiB

BIN
output_image.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Binary file not shown.

BIN
scenes/mirror.blend Normal file

Binary file not shown.

BIN
scenes/mirror.blend1 Normal file

Binary file not shown.

BIN
scenes/mirror.glb Normal file

Binary file not shown.

BIN
scenes/reflective.bin Normal file

Binary file not shown.

BIN
scenes/reflective.blend Normal file

Binary file not shown.

BIN
scenes/reflective.blend1 Normal file

Binary file not shown.

BIN
scenes/reflective.glb Normal file

Binary file not shown.

452
scenes/reflective.gltf Normal file
View file

@ -0,0 +1,452 @@
{
"asset":{
"generator":"Khronos glTF Blender I/O v4.0.44",
"version":"2.0"
},
"extensionsUsed":[
"KHR_materials_emissive_strength"
],
"scene":0,
"scenes":[
{
"name":"Scene",
"nodes":[
0,
1,
2,
3,
4
]
}
],
"nodes":[
{
"camera":0,
"name":"Camera",
"rotation":[
-0.15949390828609467,
0.662581741809845,
0.1474284678697586,
0.7168068289756775
],
"translation":[
10.606247901916504,
4.060349464416504,
0.7527037858963013
]
},
{
"mesh":0,
"name":"Plane",
"scale":[
7.545622825622559,
7.545622825622559,
7.545622825622559
]
},
{
"mesh":1,
"name":"Plane.001",
"rotation":[
0,
0,
-0.7071068286895752,
0.7071068286895752
],
"translation":[
6.896522521972656,
1.2417194843292236,
-3.40067195892334
]
},
{
"mesh":2,
"name":"Plane.002",
"rotation":[
0,
0,
-0.7071068286895752,
0.7071068286895752
],
"scale":[
3.870809316635132,
1,
5.133298873901367
],
"translation":[
0,
0,
-1.2463083267211914
]
},
{
"mesh":3,
"name":"Cube",
"scale":[
0.7817667722702026,
0.7817667722702026,
0.7817667722702026
],
"translation":[
0.8038264513015747,
0.7851558327674866,
0
]
}
],
"cameras":[
{
"name":"Camera",
"perspective":{
"aspectRatio":1,
"yfov":0.6911112070083618,
"zfar":100,
"znear":0.10000000149011612
},
"type":"perspective"
}
],
"materials":[
{
"doubleSided":true,
"name":"Specular_black",
"pbrMetallicRoughness":{
"baseColorFactor":[
0,
0,
0,
1
],
"metallicFactor":0,
"roughnessFactor":0
}
},
{
"doubleSided":true,
"emissiveFactor":[
1,
1,
1
],
"extensions":{
"KHR_materials_emissive_strength":{
"emissiveStrength":60
}
},
"name":"Emissive",
"pbrMetallicRoughness":{
"baseColorFactor":[
0.800000011920929,
0.800000011920929,
0.800000011920929,
1
],
"metallicFactor":0,
"roughnessFactor":0
}
},
{
"doubleSided":true,
"name":"Specular_red",
"pbrMetallicRoughness":{
"baseColorFactor":[
0.8001416921615601,
0,
0.004359794314950705,
1
],
"metallicFactor":0,
"roughnessFactor":0
}
},
{
"doubleSided":true,
"name":"Rough_cube",
"pbrMetallicRoughness":{
"baseColorFactor":[
0.009510930627584457,
1,
0,
1
],
"metallicFactor":0
}
}
],
"meshes":[
{
"name":"Plane",
"primitives":[
{
"attributes":{
"POSITION":0,
"NORMAL":1,
"TEXCOORD_0":2
},
"indices":3,
"material":0
}
]
},
{
"name":"Plane.001",
"primitives":[
{
"attributes":{
"POSITION":4,
"NORMAL":5,
"TEXCOORD_0":6
},
"indices":3,
"material":1
}
]
},
{
"name":"Plane.002",
"primitives":[
{
"attributes":{
"POSITION":7,
"NORMAL":8,
"TEXCOORD_0":9
},
"indices":3,
"material":2
}
]
},
{
"name":"Cube.001",
"primitives":[
{
"attributes":{
"POSITION":10,
"NORMAL":11,
"TEXCOORD_0":12
},
"indices":13,
"material":3
}
]
}
],
"accessors":[
{
"bufferView":0,
"componentType":5126,
"count":4,
"max":[
1,
0,
1
],
"min":[
-1,
0,
-1
],
"type":"VEC3"
},
{
"bufferView":1,
"componentType":5126,
"count":4,
"type":"VEC3"
},
{
"bufferView":2,
"componentType":5126,
"count":4,
"type":"VEC2"
},
{
"bufferView":3,
"componentType":5123,
"count":6,
"type":"SCALAR"
},
{
"bufferView":4,
"componentType":5126,
"count":4,
"max":[
1,
0,
1
],
"min":[
-1,
0,
-1
],
"type":"VEC3"
},
{
"bufferView":5,
"componentType":5126,
"count":4,
"type":"VEC3"
},
{
"bufferView":6,
"componentType":5126,
"count":4,
"type":"VEC2"
},
{
"bufferView":7,
"componentType":5126,
"count":4,
"max":[
1,
0,
1
],
"min":[
-1,
0,
-1
],
"type":"VEC3"
},
{
"bufferView":8,
"componentType":5126,
"count":4,
"type":"VEC3"
},
{
"bufferView":9,
"componentType":5126,
"count":4,
"type":"VEC2"
},
{
"bufferView":10,
"componentType":5126,
"count":24,
"max":[
1,
1,
1
],
"min":[
-1,
-1,
-1
],
"type":"VEC3"
},
{
"bufferView":11,
"componentType":5126,
"count":24,
"type":"VEC3"
},
{
"bufferView":12,
"componentType":5126,
"count":24,
"type":"VEC2"
},
{
"bufferView":13,
"componentType":5123,
"count":36,
"type":"SCALAR"
}
],
"bufferViews":[
{
"buffer":0,
"byteLength":48,
"byteOffset":0,
"target":34962
},
{
"buffer":0,
"byteLength":48,
"byteOffset":48,
"target":34962
},
{
"buffer":0,
"byteLength":32,
"byteOffset":96,
"target":34962
},
{
"buffer":0,
"byteLength":12,
"byteOffset":128,
"target":34963
},
{
"buffer":0,
"byteLength":48,
"byteOffset":140,
"target":34962
},
{
"buffer":0,
"byteLength":48,
"byteOffset":188,
"target":34962
},
{
"buffer":0,
"byteLength":32,
"byteOffset":236,
"target":34962
},
{
"buffer":0,
"byteLength":48,
"byteOffset":268,
"target":34962
},
{
"buffer":0,
"byteLength":48,
"byteOffset":316,
"target":34962
},
{
"buffer":0,
"byteLength":32,
"byteOffset":364,
"target":34962
},
{
"buffer":0,
"byteLength":288,
"byteOffset":396,
"target":34962
},
{
"buffer":0,
"byteLength":288,
"byteOffset":684,
"target":34962
},
{
"buffer":0,
"byteLength":192,
"byteOffset":972,
"target":34962
},
{
"buffer":0,
"byteLength":72,
"byteOffset":1164,
"target":34963
}
],
"buffers":[
{
"byteLength":1236,
"uri":"reflective.bin"
}
]
}

View file

@ -5,6 +5,7 @@ mod geometry;
mod ray; mod ray;
mod scene_data; mod scene_data;
mod colors; mod colors;
mod math_utils;
use std::string::String; use std::string::String;
use cgmath::Vector4; use cgmath::Vector4;

37
src/math_utils.rs Normal file
View file

@ -0,0 +1,37 @@
use std::f32::consts::PI;
use cgmath::{InnerSpace, Vector3};
use easy_gltf::model::Triangle;
use crate::ray::Ray;
/// return a uniform random angle in the hemisphere specified by the triangle
pub fn uniform_random_angle_triangle_hemisphere(intersected_triangle: Triangle, ray: &Ray) -> Vector3<f32> {
let tangent = (intersected_triangle[0].position -
intersected_triangle[1].position).normalize();
let mut face_normal: Vector3<f32> = tangent
.cross(intersected_triangle[0].position -
intersected_triangle[2].position).normalize();
//flip normal if face is hit from behind
if face_normal.dot(-ray.direction) < 0.0 {
face_normal *= -1.0;
}
let bitangent = tangent.cross(face_normal).normalize();
/*
generate random direction on hemisphere
*/
let phi = rand::random::<f32>() * 2.0 * PI;
//allow arbitrary angles
let radius = rand::random::<f32>();
let sqrt_radius = f32::sqrt(rand::random::<f32>());
(tangent * f32::cos(phi) * sqrt_radius +
bitangent * f32::sin(phi) * sqrt_radius +
face_normal * f32::sqrt(1.0 - radius)).normalize()
}
/// Calculate a triangle from the three Vertices
pub fn face_normal_for_triangle(triangle: &Triangle) -> Vector3<f32> {
let tangent = (triangle[0].position -
triangle[1].position).normalize();
tangent.cross(triangle[0].position -
triangle[2].position).normalize()
}

View file

@ -8,6 +8,7 @@ use easy_gltf::{Camera, Projection, Scene};
use indicatif::ParallelProgressIterator; use indicatif::ParallelProgressIterator;
use crate::Args; use crate::Args;
use crate::geometry::{Intersectable}; use crate::geometry::{Intersectable};
use crate::math_utils::{face_normal_for_triangle, uniform_random_angle_triangle_hemisphere};
use crate::ray::{construct_primary_rays, Ray}; use crate::ray::{construct_primary_rays, Ray};
use crate::scene_data::IntersectionData; use crate::scene_data::IntersectionData;
@ -150,44 +151,47 @@ fn accumulate_colors(intersection_data: &IntersectionData,
recursion_depth_left: usize) -> Vector4<f32> { recursion_depth_left: usize) -> Vector4<f32> {
let mut pixel_radiosity: Vector4<f32> let mut pixel_radiosity: Vector4<f32>
= Vector4::new(0.0, 0.0, 0.0, 1.0); = Vector4::new(0.0, 0.0, 0.0, 1.0);
//accumulate colors at point //accumulate colors at point
//emissive Component //emissive Component
pixel_radiosity += pixel_radiosity +=
intersection_data.material().emissive.factor.extend(1.0) intersection_data.material().emissive.factor.extend(1.0)
.mul_element_wise( .mul_element_wise(
intersection_data.material() intersection_data.material()
//TODO: texture sampling!
.get_base_color_alpha(Vector2::new(0.0, 0.0)) .get_base_color_alpha(Vector2::new(0.0, 0.0))
); );
//get the intersected triangle and calculate the face normals //get the intersected triangle and calculate the face normals
let intersected_triangle = intersection_data.intersected_triangle(); let intersected_triangle = intersection_data.intersected_triangle();
let tangent = (intersected_triangle[0].position -
intersected_triangle[1].position).normalize(); let decision_factor = rand::random::<f32>();
let mut face_normal: Vector3<f32> = tangent let mut direction: Vector3<f32>;
.cross(intersected_triangle[0].position -
intersected_triangle[2].position).normalize(); let face_normal =
//flip normal if face is hit from behind face_normal_for_triangle(&intersection_data.intersected_triangle());
if face_normal.dot(-intersection_data.ray().direction) < 0.0 {
face_normal *= -1.0; let rough_sampling: bool = decision_factor <= intersection_data.material().pbr.roughness_factor;
//do we trace rough or specular?
if rough_sampling {
//generate random direction (rough sampling)
direction = uniform_random_angle_triangle_hemisphere(
intersected_triangle,
intersection_data.ray(),
);
} else {
//mirror vector (specular sampling)
direction = intersection_data.ray().direction -
2.0 * (intersection_data.ray().direction.dot(face_normal)) * face_normal;
} }
let bitangent = tangent.cross(face_normal).normalize();
/* direction = direction.normalize();
generate random direction on hemisphere
*/
let phi = rand::random::<f32>() * 2.0 * PI;
//allow arbitrary angles
let radius = rand::random::<f32>();
let sqrt_radius = f32::sqrt(rand::random::<f32>());
let direction = (tangent * f32::cos(phi) * sqrt_radius +
bitangent * f32::sin(phi) * sqrt_radius +
face_normal * f32::sqrt(1.0 - radius)).normalize();
let secondary_ray = Ray { let secondary_ray = Ray {
//prevent self-intersection //prevent self-intersection
source: intersection_data.intersection_point() + RAY_EPSILON * face_normal, source: intersection_data.intersection_point() + RAY_EPSILON * direction,
direction, direction,
}; };
@ -197,15 +201,27 @@ fn accumulate_colors(intersection_data: &IntersectionData,
global_scene, global_scene,
recursion_depth_left - 1); recursion_depth_left - 1);
//weigh by cosine term depending on view angel
let cos_weighting = direction.dot(
face_normal
);
let cos_weighting = direction.dot(face_normal); let brdf: Vector4<f32>;
let brdf = intersection_data.material() if rough_sampling {
.get_base_color_alpha(Vector2::new(0.0, 0.0)); brdf = intersection_data.material()
//TODO: texture sampling!
//reflected component .get_base_color_alpha(Vector2::new(0.0, 0.0));
pixel_radiosity += brdf.mul_element_wise(incoming_radiosity) * cos_weighting * 2.0 * PI; pixel_radiosity += brdf.mul_element_wise(incoming_radiosity) * cos_weighting * 2.0 * PI;
} else {
pixel_radiosity += incoming_radiosity * cos_weighting * 2.0 * PI +
intersection_data.material()
//TODO: texture sampling!
.get_base_color_alpha(Vector2::new(0.0, 0.0));
}
pixel_radiosity pixel_radiosity
} }