diff --git a/src/core/instance.rs b/src/core/instance.rs index 30c60bf..22c0491 100644 --- a/src/core/instance.rs +++ b/src/core/instance.rs @@ -9,6 +9,7 @@ pub struct Instance { #[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] pub struct InstanceRaw { pub model: [[f32; 4]; 4], + pub normal: [[f32; 3]; 3], } impl Instance { @@ -17,6 +18,7 @@ impl Instance { model: (cgmath::Matrix4::from_translation(self.position) * cgmath::Matrix4::from(self.rotation)) .into(), + normal: cgmath::Matrix3::from(self.rotation).into(), }; } } @@ -31,6 +33,7 @@ impl Vertex for InstanceRaw { // instance when the shader starts processing a new instance step_mode: wgpu::VertexStepMode::Instance, attributes: &[ + // model matrix wgpu::VertexAttribute { offset: 0, shader_location: 5, @@ -51,6 +54,22 @@ impl Vertex for InstanceRaw { shader_location: 8, format: wgpu::VertexFormat::Float32x4, }, + // normal matrix + wgpu::VertexAttribute { + offset: mem::size_of::<[f32; 16]>() as wgpu::BufferAddress, + shader_location: 9, + format: wgpu::VertexFormat::Float32x3, + }, + wgpu::VertexAttribute { + offset: mem::size_of::<[f32; 19]>() as wgpu::BufferAddress, + shader_location: 10, + format: wgpu::VertexFormat::Float32x3, + }, + wgpu::VertexAttribute { + offset: mem::size_of::<[f32; 22]>() as wgpu::BufferAddress, + shader_location: 11, + format: wgpu::VertexFormat::Float32x3, + }, ], } } diff --git a/src/core/model.rs b/src/core/model.rs index ba91138..c9dd0a4 100644 --- a/src/core/model.rs +++ b/src/core/model.rs @@ -74,7 +74,7 @@ pub struct ModelVertex { pub position: [f32; 3], pub tex_coords: [f32; 2], pub normal: [f32; 3], - pub tangent: [f32; 4], + pub tangent: [f32; 3], pub bitangent: [f32; 3], } diff --git a/src/core/resources.rs b/src/core/resources.rs index 0abc35b..c8d3d50 100644 --- a/src/core/resources.rs +++ b/src/core/resources.rs @@ -38,151 +38,6 @@ pub async fn load_texture( return Texture::from_bytes(device, queue, &data, file_name, is_normal_map); } -pub async fn load_model_obj( - file_name: &str, - device: &wgpu::Device, - queue: &wgpu::Queue, - layout: &wgpu::BindGroupLayout, -) -> anyhow::Result { - let obj_text = load_string(file_name).await?; - let obj_cursor = Cursor::new(obj_text); - let mut obj_reader = BufReader::new(obj_cursor); - - let (models, obj_materials) = tobj::load_obj_buf_async( - &mut obj_reader, - &tobj::LoadOptions { - triangulate: true, - single_index: true, - ..Default::default() - }, - |p| async move { - let mat_text = load_string(&p).await.unwrap(); - tobj::load_mtl_buf(&mut BufReader::new(Cursor::new(mat_text))) - }, - ) - .await?; - - let mut materials = Vec::new(); - for m in obj_materials? { - let diffuse_texture_result = load_texture(&m.diffuse_texture, false, device, queue).await; - let normal_texture_result = load_texture(&m.normal_texture, true, device, queue).await; - - let diffuse_texture: Texture; - let normal_texture: Texture; - - if diffuse_texture_result.is_err() { - diffuse_texture = load_texture("gray.png", false, device, queue).await?; - } else { - diffuse_texture = diffuse_texture_result?; - } - - if normal_texture_result.is_err() { - normal_texture = load_texture("gray.png", true, device, queue).await?; - } else { - normal_texture = normal_texture_result?; - } - - materials.push(Material::new( - device, - &m.name, - diffuse_texture, - normal_texture, - layout, - )); - } - - let meshes = models - .into_iter() - .map(|m| { - let mut vertices = (0..m.mesh.positions.len() / 3) - .map(|i| ModelVertex { - position: [ - m.mesh.positions[i * 3], - m.mesh.positions[i * 3 + 1], - m.mesh.positions[i * 3 + 2], - ], - tex_coords: [m.mesh.texcoords[i * 2], m.mesh.texcoords[i * 2 + 1]], - normal: [ - m.mesh.normals[i * 3], - m.mesh.normals[i * 3 + 1], - m.mesh.normals[i * 3 + 2], - ], - tangent: [0.0; 4], - bitangent: [0.0; 3], - }) - .collect::>(); - - let indices = &m.mesh.indices; - let mut triangles_included = vec![0; vertices.len()]; - - // tangents and bitangents from triangles - for chunk in indices.chunks(3) { - let v0 = vertices[chunk[0] as usize]; - let v1 = vertices[chunk[1] as usize]; - let v2 = vertices[chunk[2] as usize]; - - let pos0: cgmath::Vector3 = v0.position.into(); - let pos1: cgmath::Vector3 = v1.position.into(); - let pos2: cgmath::Vector3 = v2.position.into(); - - let uv0: cgmath::Vector2 = v0.tex_coords.into(); - let uv1: cgmath::Vector2 = v1.tex_coords.into(); - let uv2: cgmath::Vector2 = v2.tex_coords.into(); - - let delta_pos1 = pos1 - pos0; - let delta_pos2 = pos2 - pos0; - - let delta_uv1 = uv1 - uv0; - let delta_uv2 = uv2 - uv0; - - let r = 1.0 / (delta_uv1.x * delta_uv2.y - delta_uv1.y * delta_uv2.x); - let tangent = (delta_pos1 * delta_uv2.y - delta_pos2 * delta_uv1.y) * r; - let bitangent = (delta_pos2 * delta_uv1.x - delta_pos1 * delta_uv2.x) * -r; - - for i in 0..3 { - let sz = chunk[i] as usize; - vertices[sz].tangent = - (cgmath::Vector4::new(tangent.x, tangent.y, tangent.z, 0.0) - + cgmath::Vector4::from(vertices[sz].tangent)) - .into(); - vertices[sz].bitangent = - (bitangent + cgmath::Vector3::from(vertices[sz].bitangent)).into(); - triangles_included[sz] += 1; - } - } - - // Average the tangents/bitangents - for (i, n) in triangles_included.into_iter().enumerate() { - let denom = 1.0 / n as f32; - let mut v = &mut vertices[i]; - v.tangent = (cgmath::Vector4::from(v.tangent) * denom).into(); - v.bitangent = (cgmath::Vector3::from(v.bitangent) * denom).into(); - } - - let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some(&format!("{:?} Vertex Buffer", file_name)), - contents: bytemuck::cast_slice(&vertices), - usage: wgpu::BufferUsages::VERTEX, - }); - let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some(&format!("{:?} Index Buffer", file_name)), - contents: bytemuck::cast_slice(&m.mesh.indices), - usage: wgpu::BufferUsages::INDEX, - }); - - return Mesh { - name: file_name.to_string(), - vertex_buffer, - index_buffer, - num_elements: m.mesh.indices.len() as u32, - material: m.mesh.material_id.unwrap_or(0), - }; - }) - .collect::>(); - - return Ok(Model { meshes, materials }); -} - pub async fn load_model_gltf( file_name: &str, device: &wgpu::Device, @@ -227,58 +82,66 @@ pub async fn load_model_gltf( panic!(); } - // if let Some(tangent_attribute) = reader.read_tangents() { - // let mut tangent_index = 0; - // tangent_attribute.for_each(|tangent| { - // // dbg!(tangent); - // vertices[tangent_index].tangent = tangent; - // tangent_index += 1; - // }); - // } + if let Some(tangent_attribute) = reader.read_tangents() { + println!("gltf: loading tangents from file"); + let mut tangent_index = 0; + tangent_attribute.for_each(|tangent| { + // dbg!(tangent); + vertices[tangent_index].tangent = [ + tangent[0] * tangent[3], + tangent[1] * tangent[3], + tangent[2] * tangent[3], + ]; + vertices[tangent_index].bitangent = + cgmath::Vector3::from(vertices[tangent_index].normal) + .cross(cgmath::Vector3::from(vertices[tangent_index].tangent)) + .into(); + tangent_index += 1; + }); + } else { + println!("gltf: no tangents in file, calculating from tris"); + // tangents and bitangents from triangles + let mut triangles_included = vec![0; vertices.len()]; + for chunk in indices.chunks(3) { + let v0 = vertices[chunk[0] as usize]; + let v1 = vertices[chunk[1] as usize]; + let v2 = vertices[chunk[2] as usize]; - // tangents and bitangents from triangles - let mut triangles_included = vec![0; vertices.len()]; - for chunk in indices.chunks(3) { - let v0 = vertices[chunk[0] as usize]; - let v1 = vertices[chunk[1] as usize]; - let v2 = vertices[chunk[2] as usize]; + let pos0: cgmath::Vector3 = v0.position.into(); + let pos1: cgmath::Vector3 = v1.position.into(); + let pos2: cgmath::Vector3 = v2.position.into(); - let pos0: cgmath::Vector3 = v0.position.into(); - let pos1: cgmath::Vector3 = v1.position.into(); - let pos2: cgmath::Vector3 = v2.position.into(); + let uv0: cgmath::Vector2 = v0.tex_coords.into(); + let uv1: cgmath::Vector2 = v1.tex_coords.into(); + let uv2: cgmath::Vector2 = v2.tex_coords.into(); - let uv0: cgmath::Vector2 = v0.tex_coords.into(); - let uv1: cgmath::Vector2 = v1.tex_coords.into(); - let uv2: cgmath::Vector2 = v2.tex_coords.into(); + let delta_pos1 = pos1 - pos0; + let delta_pos2 = pos2 - pos0; - let delta_pos1 = pos1 - pos0; - let delta_pos2 = pos2 - pos0; + let delta_uv1 = uv1 - uv0; + let delta_uv2 = uv2 - uv0; - let delta_uv1 = uv1 - uv0; - let delta_uv2 = uv2 - uv0; + let r = 1.0 / (delta_uv1.x * delta_uv2.y - delta_uv1.y * delta_uv2.x); + let tangent = (delta_pos1 * delta_uv2.y - delta_pos2 * delta_uv1.y) * r; + let bitangent = (delta_pos2 * delta_uv1.x - delta_pos1 * delta_uv2.x) * -r; - let r = 1.0 / (delta_uv1.x * delta_uv2.y - delta_uv1.y * delta_uv2.x); - let tangent = (delta_pos1 * delta_uv2.y - delta_pos2 * delta_uv1.y) * r; - let bitangent = (delta_pos2 * delta_uv1.x - delta_pos1 * delta_uv2.x) * -r; - - for i in 0..3 { - let sz = chunk[i] as usize; - vertices[sz].tangent = - (cgmath::Vector4::new(tangent.x, tangent.y, tangent.z, 0.0) - + cgmath::Vector4::from(vertices[sz].tangent)) - .into(); - vertices[sz].bitangent = - (bitangent + cgmath::Vector3::from(vertices[sz].bitangent)).into(); - triangles_included[sz] += 1; + for i in 0..3 { + let sz = chunk[i] as usize; + vertices[sz].tangent = + (tangent + cgmath::Vector3::from(vertices[sz].tangent)).into(); + vertices[sz].bitangent = + (bitangent + cgmath::Vector3::from(vertices[sz].bitangent)).into(); + triangles_included[sz] += 1; + } } - } - // Average the tangents/bitangents - for (i, n) in triangles_included.into_iter().enumerate() { - let denom = 1.0 / n as f32; - let mut v = &mut vertices[i]; - v.tangent = (cgmath::Vector4::from(v.tangent) * denom).into(); - v.bitangent = (cgmath::Vector3::from(v.bitangent) * denom).into(); + // Average the tangents/bitangents + for (i, n) in triangles_included.into_iter().enumerate() { + let denom = 1.0 / n as f32; + let mut v = &mut vertices[i]; + v.tangent = (cgmath::Vector3::from(v.tangent) * denom).into(); + v.bitangent = (cgmath::Vector3::from(v.bitangent) * denom).into(); + } } if let Some(tex_coord_attribute) = reader.read_tex_coords(0).map(|v| v.into_f32()) { diff --git a/src/shaders/test.wgsl b/src/shaders/test.wgsl index 3deb838..a64c4ee 100644 --- a/src/shaders/test.wgsl +++ b/src/shaders/test.wgsl @@ -28,6 +28,9 @@ struct InstanceInput { @location(6) model_matrix_1: vec4, @location(7) model_matrix_2: vec4, @location(8) model_matrix_3: vec4, + @location(9) normal_matrix_0: vec3, + @location(10) normal_matrix_1: vec3, + @location(11) normal_matrix_2: vec3, } struct VertexOutput { @@ -37,7 +40,6 @@ struct VertexOutput { @location(2) tangent_light_position: vec3, @location(3) tangent_view_position: vec3, @location(4) world_position: vec3, - @location(5) world_normal: vec3, } @vertex @@ -51,18 +53,23 @@ fn vs_main( instance.model_matrix_2, instance.model_matrix_3, ); + let normal_matrix = mat3x3( + instance.normal_matrix_0, + instance.normal_matrix_1, + instance.normal_matrix_2, + ); - let world_normal = normalize((model_matrix * vec4(model.normal, 0.0)).xyz); - let world_tangent = normalize((model_matrix * vec4(model.tangent, 0.0)).xyz); - let world_bitangent = normalize((model_matrix * vec4(model.bitangent, 0.0)).xyz); - let world_position = model_matrix * vec4(model.position, 1.0); - + let world_normal = normalize(normal_matrix * model.normal); + let world_tangent = normalize(normal_matrix * model.tangent); + let world_bitangent = normalize(normal_matrix * model.bitangent); let tangent_matrix = transpose(mat3x3( world_tangent, world_bitangent, world_normal, )); + let world_position = model_matrix * vec4(model.position, 1.0); + var out: VertexOutput; out.clip_position = camera.proj * camera.view * world_position; out.tex_coords = model.tex_coords; @@ -70,7 +77,6 @@ fn vs_main( out.tangent_light_position = tangent_matrix * light.position; out.tangent_view_position = tangent_matrix * camera.position.xyz; - out.world_normal = world_normal.xyz; out.world_position = world_position.xyz; return out; @@ -88,39 +94,37 @@ var t_normal: texture_2d; @group(0) @binding(3) var s_normal: sampler; -// TODO: fix using tangent space and normal texture instead of world @fragment fn fs_main(in: VertexOutput) -> @location(0) vec4 { + // textures let object_color: vec4 = textureSample(t_diffuse, s_diffuse, in.tex_coords); let object_normal: vec4 = textureSample(t_normal, s_normal, in.tex_coords); // lighting vecs let tangent_normal = object_normal.xyz * 2.0 - 1.0; - // let light_dir = normalize(in.tangent_light_position - in.tangent_position); - var light_dir = light.position - in.world_position; - let light_dist = length(light_dir); - light_dir = normalize(light_dir); + var light_dir = normalize(in.tangent_light_position - in.tangent_position); + let view_dir = normalize(in.tangent_view_position - in.tangent_position); + let half_dir = normalize(view_dir + light_dir); + + // attenuation + let light_dist = length(light.position - in.world_position); let coef_a = 0.0; let coef_b = 1.25; let light_attenuation = 1.0 / (1.0 + coef_a * light_dist + coef_b * light_dist * light_dist); - // let view_dir = normalize(in.tangent_view_position - in.tangent_position); - let view_dir = normalize(camera.position.xyz - in.world_position); - let half_dir = normalize(view_dir + light_dir); - - // ambient - let ambient_strength = 0.025; - let ambient_color = vec3(1.0) * ambient_strength; // diffuse - // let diffuse_strength = max(dot(tangent_normal, light_dir), 0.0); - let diffuse_strength = max(dot(in.world_normal, light_dir), 0.0); + let diffuse_strength = max(dot(tangent_normal, light_dir), 0.0); let diffuse_color = diffuse_strength * light.color.xyz * light.color.w * light_attenuation; // specular - // let specular_strength = pow(max(dot(tangent_normal, half_dir), 0.0), 32.0); - let specular_strength = pow(max(dot(in.world_normal, half_dir), 0.0), 32.0); + let specular_strength = pow(max(dot(tangent_normal, half_dir), 0.0), 32.0); let specular_color = specular_strength * light.color.xyz * light.color.w * light_attenuation; + // ambient + let ambient_light_color = vec3(1.0); + let ambient_strength = 0.025; + let ambient_color = ambient_light_color * ambient_strength; + let result = (ambient_color + diffuse_color + specular_color) * object_color.xyz; return vec4(result, object_color.a);