summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/gles2/shaders/scene.glsl17
-rw-r--r--drivers/gles3/rasterizer_scene_gles3.cpp29
-rw-r--r--drivers/gles3/rasterizer_scene_gles3.h2
-rw-r--r--drivers/gles3/rasterizer_storage_gles3.cpp151
-rw-r--r--drivers/gles3/rasterizer_storage_gles3.h1
-rw-r--r--drivers/gles3/shaders/cubemap_filter.glsl119
-rw-r--r--drivers/gles3/shaders/scene.glsl29
7 files changed, 285 insertions, 63 deletions
diff --git a/drivers/gles2/shaders/scene.glsl b/drivers/gles2/shaders/scene.glsl
index e36e776881..2a220658a1 100644
--- a/drivers/gles2/shaders/scene.glsl
+++ b/drivers/gles2/shaders/scene.glsl
@@ -1547,7 +1547,11 @@ FRAGMENT_SHADER_CODE
#endif // !USE_SHADOW_TO_OPACITY
#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
ambient_light = vec3(0.0, 0.0, 0.0);
@@ -1561,12 +1565,15 @@ FRAGMENT_SHADER_CODE
ref_vec.z *= -1.0;
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 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);
}
+#endif
#else
@@ -1574,7 +1581,6 @@ FRAGMENT_SHADER_CODE
specular_light = bg_color.rgb * bg_energy;
#endif
-
#endif // AMBIENT_LIGHT_DISABLED
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 c1 = vec4(1.0, 0.0425, 1.04, -0.04);
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;
vec2 env = vec2(-1.04, 1.04) * a004 + r.zw;
-
- vec3 f0 = F0(metallic, specular, albedo);
- specular_light *= env.x * f0 + env.y;
+ specular_light *= env.x * F + env.y;
#endif
}
diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp
index 519fdf2b3b..f52fd00dd2 100644
--- a/drivers/gles3/rasterizer_scene_gles3.cpp
+++ b/drivers/gles3/rasterizer_scene_gles3.cpp
@@ -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
@@ -2019,14 +2019,15 @@ void RasterizerSceneGLES3::_render_list(RenderList::Element **p_elements, int p_
if (!p_shadow && !p_directional_add) {
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);
if (storage->config.use_texture_array_environment) {
- glBindTexture(GL_TEXTURE_2D_ARRAY, p_base_env);
+ glBindTexture(GL_TEXTURE_2D_ARRAY, p_sky->radiance);
} 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_ARRAY, storage->config.use_texture_array_environment);
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);
render_list.sort_by_key(false);
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);
glColorMask(1, 1, 1, 1);
@@ -4355,7 +4356,6 @@ void RasterizerSceneGLES3::render_scene(const Transform &p_cam_transform, const
RasterizerStorageGLES3::Sky *sky = NULL;
Ref<CameraFeed> feed;
- GLuint env_radiance_tex = 0;
if (state.debug_draw == VS::VIEWPORT_DEBUG_DRAW_OVERDRAW) {
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);
- if (sky) {
- env_radiance_tex = sky->radiance;
- }
break;
case VS::ENV_BG_CANVAS:
//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) {
- 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;
@@ -4524,7 +4521,7 @@ void RasterizerSceneGLES3::render_scene(const Transform &p_cam_transform, const
if (state.directional_light_count == 0) {
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 {
for (int i = 0; i < state.directional_light_count; i++) {
directional_light = directional_lights[i];
@@ -4532,7 +4529,7 @@ void RasterizerSceneGLES3::render_scene(const Transform &p_cam_transform, const
glEnable(GL_BLEND);
}
_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) {
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 {
for (int i = 0; i < state.directional_light_count; i++) {
directional_light = directional_lights[i];
_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) {
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_DUAL_PARABOLOID, false);
diff --git a/drivers/gles3/rasterizer_scene_gles3.h b/drivers/gles3/rasterizer_scene_gles3.h
index e6d2449653..a756ce251e 100644
--- a/drivers/gles3/rasterizer_scene_gles3.h
+++ b/drivers/gles3/rasterizer_scene_gles3.h
@@ -837,7 +837,7 @@ public:
_FORCE_INLINE_ void _render_geometry(RenderList::Element *e);
_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);
diff --git a/drivers/gles3/rasterizer_storage_gles3.cpp b/drivers/gles3/rasterizer_storage_gles3.cpp
index 71737426a9..e40164ad87 100644
--- a/drivers/gles3/rasterizer_storage_gles3.cpp
+++ b/drivers/gles3/rasterizer_storage_gles3.cpp
@@ -1757,6 +1757,7 @@ RID RasterizerStorageGLES3::sky_create() {
Sky *sky = memnew(Sky);
sky->radiance = 0;
+ sky->irradiance = 0;
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()) {
sky->panorama = RID();
glDeleteTextures(1, &sky->radiance);
+ glDeleteTextures(1, &sky->irradiance);
sky->radiance = 0;
+ sky->irradiance = 0;
}
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);
glBindTexture(texture->target, texture->tex_id);
- glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameterf(GL_TEXTURE_2D, 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); //need this for proper sampling
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 10);
+ // Need Mipmaps regardless of whether they are set in import by user
+ 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) {
@@ -1808,6 +1815,66 @@ void RasterizerStorageGLES3::sky_set_texture(RID p_sky, RID p_panorama, int p_ra
#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);
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);
- glTexParameterf(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glTexParameterf(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameterf(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameterf(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
GLuint tmp_fb2;
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);
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_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glTexParameterf(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
#ifdef DEBUG_ENABLED
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
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);
- if (j == 0) {
+ if (j < 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_DIRECT_WRITE, true);
shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_SOURCE_DUAL_PARABOLOID_ARRAY, 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_ARRAY, true);
- shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_DIRECT_WRITE, false);
shaders.cubemap_filter.bind();
glActiveTexture(GL_TEXTURE0);
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_RESOLUTION, float(size / 2));
}
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_DUAL_PARABOLOID, 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
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_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;
mm_level = mipmaps;
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) {
-
+ glBindFramebuffer(GL_FRAMEBUFFER, tmp_fb);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, sky->radiance, lod);
+
#ifdef DEBUG_ENABLED
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
ERR_CONTINUE(status != GL_FRAMEBUFFER_COMPLETE);
#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++) {
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);
}
+ 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)
size >>= 1;
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);
glDeleteFramebuffers(1, &tmp_fb);
+ glDeleteFramebuffers(1, &tmp_fb2);
+ glDeleteTextures(1, &tmp_tex);
}
}
diff --git a/drivers/gles3/rasterizer_storage_gles3.h b/drivers/gles3/rasterizer_storage_gles3.h
index 3b1021d0e1..350b259b2b 100644
--- a/drivers/gles3/rasterizer_storage_gles3.h
+++ b/drivers/gles3/rasterizer_storage_gles3.h
@@ -388,6 +388,7 @@ public:
RID panorama;
GLuint radiance;
+ GLuint irradiance;
int radiance_size;
};
diff --git a/drivers/gles3/shaders/cubemap_filter.glsl b/drivers/gles3/shaders/cubemap_filter.glsl
index 619e29b130..d83a109cb9 100644
--- a/drivers/gles3/shaders/cubemap_filter.glsl
+++ b/drivers/gles3/shaders/cubemap_filter.glsl
@@ -30,12 +30,22 @@ uniform sampler2DArray source_dual_paraboloid_array; //texunit:0
uniform int source_array_index;
#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
#endif
uniform int face_id;
uniform float roughness;
+uniform float source_resolution;
+
in highp vec2 uv_interp;
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;
}
+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
float GGX(float NdotV, float a) {
float k = a / 2.0;
@@ -160,10 +183,12 @@ vec2 Hammersley(uint i, uint N) {
#ifdef LOW_QUALITY
#define SAMPLE_COUNT 64u
+#define SAMPLE_DELTA 0.05
#else
-#define SAMPLE_COUNT 1024u
+#define SAMPLE_COUNT 512u
+#define SAMPLE_DELTA 0.01
#endif
@@ -171,7 +196,7 @@ uniform bool z_flip;
#ifdef USE_SOURCE_PANORAMA
-vec4 texturePanorama(vec3 normal, sampler2D pano) {
+vec4 texturePanorama(vec3 normal, sampler2D pano, float mipLevel) {
vec2 st = vec2(
atan(normal.x, normal.z),
@@ -182,7 +207,7 @@ vec4 texturePanorama(vec3 normal, sampler2D pano) {
st /= vec2(M_PI * 2.0, M_PI);
- return textureLod(pano, st, 0.0);
+ return textureLod(pano, st, mipLevel);
}
#endif
@@ -202,6 +227,20 @@ vec4 textureDualParaboloidArray(vec3 normal) {
#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() {
#ifdef USE_DUAL_PARABOLOID
@@ -225,7 +264,7 @@ void main() {
#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
#ifdef USE_SOURCE_DUAL_PARABOLOID_ARRAY
@@ -233,12 +272,51 @@ void main() {
frag_color = vec4(textureDualParaboloidArray(N).rgb, 1.0);
#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;
frag_color = vec4(texture(N, source_cube).rgb, 1.0);
#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
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++) {
vec2 xi = Hammersley(sampleNum, SAMPLE_COUNT);
- vec3 H = ImportanceSampleGGX(xi, roughness, N);
+ vec3 H = normalize(ImportanceSampleGGX(xi, roughness, 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) {
+
+ 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
- sum.rgb += texturePanorama(L, source_panorama).rgb * ndotl;
+ sum.rgb += texturePanorama(L, source_panorama, mipLevel).rgb * ndotl;
#endif
#ifdef USE_SOURCE_DUAL_PARABOLOID_ARRAY
@@ -262,7 +351,12 @@ void main() {
sum.rgb += textureDualParaboloidArray(L).rgb * ndotl;
#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;
sum.rgb += textureLod(source_cube, L, 0.0).rgb * ndotl;
#endif
@@ -273,5 +367,6 @@ void main() {
frag_color = vec4(sum.rgb, 1.0);
-#endif
+#endif // COMPUTE_IRRADIANCE
+#endif // USE_DIRECT_WRITE
}
diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl
index 403de25dd0..e1b0e9f595 100644
--- a/drivers/gles3/shaders/scene.glsl
+++ b/drivers/gles3/shaders/scene.glsl
@@ -627,6 +627,8 @@ layout(std140) uniform Radiance { // ubo:2
#define RADIANCE_MAX_LOD 5.0
+uniform sampler2D irradiance_map; // texunit:-6
+
#ifdef USE_RADIANCE_MAP_ARRAY
uniform sampler2DArray radiance_map; // texunit:-2
@@ -1766,6 +1768,11 @@ FRAGMENT_SHADER_CODE
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 AMBIENT_LIGHT_DISABLED
@@ -1775,22 +1782,27 @@ FRAGMENT_SHADER_CODE
{ //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);
vec3 radiance = textureDualParaboloid(radiance_map, ref_vec, roughness) * bg_energy;
env_reflection_light = radiance;
}
- //no longer a cubemap
- //vec3 radiance = textureLod(radiance_cube, r, lod).xyz * ( brdf.x + brdf.y);
}
#ifndef USE_LIGHTMAP
{
- vec3 ambient_dir = normalize((radiance_inverse_xform * vec4(normal, 0.0)).xyz);
- vec3 env_ambient = textureDualParaboloid(radiance_map, ambient_dir, 1.0) * bg_energy;
+ vec3 norm = normal;
+ 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=vec3(0.0,0.0,0.0);
}
#endif
#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 c1 = vec4(1.0, 0.0425, 1.04, -0.04);
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;
vec2 env = vec2(-1.04, 1.04) * a004 + r.zw;
-
- vec3 f0 = F0(metallic, specular, albedo);
- specular_light *= env.x * f0 + env.y;
+ specular_light *= env.x * F + env.y;
#endif
}