diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/gles3/rasterizer_scene_gles3.cpp | 1055 | ||||
-rw-r--r-- | drivers/gles3/rasterizer_scene_gles3.h | 286 | ||||
-rw-r--r-- | drivers/gles3/rasterizer_storage_gles3.cpp | 13 | ||||
-rw-r--r-- | drivers/gles3/shader_gles3.h | 5 | ||||
-rw-r--r-- | drivers/gles3/shaders/SCsub | 13 | ||||
-rw-r--r-- | drivers/gles3/shaders/cubemap_filter.glsl | 216 | ||||
-rw-r--r-- | drivers/gles3/shaders/scene.glsl | 384 | ||||
-rw-r--r-- | drivers/gles3/shaders/sky.glsl | 35 | ||||
-rw-r--r-- | drivers/gles3/shaders/stdlib_inc.glsl | 6 | ||||
-rw-r--r-- | drivers/gles3/shaders/tonemap_inc.glsl | 16 | ||||
-rw-r--r-- | drivers/gles3/storage/material_storage.cpp | 5 | ||||
-rw-r--r-- | drivers/gles3/storage/material_storage.h | 5 | ||||
-rw-r--r-- | drivers/gles3/storage/mesh_storage.cpp | 8 | ||||
-rw-r--r-- | drivers/gles3/storage/texture_storage.cpp | 5 | ||||
-rw-r--r-- | drivers/gles3/storage/texture_storage.h | 11 |
15 files changed, 1537 insertions, 526 deletions
diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index 68657b9152..7937090862 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -30,11 +30,26 @@ #include "rasterizer_scene_gles3.h" #include "core/config/project_settings.h" +#include "core/templates/sort_array.h" #include "servers/rendering/rendering_server_default.h" #include "storage/config.h" #ifdef GLES3_ENABLED +void glTexStorage2DCustom(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type) { +#ifdef GLES_OVER_GL + + for (int i = 0; i < levels; i++) { + glTexImage2D(target, i, internalformat, width, height, 0, format, type, nullptr); + width = MAX(1, (width / 2)); + height = MAX(1, (height / 2)); + } + +#else + glTexStorage2D(target, levels, internalformat, width, height); +#endif +} + uint64_t RasterizerSceneGLES3::auto_exposure_counter = 2; RasterizerSceneGLES3 *RasterizerSceneGLES3::singleton = nullptr; @@ -196,16 +211,39 @@ void RasterizerSceneGLES3::geometry_instance_set_cast_double_sided_shadows(Geome } uint32_t RasterizerSceneGLES3::geometry_instance_get_pair_mask() { - return 0; //(1 << RS::INSTANCE_LIGHT); - // For now, nothing is paired + return (1 << RS::INSTANCE_LIGHT); } void RasterizerSceneGLES3::geometry_instance_pair_light_instances(GeometryInstance *p_geometry_instance, const RID *p_light_instances, uint32_t p_light_instance_count) { GeometryInstanceGLES3 *ginstance = static_cast<GeometryInstanceGLES3 *>(p_geometry_instance); ERR_FAIL_COND(!ginstance); + GLES3::Config *config = GLES3::Config::get_singleton(); + ginstance->omni_light_count = 0; ginstance->spot_light_count = 0; + ginstance->omni_lights.clear(); + ginstance->spot_lights.clear(); + + for (uint32_t i = 0; i < p_light_instance_count; i++) { + RS::LightType type = light_instance_get_type(p_light_instances[i]); + switch (type) { + case RS::LIGHT_OMNI: { + if (ginstance->omni_light_count < (uint32_t)config->max_lights_per_object) { + ginstance->omni_lights.push_back(p_light_instances[i]); + ginstance->omni_light_count++; + } + } break; + case RS::LIGHT_SPOT: { + if (ginstance->spot_light_count < (uint32_t)config->max_lights_per_object) { + ginstance->spot_lights.push_back(p_light_instances[i]); + ginstance->spot_light_count++; + } + } break; + default: + break; + } + } } void RasterizerSceneGLES3::geometry_instance_pair_reflection_probe_instances(GeometryInstance *p_geometry_instance, const RID *p_reflection_probe_instances, uint32_t p_reflection_probe_instance_count) { @@ -495,8 +533,6 @@ void RasterizerSceneGLES3::_geometry_instance_update(GeometryInstance *p_geometr } } - //Fill push constant - bool store_transform = true; ginstance->base_flags = 0; @@ -556,12 +592,12 @@ void RasterizerSceneGLES3::set_directional_shadow_count(int p_count) { /* SKY API */ -void RasterizerSceneGLES3::Sky::free() { - if (radiance != 0) { - glDeleteTextures(1, &radiance); - radiance = 0; - glDeleteFramebuffers(1, &radiance_framebuffer); - radiance_framebuffer = 0; +void RasterizerSceneGLES3::_free_sky_data(Sky *p_sky) { + if (p_sky->radiance != 0) { + glDeleteTextures(1, &p_sky->radiance); + p_sky->radiance = 0; + glDeleteFramebuffers(1, &p_sky->radiance_framebuffer); + p_sky->radiance_framebuffer = 0; } } @@ -584,7 +620,8 @@ void RasterizerSceneGLES3::sky_set_radiance_size(RID p_sky, int p_radiance_size) sky->radiance_size = p_radiance_size; - sky->free(); + _free_sky_data(sky); + _invalidate_sky(sky); } void RasterizerSceneGLES3::sky_set_mode(RID p_sky, RS::SkyMode p_mode) { @@ -596,10 +633,7 @@ void RasterizerSceneGLES3::sky_set_mode(RID p_sky, RS::SkyMode p_mode) { } sky->mode = p_mode; - - if (sky->mode == RS::SKY_MODE_REALTIME) { - WARN_PRINT_ONCE("The OpenGL renderer does not support the Real Time Sky Update Mode yet. Please use High Quality Mode instead"); - } + _invalidate_sky(sky); } void RasterizerSceneGLES3::sky_set_material(RID p_sky, RID p_material) { @@ -611,6 +645,7 @@ void RasterizerSceneGLES3::sky_set_material(RID p_sky, RID p_material) { } sky->material = p_material; + _invalidate_sky(sky); } void RasterizerSceneGLES3::_invalidate_sky(Sky *p_sky) { @@ -626,13 +661,57 @@ void RasterizerSceneGLES3::_update_dirty_skys() { while (sky) { if (sky->radiance == 0) { - //int mipmaps = Image::get_image_required_mipmaps(sky->radiance_size, sky->radiance_size, Image::FORMAT_RGBAH) + 1; + sky->mipmap_count = Image::get_image_required_mipmaps(sky->radiance_size, sky->radiance_size, Image::FORMAT_RGBA8) + 1; - //uint32_t w = sky->radiance_size, h = sky->radiance_size; - //int layers = sky_globals.roughness_layers; + // Left uninitialized, will attach a texture at render time glGenFramebuffers(1, &sky->radiance_framebuffer); + GLenum internal_format = GL_RGB10_A2; + glGenTextures(1, &sky->radiance); + glBindTexture(GL_TEXTURE_CUBE_MAP, sky->radiance); + +#ifdef GLES_OVER_GL + GLenum format = GL_RGBA; + GLenum type = GL_UNSIGNED_INT_2_10_10_10_REV; + //TODO, on low-end compare this to allocating each face of each mip individually + // see: https://www.khronos.org/registry/OpenGL-Refpages/es3.0/html/glTexStorage2D.xhtml + for (int i = 0; i < 6; i++) { + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, internal_format, sky->radiance_size, sky->radiance_size, 0, format, type, nullptr); + } + + glGenerateMipmap(GL_TEXTURE_CUBE_MAP); +#else + glTexStorage2D(GL_TEXTURE_CUBE_MAP, sky->mipmap_count, internal_format, sky->radiance_size, sky->radiance_size); +#endif + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_LEVEL, sky->mipmap_count); + + glGenTextures(1, &sky->raw_radiance); + glBindTexture(GL_TEXTURE_CUBE_MAP, sky->raw_radiance); + +#ifdef GLES_OVER_GL + //TODO, on low-end compare this to allocating each face of each mip individually + // see: https://www.khronos.org/registry/OpenGL-Refpages/es3.0/html/glTexStorage2D.xhtml + for (int i = 0; i < 6; i++) { + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, internal_format, sky->radiance_size, sky->radiance_size, 0, format, type, nullptr); + } + + glGenerateMipmap(GL_TEXTURE_CUBE_MAP); +#else + glTexStorage2D(GL_TEXTURE_CUBE_MAP, sky->mipmap_count, internal_format, sky->radiance_size, sky->radiance_size); +#endif + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_LEVEL, sky->mipmap_count); + glBindTexture(GL_TEXTURE_CUBE_MAP, 0); } sky->reflection_dirty = true; @@ -647,6 +726,152 @@ void RasterizerSceneGLES3::_update_dirty_skys() { dirty_sky_list = nullptr; } +void RasterizerSceneGLES3::_setup_sky(Environment *p_env, RID p_render_buffers, const PagedArray<RID> &p_lights, const CameraMatrix &p_projection, const Transform3D &p_transform, const Size2i p_screen_size) { + GLES3::LightStorage *light_storage = GLES3::LightStorage::get_singleton(); + GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); + ERR_FAIL_COND(!p_env); + + GLES3::SkyMaterialData *material = nullptr; + Sky *sky = sky_owner.get_or_null(p_env->sky); + + RID sky_material; + + GLES3::SkyShaderData *shader_data = nullptr; + + if (sky) { + sky_material = sky->material; + + if (sky_material.is_valid()) { + material = static_cast<GLES3::SkyMaterialData *>(material_storage->material_get_data(sky_material, RS::SHADER_SKY)); + if (!material || !material->shader_data->valid) { + material = nullptr; + } + } + + if (!material) { + sky_material = sky_globals.default_material; + material = static_cast<GLES3::SkyMaterialData *>(material_storage->material_get_data(sky_material, RS::SHADER_SKY)); + } + + ERR_FAIL_COND(!material); + + shader_data = material->shader_data; + + ERR_FAIL_COND(!shader_data); + + if (shader_data->uses_time && time - sky->prev_time > 0.00001) { + sky->prev_time = time; + sky->reflection_dirty = true; + RenderingServerDefault::redraw_request(); + } + + if (material != sky->prev_material) { + sky->prev_material = material; + sky->reflection_dirty = true; + } + + if (material->uniform_set_updated) { + material->uniform_set_updated = false; + sky->reflection_dirty = true; + } + + if (!p_transform.origin.is_equal_approx(sky->prev_position) && shader_data->uses_position) { + sky->prev_position = p_transform.origin; + sky->reflection_dirty = true; + } + + if (shader_data->uses_light) { + sky_globals.directional_light_count = 0; + for (int i = 0; i < (int)p_lights.size(); i++) { + LightInstance *li = light_instance_owner.get_or_null(p_lights[i]); + if (!li) { + continue; + } + RID base = li->light; + + ERR_CONTINUE(base.is_null()); + + RS::LightType type = light_storage->light_get_type(base); + if (type == RS::LIGHT_DIRECTIONAL && light_storage->light_directional_get_sky_mode(base) != RS::LIGHT_DIRECTIONAL_SKY_MODE_LIGHT_ONLY) { + DirectionalLightData &sky_light_data = sky_globals.directional_lights[sky_globals.directional_light_count]; + Transform3D light_transform = li->transform; + Vector3 world_direction = light_transform.basis.xform(Vector3(0, 0, 1)).normalized(); + + sky_light_data.direction[0] = world_direction.x; + sky_light_data.direction[1] = world_direction.y; + sky_light_data.direction[2] = world_direction.z; + + float sign = light_storage->light_is_negative(base) ? -1 : 1; + sky_light_data.energy = sign * light_storage->light_get_param(base, RS::LIGHT_PARAM_ENERGY); + + Color linear_col = light_storage->light_get_color(base); + sky_light_data.color[0] = linear_col.r; + sky_light_data.color[1] = linear_col.g; + sky_light_data.color[2] = linear_col.b; + + sky_light_data.enabled = true; + + float angular_diameter = light_storage->light_get_param(base, RS::LIGHT_PARAM_SIZE); + if (angular_diameter > 0.0) { + angular_diameter = Math::tan(Math::deg2rad(angular_diameter)); + } else { + angular_diameter = 0.0; + } + sky_light_data.size = angular_diameter; + sky_globals.directional_light_count++; + if (sky_globals.directional_light_count >= sky_globals.max_directional_lights) { + break; + } + } + } + // Check whether the directional_light_buffer changes + bool light_data_dirty = false; + + // Light buffer is dirty if we have fewer or more lights + // If we have fewer lights, make sure that old lights are disabled + if (sky_globals.directional_light_count != sky_globals.last_frame_directional_light_count) { + light_data_dirty = true; + for (uint32_t i = sky_globals.directional_light_count; i < sky_globals.max_directional_lights; i++) { + sky_globals.directional_lights[i].enabled = false; + } + } + + if (!light_data_dirty) { + for (uint32_t i = 0; i < sky_globals.directional_light_count; i++) { + if (sky_globals.directional_lights[i].direction[0] != sky_globals.last_frame_directional_lights[i].direction[0] || + sky_globals.directional_lights[i].direction[1] != sky_globals.last_frame_directional_lights[i].direction[1] || + sky_globals.directional_lights[i].direction[2] != sky_globals.last_frame_directional_lights[i].direction[2] || + sky_globals.directional_lights[i].energy != sky_globals.last_frame_directional_lights[i].energy || + sky_globals.directional_lights[i].color[0] != sky_globals.last_frame_directional_lights[i].color[0] || + sky_globals.directional_lights[i].color[1] != sky_globals.last_frame_directional_lights[i].color[1] || + sky_globals.directional_lights[i].color[2] != sky_globals.last_frame_directional_lights[i].color[2] || + sky_globals.directional_lights[i].enabled != sky_globals.last_frame_directional_lights[i].enabled || + sky_globals.directional_lights[i].size != sky_globals.last_frame_directional_lights[i].size) { + light_data_dirty = true; + break; + } + } + } + + if (light_data_dirty) { + glBindBufferBase(GL_UNIFORM_BUFFER, SKY_DIRECTIONAL_LIGHT_UNIFORM_LOCATION, sky_globals.directional_light_buffer); + glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(DirectionalLightData) * sky_globals.directional_light_count, sky_globals.directional_lights); + glBindBuffer(GL_UNIFORM_BUFFER, 0); + + DirectionalLightData *temp = sky_globals.last_frame_directional_lights; + sky_globals.last_frame_directional_lights = sky_globals.directional_lights; + sky_globals.directional_lights = temp; + sky_globals.last_frame_directional_light_count = sky_globals.directional_light_count; + sky->reflection_dirty = true; + } + } + + if (!sky->radiance) { + _update_dirty_skys(); + } + } +} + void RasterizerSceneGLES3::_draw_sky(Environment *p_env, const CameraMatrix &p_projection, const Transform3D &p_transform) { GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); ERR_FAIL_COND(!p_env); @@ -686,8 +911,6 @@ void RasterizerSceneGLES3::_draw_sky(Environment *p_env, const CameraMatrix &p_p ERR_FAIL_COND(!shader_data); - //glBindBufferBase(GL_UNIFORM_BUFFER, 2, p_sky.directional light data); // Directional light data - // Camera CameraMatrix camera; @@ -709,11 +932,178 @@ void RasterizerSceneGLES3::_draw_sky(Environment *p_env, const CameraMatrix &p_p GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::PROJECTION, camera.matrix[2][0], camera.matrix[0][0], camera.matrix[2][1], camera.matrix[1][1], shader_data->version, SkyShaderGLES3::MODE_BACKGROUND); GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::POSITION, p_transform.origin, shader_data->version, SkyShaderGLES3::MODE_BACKGROUND); GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::TIME, time, shader_data->version, SkyShaderGLES3::MODE_BACKGROUND); - // Bind a vertex array or else OpenGL complains. We won't actually use it - glBindVertexArray(sky_globals.quad_array); + + glBindVertexArray(sky_globals.screen_triangle_array); glDrawArrays(GL_TRIANGLES, 0, 3); } +void RasterizerSceneGLES3::_update_sky_radiance(Environment *p_env, const CameraMatrix &p_projection, const Transform3D &p_transform) { + GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); + ERR_FAIL_COND(!p_env); + + Sky *sky = sky_owner.get_or_null(p_env->sky); + ERR_FAIL_COND(!sky); + + GLES3::SkyMaterialData *material_data = nullptr; + RID sky_material; + + RS::EnvironmentBG background = p_env->background; + + if (sky) { + ERR_FAIL_COND(!sky); + sky_material = sky->material; + + if (sky_material.is_valid()) { + material_data = static_cast<GLES3::SkyMaterialData *>(material_storage->material_get_data(sky_material, RS::SHADER_SKY)); + if (!material_data || !material_data->shader_data->valid) { + material_data = nullptr; + } + } + + if (!material_data) { + sky_material = sky_globals.default_material; + material_data = static_cast<GLES3::SkyMaterialData *>(material_storage->material_get_data(sky_material, RS::SHADER_SKY)); + } + } else if (background == RS::ENV_BG_CLEAR_COLOR || background == RS::ENV_BG_COLOR) { + sky_material = sky_globals.fog_material; + material_data = static_cast<GLES3::SkyMaterialData *>(material_storage->material_get_data(sky_material, RS::SHADER_SKY)); + } + + ERR_FAIL_COND(!material_data); + material_data->bind_uniforms(); + + GLES3::SkyShaderData *shader_data = material_data->shader_data; + + ERR_FAIL_COND(!shader_data); + + bool update_single_frame = sky->mode == RS::SKY_MODE_REALTIME || sky->mode == RS::SKY_MODE_QUALITY; + RS::SkyMode sky_mode = sky->mode; + + if (sky_mode == RS::SKY_MODE_AUTOMATIC) { + if (shader_data->uses_time || shader_data->uses_position) { + update_single_frame = true; + sky_mode = RS::SKY_MODE_REALTIME; + } else if (shader_data->uses_light || shader_data->ubo_size > 0) { + update_single_frame = false; + sky_mode = RS::SKY_MODE_INCREMENTAL; + } else { + update_single_frame = true; + sky_mode = RS::SKY_MODE_QUALITY; + } + } + + if (sky->processing_layer == 0 && sky_mode == RS::SKY_MODE_INCREMENTAL) { + // On the first frame after creating sky, rebuild in single frame + update_single_frame = true; + sky_mode = RS::SKY_MODE_QUALITY; + } + + int max_processing_layer = sky->mipmap_count; + + // Update radiance cubemap + if (sky->reflection_dirty && (sky->processing_layer >= max_processing_layer || update_single_frame)) { + static const Vector3 view_normals[6] = { + Vector3(+1, 0, 0), + Vector3(-1, 0, 0), + Vector3(0, +1, 0), + Vector3(0, -1, 0), + Vector3(0, 0, +1), + Vector3(0, 0, -1) + }; + static const Vector3 view_up[6] = { + Vector3(0, -1, 0), + Vector3(0, -1, 0), + Vector3(0, 0, +1), + Vector3(0, 0, -1), + Vector3(0, -1, 0), + Vector3(0, -1, 0) + }; + + CameraMatrix cm; + cm.set_perspective(90, 1, 0.01, 10.0); + CameraMatrix correction; + correction.set_depth_correction(true); + cm = correction * cm; + + GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_bind_shader(shader_data->version, SkyShaderGLES3::MODE_CUBEMAP); + + GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::POSITION, p_transform.origin, shader_data->version, SkyShaderGLES3::MODE_CUBEMAP); + GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::TIME, time, shader_data->version, SkyShaderGLES3::MODE_CUBEMAP); + GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::PROJECTION, cm.matrix[2][0], cm.matrix[0][0], cm.matrix[2][1], cm.matrix[1][1], shader_data->version, SkyShaderGLES3::MODE_CUBEMAP); + + // Bind a vertex array or else OpenGL complains. We won't actually use it + glBindVertexArray(sky_globals.screen_triangle_array); + + glViewport(0, 0, sky->radiance_size, sky->radiance_size); + glBindFramebuffer(GL_FRAMEBUFFER, sky->radiance_framebuffer); + + for (int i = 0; i < 6; i++) { + Basis local_view = Basis::looking_at(view_normals[i], view_up[i]); + GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::ORIENTATION, local_view, shader_data->version, SkyShaderGLES3::MODE_CUBEMAP); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, sky->raw_radiance, 0); + glDrawArrays(GL_TRIANGLES, 0, 3); + } + + if (update_single_frame) { + for (int i = 0; i < max_processing_layer; i++) { + _filter_sky_radiance(sky, i); + } + } else { + _filter_sky_radiance(sky, 0); //Just copy over the first mipmap + } + sky->processing_layer = 1; + + sky->reflection_dirty = false; + } else { + if (sky_mode == RS::SKY_MODE_INCREMENTAL && sky->processing_layer < max_processing_layer) { + _filter_sky_radiance(sky, sky->processing_layer); + sky->processing_layer++; + } + } +} + +void RasterizerSceneGLES3::_filter_sky_radiance(Sky *p_sky, int p_base_layer) { + GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_CUBE_MAP, p_sky->raw_radiance); + glBindFramebuffer(GL_FRAMEBUFFER, p_sky->radiance_framebuffer); + + CubemapFilterShaderGLES3::ShaderVariant mode = CubemapFilterShaderGLES3::MODE_DEFAULT; + + if (p_base_layer == 0) { + glGenerateMipmap(GL_TEXTURE_CUBE_MAP); + mode = CubemapFilterShaderGLES3::MODE_COPY; + + //Copy over base layer + } + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, sky_globals.radical_inverse_vdc_cache_tex); + + int size = p_sky->radiance_size >> p_base_layer; + glViewport(0, 0, size, size); + glBindVertexArray(sky_globals.screen_triangle_array); + + material_storage->shaders.cubemap_filter_shader.version_bind_shader(scene_globals.cubemap_filter_shader_version, mode); + material_storage->shaders.cubemap_filter_shader.version_set_uniform(CubemapFilterShaderGLES3::SAMPLE_COUNT, sky_globals.ggx_samples, scene_globals.cubemap_filter_shader_version, mode); + material_storage->shaders.cubemap_filter_shader.version_set_uniform(CubemapFilterShaderGLES3::ROUGHNESS, float(p_base_layer) / (p_sky->mipmap_count - 1.0), scene_globals.cubemap_filter_shader_version, mode); + material_storage->shaders.cubemap_filter_shader.version_set_uniform(CubemapFilterShaderGLES3::FACE_SIZE, float(size), scene_globals.cubemap_filter_shader_version, mode); + + for (int i = 0; i < 6; i++) { + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, p_sky->radiance, p_base_layer); +#ifdef DEBUG_ENABLED + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + ERR_CONTINUE(status != GL_FRAMEBUFFER_COMPLETE); +#endif + material_storage->shaders.cubemap_filter_shader.version_set_uniform(CubemapFilterShaderGLES3::FACE_ID, i, scene_globals.cubemap_filter_shader_version, mode); + + glDrawArrays(GL_TRIANGLES, 0, 3); + } + glBindVertexArray(0); + glViewport(0, 0, p_sky->screen_size.x, p_sky->screen_size.y); + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + Ref<Image> RasterizerSceneGLES3::sky_bake_panorama(RID p_sky, float p_energy, bool p_bake_irradiance, const Size2i &p_size) { return Ref<Image>(); } @@ -941,13 +1331,29 @@ void RasterizerSceneGLES3::directional_shadow_quality_set(RS::ShadowQuality p_qu } RID RasterizerSceneGLES3::light_instance_create(RID p_light) { - return RID(); + RID li = light_instance_owner.make_rid(LightInstance()); + + LightInstance *light_instance = light_instance_owner.get_or_null(li); + + light_instance->self = li; + light_instance->light = p_light; + light_instance->light_type = RSG::light_storage->light_get_type(p_light); + + return li; } void RasterizerSceneGLES3::light_instance_set_transform(RID p_light_instance, const Transform3D &p_transform) { + LightInstance *light_instance = light_instance_owner.get_or_null(p_light_instance); + ERR_FAIL_COND(!light_instance); + + light_instance->transform = p_transform; } void RasterizerSceneGLES3::light_instance_set_aabb(RID p_light_instance, const AABB &p_aabb) { + LightInstance *light_instance = light_instance_owner.get_or_null(p_light_instance); + ERR_FAIL_COND(!light_instance); + + light_instance->aabb = p_aabb; } void RasterizerSceneGLES3::light_instance_set_shadow_transform(RID p_light_instance, const CameraMatrix &p_projection, const Transform3D &p_transform, float p_far, float p_split, int p_pass, float p_shadow_texel_size, float p_bias_scale, float p_range_begin, const Vector2 &p_uv_scale) { @@ -1090,8 +1496,22 @@ void RasterizerSceneGLES3::_fill_render_list(RenderListType p_render_list, const flags |= INSTANCE_DATA_FLAGS_NON_UNIFORM_SCALE; } - //Process lights here, determine if they need extra passes + // Sets the index values for lookup in the shader + // This has to be done after _setup_lights was called this frame + // TODO, check shadow status of lights here, if using shadows, skip here and add below if (p_pass_mode == PASS_MODE_COLOR) { + if (inst->omni_light_count) { + inst->omni_light_gl_cache.resize(inst->omni_light_count); + for (uint32_t j = 0; j < inst->omni_light_count; j++) { + inst->omni_light_gl_cache[j] = light_instance_get_gl_id(inst->omni_lights[j]); + } + } + if (inst->spot_light_count) { + inst->spot_light_gl_cache.resize(inst->spot_light_count); + for (uint32_t j = 0; j < inst->spot_light_count; j++) { + inst->spot_light_gl_cache[j] = light_instance_get_gl_id(inst->spot_lights[j]); + } + } } inst->flags_cache = flags; @@ -1197,6 +1617,7 @@ void RasterizerSceneGLES3::_fill_render_list(RenderListType p_render_list, const } } +// Needs to be called after _setup_lights so that directional_light_count is accurate. void RasterizerSceneGLES3::_setup_environment(const RenderDataGLES3 *p_render_data, bool p_no_fog, const Size2i &p_screen_size, bool p_flip_y, const Color &p_default_bg_color, bool p_pancake_shadows) { CameraMatrix correction; correction.set_depth_correction(p_flip_y); @@ -1205,15 +1626,13 @@ void RasterizerSceneGLES3::_setup_environment(const RenderDataGLES3 *p_render_da RasterizerStorageGLES3::store_camera(projection, scene_state.ubo.projection_matrix); RasterizerStorageGLES3::store_camera(projection.inverse(), scene_state.ubo.inv_projection_matrix); RasterizerStorageGLES3::store_transform(p_render_data->cam_transform, scene_state.ubo.inv_view_matrix); - RasterizerStorageGLES3::store_transform(p_render_data->cam_transform.affine_inverse(), scene_state.ubo.view_matrix); + RasterizerStorageGLES3::store_transform(p_render_data->inv_cam_transform, scene_state.ubo.view_matrix); - scene_state.ubo.directional_light_count = 1; + scene_state.ubo.directional_light_count = p_render_data->directional_light_count; scene_state.ubo.z_far = p_render_data->z_far; scene_state.ubo.z_near = p_render_data->z_near; - scene_state.ubo.pancake_shadows = p_pancake_shadows; - scene_state.ubo.viewport_size[0] = p_screen_size.x; scene_state.ubo.viewport_size[1] = p_screen_size.y; @@ -1242,6 +1661,8 @@ void RasterizerSceneGLES3::_setup_environment(const RenderDataGLES3 *p_render_da scene_state.ubo.ambient_light_color_energy[0] = color.r * bg_energy; scene_state.ubo.ambient_light_color_energy[1] = color.g * bg_energy; scene_state.ubo.ambient_light_color_energy[2] = color.b * bg_energy; + scene_state.ubo.use_ambient_light = true; + scene_state.ubo.use_ambient_cubemap = false; } else { float energy = env->ambient_light_energy; Color color = env->ambient_light; @@ -1253,6 +1674,16 @@ void RasterizerSceneGLES3::_setup_environment(const RenderDataGLES3 *p_render_da Basis sky_transform = env->sky_orientation; sky_transform = sky_transform.inverse() * p_render_data->cam_transform.basis; RasterizerStorageGLES3::store_transform_3x3(sky_transform, scene_state.ubo.radiance_inverse_xform); + scene_state.ubo.use_ambient_cubemap = (ambient_src == RS::ENV_AMBIENT_SOURCE_BG && env_bg == RS::ENV_BG_SKY) || ambient_src == RS::ENV_AMBIENT_SOURCE_SKY; + scene_state.ubo.use_ambient_light = scene_state.ubo.use_ambient_cubemap || ambient_src == RS::ENV_AMBIENT_SOURCE_COLOR; + } + + //specular + RS::EnvironmentReflectionSource ref_src = env->reflection_source; + if ((ref_src == RS::ENV_REFLECTION_SOURCE_BG && env_bg == RS::ENV_BG_SKY) || ref_src == RS::ENV_REFLECTION_SOURCE_SKY) { + scene_state.ubo.use_reflection_cubemap = true; + } else { + scene_state.ubo.use_reflection_cubemap = false; } scene_state.ubo.fog_enabled = env->fog_enabled; @@ -1281,6 +1712,210 @@ void RasterizerSceneGLES3::_setup_environment(const RenderDataGLES3 *p_render_da glBindBuffer(GL_UNIFORM_BUFFER, 0); } +// Puts lights into Uniform Buffers. Needs to be called before _fill_list as this caches the index of each light in the Uniform Buffer +void RasterizerSceneGLES3::_setup_lights(const RenderDataGLES3 *p_render_data, bool p_using_shadows, uint32_t &r_directional_light_count, uint32_t &r_omni_light_count, uint32_t &r_spot_light_count) { + GLES3::LightStorage *light_storage = GLES3::LightStorage::get_singleton(); + GLES3::Config *config = GLES3::Config::get_singleton(); + + const Transform3D inverse_transform = p_render_data->inv_cam_transform; + + const PagedArray<RID> &lights = *p_render_data->lights; + + r_directional_light_count = 0; + r_omni_light_count = 0; + r_spot_light_count = 0; + + int num_lights = lights.size(); + + for (int i = 0; i < num_lights; i++) { + LightInstance *li = light_instance_owner.get_or_null(lights[i]); + if (!li) { + continue; + } + RID base = li->light; + + ERR_CONTINUE(base.is_null()); + + RS::LightType type = light_storage->light_get_type(base); + switch (type) { + case RS::LIGHT_DIRECTIONAL: { + if (r_directional_light_count >= RendererSceneRender::MAX_DIRECTIONAL_LIGHTS || light_storage->light_directional_get_sky_mode(base) == RS::LIGHT_DIRECTIONAL_SKY_MODE_SKY_ONLY) { + continue; + } + + DirectionalLightData &light_data = scene_state.directional_lights[r_directional_light_count]; + + Transform3D light_transform = li->transform; + + Vector3 direction = inverse_transform.basis.xform(light_transform.basis.xform(Vector3(0, 0, 1))).normalized(); + + light_data.direction[0] = direction.x; + light_data.direction[1] = direction.y; + light_data.direction[2] = direction.z; + + float sign = light_storage->light_is_negative(base) ? -1 : 1; + + light_data.energy = sign * light_storage->light_get_param(base, RS::LIGHT_PARAM_ENERGY) * Math_PI; + + Color linear_col = light_storage->light_get_color(base).srgb_to_linear(); + light_data.color[0] = linear_col.r; + light_data.color[1] = linear_col.g; + light_data.color[2] = linear_col.b; + + float size = light_storage->light_get_param(base, RS::LIGHT_PARAM_SIZE); + light_data.size = 1.0 - Math::cos(Math::deg2rad(size)); //angle to cosine offset + + light_data.specular = light_storage->light_get_param(base, RS::LIGHT_PARAM_SPECULAR); + + r_directional_light_count++; + } break; + case RS::LIGHT_OMNI: { + if (r_omni_light_count >= (uint32_t)config->max_renderable_lights) { + continue; + } + + const real_t distance = p_render_data->cam_transform.origin.distance_to(li->transform.origin); + + if (light_storage->light_is_distance_fade_enabled(li->light)) { + const float fade_begin = light_storage->light_get_distance_fade_begin(li->light); + const float fade_length = light_storage->light_get_distance_fade_length(li->light); + + if (distance > fade_begin) { + if (distance > fade_begin + fade_length) { + // Out of range, don't draw this light to improve performance. + continue; + } + } + } + + li->gl_id = r_omni_light_count; + + scene_state.omni_light_sort[r_omni_light_count].instance = li; + scene_state.omni_light_sort[r_omni_light_count].depth = distance; + r_omni_light_count++; + } break; + case RS::LIGHT_SPOT: { + if (r_spot_light_count >= (uint32_t)config->max_renderable_lights) { + continue; + } + + const real_t distance = p_render_data->cam_transform.origin.distance_to(li->transform.origin); + + if (light_storage->light_is_distance_fade_enabled(li->light)) { + const float fade_begin = light_storage->light_get_distance_fade_begin(li->light); + const float fade_length = light_storage->light_get_distance_fade_length(li->light); + + if (distance > fade_begin) { + if (distance > fade_begin + fade_length) { + // Out of range, don't draw this light to improve performance. + continue; + } + } + } + + li->gl_id = r_spot_light_count; + + scene_state.spot_light_sort[r_spot_light_count].instance = li; + scene_state.spot_light_sort[r_spot_light_count].depth = distance; + r_spot_light_count++; + } break; + } + } + + if (r_omni_light_count) { + SortArray<InstanceSort<LightInstance>> sorter; + sorter.sort(scene_state.omni_light_sort, r_omni_light_count); + } + + if (r_spot_light_count) { + SortArray<InstanceSort<LightInstance>> sorter; + sorter.sort(scene_state.spot_light_sort, r_spot_light_count); + } + + for (uint32_t i = 0; i < (r_omni_light_count + r_spot_light_count); i++) { + uint32_t index = (i < r_omni_light_count) ? i : i - (r_omni_light_count); + LightData &light_data = (i < r_omni_light_count) ? scene_state.omni_lights[index] : scene_state.spot_lights[index]; + //RS::LightType type = (i < omni_light_count) ? RS::LIGHT_OMNI : RS::LIGHT_SPOT; + LightInstance *li = (i < r_omni_light_count) ? scene_state.omni_light_sort[index].instance : scene_state.spot_light_sort[index].instance; + RID base = li->light; + + Transform3D light_transform = li->transform; + Vector3 pos = inverse_transform.xform(light_transform.origin); + + light_data.position[0] = pos.x; + light_data.position[1] = pos.y; + light_data.position[2] = pos.z; + + float radius = MAX(0.001, light_storage->light_get_param(base, RS::LIGHT_PARAM_RANGE)); + light_data.inv_radius = 1.0 / radius; + + Vector3 direction = inverse_transform.basis.xform(light_transform.basis.xform(Vector3(0, 0, -1))).normalized(); + + light_data.direction[0] = direction.x; + light_data.direction[1] = direction.y; + light_data.direction[2] = direction.z; + + float size = light_storage->light_get_param(base, RS::LIGHT_PARAM_SIZE); + + light_data.size = size; + + float sign = light_storage->light_is_negative(base) ? -1 : 1; + Color linear_col = light_storage->light_get_color(base).srgb_to_linear(); + + // Reuse fade begin, fade length and distance for shadow LOD determination later. + float fade_begin = 0.0; + float fade_length = 0.0; + real_t distance = 0.0; + + float fade = 1.0; + if (light_storage->light_is_distance_fade_enabled(li->light)) { + fade_begin = light_storage->light_get_distance_fade_begin(li->light); + fade_length = light_storage->light_get_distance_fade_length(li->light); + distance = p_render_data->cam_transform.origin.distance_to(li->transform.origin); + + if (distance > fade_begin) { + // Use `smoothstep()` to make opacity changes more gradual and less noticeable to the player. + fade = Math::smoothstep(0.0f, 1.0f, 1.0f - float(distance - fade_begin) / fade_length); + } + } + + float energy = sign * light_storage->light_get_param(base, RS::LIGHT_PARAM_ENERGY) * Math_PI * fade; + + light_data.color[0] = linear_col.r * energy; + light_data.color[1] = linear_col.g * energy; + light_data.color[2] = linear_col.b * energy; + + light_data.attenuation = light_storage->light_get_param(base, RS::LIGHT_PARAM_ATTENUATION); + + light_data.inv_spot_attenuation = 1.0f / light_storage->light_get_param(base, RS::LIGHT_PARAM_SPOT_ATTENUATION); + + float spot_angle = light_storage->light_get_param(base, RS::LIGHT_PARAM_SPOT_ANGLE); + light_data.cos_spot_angle = Math::cos(Math::deg2rad(spot_angle)); + + light_data.specular_amount = light_storage->light_get_param(base, RS::LIGHT_PARAM_SPECULAR) * 2.0; + + light_data.shadow_enabled = false; + } + + // TODO, to avoid stalls, should rotate between 3 buffers based on frame index. + // TODO, consider mapping the buffer as in 2D + if (r_omni_light_count) { + glBindBufferBase(GL_UNIFORM_BUFFER, SCENE_OMNILIGHT_UNIFORM_LOCATION, scene_state.omni_light_buffer); + glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(LightData) * r_omni_light_count, scene_state.omni_lights); + } + + if (r_spot_light_count) { + glBindBufferBase(GL_UNIFORM_BUFFER, SCENE_SPOTLIGHT_UNIFORM_LOCATION, scene_state.spot_light_buffer); + glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(LightData) * r_spot_light_count, scene_state.spot_lights); + } + + if (r_directional_light_count) { + glBindBufferBase(GL_UNIFORM_BUFFER, SCENE_DIRECTIONAL_LIGHT_UNIFORM_LOCATION, scene_state.directional_light_buffer); + glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(DirectionalLightData) * r_directional_light_count, scene_state.directional_lights); + } + glBindBuffer(GL_UNIFORM_BUFFER, 0); +} + void RasterizerSceneGLES3::render_scene(RID p_render_buffers, const CameraData *p_camera_data, const PagedArray<GeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, const PagedArray<RID> &p_fog_volumes, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data, RendererScene::RenderInfo *r_render_info) { GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); GLES3::Config *config = GLES3::Config::get_singleton(); @@ -1300,6 +1935,7 @@ void RasterizerSceneGLES3::render_scene(RID p_render_buffers, const CameraData * render_data.transparent_bg = rb->is_transparent; // Our first camera is used by default render_data.cam_transform = p_camera_data->main_transform; + render_data.inv_cam_transform = render_data.cam_transform.affine_inverse(); render_data.cam_projection = p_camera_data->main_projection; render_data.view_projection[0] = p_camera_data->main_projection; render_data.cam_orthogonal = p_camera_data->is_orthogonal; @@ -1317,8 +1953,6 @@ void RasterizerSceneGLES3::render_scene(RID p_render_buffers, const CameraData * render_data.reflection_probes = &p_reflection_probes; render_data.environment = p_environment; render_data.camera_effects = p_camera_effects; - render_data.shadow_atlas = p_shadow_atlas; - render_data.reflection_atlas = p_reflection_atlas; render_data.reflection_probe = p_reflection_probe; render_data.reflection_probe_pass = p_reflection_probe_pass; @@ -1365,8 +1999,10 @@ void RasterizerSceneGLES3::render_scene(RID p_render_buffers, const CameraData * screen_size.x = rb->width; screen_size.y = rb->height; + bool use_wireframe = get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME; + SceneState::TonemapUBO tonemap_ubo; - if (is_environment(p_environment)) { + if (env) { tonemap_ubo.exposure = env->exposure; tonemap_ubo.white = env->white; tonemap_ubo.tonemapper = int32_t(env->tone_mapper); @@ -1379,12 +2015,79 @@ void RasterizerSceneGLES3::render_scene(RID p_render_buffers, const CameraData * glBindBufferBase(GL_UNIFORM_BUFFER, SCENE_TONEMAP_UNIFORM_LOCATION, scene_state.tonemap_buffer); glBufferData(GL_UNIFORM_BUFFER, sizeof(SceneState::TonemapUBO), &tonemap_ubo, GL_STREAM_DRAW); + _setup_lights(&render_data, false, render_data.directional_light_count, render_data.omni_light_count, render_data.spot_light_count); _setup_environment(&render_data, render_data.reflection_probe.is_valid(), screen_size, !render_data.reflection_probe.is_valid(), clear_color, false); _fill_render_list(RENDER_LIST_OPAQUE, &render_data, PASS_MODE_COLOR); render_list[RENDER_LIST_OPAQUE].sort_by_key(); render_list[RENDER_LIST_ALPHA].sort_by_reverse_depth_and_priority(); + bool draw_sky = false; + bool draw_sky_fog_only = false; + bool keep_color = false; + + if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_OVERDRAW) { + clear_color = Color(0, 0, 0, 1); //in overdraw mode, BG should always be black + } else if (env) { + RS::EnvironmentBG bg_mode = env->background; + float bg_energy = env->bg_energy; + switch (bg_mode) { + case RS::ENV_BG_CLEAR_COLOR: { + clear_color.r *= bg_energy; + clear_color.g *= bg_energy; + clear_color.b *= bg_energy; + if (env->fog_enabled) { + draw_sky_fog_only = true; + GLES3::MaterialStorage::get_singleton()->material_set_param(sky_globals.fog_material, "clear_color", Variant(clear_color)); + } + } break; + case RS::ENV_BG_COLOR: { + clear_color = env->bg_color; + clear_color.r *= bg_energy; + clear_color.g *= bg_energy; + clear_color.b *= bg_energy; + if (env->fog_enabled) { + draw_sky_fog_only = true; + GLES3::MaterialStorage::get_singleton()->material_set_param(sky_globals.fog_material, "clear_color", Variant(clear_color)); + } + } break; + case RS::ENV_BG_SKY: { + draw_sky = true; + } break; + case RS::ENV_BG_CANVAS: { + keep_color = true; + } break; + case RS::ENV_BG_KEEP: { + keep_color = true; + } break; + case RS::ENV_BG_CAMERA_FEED: { + } break; + default: { + } + } + // setup sky if used for ambient, reflections, or background + if (draw_sky || draw_sky_fog_only || env->reflection_source == RS::ENV_REFLECTION_SOURCE_SKY || env->ambient_source == RS::ENV_AMBIENT_SOURCE_SKY) { + RENDER_TIMESTAMP("Setup Sky"); + CameraMatrix projection = render_data.cam_projection; + if (render_data.reflection_probe.is_valid()) { + CameraMatrix correction; + correction.set_depth_correction(true); + projection = correction * render_data.cam_projection; + } + + _setup_sky(env, p_render_buffers, *render_data.lights, projection, render_data.cam_transform, screen_size); + + if (env->sky.is_valid()) { + if (env->reflection_source == RS::ENV_REFLECTION_SOURCE_SKY || env->ambient_source == RS::ENV_AMBIENT_SOURCE_SKY || (env->reflection_source == RS::ENV_REFLECTION_SOURCE_BG && env->background == RS::ENV_BG_SKY)) { + _update_sky_radiance(env, projection, render_data.cam_transform); + } + } else { + // do not try to draw sky if invalid + draw_sky = false; + } + } + } + glBindFramebuffer(GL_FRAMEBUFFER, rb->framebuffer); glViewport(0, 0, rb->width, rb->height); @@ -1395,6 +2098,7 @@ void RasterizerSceneGLES3::render_scene(RID p_render_buffers, const CameraData * use_depth_prepass = use_depth_prepass && get_debug_draw_mode() != RS::VIEWPORT_DEBUG_DRAW_OVERDRAW; if (use_depth_prepass) { + RENDER_TIMESTAMP("Depth Prepass"); //pre z pass glDisable(GL_BLEND); @@ -1410,9 +2114,7 @@ void RasterizerSceneGLES3::render_scene(RID p_render_buffers, const CameraData * glClearDepth(1.0f); glClear(GL_DEPTH_BUFFER_BIT); - uint32_t spec_constant_base_flags = 0; - - RenderListParameters render_list_params(render_list[RENDER_LIST_OPAQUE].elements.ptr(), render_list[RENDER_LIST_OPAQUE].elements.size(), reverse_cull, spec_constant_base_flags, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, render_data.lod_camera_plane, render_data.lod_distance_multiplier, render_data.screen_mesh_lod_threshold); + RenderListParameters render_list_params(render_list[RENDER_LIST_OPAQUE].elements.ptr(), render_list[RENDER_LIST_OPAQUE].elements.size(), reverse_cull, 0, use_wireframe); _render_list_template<PASS_MODE_DEPTH>(&render_list_params, &render_data, 0, render_list[RENDER_LIST_OPAQUE].elements.size()); glColorMask(1, 1, 1, 1); @@ -1429,7 +2131,7 @@ void RasterizerSceneGLES3::render_scene(RID p_render_buffers, const CameraData * glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); } else { - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE); glDisable(GL_BLEND); } scene_state.current_blend_mode = GLES3::SceneShaderData::BLEND_MODE_MIX; @@ -1445,54 +2147,29 @@ void RasterizerSceneGLES3::render_scene(RID p_render_buffers, const CameraData * glClear(GL_DEPTH_BUFFER_BIT); } - bool draw_sky = false; - bool keep_color = false; - - if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_OVERDRAW) { - clear_color = Color(0, 0, 0, 1); //in overdraw mode, BG should always be black - } else if (is_environment(p_environment)) { - RS::EnvironmentBG bg_mode = environment_get_background(p_environment); - float bg_energy = env->bg_energy; //environment_get_bg_energy(p_environment); - switch (bg_mode) { - case RS::ENV_BG_CLEAR_COLOR: { - clear_color.r *= bg_energy; - clear_color.g *= bg_energy; - clear_color.b *= bg_energy; - } break; - case RS::ENV_BG_COLOR: { - clear_color = env->bg_color; //environment_get_bg_color(p_environment); - clear_color.r *= bg_energy; - clear_color.g *= bg_energy; - clear_color.b *= bg_energy; - } break; - case RS::ENV_BG_SKY: { - draw_sky = true; - } break; - case RS::ENV_BG_CANVAS: { - keep_color = true; - } break; - case RS::ENV_BG_KEEP: { - keep_color = true; - } break; - case RS::ENV_BG_CAMERA_FEED: { - } break; - default: { - } - } - // Draw sky cubemap - } - if (!keep_color) { glClearBufferfv(GL_COLOR, 0, clear_color.components); } - + RENDER_TIMESTAMP("Render Opaque Pass"); uint32_t spec_constant_base_flags = 0; - //Render Opaque Objects - RenderListParameters render_list_params(render_list[RENDER_LIST_OPAQUE].elements.ptr(), render_list[RENDER_LIST_OPAQUE].elements.size(), reverse_cull, spec_constant_base_flags, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, render_data.lod_camera_plane, render_data.lod_distance_multiplier, render_data.screen_mesh_lod_threshold); + + { + // Specialization Constants that apply for entire rendering pass. + if (render_data.directional_light_count == 0) { + spec_constant_base_flags |= 1 << SPEC_CONSTANT_DISABLE_DIRECTIONAL_LIGHTS; + } + + if (!env || (env && !env->fog_enabled)) { + spec_constant_base_flags |= 1 << SPEC_CONSTANT_DISABLE_FOG; + } + } + // Render Opaque Objects. + RenderListParameters render_list_params(render_list[RENDER_LIST_OPAQUE].elements.ptr(), render_list[RENDER_LIST_OPAQUE].elements.size(), reverse_cull, spec_constant_base_flags, use_wireframe); _render_list_template<PASS_MODE_COLOR>(&render_list_params, &render_data, 0, render_list[RENDER_LIST_OPAQUE].elements.size()); if (draw_sky) { + RENDER_TIMESTAMP("Render Sky"); if (scene_state.current_depth_test != GLES3::SceneShaderData::DEPTH_TEST_ENABLED) { glEnable(GL_DEPTH_TEST); scene_state.current_depth_test = GLES3::SceneShaderData::DEPTH_TEST_ENABLED; @@ -1509,19 +2186,15 @@ void RasterizerSceneGLES3::render_scene(RID p_render_buffers, const CameraData * _draw_sky(env, render_data.cam_projection, render_data.cam_transform); } + RENDER_TIMESTAMP("Render 3D Transparent Pass"); glEnable(GL_BLEND); //Render transparent pass - RenderListParameters render_list_params_alpha(render_list[RENDER_LIST_ALPHA].elements.ptr(), render_list[RENDER_LIST_ALPHA].elements.size(), reverse_cull, spec_constant_base_flags, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, render_data.lod_camera_plane, render_data.lod_distance_multiplier, render_data.screen_mesh_lod_threshold); + RenderListParameters render_list_params_alpha(render_list[RENDER_LIST_ALPHA].elements.ptr(), render_list[RENDER_LIST_ALPHA].elements.size(), reverse_cull, spec_constant_base_flags, use_wireframe); _render_list_template<PASS_MODE_COLOR_TRANSPARENT>(&render_list_params_alpha, &render_data, 0, render_list[RENDER_LIST_ALPHA].elements.size(), true); if (p_render_buffers.is_valid()) { - /* - RENDER_TIMESTAMP("Tonemap"); - _render_buffers_post_process_and_tonemap(&render_data); - */ - _render_buffers_debug_draw(p_render_buffers, p_shadow_atlas, p_occluder_debug_tex); } glDisable(GL_BLEND); @@ -1531,12 +2204,16 @@ void RasterizerSceneGLES3::render_scene(RID p_render_buffers, const CameraData * template <PassMode p_pass_mode> void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, const RenderDataGLES3 *p_render_data, uint32_t p_from_element, uint32_t p_to_element, bool p_alpha_pass) { GLES3::MeshStorage *mesh_storage = GLES3::MeshStorage::get_singleton(); + GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); + GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); + GLES3::Config *config = GLES3::Config::get_singleton(); GLuint prev_vertex_array_gl = 0; GLuint prev_index_array_gl = 0; GLES3::SceneMaterialData *prev_material_data = nullptr; GLES3::SceneShaderData *prev_shader = nullptr; + GeometryInstanceGLES3 *prev_inst = nullptr; SceneShaderGLES3::ShaderVariant shader_variant = SceneShaderGLES3::MODE_COLOR; // Assigned to silence wrong -Wmaybe-initialized. @@ -1553,9 +2230,23 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, } break; } + if (p_pass_mode == PASS_MODE_COLOR || p_pass_mode == PASS_MODE_COLOR_TRANSPARENT) { + Environment *env = environment_owner.get_or_null(p_render_data->environment); + glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 2); + GLuint texture_to_bind = texture_storage->get_texture(texture_storage->texture_gl_get_default(GLES3::DEFAULT_GL_TEXTURE_CUBEMAP_BLACK))->tex_id; + if (env) { + Sky *sky = sky_owner.get_or_null(env->sky); + if (sky && sky->radiance != 0) { + texture_to_bind = sky->radiance; + // base_spec_constant |= USE_RADIANCE_MAP; + } + glBindTexture(GL_TEXTURE_CUBE_MAP, texture_to_bind); + } + } + for (uint32_t i = p_from_element; i < p_to_element; i++) { const GeometryInstanceSurface *surf = p_params->elements[i]; - const GeometryInstanceGLES3 *inst = surf->owner; + GeometryInstanceGLES3 *inst = surf->owner; if (p_pass_mode == PASS_MODE_COLOR && !(surf->flags & GeometryInstanceSurface::FLAG_PASS_OPAQUE)) { continue; // Objects with "Depth-prepass" transparency are included in both render lists, but should only be rendered in the transparent pass @@ -1627,7 +2318,7 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, if (p_render_data->transparent_bg) { glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); } else { - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE); } } break; @@ -1639,6 +2330,7 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, case GLES3::SceneShaderData::BLEND_MODE_SUB: { glBlendEquation(GL_FUNC_REVERSE_SUBTRACT); glBlendFunc(GL_SRC_ALPHA, GL_ONE); + } break; case GLES3::SceneShaderData::BLEND_MODE_MUL: { glBlendEquation(GL_FUNC_ADD); @@ -1718,8 +2410,6 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, prev_index_array_gl = index_array_gl; } - // Update pipeline information here - Transform3D world_transform; if (inst->store_transform_cache) { world_transform = inst->transform; @@ -1727,13 +2417,40 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, if (prev_material_data != material_data) { material_data->bind_uniforms(); + prev_material_data = material_data; } if (prev_shader != shader) { - GLES3::MaterialStorage::get_singleton()->shaders.scene_shader.version_bind_shader(shader->version, shader_variant); + material_storage->shaders.scene_shader.version_bind_shader(shader->version, shader_variant); + float opaque_prepass_threshold = 0.0; + if (p_pass_mode == PASS_MODE_DEPTH) { + opaque_prepass_threshold = 0.99; + } else if (p_pass_mode == PASS_MODE_SHADOW) { + opaque_prepass_threshold = 0.1; + } + + material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::OPAQUE_PREPASS_THRESHOLD, opaque_prepass_threshold, shader->version, shader_variant); + + prev_shader = shader; + } + + if (prev_inst != inst) { + // Rebind the light indices. + material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::OMNI_LIGHT_COUNT, inst->omni_light_count, shader->version, shader_variant); + material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::SPOT_LIGHT_COUNT, inst->spot_light_count, shader->version, shader_variant); + + if (inst->omni_light_count) { + glUniform1uiv(material_storage->shaders.scene_shader.version_get_uniform(SceneShaderGLES3::OMNI_LIGHT_INDICES, shader->version, shader_variant), inst->omni_light_count, inst->omni_light_gl_cache.ptr()); + } + + if (inst->spot_light_count) { + glUniform1uiv(material_storage->shaders.scene_shader.version_get_uniform(SceneShaderGLES3::SPOT_LIGHT_INDICES, shader->version, shader_variant), inst->spot_light_count, inst->spot_light_gl_cache.ptr()); + } + + prev_inst = inst; } - GLES3::MaterialStorage::get_singleton()->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::WORLD_TRANSFORM, world_transform, shader->version, shader_variant); + material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::WORLD_TRANSFORM, world_transform, shader->version, shader_variant); if (use_index_buffer) { glDrawElements(primitive_gl, mesh_storage->mesh_surface_get_vertices_drawn_count(mesh_surface), mesh_storage->mesh_surface_get_index_type(mesh_surface), 0); @@ -1976,7 +2693,7 @@ bool RasterizerSceneGLES3::free(RID p_rid) { } else if (sky_owner.owns(p_rid)) { Sky *sky = sky_owner.get_or_null(p_rid); ERR_FAIL_COND_V(!sky, false); - sky->free(); + _free_sky_data(sky); sky_owner.free(p_rid); } else if (render_buffers_owner.owns(p_rid)) { RenderBuffers *rb = render_buffers_owner.get_or_null(p_rid); @@ -1984,6 +2701,10 @@ bool RasterizerSceneGLES3::free(RID p_rid) { _free_render_buffer_data(rb); render_buffers_owner.free(p_rid); + } else if (light_instance_owner.owns(p_rid)) { + LightInstance *light_instance = light_instance_owner.get_or_null(p_rid); + ERR_FAIL_COND_V(!light_instance, false); + light_instance_owner.free(p_rid); } else { return false; } @@ -2005,11 +2726,55 @@ void RasterizerSceneGLES3::light_projectors_set_filter(RS::LightProjectorFilter RasterizerSceneGLES3::RasterizerSceneGLES3(RasterizerStorageGLES3 *p_storage) { GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); + GLES3::Config *config = GLES3::Config::get_singleton(); + storage = p_storage; { + // Setup Lights + + config->max_renderable_lights = MIN(config->max_renderable_lights, config->max_uniform_buffer_size / (int)sizeof(RasterizerSceneGLES3::LightData)); + config->max_lights_per_object = MIN(config->max_lights_per_object, config->max_renderable_lights); + + uint32_t light_buffer_size = config->max_renderable_lights * sizeof(LightData); + scene_state.omni_lights = memnew_arr(LightData, config->max_renderable_lights); + scene_state.omni_light_sort = memnew_arr(InstanceSort<LightInstance>, config->max_renderable_lights); + glGenBuffers(1, &scene_state.omni_light_buffer); + glBindBuffer(GL_UNIFORM_BUFFER, scene_state.omni_light_buffer); + glBufferData(GL_UNIFORM_BUFFER, light_buffer_size, nullptr, GL_STREAM_DRAW); + + scene_state.spot_lights = memnew_arr(LightData, config->max_renderable_lights); + scene_state.spot_light_sort = memnew_arr(InstanceSort<LightInstance>, config->max_renderable_lights); + glGenBuffers(1, &scene_state.spot_light_buffer); + glBindBuffer(GL_UNIFORM_BUFFER, scene_state.spot_light_buffer); + glBufferData(GL_UNIFORM_BUFFER, light_buffer_size, nullptr, GL_STREAM_DRAW); + + uint32_t directional_light_buffer_size = MAX_DIRECTIONAL_LIGHTS * sizeof(DirectionalLightData); + scene_state.directional_lights = memnew_arr(DirectionalLightData, MAX_DIRECTIONAL_LIGHTS); + glGenBuffers(1, &scene_state.directional_light_buffer); + glBindBuffer(GL_UNIFORM_BUFFER, scene_state.directional_light_buffer); + glBufferData(GL_UNIFORM_BUFFER, directional_light_buffer_size, nullptr, GL_STREAM_DRAW); + glBindBuffer(GL_UNIFORM_BUFFER, 0); + } + + { + sky_globals.max_directional_lights = 4; + uint32_t directional_light_buffer_size = sky_globals.max_directional_lights * sizeof(DirectionalLightData); + sky_globals.directional_lights = memnew_arr(DirectionalLightData, sky_globals.max_directional_lights); + sky_globals.last_frame_directional_lights = memnew_arr(DirectionalLightData, sky_globals.max_directional_lights); + sky_globals.last_frame_directional_light_count = sky_globals.max_directional_lights + 1; + glGenBuffers(1, &sky_globals.directional_light_buffer); + glBindBuffer(GL_UNIFORM_BUFFER, sky_globals.directional_light_buffer); + glBufferData(GL_UNIFORM_BUFFER, directional_light_buffer_size, nullptr, GL_STREAM_DRAW); + glBindBuffer(GL_UNIFORM_BUFFER, 0); + } + + { String global_defines; global_defines += "#define MAX_GLOBAL_VARIABLES 256\n"; // TODO: this is arbitrary for now + global_defines += "\n#define MAX_LIGHT_DATA_STRUCTS " + itos(config->max_renderable_lights) + "\n"; + global_defines += "\n#define MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS " + itos(MAX_DIRECTIONAL_LIGHTS) + "\n"; + global_defines += "\n#define MAX_FORWARD_LIGHTS " + itos(config->max_lights_per_object) + "\n"; material_storage->shaders.scene_shader.initialize(global_defines); scene_globals.shader_default_version = material_storage->shaders.scene_shader.version_create(); material_storage->shaders.scene_shader.version_bind_shader(scene_globals.shader_default_version, SceneShaderGLES3::MODE_COLOR); @@ -2050,6 +2815,10 @@ void fragment() { material_storage->shaders.sky_shader.initialize(global_defines); sky_globals.shader_default_version = material_storage->shaders.sky_shader.version_create(); material_storage->shaders.sky_shader.version_bind_shader(sky_globals.shader_default_version, SkyShaderGLES3::MODE_BACKGROUND); + + material_storage->shaders.cubemap_filter_shader.initialize(); + scene_globals.cubemap_filter_shader_version = material_storage->shaders.cubemap_filter_shader.version_create(); + material_storage->shaders.cubemap_filter_shader.version_bind_shader(scene_globals.cubemap_filter_shader_version, CubemapFilterShaderGLES3::MODE_DEFAULT); } { @@ -2091,59 +2860,95 @@ void sky() { material_storage->material_set_shader(sky_globals.fog_material, sky_globals.fog_shader); } + { - { - //quad buffers - - glGenBuffers(1, &sky_globals.quad); - glBindBuffer(GL_ARRAY_BUFFER, sky_globals.quad); - { - const float qv[16] = { - -1, - -1, - 0, - 0, - -1, - 1, - 0, - 1, - 1, - 1, - 1, - 1, - 1, - -1, - 1, - 0, - }; - - glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 16, qv, GL_STATIC_DRAW); - } + glGenBuffers(1, &sky_globals.screen_triangle); + glBindBuffer(GL_ARRAY_BUFFER, sky_globals.screen_triangle); + + const float qv[6] = { + -1.0f, + -1.0f, + 3.0f, + -1.0f, + -1.0f, + 3.0f, + }; + + glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 6, qv, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind + + glGenVertexArrays(1, &sky_globals.screen_triangle_array); + glBindVertexArray(sky_globals.screen_triangle_array); + glBindBuffer(GL_ARRAY_BUFFER, sky_globals.screen_triangle); + glVertexAttribPointer(RS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, nullptr); + glEnableVertexAttribArray(RS::ARRAY_VERTEX); + glBindVertexArray(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind + } + + // Radical inverse vdc cache texture used for cubemap filtering. + { + glGenTextures(1, &sky_globals.radical_inverse_vdc_cache_tex); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, sky_globals.radical_inverse_vdc_cache_tex); + + uint8_t radical_inverse[512]; - glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind + for (uint32_t i = 0; i < 512; i++) { + uint32_t bits = i; - glGenVertexArrays(1, &sky_globals.quad_array); - glBindVertexArray(sky_globals.quad_array); - glBindBuffer(GL_ARRAY_BUFFER, sky_globals.quad); - glVertexAttribPointer(RS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, nullptr); - glEnableVertexAttribArray(RS::ARRAY_VERTEX); - glVertexAttribPointer(RS::ARRAY_TEX_UV, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, CAST_INT_TO_UCHAR_PTR(8)); - glEnableVertexAttribArray(RS::ARRAY_TEX_UV); - glBindVertexArray(0); - glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind + bits = (bits << 16) | (bits >> 16); + bits = ((bits & 0x55555555) << 1) | ((bits & 0xAAAAAAAA) >> 1); + bits = ((bits & 0x33333333) << 2) | ((bits & 0xCCCCCCCC) >> 2); + bits = ((bits & 0x0F0F0F0F) << 4) | ((bits & 0xF0F0F0F0) >> 4); + bits = ((bits & 0x00FF00FF) << 8) | ((bits & 0xFF00FF00) >> 8); + + float value = float(bits) * 2.3283064365386963e-10; + radical_inverse[i] = uint8_t(CLAMP(value * 255.0, 0, 255)); } + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, 512, 1, 0, GL_RED, GL_UNSIGNED_BYTE, radical_inverse); + 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_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); //need this for proper sampling + + glBindTexture(GL_TEXTURE_2D, 0); } +#ifdef GLES_OVER_GL + glEnable(_EXT_TEXTURE_CUBE_MAP_SEAMLESS); +#endif } RasterizerSceneGLES3::~RasterizerSceneGLES3() { + glDeleteBuffers(1, &scene_state.directional_light_buffer); + glDeleteBuffers(1, &scene_state.omni_light_buffer); + glDeleteBuffers(1, &scene_state.spot_light_buffer); + memdelete_arr(scene_state.directional_lights); + memdelete_arr(scene_state.omni_lights); + memdelete_arr(scene_state.spot_lights); + memdelete_arr(scene_state.omni_light_sort); + memdelete_arr(scene_state.spot_light_sort); + + // Scene Shader GLES3::MaterialStorage::get_singleton()->shaders.scene_shader.version_free(scene_globals.shader_default_version); + GLES3::MaterialStorage::get_singleton()->shaders.cubemap_filter_shader.version_free(scene_globals.cubemap_filter_shader_version); storage->free(scene_globals.default_material); storage->free(scene_globals.default_shader); + + // Sky Shader GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_free(sky_globals.shader_default_version); storage->free(sky_globals.default_material); storage->free(sky_globals.default_shader); storage->free(sky_globals.fog_material); storage->free(sky_globals.fog_shader); + glDeleteBuffers(1, &sky_globals.screen_triangle); + glDeleteVertexArrays(1, &sky_globals.screen_triangle_array); + glDeleteTextures(1, &sky_globals.radical_inverse_vdc_cache_tex); + glDeleteBuffers(1, &sky_globals.directional_light_buffer); + memdelete_arr(sky_globals.directional_lights); + memdelete_arr(sky_globals.last_frame_directional_lights); } #endif // GLES3_ENABLED diff --git a/drivers/gles3/rasterizer_scene_gles3.h b/drivers/gles3/rasterizer_scene_gles3.h index ac2f3c932a..d9a848c0f6 100644 --- a/drivers/gles3/rasterizer_scene_gles3.h +++ b/drivers/gles3/rasterizer_scene_gles3.h @@ -43,6 +43,7 @@ #include "servers/rendering/renderer_scene_render.h" #include "servers/rendering_server.h" #include "shader_gles3.h" +#include "shaders/cubemap_filter.glsl.gen.h" #include "shaders/sky.glsl.gen.h" enum RenderListType { @@ -66,17 +67,26 @@ enum SceneUniformLocation { SCENE_GLOBALS_UNIFORM_LOCATION, SCENE_DATA_UNIFORM_LOCATION, SCENE_MATERIAL_UNIFORM_LOCATION, - SCENE_RADIANCE_UNIFORM_LOCATION, + SCENE_EMPTY, // Unused, put here to avoid conflicts with SKY_DIRECTIONAL_LIGHT_UNIFORM_LOCATION. SCENE_OMNILIGHT_UNIFORM_LOCATION, SCENE_SPOTLIGHT_UNIFORM_LOCATION, + SCENE_DIRECTIONAL_LIGHT_UNIFORM_LOCATION, }; enum SkyUniformLocation { SKY_TONEMAP_UNIFORM_LOCATION, SKY_GLOBALS_UNIFORM_LOCATION, - SKY_SCENE_DATA_UNIFORM_LOCATION, - SKY_DIRECTIONAL_LIGHT_UNIFORM_LOCATION, + SKY_EMPTY, // Unused, put here to avoid conflicts with SCENE_DATA_UNIFORM_LOCATION. SKY_MATERIAL_UNIFORM_LOCATION, + SKY_DIRECTIONAL_LIGHT_UNIFORM_LOCATION, +}; + +enum { + SPEC_CONSTANT_DISABLE_LIGHTMAP = 0, + SPEC_CONSTANT_DISABLE_DIRECTIONAL_LIGHTS = 1, + SPEC_CONSTANT_DISABLE_OMNI_LIGHTS = 2, + SPEC_CONSTANT_DISABLE_SPOT_LIGHTS = 3, + SPEC_CONSTANT_DISABLE_FOG = 4, }; struct RenderDataGLES3 { @@ -84,6 +94,7 @@ struct RenderDataGLES3 { bool transparent_bg = false; Transform3D cam_transform = Transform3D(); + Transform3D inv_cam_transform = Transform3D(); CameraMatrix cam_projection = CameraMatrix(); bool cam_orthogonal = false; @@ -97,14 +108,8 @@ struct RenderDataGLES3 { const PagedArray<RendererSceneRender::GeometryInstance *> *instances = nullptr; const PagedArray<RID> *lights = nullptr; const PagedArray<RID> *reflection_probes = nullptr; - //const PagedArray<RID> *voxel_gi_instances = nullptr; - //const PagedArray<RID> *decals = nullptr; - //const PagedArray<RID> *lightmaps = nullptr; - //const PagedArray<RID> *fog_volumes = nullptr; RID environment = RID(); RID camera_effects = RID(); - RID shadow_atlas = RID(); - RID reflection_atlas = RID(); RID reflection_probe = RID(); int reflection_probe_pass = 0; @@ -113,6 +118,8 @@ struct RenderDataGLES3 { float screen_mesh_lod_threshold = 0.0; uint32_t directional_light_count = 0; + uint32_t spot_light_count = 0; + uint32_t omni_light_count = 0; RendererScene::RenderInfo *render_info = nullptr; }; @@ -126,90 +133,81 @@ private: RS::ViewportDebugDraw debug_draw = RS::VIEWPORT_DEBUG_DRAW_DISABLED; uint64_t scene_pass = 0; - struct SkyGlobals { - RID shader_default_version; - RID default_material; - RID default_shader; - RID fog_material; - RID fog_shader; - GLuint quad = 0; - GLuint quad_array = 0; - uint32_t max_directional_lights = 4; - uint32_t roughness_layers = 8; - uint32_t ggx_samples = 128; - } sky_globals; + template <class T> + struct InstanceSort { + float depth; + T *instance = nullptr; + bool operator<(const InstanceSort &p_sort) const { + return depth < p_sort.depth; + } + }; struct SceneGlobals { RID shader_default_version; RID default_material; RID default_shader; + RID cubemap_filter_shader_version; } scene_globals; - struct SceneState { - struct UBO { - float projection_matrix[16]; - float inv_projection_matrix[16]; - float inv_view_matrix[16]; - float view_matrix[16]; + /* LIGHT INSTANCE */ - float viewport_size[2]; - float screen_pixel_size[2]; + struct LightData { + float position[3]; + float inv_radius; - float ambient_light_color_energy[4]; + float direction[3]; // Only used by SpotLight + float size; - float ambient_color_sky_mix; - uint32_t ambient_flags; - uint32_t material_uv2_mode; - float opaque_prepass_threshold; - //bool use_ambient_light; - //bool use_ambient_cubemap; - //bool use_reflection_cubemap; + float color[3]; + float attenuation; - float radiance_inverse_xform[12]; + float inv_spot_attenuation; + float cos_spot_angle; + float specular_amount; + uint32_t shadow_enabled; + }; + static_assert(sizeof(LightData) % 16 == 0, "LightData size must be a multiple of 16 bytes"); - uint32_t directional_light_count; - float z_far; - float z_near; - uint32_t pancake_shadows; + struct DirectionalLightData { + float direction[3]; + float energy; - uint32_t fog_enabled; - float fog_density; - float fog_height; - float fog_height_density; + float color[3]; + float size; - float fog_light_color[3]; - float fog_sun_scatter; + uint32_t enabled; // For use by SkyShaders + float pad[2]; + float specular; + }; + static_assert(sizeof(DirectionalLightData) % 16 == 0, "DirectionalLightData size must be a multiple of 16 bytes"); - float fog_aerial_perspective; - float time; - uint32_t pad[2]; - }; - static_assert(sizeof(UBO) % 16 == 0, "Scene UBO size must be a multiple of 16 bytes"); + struct LightInstance { + RS::LightType light_type = RS::LIGHT_DIRECTIONAL; - struct TonemapUBO { - float exposure = 1.0; - float white = 1.0; - int32_t tonemapper = 0; - int32_t pad = 0; - }; - static_assert(sizeof(TonemapUBO) % 16 == 0, "Tonemap UBO size must be a multiple of 16 bytes"); + AABB aabb; + RID self; + RID light; + Transform3D transform; - UBO ubo; - GLuint ubo_buffer = 0; - GLuint tonemap_buffer = 0; + Vector3 light_vector; + Vector3 spot_vector; + float linear_att = 0.0; - bool used_depth_prepass = false; + uint64_t shadow_pass = 0; + uint64_t last_scene_pass = 0; + uint64_t last_scene_shadow_pass = 0; + uint64_t last_pass = 0; + uint32_t cull_mask = 0; + uint32_t light_directional_index = 0; - GLES3::SceneShaderData::BlendMode current_blend_mode = GLES3::SceneShaderData::BLEND_MODE_MIX; - GLES3::SceneShaderData::DepthDraw current_depth_draw = GLES3::SceneShaderData::DEPTH_DRAW_OPAQUE; - GLES3::SceneShaderData::DepthTest current_depth_test = GLES3::SceneShaderData::DEPTH_TEST_DISABLED; - GLES3::SceneShaderData::Cull cull_mode = GLES3::SceneShaderData::CULL_BACK; + Rect2 directional_rect; - bool texscreen_copied = false; - bool used_screen_texture = false; - bool used_normal_texture = false; - bool used_depth_texture = false; - } scene_state; + uint32_t gl_id = -1; + + LightInstance() {} + }; + + mutable RID_Owner<LightInstance> light_instance_owner; struct GeometryInstanceGLES3; @@ -295,9 +293,11 @@ private: float parent_fade_alpha = 1.0; uint32_t omni_light_count = 0; - uint32_t omni_lights[8]; + LocalVector<RID> omni_lights; uint32_t spot_light_count = 0; - uint32_t spot_lights[8]; + LocalVector<RID> spot_lights; + LocalVector<uint32_t> omni_light_gl_cache; + LocalVector<uint32_t> spot_light_gl_cache; //used during setup uint32_t base_flags = 0; @@ -360,25 +360,96 @@ private: void _geometry_instance_update(GeometryInstance *p_geometry_instance); void _update_dirty_geometry_instances(); + struct SceneState { + struct UBO { + float projection_matrix[16]; + float inv_projection_matrix[16]; + float inv_view_matrix[16]; + float view_matrix[16]; + + float viewport_size[2]; + float screen_pixel_size[2]; + + float ambient_light_color_energy[4]; + + float ambient_color_sky_mix; + uint32_t material_uv2_mode; + float pad2; + uint32_t use_ambient_light = 0; + + uint32_t use_ambient_cubemap = 0; + uint32_t use_reflection_cubemap = 0; + float fog_aerial_perspective; + float time; + + float radiance_inverse_xform[12]; + + uint32_t directional_light_count; + float z_far; + float z_near; + float pad1; + + uint32_t fog_enabled; + float fog_density; + float fog_height; + float fog_height_density; + + float fog_light_color[3]; + float fog_sun_scatter; + }; + static_assert(sizeof(UBO) % 16 == 0, "Scene UBO size must be a multiple of 16 bytes"); + + struct TonemapUBO { + float exposure = 1.0; + float white = 1.0; + int32_t tonemapper = 0; + int32_t pad = 0; + }; + static_assert(sizeof(TonemapUBO) % 16 == 0, "Tonemap UBO size must be a multiple of 16 bytes"); + + UBO ubo; + GLuint ubo_buffer = 0; + GLuint tonemap_buffer = 0; + + bool used_depth_prepass = false; + + GLES3::SceneShaderData::BlendMode current_blend_mode = GLES3::SceneShaderData::BLEND_MODE_MIX; + GLES3::SceneShaderData::DepthDraw current_depth_draw = GLES3::SceneShaderData::DEPTH_DRAW_OPAQUE; + GLES3::SceneShaderData::DepthTest current_depth_test = GLES3::SceneShaderData::DEPTH_TEST_DISABLED; + GLES3::SceneShaderData::Cull cull_mode = GLES3::SceneShaderData::CULL_BACK; + + bool texscreen_copied = false; + bool used_screen_texture = false; + bool used_normal_texture = false; + bool used_depth_texture = false; + + LightData *omni_lights = nullptr; + LightData *spot_lights = nullptr; + + InstanceSort<LightInstance> *omni_light_sort; + InstanceSort<LightInstance> *spot_light_sort; + GLuint omni_light_buffer = 0; + GLuint spot_light_buffer = 0; + uint32_t omni_light_count = 0; + uint32_t spot_light_count = 0; + + DirectionalLightData *directional_lights = nullptr; + GLuint directional_light_buffer = 0; + } scene_state; + struct RenderListParameters { GeometryInstanceSurface **elements = nullptr; int element_count = 0; bool reverse_cull = false; uint32_t spec_constant_base_flags = 0; bool force_wireframe = false; - Plane lod_plane; - float lod_distance_multiplier = 0.0; - float screen_mesh_lod_threshold = 0.0; - RenderListParameters(GeometryInstanceSurface **p_elements, int p_element_count, bool p_reverse_cull, uint32_t p_spec_constant_base_flags, bool p_force_wireframe = false, const Plane &p_lod_plane = Plane(), float p_lod_distance_multiplier = 0.0, float p_screen_mesh_lod_threshold = 0.0) { + RenderListParameters(GeometryInstanceSurface **p_elements, int p_element_count, bool p_reverse_cull, uint32_t p_spec_constant_base_flags, bool p_force_wireframe = false) { elements = p_elements; element_count = p_element_count; reverse_cull = p_reverse_cull; spec_constant_base_flags = p_spec_constant_base_flags; force_wireframe = p_force_wireframe; - lod_plane = p_lod_plane; - lod_distance_multiplier = p_lod_distance_multiplier; - screen_mesh_lod_threshold = p_screen_mesh_lod_threshold; } }; @@ -438,6 +509,7 @@ private: RenderList render_list[RENDER_LIST_MAX]; + void _setup_lights(const RenderDataGLES3 *p_render_data, bool p_using_shadows, uint32_t &r_directional_light_count, uint32_t &r_omni_light_count, uint32_t &r_spot_light_count); void _setup_environment(const RenderDataGLES3 *p_render_data, bool p_no_fog, const Size2i &p_screen_size, bool p_flip_y, const Color &p_default_bg_color, bool p_pancake_shadows); void _fill_render_list(RenderListType p_render_list, const RenderDataGLES3 *p_render_data, PassMode p_pass_mode, bool p_append = false); @@ -637,6 +709,33 @@ protected: /* Sky */ + struct SkyGlobals { + float fog_aerial_perspective = 0.0; + Color fog_light_color; + float fog_sun_scatter = 0.0; + bool fog_enabled = false; + float fog_density = 0.0; + float z_far = 0.0; + uint32_t directional_light_count = 0; + + DirectionalLightData *directional_lights = nullptr; + DirectionalLightData *last_frame_directional_lights = nullptr; + uint32_t last_frame_directional_light_count = 0; + GLuint directional_light_buffer = 0; + + RID shader_default_version; + RID default_material; + RID default_shader; + RID fog_material; + RID fog_shader; + GLuint screen_triangle = 0; + GLuint screen_triangle_array = 0; + GLuint radical_inverse_vdc_cache_tex = 0; + uint32_t max_directional_lights = 4; + uint32_t roughness_layers = 8; + uint32_t ggx_samples = 128; + } sky_globals; + struct Sky { // Screen Buffers GLuint half_res_pass = 0; @@ -648,11 +747,13 @@ protected: // Radiance Cubemap GLuint radiance = 0; GLuint radiance_framebuffer = 0; + GLuint raw_radiance = 0; RID material; - RID uniform_buffer; + GLuint uniform_buffer; int radiance_size = 256; + int mipmap_count = 1; RS::SkyMode mode = RS::SKY_MODE_AUTOMATIC; @@ -666,20 +767,18 @@ protected: GLES3::SkyMaterialData *prev_material; Vector3 prev_position = Vector3(0.0, 0.0, 0.0); float prev_time = 0.0f; - - void free(); - bool set_radiance_size(int p_radiance_size); - bool set_mode(RS::SkyMode p_mode); - bool set_material(RID p_material); - Ref<Image> bake_panorama(float p_energy, int p_roughness_layers, const Size2i &p_size); }; Sky *dirty_sky_list = nullptr; mutable RID_Owner<Sky, true> sky_owner; + void _setup_sky(Environment *p_env, RID p_render_buffers, const PagedArray<RID> &p_lights, const CameraMatrix &p_projection, const Transform3D &p_transform, const Size2i p_screen_size); void _invalidate_sky(Sky *p_sky); void _update_dirty_skys(); + void _update_sky_radiance(Environment *p_env, const CameraMatrix &p_projection, const Transform3D &p_transform); + void _filter_sky_radiance(Sky *p_sky, int p_base_layer); void _draw_sky(Environment *p_env, const CameraMatrix &p_projection, const Transform3D &p_transform); + void _free_sky_data(Sky *p_sky); public: RasterizerStorageGLES3 *storage; @@ -808,6 +907,15 @@ public: void light_instance_set_shadow_transform(RID p_light_instance, const CameraMatrix &p_projection, const Transform3D &p_transform, float p_far, float p_split, int p_pass, float p_shadow_texel_size, float p_bias_scale = 1.0, float p_range_begin = 0, const Vector2 &p_uv_scale = Vector2()) override; void light_instance_mark_visible(RID p_light_instance) override; + _FORCE_INLINE_ RS::LightType light_instance_get_type(RID p_light_instance) { + LightInstance *li = light_instance_owner.get_or_null(p_light_instance); + return li->light_type; + } + _FORCE_INLINE_ uint32_t light_instance_get_gl_id(RID p_light_instance) { + LightInstance *li = light_instance_owner.get_or_null(p_light_instance); + return li->gl_id; + } + RID fog_volume_instance_create(RID p_fog_volume) override; void fog_volume_instance_set_transform(RID p_fog_volume_instance, const Transform3D &p_transform) override; void fog_volume_instance_set_active(RID p_fog_volume_instance, bool p_active) override; diff --git a/drivers/gles3/rasterizer_storage_gles3.cpp b/drivers/gles3/rasterizer_storage_gles3.cpp index 3c28289bd0..8046a18f05 100644 --- a/drivers/gles3/rasterizer_storage_gles3.cpp +++ b/drivers/gles3/rasterizer_storage_gles3.cpp @@ -39,6 +39,19 @@ #include "servers/rendering/shader_language.h" void RasterizerStorageGLES3::base_update_dependency(RID p_base, DependencyTracker *p_instance) { + if (GLES3::MeshStorage::get_singleton()->owns_mesh(p_base)) { + GLES3::Mesh *mesh = GLES3::MeshStorage::get_singleton()->get_mesh(p_base); + p_instance->update_dependency(&mesh->dependency); + } else if (GLES3::MeshStorage::get_singleton()->owns_multimesh(p_base)) { + GLES3::MultiMesh *multimesh = GLES3::MeshStorage::get_singleton()->get_multimesh(p_base); + p_instance->update_dependency(&multimesh->dependency); + if (multimesh->mesh.is_valid()) { + base_update_dependency(multimesh->mesh, p_instance); + } + } else if (GLES3::LightStorage::get_singleton()->owns_light(p_base)) { + GLES3::Light *l = GLES3::LightStorage::get_singleton()->get_light(p_base); + p_instance->update_dependency(&l->dependency); + } } /* VOXEL GI API */ diff --git a/drivers/gles3/shader_gles3.h b/drivers/gles3/shader_gles3.h index 35fb202342..228bed6f9b 100644 --- a/drivers/gles3/shader_gles3.h +++ b/drivers/gles3/shader_gles3.h @@ -219,7 +219,10 @@ protected: Version *version = version_owner.get_or_null(p_version); ERR_FAIL_COND_V(!version, -1); ERR_FAIL_INDEX_V(p_variant, int(version->variants.size()), -1); - return version->variants[p_variant].lookup_ptr(p_specialization)->uniform_location[p_which]; + Version::Specialization *spec = version->variants[p_variant].lookup_ptr(p_specialization); + ERR_FAIL_COND_V(!spec, -1); + ERR_FAIL_INDEX_V(p_which, int(spec->uniform_location.size()), -1); + return spec->uniform_location[p_which]; } virtual void _init() = 0; diff --git a/drivers/gles3/shaders/SCsub b/drivers/gles3/shaders/SCsub index ec32badc19..d8dd573f57 100644 --- a/drivers/gles3/shaders/SCsub +++ b/drivers/gles3/shaders/SCsub @@ -2,11 +2,18 @@ Import("env") -env.Depends("#drivers/gles3/shaders/copy.glsl.gen.h", "#core/math/basis.h") -env.Depends("#drivers/gles3/shaders/copy.glsl.gen.h", "#core/math/transform_2d.h") - if "GLES3_GLSL" in env["BUILDERS"]: + # find all include files + gl_include_files = [str(f) for f in Glob("*_inc.glsl")] + + # find all shader code(all glsl files excluding our include files) + glsl_files = [str(f) for f in Glob("*.glsl") if str(f) not in gl_include_files] + + # make sure we recompile shaders if include files change + env.Depends([f + ".gen.h" for f in glsl_files], gl_include_files) + env.GLES3_GLSL("canvas.glsl") env.GLES3_GLSL("copy.glsl") env.GLES3_GLSL("scene.glsl") env.GLES3_GLSL("sky.glsl") + env.GLES3_GLSL("cubemap_filter.glsl") diff --git a/drivers/gles3/shaders/cubemap_filter.glsl b/drivers/gles3/shaders/cubemap_filter.glsl index 2081abfef6..81e66c956c 100644 --- a/drivers/gles3/shaders/cubemap_filter.glsl +++ b/drivers/gles3/shaders/cubemap_filter.glsl @@ -1,136 +1,102 @@ /* clang-format off */ -[vertex] +#[modes] -#ifdef USE_GLES_OVER_GL -#define lowp -#define mediump -#define highp -#else -precision highp float; -precision highp int; -#endif +mode_default = +mode_copy = #define MODE_DIRECT_WRITE + +#[specializations] -layout(location = 0) in highp vec2 vertex; +#[vertex] + +layout(location = 0) in highp vec2 vertex_attrib; /* clang-format on */ -layout(location = 4) in highp vec2 uv; out highp vec2 uv_interp; void main() { - uv_interp = uv; - gl_Position = vec4(vertex, 0, 1); + uv_interp = vertex_attrib; + gl_Position = vec4(uv_interp, 0.0, 1.0); } /* clang-format off */ -[fragment] +#[fragment] -#ifdef USE_GLES_OVER_GL -#define lowp -#define mediump -#define highp -#else -#if defined(USE_HIGHP_PRECISION) -precision highp float; -precision highp int; -#else -precision mediump float; -precision mediump int; -#endif -#endif +#define M_PI 3.14159265359 -#ifdef USE_SOURCE_PANORAMA -uniform sampler2D source_panorama; //texunit:0 -#else uniform samplerCube source_cube; //texunit:0 -#endif + /* clang-format on */ uniform int face_id; uniform float roughness; -in highp vec2 uv_interp; - -uniform sampler2D radical_inverse_vdc_cache; // texunit:1 - -#define M_PI 3.14159265359 - -#ifdef LOW_QUALITY - -#define SAMPLE_COUNT 64 - -#else - -#define SAMPLE_COUNT 512 +uniform float face_size; +uniform int sample_count; +//Todo, profile on low end hardware to see if fixed loop is faster +#ifdef USE_FIXED_SAMPLES +#define FIXED_SAMPLE_COUNT 32 #endif -#ifdef USE_SOURCE_PANORAMA +in highp vec2 uv_interp; -vec4 texturePanorama(sampler2D pano, vec3 normal) { - vec2 st = vec2( - atan(normal.x, normal.z), - acos(normal.y)); +uniform sampler2D radical_inverse_vdc_cache; // texunit:1 - if (st.x < 0.0) - st.x += M_PI * 2.0; +layout(location = 0) out vec4 frag_color; - st /= vec2(M_PI * 2.0, M_PI); +#define M_PI 3.14159265359 - return textureLod(pano, st, 0.0); +// Don't include tonemap_inc.glsl because all we want is these functions, we don't want the uniforms +vec3 linear_to_srgb(vec3 color) { + return max(vec3(1.055) * pow(color, vec3(0.416666667)) - vec3(0.055), vec3(0.0)); } -#endif +vec3 srgb_to_linear(vec3 color) { + return color * (color * (color * 0.305306011 + 0.682171111) + 0.012522878); +} vec3 texelCoordToVec(vec2 uv, int faceID) { mat3 faceUvVectors[6]; // -x - faceUvVectors[0][0] = vec3(0.0, 0.0, 1.0); // u -> +z - faceUvVectors[0][1] = vec3(0.0, -1.0, 0.0); // v -> -y - faceUvVectors[0][2] = vec3(-1.0, 0.0, 0.0); // -x face + faceUvVectors[1][0] = vec3(0.0, 0.0, 1.0); // u -> +z + faceUvVectors[1][1] = vec3(0.0, -1.0, 0.0); // v -> -y + faceUvVectors[1][2] = vec3(-1.0, 0.0, 0.0); // -x face // +x - faceUvVectors[1][0] = vec3(0.0, 0.0, -1.0); // u -> -z - faceUvVectors[1][1] = vec3(0.0, -1.0, 0.0); // v -> -y - faceUvVectors[1][2] = vec3(1.0, 0.0, 0.0); // +x face + faceUvVectors[0][0] = vec3(0.0, 0.0, -1.0); // u -> -z + faceUvVectors[0][1] = vec3(0.0, -1.0, 0.0); // v -> -y + faceUvVectors[0][2] = vec3(1.0, 0.0, 0.0); // +x face // -y - faceUvVectors[2][0] = vec3(1.0, 0.0, 0.0); // u -> +x - faceUvVectors[2][1] = vec3(0.0, 0.0, -1.0); // v -> -z - faceUvVectors[2][2] = vec3(0.0, -1.0, 0.0); // -y face + faceUvVectors[3][0] = vec3(1.0, 0.0, 0.0); // u -> +x + faceUvVectors[3][1] = vec3(0.0, 0.0, -1.0); // v -> -z + faceUvVectors[3][2] = vec3(0.0, -1.0, 0.0); // -y face // +y - faceUvVectors[3][0] = vec3(1.0, 0.0, 0.0); // u -> +x - faceUvVectors[3][1] = vec3(0.0, 0.0, 1.0); // v -> +z - faceUvVectors[3][2] = vec3(0.0, 1.0, 0.0); // +y face + faceUvVectors[2][0] = vec3(1.0, 0.0, 0.0); // u -> +x + faceUvVectors[2][1] = vec3(0.0, 0.0, 1.0); // v -> +z + faceUvVectors[2][2] = vec3(0.0, 1.0, 0.0); // +y face // -z - faceUvVectors[4][0] = vec3(-1.0, 0.0, 0.0); // u -> -x - faceUvVectors[4][1] = vec3(0.0, -1.0, 0.0); // v -> -y - faceUvVectors[4][2] = vec3(0.0, 0.0, -1.0); // -z face + faceUvVectors[5][0] = vec3(-1.0, 0.0, 0.0); // u -> -x + faceUvVectors[5][1] = vec3(0.0, -1.0, 0.0); // v -> -y + faceUvVectors[5][2] = vec3(0.0, 0.0, -1.0); // -z face // +z - faceUvVectors[5][0] = vec3(1.0, 0.0, 0.0); // u -> +x - faceUvVectors[5][1] = vec3(0.0, -1.0, 0.0); // v -> -y - faceUvVectors[5][2] = vec3(0.0, 0.0, 1.0); // +z face + faceUvVectors[4][0] = vec3(1.0, 0.0, 0.0); // u -> +x + faceUvVectors[4][1] = vec3(0.0, -1.0, 0.0); // v -> -y + faceUvVectors[4][2] = vec3(0.0, 0.0, 1.0); // +z face // out = u * s_faceUv[0] + v * s_faceUv[1] + s_faceUv[2]. - vec3 result; - for (int i = 0; i < 6; i++) { - if (i == faceID) { - result = (faceUvVectors[i][0] * uv.x) + (faceUvVectors[i][1] * uv.y) + faceUvVectors[i][2]; - break; - } - } + vec3 result = (faceUvVectors[faceID][0] * uv.x) + (faceUvVectors[faceID][1] * uv.y) + faceUvVectors[faceID][2]; return normalize(result); } -vec3 ImportanceSampleGGX(vec2 Xi, float Roughness, vec3 N) { - float a = Roughness * Roughness; // DISNEY'S ROUGHNESS [see Burley'12 siggraph] - +vec3 ImportanceSampleGGX(vec2 xi, float roughness4) { // Compute distribution direction - float Phi = 2.0 * M_PI * Xi.x; - float CosTheta = sqrt((1.0 - Xi.y) / (1.0 + (a * a - 1.0) * Xi.y)); + float Phi = 2.0 * M_PI * xi.x; + float CosTheta = sqrt((1.0 - xi.y) / (1.0 + (roughness4 - 1.0) * xi.y)); float SinTheta = sqrt(1.0 - CosTheta * CosTheta); // Convert to spherical direction @@ -139,12 +105,26 @@ vec3 ImportanceSampleGGX(vec2 Xi, float Roughness, vec3 N) { H.y = SinTheta * sin(Phi); H.z = CosTheta; - vec3 UpVector = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0); - vec3 TangentX = normalize(cross(UpVector, N)); - vec3 TangentY = cross(N, TangentX); + return H; +} + +float DistributionGGX(float NdotH, float roughness4) { + float NdotH2 = NdotH * NdotH; + float denom = (NdotH2 * (roughness4 - 1.0) + 1.0); + denom = M_PI * denom * denom; + + return roughness4 / denom; +} + +// https://graphicrants.blogspot.com.au/2013/08/specular-brdf-reference.html +float GGX(float NdotV, float a) { + float k = a / 2.0; + return NdotV / (NdotV * (1.0 - k) + k); +} - // Tangent to world space - return TangentX * H.x + TangentY * H.y + N * H.z; +// https://graphicrants.blogspot.com.au/2013/08/specular-brdf-reference.html +float G_Smith(float a, float nDotV, float nDotL) { + return GGX(nDotL, a * a) * GGX(nDotV, a * a); } float radical_inverse_VdC(int i) { @@ -155,60 +135,54 @@ vec2 Hammersley(int i, int N) { return vec2(float(i) / float(N), radical_inverse_VdC(i)); } -uniform bool z_flip; - -layout(location = 0) out vec4 frag_color; - void main() { vec3 color = vec3(0.0); - - vec2 uv = (uv_interp * 2.0) - 1.0; + vec2 uv = uv_interp; vec3 N = texelCoordToVec(uv, face_id); -#ifdef USE_DIRECT_WRITE - -#ifdef USE_SOURCE_PANORAMA - - frag_color = vec4(texturePanorama(source_panorama, N).rgb, 1.0); -#else - - frag_color = vec4(textureCube(source_cube, N).rgb, 1.0); -#endif //USE_SOURCE_PANORAMA - +#ifdef MODE_DIRECT_WRITE + frag_color = vec4(textureCubeLod(source_cube, N, 0.0).rgb, 1.0); #else vec4 sum = vec4(0.0); + float solid_angle_texel = 4.0 * M_PI / (6.0 * face_size * face_size); + float roughness2 = roughness * roughness; + float roughness4 = roughness2 * roughness2; + vec3 UpVector = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0); + mat3 T; + T[0] = normalize(cross(UpVector, N)); + T[1] = cross(N, T[0]); + T[2] = N; - for (int sample_num = 0; sample_num < SAMPLE_COUNT; sample_num++) { - vec2 xi = Hammersley(sample_num, SAMPLE_COUNT); + for (int sample_num = 0; sample_num < sample_count; sample_num++) { + vec2 xi = Hammersley(sample_num, sample_count); - vec3 H = ImportanceSampleGGX(xi, roughness, N); - vec3 V = N; - vec3 L = (2.0 * dot(V, H) * H - V); + vec3 H = T * ImportanceSampleGGX(xi, roughness4); + float NdotH = dot(N, H); + vec3 L = (2.0 * NdotH * H - N); float NdotL = clamp(dot(N, L), 0.0, 1.0); if (NdotL > 0.0) { + float D = DistributionGGX(NdotH, roughness4); + float pdf = D * NdotH / (4.0 * NdotH) + 0.0001; -#ifdef USE_SOURCE_PANORAMA - vec3 val = texturePanorama(source_panorama, L).rgb; -#else - vec3 val = textureCubeLod(source_cube, L, 0.0).rgb; -#endif - //mix using Linear, to approximate high end back-end - val = mix(pow((val + vec3(0.055)) * (1.0 / (1.0 + 0.055)), vec3(2.4)), val * (1.0 / 12.92), vec3(lessThan(val, vec3(0.04045)))); + float solid_angle_sample = 1.0 / (float(sample_count) * pdf + 0.0001); - sum.rgb += val * NdotL; + float mipLevel = roughness == 0.0 ? 0.0 : 0.5 * log2(solid_angle_sample / solid_angle_texel); + + vec3 val = textureCubeLod(source_cube, L, mipLevel).rgb; + // Mix using linear + val = srgb_to_linear(val); + sum.rgb += val * NdotL; sum.a += NdotL; } } sum /= sum.a; - vec3 a = vec3(0.055); - sum.rgb = mix((vec3(1.0) + a) * pow(sum.rgb, vec3(1.0 / 2.4)) - a, 12.92 * sum.rgb, vec3(lessThan(sum.rgb, vec3(0.0031308)))); - + sum.rgb = linear_to_srgb(sum.rgb); frag_color = vec4(sum.rgb, 1.0); #endif } diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl index 12d70db7dd..198d1d9ec7 100644 --- a/drivers/gles3/shaders/scene.glsl +++ b/drivers/gles3/shaders/scene.glsl @@ -7,9 +7,12 @@ mode_depth = #define MODE_RENDER_DEPTH #[specializations] -USE_LIGHTMAP = false -USE_LIGHT_DIRECTIONAL = false -USE_LIGHT_POSITIONAL = false +DISABLE_LIGHTMAP = false +DISABLE_LIGHT_DIRECTIONAL = false +DISABLE_LIGHT_OMNI = false +DISABLE_LIGHT_SPOT = false +DISABLE_FOG = false +USE_RADIANCE_MAP = true #[vertex] @@ -109,12 +112,14 @@ layout(std140) uniform SceneData { // ubo:2 mediump vec4 ambient_light_color_energy; mediump float ambient_color_sky_mix; - uint ambient_flags; bool material_uv2_mode; - float opaque_prepass_threshold; - //bool use_ambient_light; - //bool use_ambient_cubemap; - //bool use_reflection_cubemap; + float pad2; + bool use_ambient_light; + bool use_ambient_cubemap; + bool use_reflection_cubemap; + + float fog_aerial_perspective; + float time; mat3 radiance_inverse_xform; @@ -130,13 +135,6 @@ layout(std140) uniform SceneData { // ubo:2 vec3 fog_light_color; float fog_sun_scatter; - - float fog_aerial_perspective; - - float time; - float reflection_multiplier; // one normally, zero when rendering reflections - - bool pancake_shadows; } scene_data; @@ -169,7 +167,7 @@ out vec2 uv2_interp; #endif #endif -#if defined(TANGENT_USED) || defined(ENABLE_NORMALMAP) || defined(LIGHT_USE_ANISOTROPY) +#if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED) out vec3 tangent_interp; out vec3 binormal_interp; #endif @@ -191,9 +189,6 @@ layout(std140) uniform MaterialUniforms { // ubo:3 #GLOBALS /* clang-format on */ - -out highp vec4 position_interp; - invariant gl_Position; void main() { @@ -206,21 +201,16 @@ void main() { #endif highp mat3 model_normal_matrix = mat3(model_matrix); -#if defined(TANGENT_USED) || defined(ENABLE_NORMALMAP) || defined(LIGHT_USE_ANISOTROPY) - vec3 tangent; - float binormalf; - tangent = normal_tangent_attrib.xyz; - binormalf = normal_tangent_attrib.a; +#if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED) + vec3 tangent = tangent_attrib.xyz * 2.0 - 1.0; + float binormalf = tangent_attrib.a * 2.0 - 1.0; + vec3 binormal = normalize(cross(normal, tangent) * binormalf); #endif #if defined(COLOR_USED) color_interp = color_attrib; #endif -#if defined(TANGENT_USED) || defined(ENABLE_NORMALMAP) || defined(LIGHT_USE_ANISOTROPY) - vec3 binormal = normalize(cross(normal, tangent) * binormalf); -#endif - #if defined(UV_USED) uv_interp = uv_attrib; #endif @@ -306,7 +296,7 @@ void main() { normal_interp = normal; #endif -#if defined(TANGENT_USED) || defined(ENABLE_NORMALMAP) || defined(LIGHT_USE_ANISOTROPY) +#if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED) tangent_interp = tangent; binormal_interp = binormal; #endif @@ -316,16 +306,6 @@ void main() { #else gl_Position = projection_matrix * vec4(vertex_interp, 1.0); #endif - -#ifdef MODE_RENDER_DEPTH - if (scene_data.pancake_shadows) { - if (gl_Position.z <= 0.00001) { - gl_Position.z = 0.00001; - } - } -#endif - - position_interp = gl_Position; } /* clang-format off */ @@ -357,10 +337,9 @@ void main() { */ -uniform highp mat4 world_transform; +#define M_PI 3.14159265359 /* clang-format on */ -#define M_PI 3.14159265359 #define SHADER_IS_SRGB true /* Varyings */ @@ -381,7 +360,7 @@ in vec2 uv2_interp; #endif #endif -#if defined(TANGENT_USED) || defined(ENABLE_NORMALMAP) || defined(LIGHT_USE_ANISOTROPY) +#if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED) in vec3 tangent_interp; in vec3 binormal_interp; #endif @@ -392,29 +371,11 @@ in vec3 normal_interp; in highp vec3 vertex_interp; -/* PBR CHANNELS */ - #ifdef USE_RADIANCE_MAP -layout(std140) uniform Radiance { // ubo:4 - - mat4 radiance_inverse_xform; - float radiance_ambient_contribution; -}; - #define RADIANCE_MAX_LOD 5.0 -uniform sampler2D radiance_map; // texunit:-2 - -vec3 textureDualParaboloid(sampler2D p_tex, vec3 p_vec, float p_roughness) { - vec3 norm = normalize(p_vec); - 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(p_tex, norm.xy, p_roughness * RADIANCE_MAX_LOD).xyz; -} +uniform samplerCube radiance_map; // texunit:-2 #endif @@ -448,12 +409,14 @@ layout(std140) uniform SceneData { // ubo:2 mediump vec4 ambient_light_color_energy; mediump float ambient_color_sky_mix; - uint ambient_flags; bool material_uv2_mode; - float opaque_prepass_threshold; - //bool use_ambient_light; - //bool use_ambient_cubemap; - //bool use_reflection_cubemap; + float pad2; + bool use_ambient_light; + bool use_ambient_cubemap; + bool use_reflection_cubemap; + + float fog_aerial_perspective; + float time; mat3 radiance_inverse_xform; @@ -469,13 +432,6 @@ layout(std140) uniform SceneData { // ubo:2 vec3 fog_light_color; float fog_sun_scatter; - - float fog_aerial_perspective; - - float time; - float reflection_multiplier; // one normally, zero when rendering reflections - - bool pancake_shadows; } scene_data; @@ -487,7 +443,7 @@ scene_data; //directional light data -#ifdef USE_LIGHT_DIRECTIONAL +#ifndef DISABLE_LIGHT_DIRECTIONAL struct DirectionalLightData { mediump vec3 direction; @@ -498,10 +454,14 @@ struct DirectionalLightData { mediump float specular; }; +layout(std140) uniform DirectionalLights { // ubo:7 + DirectionalLightData directional_lights[MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS]; +}; + #endif // omni and spot -#ifdef USE_LIGHT_POSITIONAL +#if !defined(DISABLE_LIGHT_OMNI) && !defined(DISABLE_LIGHT_SPOT) struct LightData { //this structure needs to be as packed as possible highp vec3 position; highp float inv_radius; @@ -517,36 +477,38 @@ struct LightData { //this structure needs to be as packed as possible mediump float specular_amount; bool shadow_enabled; }; - +#ifndef DISABLE_LIGHT_OMNI layout(std140) uniform OmniLightData { // ubo:5 LightData omni_lights[MAX_LIGHT_DATA_STRUCTS]; }; +uniform uint omni_light_indices[MAX_FORWARD_LIGHTS]; +uniform int omni_light_count; +#endif + +#ifndef DISABLE_LIGHT_SPOT layout(std140) uniform SpotLightData { // ubo:6 LightData spot_lights[MAX_LIGHT_DATA_STRUCTS]; }; - -uniform highp samplerCubeShadow positional_shadow; // texunit:-6 - -uniform int omni_light_indices[MAX_FORWARD_LIGHTS]; -uniform int omni_light_count; - -uniform int spot_light_indices[MAX_FORWARD_LIGHTS]; +uniform uint spot_light_indices[MAX_FORWARD_LIGHTS]; uniform int spot_light_count; +#endif -uniform int reflection_indices[MAX_FORWARD_LIGHTS]; -uniform int reflection_count; - +#ifdef USE_ADDITIVE_LIGHTING +uniform highp samplerCubeShadow positional_shadow; // texunit:-4 #endif +#endif // !defined(DISABLE_LIGHT_OMNI) && !defined(DISABLE_LIGHT_SPOT) + uniform highp sampler2D screen_texture; // texunit:-5 uniform highp sampler2D depth_buffer; // texunit:-6 -layout(location = 0) out vec4 frag_color; +uniform highp mat4 world_transform; +uniform mediump float opaque_prepass_threshold; -in highp vec4 position_interp; +layout(location = 0) out vec4 frag_color; vec3 F0(float metallic, float specular, vec3 albedo) { float dielectric = 0.16 * specular * specular; @@ -555,7 +517,7 @@ vec3 F0(float metallic, float specular, vec3 albedo) { return mix(vec3(dielectric), albedo, vec3(metallic)); } -#if defined(USE_LIGHT_DIRECTIONAL) || defined(USE_LIGHT_POSITIONAL) +#if !defined(DISABLE_LIGHT_DIRECTIONAL) || !defined(DISABLE_LIGHT_OMNI) || !defined(DISABLE_LIGHT_SPOT) float D_GGX(float cos_theta_m, float alpha) { float a = cos_theta_m * alpha; float k = alpha / (1.0 - cos_theta_m * cos_theta_m + a * a); @@ -588,7 +550,7 @@ float SchlickFresnel(float u) { return m2 * m2 * m; // pow(m,5) } -void light_compute(vec3 N, vec3 L, vec3 V, float A, vec3 light_color, float attenuation, vec3 f0, uint orms, float specular_amount, vec3 albedo, inout float alpha, +void light_compute(vec3 N, vec3 L, vec3 V, float A, vec3 light_color, float attenuation, vec3 f0, float roughness, float metallic, float specular_amount, vec3 albedo, inout float alpha, #ifdef LIGHT_BACKLIGHT_USED vec3 backlight, #endif @@ -603,11 +565,6 @@ void light_compute(vec3 N, vec3 L, vec3 V, float A, vec3 light_color, float atte #endif inout vec3 diffuse_light, inout vec3 specular_light) { - vec4 orms_unpacked = unpackUnorm4x8(orms); - - float roughness = orms_unpacked.y; - float metallic = orms_unpacked.z; - #if defined(USE_LIGHT_SHADER_CODE) // light is written by the light shader @@ -626,7 +583,7 @@ void light_compute(vec3 N, vec3 L, vec3 V, float A, vec3 light_color, float atte float NdotL = min(A + dot(N, L), 1.0); float cNdotL = max(NdotL, 0.0); // clamped NdotL float NdotV = dot(N, V); - float cNdotV = max(NdotV, 0.0); + float cNdotV = max(NdotV, 1e-4); #if defined(DIFFUSE_BURLEY) || defined(SPECULAR_SCHLICK_GGX) || defined(LIGHT_CLEARCOAT_USED) vec3 H = normalize(V + L); @@ -752,15 +709,10 @@ float get_omni_attenuation(float distance, float inv_range, float decay) { return nd * pow(max(distance, 0.0001), -decay); } -void light_process_omni(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 f0, uint orms, float shadow, vec3 albedo, inout float alpha, +void light_process_omni(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 f0, float roughness, float metallic, float shadow, vec3 albedo, inout float alpha, #ifdef LIGHT_BACKLIGHT_USED vec3 backlight, #endif -#ifdef LIGHT_TRANSMITTANCE_USED - vec4 transmittance_color, - float transmittance_depth, - float transmittance_boost, -#endif #ifdef LIGHT_RIM_USED float rim, float rim_tint, #endif @@ -774,16 +726,15 @@ void light_process_omni(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 f vec3 light_rel_vec = omni_lights[idx].position - vertex; float light_length = length(light_rel_vec); float omni_attenuation = get_omni_attenuation(light_length, omni_lights[idx].inv_radius, omni_lights[idx].attenuation); - vec3 light_attenuation = vec3(omni_attenuation); vec3 color = omni_lights[idx].color; float size_A = 0.0; - if (omni_lights.data[idx].size > 0.0) { + if (omni_lights[idx].size > 0.0) { float t = omni_lights[idx].size / max(0.001, light_length); size_A = max(0.0, 1.0 - 1 / sqrt(1 + t * t)); } - light_compute(normal, normalize(light_rel_vec), eye_vec, size_A, color, light_attenuation, f0, orms, omni_lights[idx].specular_amount, albedo, alpha, + light_compute(normal, normalize(light_rel_vec), eye_vec, size_A, color, omni_attenuation, f0, roughness, metallic, omni_lights[idx].specular_amount, albedo, alpha, #ifdef LIGHT_BACKLIGHT_USED backlight, #endif @@ -800,7 +751,7 @@ void light_process_omni(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 f specular_light); } -void light_process_spot(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 f0, uint orms, float shadow, vec3 albedo, inout float alpha, +void light_process_spot(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 f0, float roughness, float metallic, float shadow, vec3 albedo, inout float alpha, #ifdef LIGHT_BACKLIGHT_USED vec3 backlight, #endif @@ -823,17 +774,16 @@ void light_process_spot(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 f float scos = max(dot(-normalize(light_rel_vec), spot_dir), spot_lights[idx].cone_angle); float spot_rim = max(0.0001, (1.0 - scos) / (1.0 - spot_lights[idx].cone_angle)); spot_attenuation *= 1.0 - pow(spot_rim, spot_lights[idx].cone_attenuation); - float light_attenuation = spot_attenuation; vec3 color = spot_lights[idx].color; float size_A = 0.0; - if (spot_lights.data[idx].size > 0.0) { - float t = spot_lights.data[idx].size / max(0.001, light_length); + if (spot_lights[idx].size > 0.0) { + float t = spot_lights[idx].size / max(0.001, light_length); size_A = max(0.0, 1.0 - 1 / sqrt(1 + t * t)); } - light_compute(normal, normalize(light_rel_vec), eye_vec, size_A, color, light_attenuation, f0, orms, spot_lights[idx].specular_amount, albedo, alpha, + light_compute(normal, normalize(light_rel_vec), eye_vec, size_A, color, spot_attenuation, f0, roughness, metallic, spot_lights[idx].specular_amount, albedo, alpha, #ifdef LIGHT_BACKLIGHT_USED backlight, #endif @@ -848,7 +798,56 @@ void light_process_spot(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 f #endif diffuse_light, specular_light); } -#endif // defined(USE_LIGHT_DIRECTIONAL) || defined(USE_LIGHT_POSITIONAL) +#endif // !defined(DISABLE_LIGHT_DIRECTIONAL) || !defined(DISABLE_LIGHT_OMNI) && !defined(DISABLE_LIGHT_SPOT) + +#ifndef MODE_RENDER_DEPTH +vec4 fog_process(vec3 vertex) { + vec3 fog_color = scene_data.fog_light_color; + +#ifdef USE_RADIANCE_MAP +/* + if (scene_data.fog_aerial_perspective > 0.0) { + vec3 sky_fog_color = vec3(0.0); + vec3 cube_view = scene_data.radiance_inverse_xform * vertex; + // mip_level always reads from the second mipmap and higher so the fog is always slightly blurred + float mip_level = mix(1.0 / MAX_ROUGHNESS_LOD, 1.0, 1.0 - (abs(vertex.z) - scene_data.z_near) / (scene_data.z_far - scene_data.z_near)); + + sky_fog_color = textureCubeLod(radiance_map, cube_view, mip_level * RADIANCE_MAX_LOD).rgb; + + fog_color = mix(fog_color, sky_fog_color, scene_data.fog_aerial_perspective); + } + */ +#endif + +#ifndef DISABLE_LIGHT_DIRECTIONAL + if (scene_data.fog_sun_scatter > 0.001) { + vec4 sun_scatter = vec4(0.0); + float sun_total = 0.0; + vec3 view = normalize(vertex); + for (uint i = uint(0); i < scene_data.directional_light_count; i++) { + vec3 light_color = directional_lights[i].color * directional_lights[i].energy; + float light_amount = pow(max(dot(view, directional_lights[i].direction), 0.0), 8.0); + fog_color += light_color * light_amount * scene_data.fog_sun_scatter; + } + } +#endif // !DISABLE_LIGHT_DIRECTIONAL + + float fog_amount = 1.0 - exp(min(0.0, -length(vertex) * scene_data.fog_density)); + + if (abs(scene_data.fog_height_density) >= 0.0001) { + float y = (scene_data.inv_view_matrix * vec4(vertex, 1.0)).y; + + float y_dist = y - scene_data.fog_height; + + float vfog_amount = 1.0 - exp(min(0.0, y_dist * scene_data.fog_height_density)); + + fog_amount = max(vfog_amount, fog_amount); + } + + return vec4(fog_color, fog_amount); +} + +#endif // !MODE_RENDER_DEPTH void main() { //lay out everything, whatever is unused is optimized away anyway @@ -951,7 +950,7 @@ void main() { #ifdef USE_OPAQUE_PREPASS #if !defined(ALPHA_SCISSOR_USED) - if (alpha < scene_data.opaque_prepass_threshold) { + if (alpha < opaque_prepass_threshold) { discard; } @@ -982,9 +981,31 @@ void main() { #endif #ifndef MODE_RENDER_DEPTH + +#ifndef CUSTOM_FOG_USED +#ifndef DISABLE_FOG + // fog must be processed as early as possible and then packed. + // to maximize VGPR usage + + if (scene_data.fog_enabled) { + fog = fog_process(vertex); + } +#endif // !DISABLE_FOG +#endif //!CUSTOM_FOG_USED + + uint fog_rg = packHalf2x16(fog.rg); + uint fog_ba = packHalf2x16(fog.ba); + +#endif //!MODE_RENDER_DEPTH + +#ifndef MODE_RENDER_DEPTH + + // Convert colors to linear + albedo = srgb_to_linear(albedo); + emission = srgb_to_linear(emission); + // TODO Backlight and transmittance when used +#ifndef MODE_UNSHADED vec3 f0 = F0(metallic, specular, albedo); - // Convert albedo to linear. Approximation from: http://chilliant.blogspot.com/2012/08/srgb-approximations-for-hlsl.html - albedo = albedo * (albedo * (albedo * 0.305306011 + 0.682171111) + 0.012522878); vec3 specular_light = vec3(0.0, 0.0, 0.0); vec3 diffuse_light = vec3(0.0, 0.0, 0.0); vec3 ambient_light = vec3(0.0, 0.0, 0.0); @@ -996,15 +1017,58 @@ void main() { float ndotv = clamp(dot(normal, view), 0.0, 1.0); vec3 F = f0 + (max(vec3(1.0 - roughness), f0) - f0) * pow(1.0 - ndotv, 5.0); - // Calculate IBL +#ifdef USE_RADIANCE_MAP + if (scene_data.use_reflection_cubemap) { +#ifdef LIGHT_ANISOTROPY_USED + // https://google.github.io/filament/Filament.html#lighting/imagebasedlights/anisotropy + vec3 anisotropic_direction = anisotropy >= 0.0 ? binormal : tangent; + vec3 anisotropic_tangent = cross(anisotropic_direction, view); + vec3 anisotropic_normal = cross(anisotropic_tangent, anisotropic_direction); + vec3 bent_normal = normalize(mix(normal, anisotropic_normal, abs(anisotropy) * clamp(5.0 * roughness, 0.0, 1.0))); + vec3 ref_vec = reflect(-view, bent_normal); +#else + vec3 ref_vec = reflect(-view, normal); +#endif + float horizon = min(1.0 + dot(ref_vec, normal), 1.0); + ref_vec = scene_data.radiance_inverse_xform * ref_vec; + specular_light = textureCubeLod(radiance_map, ref_vec, roughness * RADIANCE_MAX_LOD).rgb; + specular_light = srgb_to_linear(specular_light); + specular_light *= horizon * horizon; + specular_light *= scene_data.ambient_light_color_energy.a; + } +#endif + // Calculate Reflection probes - // Caclculate Lightmaps + // Calculate Lightmaps - float specular_blob_intensity = 1.0; +#if defined(CUSTOM_RADIANCE_USED) + specular_light = mix(specular_light, custom_radiance.rgb, custom_radiance.a); +#endif // CUSTOM_RADIANCE_USED -#if defined(SPECULAR_TOON) - specular_blob_intensity *= specular * 2.0; +#ifndef USE_LIGHTMAP + //lightmap overrides everything + if (scene_data.use_ambient_light) { + ambient_light = scene_data.ambient_light_color_energy.rgb; +#ifdef USE_RADIANCE_MAP + if (scene_data.use_ambient_cubemap) { + vec3 ambient_dir = scene_data.radiance_inverse_xform * normal; + vec3 cubemap_ambient = textureCubeLod(radiance_map, ambient_dir, RADIANCE_MAX_LOD).rgb; + cubemap_ambient = srgb_to_linear(cubemap_ambient); + ambient_light = mix(ambient_light, cubemap_ambient * scene_data.ambient_light_color_energy.a, scene_data.ambient_color_sky_mix); + } #endif + } +#endif // USE_LIGHTMAP + +#if defined(CUSTOM_IRRADIANCE_USED) + ambient_light = mix(ambient_light, custom_irradiance.rgb, custom_irradiance.a); +#endif // CUSTOM_IRRADIANCE_USED + ambient_light *= albedo.rgb; + + ambient_light *= ao; + + // convert ao to direct light ao + ao = mix(1.0, ao, ao_light_affect); { #if defined(DIFFUSE_TOON) @@ -1029,36 +1093,34 @@ void main() { #endif // BASE_PASS - //this saves some VGPRs - uint orms = packUnorm4x8(vec4(ao, roughness, metallic, specular)); - -#ifdef USE_LIGHT_DIRECTIONAL - - float size_A = directional_lights[i].size; - - light_compute(normal, directional_lights[i].direction, normalize(view), size_A, directional_lights[i].color * directional_lights[i].energy, shadow, f0, orms, 1.0, albedo, alpha, +#ifndef DISABLE_LIGHT_DIRECTIONAL + //diffuse_light = normal; // + for (uint i = uint(0); i < scene_data.directional_light_count; i++) { + light_compute(normal, normalize(directional_lights[i].direction), normalize(view), directional_lights[i].size, directional_lights[i].color * directional_lights[i].energy, 1.0, f0, roughness, metallic, 1.0, albedo, alpha, #ifdef LIGHT_BACKLIGHT_USED - backlight, + backlight, #endif #ifdef LIGHT_RIM_USED - rim, rim_tint, + rim, rim_tint, #endif #ifdef LIGHT_CLEARCOAT_USED - clearcoat, clearcoat_roughness, normalize(normal_interp), + clearcoat, clearcoat_roughness, normalize(normal_interp), #endif #ifdef LIGHT_ANISOTROPY_USED - binormal, - tangent, anisotropy, + binormal, + tangent, anisotropy, #endif - diffuse_light, - specular_light); - -#endif //#USE_LIGHT_DIRECTIONAL + diffuse_light, + specular_light); + } +#endif //!DISABLE_LIGHT_DIRECTIONAL -#ifdef USE_LIGHT_POSITIONAL - float shadow = 0.0; - for (int i = 0; i < omni_light_count; i++) { - light_process_omni(omni_light_indices[i], vertex, view, normal, f0, orms, shadow, albedo, alpha, +#ifndef DISABLE_LIGHT_OMNI + for (int i = 0; i < MAX_FORWARD_LIGHTS; i++) { + if (i >= omni_light_count) { + break; + } + light_process_omni(omni_light_indices[i], vertex, view, normal, f0, roughness, metallic, 0.0, albedo, alpha, #ifdef LIGHT_BACKLIGHT_USED backlight, #endif @@ -1070,13 +1132,18 @@ void main() { clearcoat, clearcoat_roughness, normalize(normal_interp), #endif #ifdef LIGHT_ANISOTROPY_USED - tangent, binormal, anisotropy, + binormal, tangent, anisotropy, #endif diffuse_light, specular_light); } +#endif // !DISABLE_LIGHT_OMNI - for (int i = 0; i < spot_light_count; i++) { - light_process_spot(spot_light_indices[i], vertex, view, normal, f0, orms, shadow, albedo, alpha, +#ifndef DISABLE_LIGHT_SPOT + for (int i = 0; i < MAX_FORWARD_LIGHTS; i++) { + if (i >= spot_light_count) { + break; + } + light_process_spot(spot_light_indices[i], vertex, view, normal, f0, roughness, metallic, 0.0, albedo, alpha, #ifdef LIGHT_BACKLIGHT_USED backlight, #endif @@ -1094,8 +1161,9 @@ void main() { diffuse_light, specular_light); } -#endif // USE_LIGHT_POSITIONAL -#endif //!MODE_RENDER_DEPTH +#endif // !DISABLE_LIGHT_SPOT +#endif // !MODE_UNSHADED +#endif // !MODE_RENDER_DEPTH #if defined(USE_SHADOW_TO_OPACITY) alpha = min(alpha, clamp(length(ambient_light), 0.0, 1.0)); @@ -1122,21 +1190,31 @@ void main() { //nothing happens, so a tree-ssa optimizer will result in no fragment shader :) #else // !MODE_RENDER_DEPTH - specular_light *= scene_data.reflection_multiplier; - ambient_light *= albedo; //ambient must be multiplied by albedo at the end +#ifdef MODE_UNSHADED + frag_color = vec4(albedo, alpha); +#else + + diffuse_light *= albedo; - // base color remapping diffuse_light *= 1.0 - metallic; ambient_light *= 1.0 - metallic; -#ifdef MODE_UNSHADED - frag_color = vec4(albedo, alpha); -#else - frag_color = vec4(ambient_light + diffuse_light + specular_light, alpha); + frag_color = vec4(diffuse_light + specular_light, alpha); #ifdef BASE_PASS - frag_color.rgb += emission; + frag_color.rgb += emission + ambient_light; #endif #endif //MODE_UNSHADED + fog = vec4(unpackHalf2x16(fog_rg), unpackHalf2x16(fog_ba)); + +#ifndef DISABLE_FOG + if (scene_data.fog_enabled) { +#ifdef BASE_PASS + frag_color.rgb = mix(frag_color.rgb, fog.rgb, fog.a); +#else + frag_color.rgb *= (1.0 - fog.a); +#endif // BASE_PASS + } +#endif // Tonemap before writing as we are writing to an sRGB framebuffer frag_color.rgb *= exposure; diff --git a/drivers/gles3/shaders/sky.glsl b/drivers/gles3/shaders/sky.glsl index 3a1bcd3b28..50ab38bc31 100644 --- a/drivers/gles3/shaders/sky.glsl +++ b/drivers/gles3/shaders/sky.glsl @@ -12,13 +12,13 @@ mode_cubemap_quarter_res = #define USE_CUBEMAP_PASS \n#define USE_QUARTER_RES_PA #[vertex] +layout(location = 0) in vec2 vertex_attrib; + out vec2 uv_interp; /* clang-format on */ void main() { - // One big triangle to cover the whole screen - vec2 base_arr[3] = vec2[](vec2(-1.0, -1.0), vec2(3.0, -1.0), vec2(-1.0, 3.0)); - uv_interp = base_arr[gl_VertexID]; + uv_interp = vertex_attrib; gl_Position = vec4(uv_interp, 1.0, 1.0); } @@ -46,18 +46,13 @@ layout(std140) uniform GlobalVariableData { //ubo:1 vec4 global_variables[MAX_GLOBAL_VARIABLES]; }; -layout(std140) uniform SceneData { //ubo:2 - float pad1; - float pad2; -}; - struct DirectionalLightData { vec4 direction_energy; vec4 color_size; bool enabled; }; -layout(std140) uniform DirectionalLights { //ubo:3 +layout(std140) uniform DirectionalLights { //ubo:4 DirectionalLightData data[MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS]; } directional_lights; @@ -65,7 +60,7 @@ directional_lights; /* clang-format off */ #ifdef MATERIAL_UNIFORMS_USED -layout(std140) uniform MaterialUniforms{ //ubo:4 +layout(std140) uniform MaterialUniforms{ //ubo:3 #MATERIAL_UNIFORMS @@ -98,6 +93,14 @@ uniform vec4 projection; uniform vec3 position; uniform float time; +uniform float fog_aerial_perspective; +uniform vec3 fog_light_color; +uniform float fog_sun_scatter; +uniform bool fog_enabled; +uniform float fog_density; +uniform float z_far; +uniform uint directional_light_count; + layout(location = 0) out vec4 frag_color; void main() { @@ -106,12 +109,11 @@ void main() { cube_normal.x = (uv_interp.x + projection.x) / projection.y; cube_normal.y = (-uv_interp.y - projection.z) / projection.w; cube_normal = mat3(orientation) * cube_normal; - cube_normal.z = -cube_normal.z; cube_normal = normalize(cube_normal); vec2 uv = gl_FragCoord.xy; // uv_interp * 0.5 + 0.5; - vec2 panorama_coords = vec2(atan(cube_normal.x, cube_normal.z), acos(cube_normal.y)); + vec2 panorama_coords = vec2(atan(cube_normal.x, -cube_normal.z), acos(cube_normal.y)); if (panorama_coords.x < 0.0) { panorama_coords.x += M_PI * 2.0; @@ -126,13 +128,11 @@ void main() { vec4 custom_fog = vec4(0.0); #ifdef USE_CUBEMAP_PASS - vec3 inverted_cube_normal = cube_normal; - inverted_cube_normal.z *= -1.0; #ifdef USES_HALF_RES_COLOR - half_res_color = texture(samplerCube(half_res, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), inverted_cube_normal); + half_res_color = texture(samplerCube(half_res, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), cube_normal); #endif #ifdef USES_QUARTER_RES_COLOR - quarter_res_color = texture(samplerCube(quarter_res, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), inverted_cube_normal); + quarter_res_color = texture(samplerCube(quarter_res, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), cube_normal); #endif #else #ifdef USES_HALF_RES_COLOR @@ -149,7 +149,8 @@ void main() { } - // Tonemap before writing as we are writing to an sRGB framebuffer + // Convert to Linear for tonemapping so color matches scene shader better + color = srgb_to_linear(color); color *= exposure; color = apply_tonemapping(color, white); color = linear_to_srgb(color); diff --git a/drivers/gles3/shaders/stdlib_inc.glsl b/drivers/gles3/shaders/stdlib_inc.glsl index 6cce6c12bd..d5051760d7 100644 --- a/drivers/gles3/shaders/stdlib_inc.glsl +++ b/drivers/gles3/shaders/stdlib_inc.glsl @@ -42,11 +42,11 @@ vec2 unpackSnorm2x16(uint p) { uint packUnorm4x8(vec4 v) { uvec4 uv = uvec4(round(clamp(v, vec4(0.0), vec4(1.0)) * 255.0)); - return uv.x | uv.y << uint(8) | uv.z << uint(16) | uv.w << uint(24); + return uv.x | (uv.y << uint(8)) | (uv.z << uint(16)) | (uv.w << uint(24)); } vec4 unpackUnorm4x8(uint p) { - return vec4(float(p & uint(0xffff)), float((p >> uint(8)) & uint(0xffff)), float((p >> uint(16)) & uint(0xffff)), float(p >> uint(24))) * 0.00392156862; // 1.0 / 255.0 + return vec4(float(p & uint(0xff)), float((p >> uint(8)) & uint(0xff)), float((p >> uint(16)) & uint(0xff)), float(p >> uint(24))) * 0.00392156862; // 1.0 / 255.0 } uint packSnorm4x8(vec4 v) { @@ -55,6 +55,6 @@ uint packSnorm4x8(vec4 v) { } vec4 unpackSnorm4x8(uint p) { - vec4 v = vec4(float(p & uint(0xffff)), float((p >> uint(8)) & uint(0xffff)), float((p >> uint(16)) & uint(0xffff)), float(p >> uint(24))); + vec4 v = vec4(float(p & uint(0xff)), float((p >> uint(8)) & uint(0xff)), float((p >> uint(16)) & uint(0xff)), float(p >> uint(24))); return clamp((v - vec4(127.0)) * vec4(0.00787401574), vec4(-1.0), vec4(1.0)); } diff --git a/drivers/gles3/shaders/tonemap_inc.glsl b/drivers/gles3/shaders/tonemap_inc.glsl index ea15c05359..f8f12760ec 100644 --- a/drivers/gles3/shaders/tonemap_inc.glsl +++ b/drivers/gles3/shaders/tonemap_inc.glsl @@ -92,11 +92,19 @@ vec3 tonemap_reinhard(vec3 color, float p_white) { return (p_white * color + color) / (color * p_white + p_white); } +// This expects 0-1 range input. vec3 linear_to_srgb(vec3 color) { - //if going to srgb, clamp from 0 to 1. - color = clamp(color, vec3(0.0), vec3(1.0)); - const vec3 a = vec3(0.055f); - return mix((vec3(1.0f) + a) * pow(color.rgb, vec3(1.0f / 2.4f)) - a, 12.92f * color.rgb, lessThan(color.rgb, vec3(0.0031308f))); + //color = clamp(color, vec3(0.0), vec3(1.0)); + //const vec3 a = vec3(0.055f); + //return mix((vec3(1.0f) + a) * pow(color.rgb, vec3(1.0f / 2.4f)) - a, 12.92f * color.rgb, lessThan(color.rgb, vec3(0.0031308f))); + // Approximation from http://chilliant.blogspot.com/2012/08/srgb-approximations-for-hlsl.html + return max(vec3(1.055) * pow(color, vec3(0.416666667)) - vec3(0.055), vec3(0.0)); +} + +// This expects 0-1 range input, outside that range it behaves poorly. +vec3 srgb_to_linear(vec3 color) { + // Approximation from http://chilliant.blogspot.com/2012/08/srgb-approximations-for-hlsl.html + return color * (color * (color * 0.305306011 + 0.682171111) + 0.012522878); } #define TONEMAPPER_LINEAR 0 diff --git a/drivers/gles3/storage/material_storage.cpp b/drivers/gles3/storage/material_storage.cpp index 5ce02f6825..e93c503396 100644 --- a/drivers/gles3/storage/material_storage.cpp +++ b/drivers/gles3/storage/material_storage.cpp @@ -1024,6 +1024,7 @@ MaterialData::~MaterialData() { if (uniform_buffer) { glDeleteBuffers(1, &uniform_buffer); + uniform_buffer = 0; } } @@ -1677,9 +1678,6 @@ ShaderCompiler::DefaultIdentifierActions actions; //shaders.copy.initialize(); //shaders.copy_version = shaders.copy.version_create(); //TODO //shaders.copy.version_bind_shader(shaders.copy_version, CopyShaderGLES3::MODE_COPY_SECTION); - //shaders.cubemap_filter.init(); - //bool ggx_hq = GLOBAL_GET("rendering/quality/reflections/high_quality_ggx"); - //shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::LOW_QUALITY, !ggx_hq); } MaterialStorage::~MaterialStorage() { @@ -3133,6 +3131,7 @@ GLES3::ShaderData *GLES3::_create_sky_shader_func() { // Sky material void SkyMaterialData::update_parameters(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty) { + uniform_set_updated = true; return update_parameters_internal(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size); } diff --git a/drivers/gles3/storage/material_storage.h b/drivers/gles3/storage/material_storage.h index d6f15e7056..6a11a10d76 100644 --- a/drivers/gles3/storage/material_storage.h +++ b/drivers/gles3/storage/material_storage.h @@ -45,6 +45,7 @@ #include "drivers/gles3/shaders/copy.glsl.gen.h" #include "../shaders/canvas.glsl.gen.h" +#include "../shaders/cubemap_filter.glsl.gen.h" #include "../shaders/scene.glsl.gen.h" #include "../shaders/sky.glsl.gen.h" @@ -56,6 +57,7 @@ struct Shaders { CanvasShaderGLES3 canvas_shader; SkyShaderGLES3 sky_shader; SceneShaderGLES3 scene_shader; + CubemapFilterShaderGLES3 cubemap_filter_shader; ShaderCompiler compiler_canvas; ShaderCompiler compiler_scene; @@ -241,6 +243,7 @@ ShaderData *_create_sky_shader_func(); struct SkyMaterialData : public MaterialData { SkyShaderData *shader_data = nullptr; + bool uniform_set_updated = false; virtual void set_render_priority(int p_priority) {} virtual void set_next_pass(RID p_pass) {} @@ -458,8 +461,6 @@ private: SelfList<Material>::List material_update_list; - //static void _material_uniform_set_erased(void *p_material); - public: static MaterialStorage *get_singleton(); diff --git a/drivers/gles3/storage/mesh_storage.cpp b/drivers/gles3/storage/mesh_storage.cpp index e754d6150a..5739a63cac 100644 --- a/drivers/gles3/storage/mesh_storage.cpp +++ b/drivers/gles3/storage/mesh_storage.cpp @@ -570,24 +570,29 @@ void MeshStorage::mesh_clear(RID p_mesh) { if (s.vertex_buffer != 0) { glDeleteBuffers(1, &s.vertex_buffer); + s.vertex_buffer = 0; } if (s.version_count != 0) { for (uint32_t j = 0; j < s.version_count; j++) { glDeleteVertexArrays(1, &s.versions[j].vertex_array); + s.versions[j].vertex_array = 0; } } if (s.attribute_buffer != 0) { glDeleteBuffers(1, &s.attribute_buffer); + s.attribute_buffer = 0; } if (s.skin_buffer != 0) { glDeleteBuffers(1, &s.skin_buffer); + s.skin_buffer = 0; } if (s.index_buffer != 0) { glDeleteBuffers(1, &s.index_buffer); + s.index_buffer = 0; } memdelete(mesh->surfaces[i]); } @@ -804,17 +809,20 @@ void MeshStorage::_mesh_instance_clear(MeshInstance *mi) { if (mi->surfaces[i].version_count != 0) { for (uint32_t j = 0; j < mi->surfaces[i].version_count; j++) { glDeleteVertexArrays(1, &mi->surfaces[i].versions[j].vertex_array); + mi->surfaces[i].versions[j].vertex_array = 0; } memfree(mi->surfaces[i].versions); } if (mi->surfaces[i].vertex_buffer != 0) { glDeleteBuffers(1, &mi->surfaces[i].vertex_buffer); + mi->surfaces[i].vertex_buffer = 0; } } mi->surfaces.clear(); if (mi->blend_weights_buffer != 0) { glDeleteBuffers(1, &mi->blend_weights_buffer); + mi->blend_weights_buffer = 0; } mi->blend_weights.clear(); mi->weights_dirty = false; diff --git a/drivers/gles3/storage/texture_storage.cpp b/drivers/gles3/storage/texture_storage.cpp index 4396ca4f93..f932fa8bad 100644 --- a/drivers/gles3/storage/texture_storage.cpp +++ b/drivers/gles3/storage/texture_storage.cpp @@ -554,7 +554,7 @@ Ref<Image> TextureStorage::_get_gl_image_and_format(const Ref<Image> &p_image, I } break; */ default: { - ERR_FAIL_V(Ref<Image>()); + ERR_FAIL_V_MSG(Ref<Image>(), "Image Format: " + itos(p_format) + " is not supported by the OpenGL3 Renderer"); } } @@ -968,7 +968,7 @@ void TextureStorage::texture_set_data(RID p_texture, const Ref<Image> &p_image, Image::Format real_format; Ref<Image> img = _get_gl_image_and_format(p_image, p_image->get_format(), 0, real_format, format, internal_format, type, compressed, texture->resize_to_po2); - + ERR_FAIL_COND(img.is_null()); if (texture->resize_to_po2) { if (p_image->is_compressed()) { ERR_PRINT("Texture '" + texture->path + "' is required to be a power of 2 because it uses either mipmaps or repeat, so it was decompressed. This will hurt performance and memory usage."); @@ -1325,6 +1325,7 @@ void TextureStorage::_update_render_target(RenderTarget *rt) { rt->color_internal_format = rt->flags[RENDER_TARGET_TRANSPARENT] ? GL_RGBA8 : GL_RGB10_A2; rt->color_format = GL_RGBA; + rt->color_type = rt->flags[RENDER_TARGET_TRANSPARENT] ? GL_BYTE : GL_UNSIGNED_INT_2_10_10_10_REV; rt->image_format = Image::FORMAT_RGBA8; glDisable(GL_SCISSOR_TEST); diff --git a/drivers/gles3/storage/texture_storage.h b/drivers/gles3/storage/texture_storage.h index 8281b8c596..b092a009c1 100644 --- a/drivers/gles3/storage/texture_storage.h +++ b/drivers/gles3/storage/texture_storage.h @@ -249,7 +249,10 @@ struct Texture { [[fallthrough]]; case RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS: { pmag = GL_NEAREST; - if (config->use_nearest_mip_filter) { + if (mipmaps <= 1) { + pmin = GL_NEAREST; + max_lod = 0; + } else if (config->use_nearest_mip_filter) { pmin = GL_NEAREST_MIPMAP_NEAREST; } else { pmin = GL_NEAREST_MIPMAP_LINEAR; @@ -261,9 +264,11 @@ struct Texture { [[fallthrough]]; case RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS: { pmag = GL_LINEAR; - if (config->use_nearest_mip_filter) { + if (mipmaps <= 1) { + pmin = GL_LINEAR; + max_lod = 0; + } else if (config->use_nearest_mip_filter) { pmin = GL_LINEAR_MIPMAP_NEAREST; - } else { pmin = GL_LINEAR_MIPMAP_LINEAR; } |