Fix issues with environment mapping

This commit is contained in:
clayjohn 2019-11-16 16:56:34 -08:00
parent da4079f231
commit cd40154890
7 changed files with 285 additions and 63 deletions

View file

@ -1547,7 +1547,11 @@ FRAGMENT_SHADER_CODE
#endif // !USE_SHADOW_TO_OPACITY #endif // !USE_SHADOW_TO_OPACITY
#ifdef BASE_PASS #ifdef BASE_PASS
//none
// IBL precalculations
float ndotv = clamp(dot(normal, eye_position), 0.0, 1.0);
vec3 f0 = F0(metallic, specular, albedo);
vec3 F = f0 + (max(vec3(1.0 - roughness), f0) - f0) * pow(1.0 - ndotv, 5.0);
#ifdef AMBIENT_LIGHT_DISABLED #ifdef AMBIENT_LIGHT_DISABLED
ambient_light = vec3(0.0, 0.0, 0.0); ambient_light = vec3(0.0, 0.0, 0.0);
@ -1561,12 +1565,15 @@ FRAGMENT_SHADER_CODE
ref_vec.z *= -1.0; ref_vec.z *= -1.0;
specular_light = textureCubeLod(radiance_map, ref_vec, roughness * RADIANCE_MAX_LOD).xyz * bg_energy; specular_light = textureCubeLod(radiance_map, ref_vec, roughness * RADIANCE_MAX_LOD).xyz * bg_energy;
#ifndef USE_LIGHTMAP
{ {
vec3 ambient_dir = normalize((radiance_inverse_xform * vec4(normal, 0.0)).xyz); vec3 ambient_dir = normalize((radiance_inverse_xform * vec4(normal, 0.0)).xyz);
vec3 env_ambient = textureCubeLod(radiance_map, ambient_dir, RADIANCE_MAX_LOD).xyz * bg_energy; vec3 env_ambient = textureCubeLod(radiance_map, ambient_dir, 4.0).xyz * bg_energy;
env_ambient *= 1.0 - F;
ambient_light = mix(ambient_color.rgb, env_ambient, ambient_sky_contribution); ambient_light = mix(ambient_color.rgb, env_ambient, ambient_sky_contribution);
} }
#endif
#else #else
@ -1574,7 +1581,6 @@ FRAGMENT_SHADER_CODE
specular_light = bg_color.rgb * bg_energy; specular_light = bg_color.rgb * bg_energy;
#endif #endif
#endif // AMBIENT_LIGHT_DISABLED #endif // AMBIENT_LIGHT_DISABLED
ambient_light *= ambient_energy; ambient_light *= ambient_energy;
@ -1646,12 +1652,9 @@ FRAGMENT_SHADER_CODE
const vec4 c0 = vec4(-1.0, -0.0275, -0.572, 0.022); const vec4 c0 = vec4(-1.0, -0.0275, -0.572, 0.022);
const vec4 c1 = vec4(1.0, 0.0425, 1.04, -0.04); const vec4 c1 = vec4(1.0, 0.0425, 1.04, -0.04);
vec4 r = roughness * c0 + c1; vec4 r = roughness * c0 + c1;
float ndotv = clamp(dot(normal, eye_position), 0.0, 1.0);
float a004 = min(r.x * r.x, exp2(-9.28 * ndotv)) * r.x + r.y; float a004 = min(r.x * r.x, exp2(-9.28 * ndotv)) * r.x + r.y;
vec2 env = vec2(-1.04, 1.04) * a004 + r.zw; vec2 env = vec2(-1.04, 1.04) * a004 + r.zw;
specular_light *= env.x * F + env.y;
vec3 f0 = F0(metallic, specular, albedo);
specular_light *= env.x * f0 + env.y;
#endif #endif
} }

View file

@ -2011,7 +2011,7 @@ void RasterizerSceneGLES3::_set_cull(bool p_front, bool p_disabled, bool p_rever
} }
} }
void RasterizerSceneGLES3::_render_list(RenderList::Element **p_elements, int p_element_count, const Transform &p_view_transform, const CameraMatrix &p_projection, GLuint p_base_env, bool p_reverse_cull, bool p_alpha_pass, bool p_shadow, bool p_directional_add, bool p_directional_shadows) { void RasterizerSceneGLES3::_render_list(RenderList::Element **p_elements, int p_element_count, const Transform &p_view_transform, const CameraMatrix &p_projection, RasterizerStorageGLES3::Sky *p_sky, bool p_reverse_cull, bool p_alpha_pass, bool p_shadow, bool p_directional_add, bool p_directional_shadows) {
glBindBufferBase(GL_UNIFORM_BUFFER, 0, state.scene_ubo); //bind globals ubo glBindBufferBase(GL_UNIFORM_BUFFER, 0, state.scene_ubo); //bind globals ubo
@ -2019,14 +2019,15 @@ void RasterizerSceneGLES3::_render_list(RenderList::Element **p_elements, int p_
if (!p_shadow && !p_directional_add) { if (!p_shadow && !p_directional_add) {
glBindBufferBase(GL_UNIFORM_BUFFER, 2, state.env_radiance_ubo); //bind environment radiance info glBindBufferBase(GL_UNIFORM_BUFFER, 2, state.env_radiance_ubo); //bind environment radiance info
if (p_base_env) { if (p_sky != NULL) {
glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 2); glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 2);
if (storage->config.use_texture_array_environment) { if (storage->config.use_texture_array_environment) {
glBindTexture(GL_TEXTURE_2D_ARRAY, p_base_env); glBindTexture(GL_TEXTURE_2D_ARRAY, p_sky->radiance);
} else { } else {
glBindTexture(GL_TEXTURE_2D, p_base_env); glBindTexture(GL_TEXTURE_2D, p_sky->radiance);
} }
glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 6);
glBindTexture(GL_TEXTURE_2D, p_sky->irradiance);
state.scene_shader.set_conditional(SceneShaderGLES3::USE_RADIANCE_MAP, true); state.scene_shader.set_conditional(SceneShaderGLES3::USE_RADIANCE_MAP, true);
state.scene_shader.set_conditional(SceneShaderGLES3::USE_RADIANCE_MAP_ARRAY, storage->config.use_texture_array_environment); state.scene_shader.set_conditional(SceneShaderGLES3::USE_RADIANCE_MAP_ARRAY, storage->config.use_texture_array_environment);
use_radiance_map = true; use_radiance_map = true;
@ -4231,7 +4232,7 @@ void RasterizerSceneGLES3::render_scene(const Transform &p_cam_transform, const
_fill_render_list(p_cull_result, p_cull_count, true, false); _fill_render_list(p_cull_result, p_cull_count, true, false);
render_list.sort_by_key(false); render_list.sort_by_key(false);
state.scene_shader.set_conditional(SceneShaderGLES3::RENDER_DEPTH, true); state.scene_shader.set_conditional(SceneShaderGLES3::RENDER_DEPTH, true);
_render_list(render_list.elements, render_list.element_count, p_cam_transform, p_cam_projection, 0, false, false, true, false, false); _render_list(render_list.elements, render_list.element_count, p_cam_transform, p_cam_projection, NULL, false, false, true, false, false);
state.scene_shader.set_conditional(SceneShaderGLES3::RENDER_DEPTH, false); state.scene_shader.set_conditional(SceneShaderGLES3::RENDER_DEPTH, false);
glColorMask(1, 1, 1, 1); glColorMask(1, 1, 1, 1);
@ -4355,7 +4356,6 @@ void RasterizerSceneGLES3::render_scene(const Transform &p_cam_transform, const
RasterizerStorageGLES3::Sky *sky = NULL; RasterizerStorageGLES3::Sky *sky = NULL;
Ref<CameraFeed> feed; Ref<CameraFeed> feed;
GLuint env_radiance_tex = 0;
if (state.debug_draw == VS::VIEWPORT_DEBUG_DRAW_OVERDRAW) { if (state.debug_draw == VS::VIEWPORT_DEBUG_DRAW_OVERDRAW) {
clear_color = Color(0, 0, 0, 0); clear_color = Color(0, 0, 0, 0);
@ -4409,9 +4409,6 @@ void RasterizerSceneGLES3::render_scene(const Transform &p_cam_transform, const
sky = storage->sky_owner.getornull(env->sky); sky = storage->sky_owner.getornull(env->sky);
if (sky) {
env_radiance_tex = sky->radiance;
}
break; break;
case VS::ENV_BG_CANVAS: case VS::ENV_BG_CANVAS:
//copy canvas to 3d buffer and convert it to linear //copy canvas to 3d buffer and convert it to linear
@ -4505,7 +4502,7 @@ void RasterizerSceneGLES3::render_scene(const Transform &p_cam_transform, const
} }
if (probe && probe->probe_ptr->interior) { if (probe && probe->probe_ptr->interior) {
env_radiance_tex = 0; //for rendering probe interiors, radiance must not be used. sky = NULL; //for rendering probe interiors, radiance must not be used.
} }
state.texscreen_copied = false; state.texscreen_copied = false;
@ -4524,7 +4521,7 @@ void RasterizerSceneGLES3::render_scene(const Transform &p_cam_transform, const
if (state.directional_light_count == 0) { if (state.directional_light_count == 0) {
directional_light = NULL; directional_light = NULL;
_render_list(render_list.elements, render_list.element_count, p_cam_transform, p_cam_projection, env_radiance_tex, false, false, false, false, shadow_atlas != NULL); _render_list(render_list.elements, render_list.element_count, p_cam_transform, p_cam_projection, sky, false, false, false, false, shadow_atlas != NULL);
} else { } else {
for (int i = 0; i < state.directional_light_count; i++) { for (int i = 0; i < state.directional_light_count; i++) {
directional_light = directional_lights[i]; directional_light = directional_lights[i];
@ -4532,7 +4529,7 @@ void RasterizerSceneGLES3::render_scene(const Transform &p_cam_transform, const
glEnable(GL_BLEND); glEnable(GL_BLEND);
} }
_setup_directional_light(i, p_cam_transform.affine_inverse(), shadow_atlas != NULL && shadow_atlas->size > 0); _setup_directional_light(i, p_cam_transform.affine_inverse(), shadow_atlas != NULL && shadow_atlas->size > 0);
_render_list(render_list.elements, render_list.element_count, p_cam_transform, p_cam_projection, env_radiance_tex, false, false, false, i > 0, shadow_atlas != NULL); _render_list(render_list.elements, render_list.element_count, p_cam_transform, p_cam_projection, sky, false, false, false, i > 0, shadow_atlas != NULL);
} }
} }
@ -4610,12 +4607,12 @@ void RasterizerSceneGLES3::render_scene(const Transform &p_cam_transform, const
if (state.directional_light_count == 0) { if (state.directional_light_count == 0) {
directional_light = NULL; directional_light = NULL;
_render_list(&render_list.elements[render_list.max_elements - render_list.alpha_element_count], render_list.alpha_element_count, p_cam_transform, p_cam_projection, env_radiance_tex, false, true, false, false, shadow_atlas != NULL); _render_list(&render_list.elements[render_list.max_elements - render_list.alpha_element_count], render_list.alpha_element_count, p_cam_transform, p_cam_projection, sky, false, true, false, false, shadow_atlas != NULL);
} else { } else {
for (int i = 0; i < state.directional_light_count; i++) { for (int i = 0; i < state.directional_light_count; i++) {
directional_light = directional_lights[i]; directional_light = directional_lights[i];
_setup_directional_light(i, p_cam_transform.affine_inverse(), shadow_atlas != NULL && shadow_atlas->size > 0); _setup_directional_light(i, p_cam_transform.affine_inverse(), shadow_atlas != NULL && shadow_atlas->size > 0);
_render_list(&render_list.elements[render_list.max_elements - render_list.alpha_element_count], render_list.alpha_element_count, p_cam_transform, p_cam_projection, env_radiance_tex, false, true, false, i > 0, shadow_atlas != NULL); _render_list(&render_list.elements[render_list.max_elements - render_list.alpha_element_count], render_list.alpha_element_count, p_cam_transform, p_cam_projection, sky, false, true, false, i > 0, shadow_atlas != NULL);
} }
} }
@ -4898,7 +4895,7 @@ void RasterizerSceneGLES3::render_shadow(RID p_light, RID p_shadow_atlas, int p_
if (light->reverse_cull) { if (light->reverse_cull) {
flip_facing = !flip_facing; flip_facing = !flip_facing;
} }
_render_list(render_list.elements, render_list.element_count, light_transform, light_projection, 0, flip_facing, false, true, false, false); _render_list(render_list.elements, render_list.element_count, light_transform, light_projection, NULL, flip_facing, false, true, false, false);
state.scene_shader.set_conditional(SceneShaderGLES3::RENDER_DEPTH, false); state.scene_shader.set_conditional(SceneShaderGLES3::RENDER_DEPTH, false);
state.scene_shader.set_conditional(SceneShaderGLES3::RENDER_DEPTH_DUAL_PARABOLOID, false); state.scene_shader.set_conditional(SceneShaderGLES3::RENDER_DEPTH_DUAL_PARABOLOID, false);

View file

@ -837,7 +837,7 @@ public:
_FORCE_INLINE_ void _render_geometry(RenderList::Element *e); _FORCE_INLINE_ void _render_geometry(RenderList::Element *e);
_FORCE_INLINE_ void _setup_light(RenderList::Element *e, const Transform &p_view_transform); _FORCE_INLINE_ void _setup_light(RenderList::Element *e, const Transform &p_view_transform);
void _render_list(RenderList::Element **p_elements, int p_element_count, const Transform &p_view_transform, const CameraMatrix &p_projection, GLuint p_base_env, bool p_reverse_cull, bool p_alpha_pass, bool p_shadow, bool p_directional_add, bool p_directional_shadows); void _render_list(RenderList::Element **p_elements, int p_element_count, const Transform &p_view_transform, const CameraMatrix &p_projection, RasterizerStorageGLES3::Sky *p_sky, bool p_reverse_cull, bool p_alpha_pass, bool p_shadow, bool p_directional_add, bool p_directional_shadows);
_FORCE_INLINE_ void _add_geometry(RasterizerStorageGLES3::Geometry *p_geometry, InstanceBase *p_instance, RasterizerStorageGLES3::GeometryOwner *p_owner, int p_material, bool p_depth_pass, bool p_shadow_pass); _FORCE_INLINE_ void _add_geometry(RasterizerStorageGLES3::Geometry *p_geometry, InstanceBase *p_instance, RasterizerStorageGLES3::GeometryOwner *p_owner, int p_material, bool p_depth_pass, bool p_shadow_pass);

View file

@ -1757,6 +1757,7 @@ RID RasterizerStorageGLES3::sky_create() {
Sky *sky = memnew(Sky); Sky *sky = memnew(Sky);
sky->radiance = 0; sky->radiance = 0;
sky->irradiance = 0;
return sky_owner.make_rid(sky); return sky_owner.make_rid(sky);
} }
@ -1768,7 +1769,9 @@ void RasterizerStorageGLES3::sky_set_texture(RID p_sky, RID p_panorama, int p_ra
if (sky->panorama.is_valid()) { if (sky->panorama.is_valid()) {
sky->panorama = RID(); sky->panorama = RID();
glDeleteTextures(1, &sky->radiance); glDeleteTextures(1, &sky->radiance);
glDeleteTextures(1, &sky->irradiance);
sky->radiance = 0; sky->radiance = 0;
sky->irradiance = 0;
} }
sky->panorama = p_panorama; sky->panorama = p_panorama;
@ -1791,10 +1794,14 @@ void RasterizerStorageGLES3::sky_set_texture(RID p_sky, RID p_panorama, int p_ra
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
glBindTexture(texture->target, texture->tex_id); glBindTexture(texture->target, texture->tex_id);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 10);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); // Need Mipmaps regardless of whether they are set in import by user
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); //need this for proper sampling glGenerateMipmap(texture->target);
glTexParameterf(texture->target, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameterf(texture->target, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameterf(texture->target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameterf(texture->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR);
if (config.srgb_decode_supported && texture->srgb && !texture->using_srgb) { if (config.srgb_decode_supported && texture->srgb && !texture->using_srgb) {
@ -1808,6 +1815,66 @@ void RasterizerStorageGLES3::sky_set_texture(RID p_sky, RID p_panorama, int p_ra
#endif #endif
} }
{
//Irradiance map
glActiveTexture(GL_TEXTURE1);
glGenTextures(1, &sky->irradiance);
glBindTexture(GL_TEXTURE_2D, sky->irradiance);
GLuint tmp_fb;
glGenFramebuffers(1, &tmp_fb);
glBindFramebuffer(GL_FRAMEBUFFER, tmp_fb);
int size = 64;
bool use_float = config.framebuffer_half_float_supported;
GLenum internal_format = use_float ? GL_RGBA16F : GL_RGB10_A2;
GLenum format = GL_RGBA;
GLenum type = use_float ? GL_HALF_FLOAT : GL_UNSIGNED_INT_2_10_10_10_REV;
glTexImage2D(GL_TEXTURE_2D, 0, internal_format, size, size * 2, 0, format, type, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
glTexParameterf(texture->target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(texture->target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, sky->irradiance, 0);
shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_DUAL_PARABOLOID, true);
shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_SOURCE_PANORAMA, true);
shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::COMPUTE_IRRADIANCE, true);
shaders.cubemap_filter.bind();
// Very large Panoramas require way too much effort to compute irradiance so use a mipmap
// level that corresponds to a panorama of 1024x512
shaders.cubemap_filter.set_uniform(CubemapFilterShaderGLES3::SOURCE_MIP_LEVEL, MAX(Math::log(float(texture->width)) / Math::log(2.0f) - 10.0f, 0.0f));
for (int i = 0; i < 2; i++) {
glViewport(0, i * size, size, size);
glBindVertexArray(resources.quadie_array);
shaders.cubemap_filter.set_uniform(CubemapFilterShaderGLES3::Z_FLIP, i > 0);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glBindVertexArray(0);
}
shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_DUAL_PARABOLOID, false);
shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_SOURCE_PANORAMA, false);
shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::COMPUTE_IRRADIANCE, false);
glBindFramebuffer(GL_FRAMEBUFFER, RasterizerStorageGLES3::system_fbo);
glDeleteFramebuffers(1, &tmp_fb);
}
// Now compute radiance
glActiveTexture(GL_TEXTURE1); glActiveTexture(GL_TEXTURE1);
glGenTextures(1, &sky->radiance); glGenTextures(1, &sky->radiance);
@ -1833,8 +1900,8 @@ void RasterizerStorageGLES3::sky_set_texture(RID p_sky, RID p_panorama, int p_ra
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, internal_format, size, size * 2, array_level, 0, format, type, NULL); glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, internal_format, size, size * 2, array_level, 0, format, type, NULL);
glTexParameterf(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameterf(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameterf(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
GLuint tmp_fb2; GLuint tmp_fb2;
GLuint tmp_tex; GLuint tmp_tex;
@ -1846,8 +1913,8 @@ void RasterizerStorageGLES3::sky_set_texture(RID p_sky, RID p_panorama, int p_ra
glBindTexture(GL_TEXTURE_2D, tmp_tex); glBindTexture(GL_TEXTURE_2D, tmp_tex);
glTexImage2D(GL_TEXTURE_2D, 0, internal_format, size, size * 2, 0, format, type, NULL); glTexImage2D(GL_TEXTURE_2D, 0, internal_format, size, size * 2, 0, format, type, NULL);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tmp_tex, 0); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tmp_tex, 0);
glTexParameterf(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
#ifdef DEBUG_ENABLED #ifdef DEBUG_ENABLED
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
ERR_FAIL_COND(status != GL_FRAMEBUFFER_COMPLETE); ERR_FAIL_COND(status != GL_FRAMEBUFFER_COMPLETE);
@ -1858,25 +1925,25 @@ void RasterizerStorageGLES3::sky_set_texture(RID p_sky, RID p_panorama, int p_ra
glBindFramebuffer(GL_FRAMEBUFFER, tmp_fb2); glBindFramebuffer(GL_FRAMEBUFFER, tmp_fb2);
if (j == 0) { if (j < 3) {
shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_DUAL_PARABOLOID, true); shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_DUAL_PARABOLOID, true);
shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_SOURCE_PANORAMA, true); shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_SOURCE_PANORAMA, true);
shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_DIRECT_WRITE, true);
shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_SOURCE_DUAL_PARABOLOID_ARRAY, false); shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_SOURCE_DUAL_PARABOLOID_ARRAY, false);
shaders.cubemap_filter.bind(); shaders.cubemap_filter.bind();
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
glBindTexture(texture->target, texture->tex_id); glBindTexture(texture->target, texture->tex_id);
shaders.cubemap_filter.set_uniform(CubemapFilterShaderGLES3::SOURCE_RESOLUTION, float(texture->width / 4));
} else { } else {
shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_DUAL_PARABOLOID, true); shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_DUAL_PARABOLOID, true);
shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_SOURCE_PANORAMA, false); shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_SOURCE_PANORAMA, false);
shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_SOURCE_DUAL_PARABOLOID_ARRAY, true); shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_SOURCE_DUAL_PARABOLOID_ARRAY, true);
shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_DIRECT_WRITE, false);
shaders.cubemap_filter.bind(); shaders.cubemap_filter.bind();
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D_ARRAY, sky->radiance); glBindTexture(GL_TEXTURE_2D_ARRAY, sky->radiance);
shaders.cubemap_filter.set_uniform(CubemapFilterShaderGLES3::SOURCE_ARRAY_INDEX, j - 1); //read from previous to ensure better blur shaders.cubemap_filter.set_uniform(CubemapFilterShaderGLES3::SOURCE_ARRAY_INDEX, j - 1); //read from previous to ensure better blur
shaders.cubemap_filter.set_uniform(CubemapFilterShaderGLES3::SOURCE_RESOLUTION, float(size / 2));
} }
for (int i = 0; i < 2; i++) { for (int i = 0; i < 2; i++) {
@ -1902,7 +1969,6 @@ void RasterizerStorageGLES3::sky_set_texture(RID p_sky, RID p_panorama, int p_ra
shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_SOURCE_PANORAMA, false); shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_SOURCE_PANORAMA, false);
shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_DUAL_PARABOLOID, false); shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_DUAL_PARABOLOID, false);
shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_SOURCE_DUAL_PARABOLOID_ARRAY, false); shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_SOURCE_DUAL_PARABOLOID_ARRAY, false);
shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_DIRECT_WRITE, false);
//restore ranges //restore ranges
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
@ -1947,23 +2013,64 @@ void RasterizerStorageGLES3::sky_set_texture(RID p_sky, RID p_panorama, int p_ra
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, mipmaps - 1); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, mipmaps - 1);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
GLuint tmp_fb2;
GLuint tmp_tex;
{
// Need a temporary framebuffer for rendering so we can read from previous iterations
glGenFramebuffers(1, &tmp_fb2);
glBindFramebuffer(GL_FRAMEBUFFER, tmp_fb2);
glGenTextures(1, &tmp_tex);
glBindTexture(GL_TEXTURE_2D, tmp_tex);
glTexImage2D(GL_TEXTURE_2D, 0, internal_format, size, size * 2, 0, format, type, NULL);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tmp_tex, 0);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
#ifdef DEBUG_ENABLED
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
ERR_FAIL_COND(status != GL_FRAMEBUFFER_COMPLETE);
#endif
}
lod = 0; lod = 0;
mm_level = mipmaps; mm_level = mipmaps;
size = p_radiance_size; size = p_radiance_size;
shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_DUAL_PARABOLOID, true);
shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_SOURCE_PANORAMA, true);
shaders.cubemap_filter.bind();
while (mm_level) { while (mm_level) {
glBindFramebuffer(GL_FRAMEBUFFER, tmp_fb);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, sky->radiance, lod); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, sky->radiance, lod);
#ifdef DEBUG_ENABLED #ifdef DEBUG_ENABLED
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
ERR_CONTINUE(status != GL_FRAMEBUFFER_COMPLETE); ERR_CONTINUE(status != GL_FRAMEBUFFER_COMPLETE);
#endif #endif
glBindTexture(GL_TEXTURE_2D, tmp_tex);
glTexImage2D(GL_TEXTURE_2D, 0, internal_format, size, size * 2, 0, format, type, NULL);
glBindFramebuffer(GL_FRAMEBUFFER, tmp_fb2);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tmp_tex, 0);
if (lod < 3) {
shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_DUAL_PARABOLOID, true);
shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_SOURCE_PANORAMA, true);
shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_SOURCE_DUAL_PARABOLOID, false);
shaders.cubemap_filter.bind();
glActiveTexture(GL_TEXTURE0);
glBindTexture(texture->target, texture->tex_id);
shaders.cubemap_filter.set_uniform(CubemapFilterShaderGLES3::SOURCE_RESOLUTION, float(texture->width / 4));
} else {
shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_DUAL_PARABOLOID, true);
shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_SOURCE_PANORAMA, false);
shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_SOURCE_DUAL_PARABOLOID, true);
shaders.cubemap_filter.bind();
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, sky->radiance);
shaders.cubemap_filter.set_uniform(CubemapFilterShaderGLES3::SOURCE_MIP_LEVEL, lod - 1); //read from previous to ensure better blur
shaders.cubemap_filter.set_uniform(CubemapFilterShaderGLES3::SOURCE_RESOLUTION, float(size));
}
for (int i = 0; i < 2; i++) { for (int i = 0; i < 2; i++) {
glViewport(0, i * size, size, size); glViewport(0, i * size, size, size);
@ -1976,6 +2083,14 @@ void RasterizerStorageGLES3::sky_set_texture(RID p_sky, RID p_panorama, int p_ra
glBindVertexArray(0); glBindVertexArray(0);
} }
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, tmp_fb);
glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, sky->radiance, 0, lod);
glBindFramebuffer(GL_READ_FRAMEBUFFER, tmp_fb2);
glReadBuffer(GL_COLOR_ATTACHMENT0);
glBlitFramebuffer(0, 0, size, size * 2, 0, 0, size, size * 2, GL_COLOR_BUFFER_BIT, GL_NEAREST);
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
if (size > 1) if (size > 1)
size >>= 1; size >>= 1;
lod++; lod++;
@ -1995,6 +2110,8 @@ void RasterizerStorageGLES3::sky_set_texture(RID p_sky, RID p_panorama, int p_ra
glBindFramebuffer(GL_FRAMEBUFFER, RasterizerStorageGLES3::system_fbo); glBindFramebuffer(GL_FRAMEBUFFER, RasterizerStorageGLES3::system_fbo);
glDeleteFramebuffers(1, &tmp_fb); glDeleteFramebuffers(1, &tmp_fb);
glDeleteFramebuffers(1, &tmp_fb2);
glDeleteTextures(1, &tmp_tex);
} }
} }

View file

@ -388,6 +388,7 @@ public:
RID panorama; RID panorama;
GLuint radiance; GLuint radiance;
GLuint irradiance;
int radiance_size; int radiance_size;
}; };

View file

@ -30,12 +30,22 @@ uniform sampler2DArray source_dual_paraboloid_array; //texunit:0
uniform int source_array_index; uniform int source_array_index;
#endif #endif
#if !defined(USE_SOURCE_DUAL_PARABOLOID_ARRAY) && !defined(USE_SOURCE_PANORAMA) #ifdef USE_SOURCE_DUAL_PARABOLOID
uniform sampler2D source_dual_paraboloid; //texunit:0
#endif
#if defined(USE_SOURCE_DUAL_PARABOLOID) || defined(COMPUTE_IRRADIANCE)
uniform int source_mip_level;
#endif
#if !defined(USE_SOURCE_DUAL_PARABOLOID_ARRAY) && !defined(USE_SOURCE_PANORAMA) && !defined(USE_SOURCE_DUAL_PARABOLOID)
uniform samplerCube source_cube; //texunit:0 uniform samplerCube source_cube; //texunit:0
#endif #endif
uniform int face_id; uniform int face_id;
uniform float roughness; uniform float roughness;
uniform float source_resolution;
in highp vec2 uv_interp; in highp vec2 uv_interp;
layout(location = 0) out vec4 frag_color; layout(location = 0) out vec4 frag_color;
@ -133,6 +143,19 @@ vec3 ImportanceSampleGGX(vec2 Xi, float Roughness, vec3 N) {
return TangentX * H.x + TangentY * H.y + N * H.z; return TangentX * H.x + TangentY * H.y + N * H.z;
} }
float DistributionGGX(vec3 N, vec3 H, float roughness) {
float a = roughness * roughness;
float a2 = a * a;
float NdotH = max(dot(N, H), 0.0);
float NdotH2 = NdotH * NdotH;
float nom = a2;
float denom = (NdotH2 * (a2 - 1.0) + 1.0);
denom = M_PI * denom * denom;
return nom / denom;
}
// http://graphicrants.blogspot.com.au/2013/08/specular-brdf-reference.html // http://graphicrants.blogspot.com.au/2013/08/specular-brdf-reference.html
float GGX(float NdotV, float a) { float GGX(float NdotV, float a) {
float k = a / 2.0; float k = a / 2.0;
@ -160,10 +183,12 @@ vec2 Hammersley(uint i, uint N) {
#ifdef LOW_QUALITY #ifdef LOW_QUALITY
#define SAMPLE_COUNT 64u #define SAMPLE_COUNT 64u
#define SAMPLE_DELTA 0.05
#else #else
#define SAMPLE_COUNT 1024u #define SAMPLE_COUNT 512u
#define SAMPLE_DELTA 0.01
#endif #endif
@ -171,7 +196,7 @@ uniform bool z_flip;
#ifdef USE_SOURCE_PANORAMA #ifdef USE_SOURCE_PANORAMA
vec4 texturePanorama(vec3 normal, sampler2D pano) { vec4 texturePanorama(vec3 normal, sampler2D pano, float mipLevel) {
vec2 st = vec2( vec2 st = vec2(
atan(normal.x, normal.z), atan(normal.x, normal.z),
@ -182,7 +207,7 @@ vec4 texturePanorama(vec3 normal, sampler2D pano) {
st /= vec2(M_PI * 2.0, M_PI); st /= vec2(M_PI * 2.0, M_PI);
return textureLod(pano, st, 0.0); return textureLod(pano, st, mipLevel);
} }
#endif #endif
@ -202,6 +227,20 @@ vec4 textureDualParaboloidArray(vec3 normal) {
#endif #endif
#ifdef USE_SOURCE_DUAL_PARABOLOID
vec4 textureDualParaboloid(vec3 normal) {
vec3 norm = normalize(normal);
norm.xy /= 1.0 + abs(norm.z);
norm.xy = norm.xy * vec2(0.5, 0.25) + vec2(0.5, 0.25);
if (norm.z < 0.0) {
norm.y = 0.5 - norm.y + 0.5;
}
return textureLod(source_dual_paraboloid, norm.xy, float(source_mip_level));
}
#endif
void main() { void main() {
#ifdef USE_DUAL_PARABOLOID #ifdef USE_DUAL_PARABOLOID
@ -225,7 +264,7 @@ void main() {
#ifdef USE_SOURCE_PANORAMA #ifdef USE_SOURCE_PANORAMA
frag_color = vec4(texturePanorama(N, source_panorama).rgb, 1.0); frag_color = vec4(texturePanorama(N, source_panorama, 0.0).rgb, 1.0);
#endif #endif
#ifdef USE_SOURCE_DUAL_PARABOLOID_ARRAY #ifdef USE_SOURCE_DUAL_PARABOLOID_ARRAY
@ -233,12 +272,51 @@ void main() {
frag_color = vec4(textureDualParaboloidArray(N).rgb, 1.0); frag_color = vec4(textureDualParaboloidArray(N).rgb, 1.0);
#endif #endif
#if !defined(USE_SOURCE_DUAL_PARABOLOID_ARRAY) && !defined(USE_SOURCE_PANORAMA) #ifdef USE_SOURCE_DUAL_PARABOLOID
frag_color = vec4(textureDualParaboloid(N).rgb, 1.0);
#endif
#if !defined(USE_SOURCE_DUAL_PARABOLOID_ARRAY) && !defined(USE_SOURCE_PANORAMA) && !defined(USE_SOURCE_DUAL_PARABOLOID)
N.y = -N.y; N.y = -N.y;
frag_color = vec4(texture(N, source_cube).rgb, 1.0); frag_color = vec4(texture(N, source_cube).rgb, 1.0);
#endif #endif
#else // USE_DIRECT_WRITE
#ifdef COMPUTE_IRRADIANCE
vec3 irradiance = vec3(0.0);
// tangent space calculation from origin point
vec3 UpVector = vec3(0.0, 1.0, 0.0);
vec3 TangentX = cross(UpVector, N);
vec3 TangentY = cross(N, TangentX);
float num_samples = 0.0f;
for (float phi = 0.0; phi < 2.0 * M_PI; phi += SAMPLE_DELTA) {
for (float theta = 0.0; theta < 0.5 * M_PI; theta += SAMPLE_DELTA) {
// Calculate sample positions
vec3 tangentSample = vec3(sin(theta) * cos(phi), sin(theta) * sin(phi), cos(theta));
// Find world vector of sample position
vec3 H = tangentSample.x * TangentX + tangentSample.y * TangentY + tangentSample.z * N;
vec2 st = vec2(atan(H.x, H.z), acos(H.y));
if (st.x < 0.0) {
st.x += M_PI * 2.0;
}
st /= vec2(M_PI * 2.0, M_PI);
irradiance += texture(source_panorama, st, source_mip_level).rgb * cos(theta) * sin(theta);
num_samples++;
}
}
irradiance = M_PI * irradiance * (1.0 / float(num_samples));
frag_color = vec4(irradiance, 1.0);
#else #else
vec4 sum = vec4(0.0, 0.0, 0.0, 0.0); vec4 sum = vec4(0.0, 0.0, 0.0, 0.0);
@ -246,15 +324,26 @@ void main() {
for (uint sampleNum = 0u; sampleNum < SAMPLE_COUNT; sampleNum++) { for (uint sampleNum = 0u; sampleNum < SAMPLE_COUNT; sampleNum++) {
vec2 xi = Hammersley(sampleNum, SAMPLE_COUNT); vec2 xi = Hammersley(sampleNum, SAMPLE_COUNT);
vec3 H = ImportanceSampleGGX(xi, roughness, N); vec3 H = normalize(ImportanceSampleGGX(xi, roughness, N));
vec3 V = N; vec3 V = N;
vec3 L = (2.0 * dot(V, H) * H - V); vec3 L = normalize(2.0 * dot(V, H) * H - V);
float ndotl = clamp(dot(N, L), 0.0, 1.0); float ndotl = max(dot(N, L), 0.0);
if (ndotl > 0.0) { if (ndotl > 0.0) {
float D = DistributionGGX(N, H, roughness);
float ndoth = max(dot(N, H), 0.0);
float hdotv = max(dot(H, V), 0.0);
float pdf = D * ndoth / (4.0 * hdotv) + 0.0001;
float saTexel = 4.0 * M_PI / (6.0 * source_resolution * source_resolution);
float saSample = 1.0 / (float(SAMPLE_COUNT) * pdf + 0.0001);
float mipLevel = roughness == 0.0 ? 0.0 : 0.5 * log2(saSample / saTexel);
#ifdef USE_SOURCE_PANORAMA #ifdef USE_SOURCE_PANORAMA
sum.rgb += texturePanorama(L, source_panorama).rgb * ndotl; sum.rgb += texturePanorama(L, source_panorama, mipLevel).rgb * ndotl;
#endif #endif
#ifdef USE_SOURCE_DUAL_PARABOLOID_ARRAY #ifdef USE_SOURCE_DUAL_PARABOLOID_ARRAY
@ -262,7 +351,12 @@ void main() {
sum.rgb += textureDualParaboloidArray(L).rgb * ndotl; sum.rgb += textureDualParaboloidArray(L).rgb * ndotl;
#endif #endif
#if !defined(USE_SOURCE_DUAL_PARABOLOID_ARRAY) && !defined(USE_SOURCE_PANORAMA) #ifdef USE_SOURCE_DUAL_PARABOLOID
sum.rgb += textureDualParaboloid(L).rgb * ndotl;
#endif
#if !defined(USE_SOURCE_DUAL_PARABOLOID_ARRAY) && !defined(USE_SOURCE_PANORAMA) && !defined(USE_SOURCE_DUAL_PARABOLOID)
L.y = -L.y; L.y = -L.y;
sum.rgb += textureLod(source_cube, L, 0.0).rgb * ndotl; sum.rgb += textureLod(source_cube, L, 0.0).rgb * ndotl;
#endif #endif
@ -273,5 +367,6 @@ void main() {
frag_color = vec4(sum.rgb, 1.0); frag_color = vec4(sum.rgb, 1.0);
#endif #endif // COMPUTE_IRRADIANCE
#endif // USE_DIRECT_WRITE
} }

View file

@ -627,6 +627,8 @@ layout(std140) uniform Radiance { // ubo:2
#define RADIANCE_MAX_LOD 5.0 #define RADIANCE_MAX_LOD 5.0
uniform sampler2D irradiance_map; // texunit:-6
#ifdef USE_RADIANCE_MAP_ARRAY #ifdef USE_RADIANCE_MAP_ARRAY
uniform sampler2DArray radiance_map; // texunit:-2 uniform sampler2DArray radiance_map; // texunit:-2
@ -1766,6 +1768,11 @@ FRAGMENT_SHADER_CODE
vec3 eye_vec = view; vec3 eye_vec = view;
// IBL precalculations
float ndotv = clamp(dot(normal, eye_vec), 0.0, 1.0);
vec3 f0 = F0(metallic, specular, albedo);
vec3 F = f0 + (max(vec3(1.0 - roughness), f0) - f0) * pow(1.0 - ndotv, 5.0);
#ifdef USE_RADIANCE_MAP #ifdef USE_RADIANCE_MAP
#ifdef AMBIENT_LIGHT_DISABLED #ifdef AMBIENT_LIGHT_DISABLED
@ -1775,22 +1782,27 @@ FRAGMENT_SHADER_CODE
{ //read radiance from dual paraboloid { //read radiance from dual paraboloid
vec3 ref_vec = reflect(-eye_vec, normal); //2.0 * ndotv * normal - view; // reflect(v, n); vec3 ref_vec = reflect(-eye_vec, normal);
ref_vec = normalize((radiance_inverse_xform * vec4(ref_vec, 0.0)).xyz); ref_vec = normalize((radiance_inverse_xform * vec4(ref_vec, 0.0)).xyz);
vec3 radiance = textureDualParaboloid(radiance_map, ref_vec, roughness) * bg_energy; vec3 radiance = textureDualParaboloid(radiance_map, ref_vec, roughness) * bg_energy;
env_reflection_light = radiance; env_reflection_light = radiance;
} }
//no longer a cubemap
//vec3 radiance = textureLod(radiance_cube, r, lod).xyz * ( brdf.x + brdf.y);
} }
#ifndef USE_LIGHTMAP #ifndef USE_LIGHTMAP
{ {
vec3 ambient_dir = normalize((radiance_inverse_xform * vec4(normal, 0.0)).xyz); vec3 norm = normal;
vec3 env_ambient = textureDualParaboloid(radiance_map, ambient_dir, 1.0) * bg_energy; norm = normalize((radiance_inverse_xform * vec4(norm, 0.0)).xyz);
norm.xy /= 1.0 + abs(norm.z);
norm.xy = norm.xy * vec2(0.5, 0.25) + vec2(0.5, 0.25);
if (norm.z > 0.0) {
norm.y = 0.5 - norm.y + 0.5;
}
vec3 env_ambient = texture(irradiance_map, norm.xy).rgb * bg_energy;
env_ambient *= 1.0 - F;
ambient_light = mix(ambient_light_color.rgb, env_ambient, radiance_ambient_contribution); ambient_light = mix(ambient_light_color.rgb, env_ambient, radiance_ambient_contribution);
//ambient_light=vec3(0.0,0.0,0.0);
} }
#endif #endif
#endif //AMBIENT_LIGHT_DISABLED #endif //AMBIENT_LIGHT_DISABLED
@ -1892,12 +1904,9 @@ FRAGMENT_SHADER_CODE
const vec4 c0 = vec4(-1.0, -0.0275, -0.572, 0.022); const vec4 c0 = vec4(-1.0, -0.0275, -0.572, 0.022);
const vec4 c1 = vec4(1.0, 0.0425, 1.04, -0.04); const vec4 c1 = vec4(1.0, 0.0425, 1.04, -0.04);
vec4 r = roughness * c0 + c1; vec4 r = roughness * c0 + c1;
float ndotv = clamp(dot(normal, eye_vec), 0.0, 1.0);
float a004 = min(r.x * r.x, exp2(-9.28 * ndotv)) * r.x + r.y; float a004 = min(r.x * r.x, exp2(-9.28 * ndotv)) * r.x + r.y;
vec2 env = vec2(-1.04, 1.04) * a004 + r.zw; vec2 env = vec2(-1.04, 1.04) * a004 + r.zw;
specular_light *= env.x * F + env.y;
vec3 f0 = F0(metallic, specular, albedo);
specular_light *= env.x * f0 + env.y;
#endif #endif
} }