Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions drivers/gles3/shaders/scene.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -1666,20 +1666,32 @@ void reflection_process(samplerCube reflection_map,
vec3 ref_normal = normalize(reflect(vertex, normal));
ref_normal = (local_matrix * vec4(ref_normal, 0.0)).xyz;

float mip = sqrt(roughness) * MAX_ROUGHNESS_LOD;
float mip_min = pow(roughness, 2.0) * MAX_ROUGHNESS_LOD; // Ensures fully rough materials don't have reflection contact hardening.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
float mip_min = pow(roughness, 2.0) * MAX_ROUGHNESS_LOD; // Ensures fully rough materials don't have reflection contact hardening.
float mip_min = (roughness * roughness) * MAX_ROUGHNESS_LOD; // Ensures fully rough materials don't have reflection contact hardening.

I would feel better if you avoided the pow function in this case. Sure, some compilers might recognize that this has an integer exponent of two, but I would rather not rely on that.

if (use_box_project) { //box project

vec3 nrdir = normalize(ref_normal);
vec3 rbmax = (box_extents - local_pos) / nrdir;
vec3 rbmin = (-box_extents - local_pos) / nrdir;

vec3 rbminmax = mix(rbmin, rbmax, vec3(greaterThan(nrdir, vec3(0.0, 0.0, 0.0))));
float distance_to_hit_point = min(min(rbminmax.x, rbminmax.y), rbminmax.z);

float fa = min(min(rbminmax.x, rbminmax.y), rbminmax.z);
vec3 posonbox = local_pos + nrdir * fa;
vec3 posonbox = local_pos + nrdir * distance_to_hit_point;
ref_normal = posonbox - box_offset.xyz;

float fresnel = 1.0 - max(dot(normal, -normalize(vertex)), 0.0);
fresnel = pow(fresnel, 2.0);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here as well


float reflection_roughness = distance_to_hit_point / MAX_ROUGHNESS_LOD; // Remap distance to be relative to amount of mips.
reflection_roughness *= 1.0 - fresnel;
reflection_roughness += ((1.0 - fresnel) * sqrt(roughness)); // Increase roughness when viewing angle is perpendicular to avoid overly sharp reflections on rough surfaces.

float mip_offset = clamp(reflection_roughness, 0.0, 1.0); // Compute new mip level based on the mip offset value (this is mostly arbitrary).
mip = mix(mip_min, mip, mip_offset);
}

reflection.rgb = srgb_to_linear(textureLod(reflection_map, ref_normal, roughness * MAX_ROUGHNESS_LOD).rgb);
reflection.rgb = srgb_to_linear(textureLod(reflection_map, ref_normal, mip).rgb);

if (exterior) {
reflection.rgb = mix(skybox, reflection.rgb, blend);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -930,23 +930,35 @@ void reflection_process(uint ref_index, vec3 vertex, hvec3 ref_vec, hvec3 normal

vec3 local_ref_vec = (reflections.data[ref_index].local_matrix * vec4(ref_vec, 0.0)).xyz;

float mip = sqrt(roughness) * MAX_ROUGHNESS_LOD;
float mip_min = pow(roughness, 2.0) * MAX_ROUGHNESS_LOD; // Ensures fully rough materials don't have reflection contact hardening.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Squaring roughness doesn't make sense here. Previously we sqrted the roughness value because that is how we mapped roughness to the array layers when baking the reflection probe. Therefore mip_min doesn't really make any sense, it is an arbitrary value that is small than mip but converges with mip at 0 and 1.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is an arbitrary value that is small than mip but converges with mip at 0 and 1

This is done to prevent fully rough surfaces exhibiting reflections. If we just lerp from 0.0 to mip, there will always be mirror-like reflections where distance_to_hit_point is small, regardless the roughness. This fades out the hardening effect as the roughness is increased, which produces results significantly closer to Cycles and SSR.

In the frostbite implementation this is handled by doing return lerp ( newLinearRoughness , linearRoughness , linearRoughness ), they state the same reasoning:

This works decently for low roughness values but becomes inaccurate for high roughness. In order to avoid this issue and since this effect is more visible for low roughness values, we linearly interpolate the “distance based roughness” to the original roughness, based on roughness values.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above, I don't trust mobile compilers to realize that the pow can be computed with a single multiplication.

if (reflections.data[ref_index].box_project) { //box project

vec3 nrdir = normalize(local_ref_vec);
vec3 rbmax = (box_extents - local_pos) / nrdir;
vec3 rbmin = (-box_extents - local_pos) / nrdir;

vec3 rbminmax = mix(rbmin, rbmax, greaterThan(nrdir, vec3(0.0, 0.0, 0.0)));
float distance_to_hit_point = min(min(rbminmax.x, rbminmax.y), rbminmax.z);

float fa = min(min(rbminmax.x, rbminmax.y), rbminmax.z);
vec3 posonbox = local_pos + nrdir * fa;
vec3 posonbox = local_pos + nrdir * distance_to_hit_point;
local_ref_vec = posonbox - reflections.data[ref_index].box_offset;

float fresnel = 1.0 - max(dot(normal, -normalize(vertex)), 0.0);
fresnel = pow(fresnel, 4.0);

float reflection_roughness = distance_to_hit_point / MAX_ROUGHNESS_LOD; // Remap distance to be relative to amount of mips.
reflection_roughness *= 1.0 - fresnel;
reflection_roughness += ((1.0 - fresnel) * sqrt(roughness)); // Increase roughness when viewing angle is perpendicular to avoid overly sharp reflections on rough surfaces.

float mip_offset = clamp(reflection_roughness, 0.0, 1.0); // Compute new mip level based on the mip offset value (this is mostly arbitrary).
mip = mix(mip_min, mip, mip_offset);
}

hvec4 reflection;
half reflection_blend = max(half(0.0), blend - reflection_accum.a);

reflection.rgb = hvec3(textureLod(samplerCubeArray(reflection_atlas, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(local_ref_vec, reflections.data[ref_index].index), sqrt(roughness) * MAX_ROUGHNESS_LOD).rgb) * sc_luminance_multiplier();
reflection.rgb = hvec3(textureLod(samplerCubeArray(reflection_atlas, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(local_ref_vec, reflections.data[ref_index].index), mip).rgb) * sc_luminance_multiplier();
reflection.rgb *= half(reflections.data[ref_index].exposure_normalization);
reflection.a = reflection_blend;

Expand Down
Loading