1
0
Fork 0

tutorial11-normals

This commit is contained in:
Adrian Hedqvist 2020-10-10 11:35:33 +02:00
parent 0137cd6e22
commit 129619bb3d
7 changed files with 160 additions and 37 deletions

View file

@ -1,8 +1,8 @@
# doing the wgpu-rs tutorial
[current chapter](https://sotrh.github.io/learn-wgpu/intermediate/tutorial11-normals/#tangent-space-to-world-space)
[current chapter](https://sotrh.github.io/learn-wgpu/intermediate/tutorial12-camera/)
## stuff to maybe do after the tutorial is done
* [glsl preprocessor using tera](https://codecrash.me/an-opengl-preprocessor-for-rust)
* [line drawing](https://vladjuckov.github.io/hqlines/)
* [line drawing](https://vladjuckov.github.io/hqlines/)

View file

@ -184,6 +184,7 @@ impl State {
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("texture_bind_group_layout"),
entries: &[
// Diffuse texture
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStage::FRAGMENT,
@ -200,6 +201,23 @@ impl State {
ty: wgpu::BindingType::Sampler { comparison: false },
count: None,
},
// Normal map texture
wgpu::BindGroupLayoutEntry {
binding: 2,
visibility: wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::SampledTexture {
dimension: wgpu::TextureViewDimension::D2,
component_type: wgpu::TextureComponentType::Uint,
multisampled: false,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 3,
visibility: wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Sampler { comparison: false },
count: None,
},
],
});

View file

@ -14,6 +14,8 @@ pub struct ModelVertex {
position: cgmath::Vector3<f32>,
tex_coords: cgmath::Vector2<f32>,
normal: cgmath::Vector3<f32>,
tangent: cgmath::Vector3<f32>,
bitangent: cgmath::Vector3<f32>,
}
impl Vertex for ModelVertex {
@ -29,17 +31,26 @@ impl Vertex for ModelVertex {
format: wgpu::VertexFormat::Float3,
},
wgpu::VertexAttributeDescriptor {
offset: mem::size_of::<cgmath::Vector3<f32>>() as wgpu::BufferAddress,
offset: mem::size_of::<[f32; 3]>() as wgpu::BufferAddress,
shader_location: 1,
format: wgpu::VertexFormat::Float2,
},
wgpu::VertexAttributeDescriptor {
offset: (mem::size_of::<cgmath::Vector3<f32>>()
+ mem::size_of::<cgmath::Vector2<f32>>())
as wgpu::BufferAddress,
offset: mem::size_of::<[f32; 5]>() as wgpu::BufferAddress,
shader_location: 2,
format: wgpu::VertexFormat::Float3,
},
// Tangent & Bitangent
wgpu::VertexAttributeDescriptor {
offset: mem::size_of::<[f32; 8]>() as wgpu::BufferAddress,
shader_location: 3,
format: wgpu::VertexFormat::Float3,
},
wgpu::VertexAttributeDescriptor {
offset: mem::size_of::<[f32; 11]>() as wgpu::BufferAddress,
shader_location: 4,
format: wgpu::VertexFormat::Float3,
},
],
}
}
@ -51,9 +62,50 @@ unsafe impl bytemuck::Zeroable for ModelVertex {}
pub struct Material {
pub name: String,
pub diffuse_texture: texture::Texture,
pub normal_texture: texture::Texture,
pub bind_group: wgpu::BindGroup,
}
impl Material {
pub fn new(
device: &wgpu::Device,
name: &str,
diffuse_texture: texture::Texture,
normal_texture: texture::Texture,
layout: &wgpu::BindGroupLayout,
) -> Self {
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some(name),
layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureView(&diffuse_texture.view),
},
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::Sampler(&diffuse_texture.sampler),
},
wgpu::BindGroupEntry {
binding: 2,
resource: wgpu::BindingResource::TextureView(&normal_texture.view),
},
wgpu::BindGroupEntry {
binding: 3,
resource: wgpu::BindingResource::Sampler(&normal_texture.sampler),
},
],
});
Material {
name: name.to_string(),
diffuse_texture,
normal_texture,
bind_group,
}
}
}
pub struct Mesh {
pub name: String,
pub vertex_buffer: wgpu::Buffer,
@ -85,28 +137,22 @@ impl Model {
device,
queue,
containing_folder.join(mat.diffuse_texture),
false,
)?;
let normal_texture = texture::Texture::load(
device,
queue,
containing_folder.join(mat.normal_texture),
true,
)?;
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: None,
layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureView(&diffuse_texture.view),
},
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::Sampler(&diffuse_texture.sampler),
},
],
});
Ok(Material {
name: mat.name,
Ok(Material::new(
device,
&mat.name,
diffuse_texture,
bind_group,
})
normal_texture,
layout,
))
})
.collect();
@ -129,9 +175,35 @@ impl Model {
m.mesh.normals[i * 3 + 2],
)
.into(),
tangent: [0.0; 3].into(),
bitangent: [0.0; 3].into(),
});
}
for c in m.mesh.indices.chunks(3) {
let v0 = vertices[c[0] as usize];
let v1 = vertices[c[1] as usize];
let v2 = vertices[c[2] as usize];
let delta_pos1 = v1.position - v0.position;
let delta_pos2 = v2.position - v0.position;
let delta_uv1 = v1.tex_coords - v0.tex_coords;
let delta_uv2 = v2.tex_coords - v0.tex_coords;
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;
vertices[c[0] as usize].tangent = tangent;
vertices[c[1] as usize].tangent = tangent;
vertices[c[2] as usize].tangent = tangent;
vertices[c[0] as usize].bitangent = bitangent;
vertices[c[1] as usize].bitangent = bitangent;
vertices[c[2] as usize].bitangent = bitangent;
}
let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some(&format!("{:?} Vertex Buffer", path.as_ref())),
contents: bytemuck::cast_slice(&vertices),

View file

@ -1,13 +1,16 @@
#version 450
layout(location=0) in vec2 v_tex_coords;
layout(location=1) in vec3 v_normal;
layout(location=2) in vec3 v_position;
layout(location=1) in vec3 v_position;
layout(location=2) in vec3 v_light_position;
layout(location=3) in vec3 v_view_position;
layout(location=0) out vec4 f_color;
layout(set = 0, binding = 0) uniform texture2D t_diffuse;
layout(set = 0, binding = 1) uniform sampler s_diffuse;
layout(set = 0, binding = 2) uniform texture2D t_normal;
layout(set = 0, binding = 3) uniform sampler s_normal;
layout(set=1, binding=0)
uniform Uniforms {
@ -22,11 +25,13 @@ layout(set=2, binding=0) uniform Light {
void main() {
vec4 object_color = texture(sampler2D(t_diffuse, s_diffuse), v_tex_coords);
vec3 normal = normalize(v_normal);
vec3 light_dir = normalize(light_position - v_position);
vec3 view_dir = normalize(u_view_position - v_position);
vec4 object_normal = texture(sampler2D(t_normal, s_normal), v_tex_coords);
vec3 normal = normalize(object_normal.rgb);
vec3 light_dir = normalize(v_light_position - v_position);
vec3 view_dir = normalize(v_view_position - v_position);
vec3 half_dir = normalize(view_dir + light_dir);
float specular_strength = pow(max(dot(normal, half_dir), 0.0), 32);
vec3 specular_color = specular_strength * light_color;

View file

@ -3,10 +3,13 @@
layout(location=0) in vec3 a_position;
layout(location=1) in vec2 a_tex_coords;
layout(location=2) in vec3 a_normal;
layout(location=3) in vec3 a_tangent;
layout(location=4) in vec3 a_bitangent;
layout(location=0) out vec2 v_tex_coords;
layout(location=1) out vec3 v_normal;
layout(location=2) out vec3 v_position;
layout(location=1) out vec3 v_position;
layout(location=2) out vec3 v_light_position;
layout(location=3) out vec3 v_view_position;
layout(set=1, binding=0) uniform Uniforms {
vec3 u_view_position;
@ -17,12 +20,30 @@ layout(set=1, binding=1) buffer Instances {
mat4 s_models[];
};
layout(set=2, binding=0) uniform Light {
vec3 light_position;
vec3 light_color;
};
void main() {
v_tex_coords = a_tex_coords;
mat4 model_matrix = s_models[gl_InstanceIndex];
mat3 normal_matrix = mat3(transpose(inverse(model_matrix))); // TODO: calculate this on CPU and pass in as an uniform
vec3 normal = normalize(normal_matrix * a_normal);
vec3 tangent = normalize(normal_matrix * a_tangent);
vec3 bitangent = normalize(normal_matrix * a_bitangent);
mat3 tangent_matrix = transpose(mat3(
tangent,
bitangent,
normal
));
vec4 model_space = model_matrix * vec4(a_position, 1.0);
v_normal = normal_matrix * a_normal;
v_position = model_space.xyz;
v_position = tangent_matrix * model_space.xyz;
v_light_position = tangent_matrix * light_position;
v_view_position = tangent_matrix * u_view_position;
gl_Position = u_view_proj * model_space;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -14,16 +14,18 @@ impl Texture {
device: &wgpu::Device,
queue: &wgpu::Queue,
bytes: &[u8],
is_normal_map: bool,
label: &str,
) -> Result<Self> {
let img = image::load_from_memory(bytes)?;
Self::from_image(device, queue, &img, Some(label))
Self::from_image(device, queue, &img, is_normal_map, Some(label))
}
pub fn from_image(
device: &wgpu::Device,
queue: &wgpu::Queue,
img: &image::DynamicImage,
is_normal_map: bool,
label: Option<&str>,
) -> Result<Self> {
let rgba = img.to_rgba();
@ -41,7 +43,11 @@ impl Texture {
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Rgba8UnormSrgb,
format: if is_normal_map {
wgpu::TextureFormat::Rgba8Unorm
} else {
wgpu::TextureFormat::Rgba8UnormSrgb
},
usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST,
});
@ -147,12 +153,13 @@ impl Texture {
device: &wgpu::Device,
queue: &wgpu::Queue,
path: P,
is_normal_map: bool,
) -> Result<Self> {
// Needed to appease the borrow checker
let path_copy = path.as_ref().to_path_buf();
let label = path_copy.to_str();
let img = image::open(path)?;
Self::from_image(device, queue, &img, label)
Self::from_image(device, queue, &img, is_normal_map, label)
}
}