159 lines
5.2 KiB
GLSL
159 lines
5.2 KiB
GLSL
// Material handling and PBR scattering
|
|
|
|
#ifndef MATERIAL_GLSL
|
|
#define MATERIAL_GLSL
|
|
|
|
// Helper function to sample texture from array by index
|
|
vec4 sample_texture_array(int slot, int index, vec2 uv) {
|
|
if (index <= 0) return vec4(1.0);
|
|
|
|
if (slot == 0) return texture(u_texture_albedo_array, vec3(uv, float(index - 1)));
|
|
if (slot == 1) return texture(u_texture_normal_array, vec3(uv, float(index - 1)));
|
|
if (slot == 2) return texture(u_texture_metallic_array, vec3(uv, float(index - 1)));
|
|
if (slot == 3) return texture(u_texture_roughness_array, vec3(uv, float(index - 1)));
|
|
if (slot == 4) return texture(u_texture_ao_array, vec3(uv, float(index - 1)));
|
|
if (slot == 5) return texture(u_texture_emission_array, vec3(uv, float(index - 1)));
|
|
|
|
return vec4(1.0);
|
|
}
|
|
|
|
// Apply normal map in world space
|
|
vec3 apply_normal_map(vec3 normal, vec2 texcoord, vec3 tangent, uint normal_handle) {
|
|
if (normal_handle == 0 || !u_enable_textures) return normal;
|
|
|
|
vec3 T = normalize(tangent - normal * dot(tangent, normal));
|
|
vec3 B = cross(normal, T);
|
|
mat3 TBN = mat3(T, B, normal);
|
|
|
|
vec3 map_n = sample_texture_array(1, int(normal_handle), texcoord).xyz * 2.0 - 1.0;
|
|
return normalize(TBN * map_n);
|
|
}
|
|
|
|
// Apply material textures to get final PBR values
|
|
void apply_material_textures(inout Material mat, inout vec3 normal, vec2 texcoord, vec3 tangent) {
|
|
if (!u_enable_textures) return;
|
|
|
|
if (mat.texture_handles[0] != 0) {
|
|
mat.albedo = sample_texture_array(0, int(mat.texture_handles[0]), texcoord).rgb;
|
|
}
|
|
|
|
if (mat.texture_handles[1] != 0) {
|
|
normal = apply_normal_map(normal, texcoord, tangent, mat.texture_handles[1]);
|
|
}
|
|
|
|
if (mat.texture_handles[2] != 0) {
|
|
mat.metallic = sample_texture_array(2, int(mat.texture_handles[2]), texcoord).r;
|
|
}
|
|
|
|
if (mat.texture_handles[3] != 0) {
|
|
mat.roughness = sample_texture_array(3, int(mat.texture_handles[3]), texcoord).r;
|
|
}
|
|
|
|
if (mat.texture_handles[4] != 0) {
|
|
mat.ao = sample_texture_array(4, int(mat.texture_handles[4]), texcoord).r;
|
|
}
|
|
|
|
if (mat.texture_handles[5] != 0) {
|
|
mat.emission = sample_texture_array(5, int(mat.texture_handles[5]), texcoord).rgb;
|
|
}
|
|
}
|
|
|
|
// Fresnel functions
|
|
vec3 fresnel_schlick(float cos_theta, vec3 f0) {
|
|
return f0 + (1.0 - f0) * pow(1.0 - cos_theta, 5.0);
|
|
}
|
|
|
|
float fresnel_dielectric(float cos_theta, float ior) {
|
|
float r0 = (1.0 - ior) / (1.0 + ior);
|
|
r0 = r0 * r0;
|
|
return r0 + (1.0 - r0) * pow(1.0 - cos_theta, 5.0);
|
|
}
|
|
|
|
// Scatter functions
|
|
ScatterResult scatter_diffuse(Ray ray_in, HitInfo hit, Material mat, inout uint seed) {
|
|
ScatterResult r;
|
|
r.scattered = true;
|
|
r.attenuation = mat.albedo;
|
|
|
|
vec3 dir = hit.normal + random_unit_vector(seed);
|
|
if (near_zero(dir)) dir = hit.normal;
|
|
|
|
r.scattered_ray.origin = hit.position + hit.normal * EPSILON;
|
|
r.scattered_ray.direction = normalize(dir);
|
|
return r;
|
|
}
|
|
|
|
ScatterResult scatter_metal(Ray ray_in, HitInfo hit, Material mat, inout uint seed) {
|
|
ScatterResult r;
|
|
|
|
vec3 reflected = reflect_vector(normalize(ray_in.direction), hit.normal);
|
|
vec3 fuzz = mat.roughness * random_in_unit_sphere(seed);
|
|
vec3 dir = reflected + fuzz;
|
|
|
|
r.scattered = dot(dir, hit.normal) > 0.0;
|
|
r.attenuation = mat.albedo;
|
|
r.scattered_ray.origin = hit.position + hit.normal * EPSILON;
|
|
r.scattered_ray.direction = normalize(dir);
|
|
return r;
|
|
}
|
|
|
|
ScatterResult scatter_dielectric(Ray ray_in, HitInfo hit, Material mat, inout uint seed) {
|
|
ScatterResult r;
|
|
r.scattered = true;
|
|
r.attenuation = vec3(1.0);
|
|
|
|
vec3 unit_dir = normalize(ray_in.direction);
|
|
float cos_theta = dot(-unit_dir, hit.normal);
|
|
float sin_theta = sqrt(max(0.0, 1.0 - cos_theta * cos_theta));
|
|
|
|
bool entering = cos_theta > 0.0;
|
|
float eta = entering ? (1.0 / mat.ior) : mat.ior;
|
|
vec3 normal = entering ? hit.normal : -hit.normal;
|
|
|
|
float sin_theta_t = eta * sin_theta;
|
|
bool total_internal_reflection = sin_theta_t >= 1.0;
|
|
|
|
float f0 = pow((1.0 - mat.ior) / (1.0 + mat.ior), 2.0);
|
|
float f = f0 + (1.0 - f0) * pow(1.0 - abs(cos_theta), 5.0);
|
|
|
|
vec3 dir;
|
|
if (total_internal_reflection || random_float(seed) < f) {
|
|
dir = reflect_vector(unit_dir, normal);
|
|
} else {
|
|
dir = refract_vector(unit_dir, normal, eta);
|
|
}
|
|
|
|
r.scattered_ray.origin = hit.position + dir * EPSILON;
|
|
r.scattered_ray.direction = normalize(dir);
|
|
return r;
|
|
}
|
|
|
|
ScatterResult scatter_ray(Ray ray_in, HitInfo hit, Material mat, inout uint seed) {
|
|
if (mat.type == MATERIAL_DIFFUSE) return scatter_diffuse(ray_in, hit, mat, seed);
|
|
if (mat.type == MATERIAL_METAL) return scatter_metal(ray_in, hit, mat, seed);
|
|
if (mat.type == MATERIAL_DIELECTRIC) return scatter_dielectric(ray_in, hit, mat, seed);
|
|
|
|
ScatterResult r;
|
|
r.scattered = false;
|
|
r.attenuation = vec3(0.0);
|
|
return r;
|
|
}
|
|
|
|
// Fetch material with fallback
|
|
Material fetch_material(uint material_id) {
|
|
uint cnt = uint(materials.length());
|
|
if (material_id < cnt) return materials[material_id];
|
|
|
|
Material m;
|
|
m.albedo = vec3(0.5);
|
|
m.metallic = 0.0;
|
|
m.emission = vec3(0.0);
|
|
m.roughness = 0.5;
|
|
m.type = MATERIAL_DIFFUSE;
|
|
m.ior = 1.5;
|
|
m.ao = 1.0;
|
|
return m;
|
|
}
|
|
|
|
#endif // MATERIAL_GLSL
|