OpenGL: Implement rendering of lightmaps
This commit is contained in:
parent
654132cb9c
commit
749f60ee36
|
@ -130,9 +130,27 @@ void RasterizerSceneGLES3::GeometryInstanceGLES3::_mark_dirty() {
|
|||
}
|
||||
|
||||
void RasterizerSceneGLES3::GeometryInstanceGLES3::set_use_lightmap(RID p_lightmap_instance, const Rect2 &p_lightmap_uv_scale, int p_lightmap_slice_index) {
|
||||
lightmap_instance = p_lightmap_instance;
|
||||
lightmap_uv_scale = p_lightmap_uv_scale;
|
||||
lightmap_slice_index = p_lightmap_slice_index;
|
||||
|
||||
_mark_dirty();
|
||||
}
|
||||
|
||||
void RasterizerSceneGLES3::GeometryInstanceGLES3::set_lightmap_capture(const Color *p_sh9) {
|
||||
if (p_sh9) {
|
||||
if (lightmap_sh == nullptr) {
|
||||
lightmap_sh = memnew(GeometryInstanceLightmapSH);
|
||||
}
|
||||
|
||||
memcpy(lightmap_sh->sh, p_sh9, sizeof(Color) * 9);
|
||||
} else {
|
||||
if (lightmap_sh != nullptr) {
|
||||
memdelete(lightmap_sh);
|
||||
lightmap_sh = nullptr;
|
||||
}
|
||||
}
|
||||
_mark_dirty();
|
||||
}
|
||||
|
||||
void RasterizerSceneGLES3::_update_dirty_geometry_instances() {
|
||||
|
@ -1271,12 +1289,15 @@ void RasterizerSceneGLES3::_fill_render_list(RenderListType p_render_list, const
|
|||
int32_t shadow_id = GLES3::LightStorage::get_singleton()->light_instance_get_shadow_id(light_instance);
|
||||
|
||||
if (GLES3::LightStorage::get_singleton()->light_has_shadow(light) && shadow_id >= 0) {
|
||||
GeometryInstanceGLES3::LightPass pass;
|
||||
pass.light_id = GLES3::LightStorage::get_singleton()->light_instance_get_gl_id(light_instance);
|
||||
pass.shadow_id = shadow_id;
|
||||
pass.light_instance_rid = light_instance;
|
||||
pass.is_omni = true;
|
||||
inst->light_passes.push_back(pass);
|
||||
// Skip static lights when a lightmap is used.
|
||||
if (!inst->lightmap_instance.is_valid() || GLES3::LightStorage::get_singleton()->light_get_bake_mode(light) != RenderingServer::LIGHT_BAKE_STATIC) {
|
||||
GeometryInstanceGLES3::LightPass pass;
|
||||
pass.light_id = GLES3::LightStorage::get_singleton()->light_instance_get_gl_id(light_instance);
|
||||
pass.shadow_id = shadow_id;
|
||||
pass.light_instance_rid = light_instance;
|
||||
pass.is_omni = true;
|
||||
inst->light_passes.push_back(pass);
|
||||
}
|
||||
} else {
|
||||
// Lights without shadow can all go in base pass.
|
||||
inst->omni_light_gl_cache.push_back((uint32_t)GLES3::LightStorage::get_singleton()->light_instance_get_gl_id(light_instance));
|
||||
|
@ -1294,11 +1315,14 @@ void RasterizerSceneGLES3::_fill_render_list(RenderListType p_render_list, const
|
|||
int32_t shadow_id = GLES3::LightStorage::get_singleton()->light_instance_get_shadow_id(light_instance);
|
||||
|
||||
if (GLES3::LightStorage::get_singleton()->light_has_shadow(light) && shadow_id >= 0) {
|
||||
GeometryInstanceGLES3::LightPass pass;
|
||||
pass.light_id = GLES3::LightStorage::get_singleton()->light_instance_get_gl_id(light_instance);
|
||||
pass.shadow_id = shadow_id;
|
||||
pass.light_instance_rid = light_instance;
|
||||
inst->light_passes.push_back(pass);
|
||||
// Skip static lights when a lightmap is used.
|
||||
if (!inst->lightmap_instance.is_valid() || GLES3::LightStorage::get_singleton()->light_get_bake_mode(light) != RenderingServer::LIGHT_BAKE_STATIC) {
|
||||
GeometryInstanceGLES3::LightPass pass;
|
||||
pass.light_id = GLES3::LightStorage::get_singleton()->light_instance_get_gl_id(light_instance);
|
||||
pass.shadow_id = shadow_id;
|
||||
pass.light_instance_rid = light_instance;
|
||||
inst->light_passes.push_back(pass);
|
||||
}
|
||||
} else {
|
||||
// Lights without shadow can all go in base pass.
|
||||
inst->spot_light_gl_cache.push_back((uint32_t)GLES3::LightStorage::get_singleton()->light_instance_get_gl_id(light_instance));
|
||||
|
@ -1610,6 +1634,8 @@ void RasterizerSceneGLES3::_setup_lights(const RenderDataGLES3 *p_render_data, b
|
|||
light_data.direction[1] = direction.y;
|
||||
light_data.direction[2] = direction.z;
|
||||
|
||||
light_data.bake_mode = light_storage->light_get_bake_mode(base);
|
||||
|
||||
float sign = light_storage->light_is_negative(base) ? -1 : 1;
|
||||
|
||||
light_data.energy = sign * light_storage->light_get_param(base, RS::LIGHT_PARAM_ENERGY);
|
||||
|
@ -1758,6 +1784,8 @@ void RasterizerSceneGLES3::_setup_lights(const RenderDataGLES3 *p_render_data, b
|
|||
light_data.position[1] = pos.y;
|
||||
light_data.position[2] = pos.z;
|
||||
|
||||
light_data.bake_mode = light_storage->light_get_bake_mode(base);
|
||||
|
||||
float radius = MAX(0.001, light_storage->light_get_param(base, RS::LIGHT_PARAM_RANGE));
|
||||
light_data.inv_radius = 1.0 / radius;
|
||||
|
||||
|
@ -2621,6 +2649,7 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params,
|
|||
|
||||
bool uses_additive_lighting = (inst->light_passes.size() + p_render_data->directional_shadow_count) > 0;
|
||||
uses_additive_lighting = uses_additive_lighting && !shader->unshaded;
|
||||
|
||||
// TODOS
|
||||
/*
|
||||
* Still a bug when atlas space is limited. Somehow need to evict light when it doesn't have a spot on the atlas, current check isn't enough
|
||||
|
@ -2650,6 +2679,12 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params,
|
|||
// Shadow wasn't able to get a spot on the atlas. So skip it.
|
||||
continue;
|
||||
}
|
||||
} else if (pass > 0) {
|
||||
uint32_t shadow_id = MAX_DIRECTIONAL_LIGHTS - 1 - (pass - int32_t(inst->light_passes.size()));
|
||||
if (inst->lightmap_instance.is_valid() && scene_state.directional_lights[shadow_id].bake_mode == RenderingServer::LIGHT_BAKE_STATIC) {
|
||||
// Skip shadows for static lights on meshes with a lightmap.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2738,12 +2773,16 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params,
|
|||
|
||||
GLuint vertex_array_gl = 0;
|
||||
GLuint index_array_gl = 0;
|
||||
uint64_t vertex_input_mask = shader->vertex_input_mask;
|
||||
if (inst->lightmap_instance.is_valid()) {
|
||||
vertex_input_mask |= 1 << RS::ARRAY_TEX_UV2;
|
||||
}
|
||||
|
||||
//skeleton and blend shape
|
||||
// Skeleton and blend shapes.
|
||||
if (surf->owner->mesh_instance.is_valid()) {
|
||||
mesh_storage->mesh_instance_surface_get_vertex_arrays_and_format(surf->owner->mesh_instance, surf->surface_index, shader->vertex_input_mask, vertex_array_gl);
|
||||
mesh_storage->mesh_instance_surface_get_vertex_arrays_and_format(surf->owner->mesh_instance, surf->surface_index, vertex_input_mask, vertex_array_gl);
|
||||
} else {
|
||||
mesh_storage->mesh_surface_get_vertex_arrays_and_format(mesh_surface, shader->vertex_input_mask, vertex_array_gl);
|
||||
mesh_storage->mesh_surface_get_vertex_arrays_and_format(mesh_surface, vertex_input_mask, vertex_array_gl);
|
||||
}
|
||||
|
||||
index_array_gl = mesh_storage->mesh_surface_get_index_buffer(mesh_surface, surf->lod_index);
|
||||
|
@ -2803,12 +2842,28 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params,
|
|||
if (p_render_data->directional_light_count == p_render_data->directional_shadow_count) {
|
||||
spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_DIRECTIONAL;
|
||||
}
|
||||
|
||||
if (inst->lightmap_instance.is_valid()) {
|
||||
spec_constants |= SceneShaderGLES3::USE_LIGHTMAP;
|
||||
|
||||
GLES3::LightmapInstance *li = GLES3::LightStorage::get_singleton()->get_lightmap_instance(inst->lightmap_instance);
|
||||
GLES3::Lightmap *lm = GLES3::LightStorage::get_singleton()->get_lightmap(li->lightmap);
|
||||
|
||||
if (lm->uses_spherical_harmonics) {
|
||||
spec_constants |= SceneShaderGLES3::USE_SH_LIGHTMAP;
|
||||
}
|
||||
} else if (inst->lightmap_sh) {
|
||||
spec_constants |= SceneShaderGLES3::USE_LIGHTMAP_CAPTURE;
|
||||
} else {
|
||||
spec_constants |= SceneShaderGLES3::DISABLE_LIGHTMAP;
|
||||
}
|
||||
} else {
|
||||
// Only base pass uses the radiance map.
|
||||
spec_constants &= ~SceneShaderGLES3::USE_RADIANCE_MAP;
|
||||
spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_OMNI;
|
||||
spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_SPOT;
|
||||
spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_DIRECTIONAL;
|
||||
spec_constants |= SceneShaderGLES3::DISABLE_LIGHTMAP;
|
||||
}
|
||||
|
||||
if (uses_additive_lighting) {
|
||||
|
@ -2831,6 +2886,10 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params,
|
|||
// Render directional lights.
|
||||
|
||||
uint32_t shadow_id = MAX_DIRECTIONAL_LIGHTS - 1 - (pass - int32_t(inst->light_passes.size()));
|
||||
if (pass == 0 && inst->lightmap_instance.is_valid() && scene_state.directional_lights[shadow_id].bake_mode == RenderingServer::LIGHT_BAKE_STATIC) {
|
||||
// Disable additive lighting with a static light and a lightmap.
|
||||
spec_constants &= ~SceneShaderGLES3::USE_ADDITIVE_LIGHTING;
|
||||
}
|
||||
if (scene_state.directional_shadows[shadow_id].shadow_split_offsets[0] == scene_state.directional_shadows[shadow_id].shadow_split_offsets[1]) {
|
||||
// Orthogonal, do nothing.
|
||||
} else if (scene_state.directional_shadows[shadow_id].shadow_split_offsets[1] == scene_state.directional_shadows[shadow_id].shadow_split_offsets[2]) {
|
||||
|
@ -2920,6 +2979,46 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params,
|
|||
glUniform1uiv(material_storage->shaders.scene_shader.version_get_uniform(SceneShaderGLES3::SPOT_LIGHT_INDICES, shader->version, instance_variant, spec_constants), inst->spot_light_gl_cache.size(), inst->spot_light_gl_cache.ptr());
|
||||
}
|
||||
|
||||
if (inst->lightmap_instance.is_valid()) {
|
||||
GLES3::LightmapInstance *li = GLES3::LightStorage::get_singleton()->get_lightmap_instance(inst->lightmap_instance);
|
||||
GLES3::Lightmap *lm = GLES3::LightStorage::get_singleton()->get_lightmap(li->lightmap);
|
||||
|
||||
GLuint tex = GLES3::TextureStorage::get_singleton()->texture_get_texid(lm->light_texture);
|
||||
glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 4);
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, tex);
|
||||
|
||||
material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::LIGHTMAP_SLICE, inst->lightmap_slice_index, shader->version, instance_variant, spec_constants);
|
||||
|
||||
Vector4 uv_scale(inst->lightmap_uv_scale.position.x, inst->lightmap_uv_scale.position.y, inst->lightmap_uv_scale.size.x, inst->lightmap_uv_scale.size.y);
|
||||
material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::LIGHTMAP_UV_SCALE, uv_scale, shader->version, instance_variant, spec_constants);
|
||||
|
||||
float exposure_normalization = 1.0;
|
||||
if (p_render_data->camera_attributes.is_valid()) {
|
||||
float enf = RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes);
|
||||
exposure_normalization = enf / lm->baked_exposure;
|
||||
}
|
||||
material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::LIGHTMAP_EXPOSURE_NORMALIZATION, exposure_normalization, shader->version, instance_variant, spec_constants);
|
||||
|
||||
if (lm->uses_spherical_harmonics) {
|
||||
Basis to_lm = li->transform.basis.inverse() * p_render_data->cam_transform.basis;
|
||||
to_lm = to_lm.inverse().transposed();
|
||||
GLfloat matrix[9] = {
|
||||
(GLfloat)to_lm.rows[0][0],
|
||||
(GLfloat)to_lm.rows[1][0],
|
||||
(GLfloat)to_lm.rows[2][0],
|
||||
(GLfloat)to_lm.rows[0][1],
|
||||
(GLfloat)to_lm.rows[1][1],
|
||||
(GLfloat)to_lm.rows[2][1],
|
||||
(GLfloat)to_lm.rows[0][2],
|
||||
(GLfloat)to_lm.rows[1][2],
|
||||
(GLfloat)to_lm.rows[2][2],
|
||||
};
|
||||
glUniformMatrix3fv(material_storage->shaders.scene_shader.version_get_uniform(SceneShaderGLES3::LIGHTMAP_NORMAL_XFORM, shader->version, instance_variant, spec_constants), 1, GL_FALSE, matrix);
|
||||
}
|
||||
} else if (inst->lightmap_sh) {
|
||||
glUniform4fv(material_storage->shaders.scene_shader.version_get_uniform(SceneShaderGLES3::LIGHTMAP_CAPTURES, shader->version, instance_variant, spec_constants), 9, reinterpret_cast<const GLfloat *>(inst->lightmap_sh->sh));
|
||||
}
|
||||
|
||||
prev_inst = inst;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -170,6 +170,9 @@ private:
|
|||
float cos_spot_angle;
|
||||
float specular_amount;
|
||||
float shadow_opacity;
|
||||
|
||||
float pad[3];
|
||||
uint32_t bake_mode;
|
||||
};
|
||||
static_assert(sizeof(LightData) % 16 == 0, "LightData size must be a multiple of 16 bytes");
|
||||
|
||||
|
@ -181,7 +184,7 @@ private:
|
|||
float size;
|
||||
|
||||
uint32_t enabled; // For use by SkyShaders
|
||||
float pad;
|
||||
uint32_t bake_mode;
|
||||
float shadow_opacity;
|
||||
float specular;
|
||||
};
|
||||
|
@ -269,6 +272,10 @@ private:
|
|||
GeometryInstanceGLES3 *owner = nullptr;
|
||||
};
|
||||
|
||||
struct GeometryInstanceLightmapSH {
|
||||
Color sh[9];
|
||||
};
|
||||
|
||||
class GeometryInstanceGLES3 : public RenderGeometryInstanceBase {
|
||||
public:
|
||||
//used during rendering
|
||||
|
@ -296,6 +303,11 @@ private:
|
|||
LocalVector<uint32_t> omni_light_gl_cache;
|
||||
LocalVector<uint32_t> spot_light_gl_cache;
|
||||
|
||||
RID lightmap_instance;
|
||||
Rect2 lightmap_uv_scale;
|
||||
uint32_t lightmap_slice_index;
|
||||
GeometryInstanceLightmapSH *lightmap_sh = nullptr;
|
||||
|
||||
// Used during setup.
|
||||
GeometryInstanceSurface *surface_caches = nullptr;
|
||||
SelfList<GeometryInstanceGLES3> dirty_list_element;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/* clang-format off */
|
||||
#[modes]
|
||||
|
||||
mode_color =
|
||||
mode_color =
|
||||
mode_color_instancing = \n#define USE_INSTANCING
|
||||
mode_depth = #define MODE_RENDER_DEPTH
|
||||
mode_depth_instancing = #define MODE_RENDER_DEPTH \n#define USE_INSTANCING
|
||||
|
@ -14,6 +14,9 @@ DISABLE_LIGHT_OMNI = false
|
|||
DISABLE_LIGHT_SPOT = false
|
||||
DISABLE_FOG = false
|
||||
USE_RADIANCE_MAP = true
|
||||
USE_LIGHTMAP = false
|
||||
USE_SH_LIGHTMAP = false
|
||||
USE_LIGHTMAP_CAPTURE = false
|
||||
USE_MULTIVIEW = false
|
||||
RENDER_SHADOWS = false
|
||||
RENDER_SHADOWS_LINEAR = false
|
||||
|
@ -676,6 +679,10 @@ multiview_data;
|
|||
|
||||
/* clang-format on */
|
||||
|
||||
#define LIGHT_BAKE_DISABLED 0u
|
||||
#define LIGHT_BAKE_STATIC 1u
|
||||
#define LIGHT_BAKE_DYNAMIC 2u
|
||||
|
||||
#ifndef MODE_RENDER_DEPTH
|
||||
// Directional light data.
|
||||
#if !defined(DISABLE_LIGHT_DIRECTIONAL) || (!defined(ADDITIVE_OMNI) && !defined(ADDITIVE_SPOT))
|
||||
|
@ -685,7 +692,8 @@ struct DirectionalLightData {
|
|||
mediump float energy;
|
||||
mediump vec3 color;
|
||||
mediump float size;
|
||||
mediump vec2 pad;
|
||||
lowp uint unused;
|
||||
lowp uint bake_mode;
|
||||
mediump float shadow_opacity;
|
||||
mediump float specular;
|
||||
};
|
||||
|
@ -718,6 +726,9 @@ struct LightData { // This structure needs to be as packed as possible.
|
|||
mediump float cone_angle;
|
||||
mediump float specular_amount;
|
||||
mediump float shadow_opacity;
|
||||
|
||||
lowp vec3 pad;
|
||||
lowp uint bake_mode;
|
||||
};
|
||||
|
||||
#if !defined(DISABLE_LIGHT_OMNI) || defined(ADDITIVE_OMNI)
|
||||
|
@ -834,6 +845,23 @@ float sample_shadow(highp sampler2DShadow shadow, float shadow_pixel_size, vec4
|
|||
|
||||
#endif // !MODE_RENDER_DEPTH
|
||||
|
||||
#ifndef DISABLE_LIGHTMAP
|
||||
#ifdef USE_LIGHTMAP
|
||||
uniform mediump sampler2DArray lightmap_textures; //texunit:-4
|
||||
uniform lowp uint lightmap_slice;
|
||||
uniform highp vec4 lightmap_uv_scale;
|
||||
uniform float lightmap_exposure_normalization;
|
||||
|
||||
#ifdef USE_SH_LIGHTMAP
|
||||
uniform mediump mat3 lightmap_normal_xform;
|
||||
#endif // USE_SH_LIGHTMAP
|
||||
#endif // USE_LIGHTMAP
|
||||
|
||||
#ifdef USE_LIGHTMAP_CAPTURE
|
||||
uniform mediump vec4[9] lightmap_captures;
|
||||
#endif // USE_LIGHTMAP_CAPTURE
|
||||
#endif // !DISABLE_LIGHTMAP
|
||||
|
||||
#ifdef USE_MULTIVIEW
|
||||
uniform highp sampler2DArray depth_buffer; // texunit:-6
|
||||
uniform highp sampler2DArray color_buffer; // texunit:-5
|
||||
|
@ -1406,7 +1434,6 @@ void main() {
|
|||
#endif
|
||||
|
||||
// Calculate Reflection probes
|
||||
// Calculate Lightmaps
|
||||
|
||||
#if defined(CUSTOM_RADIANCE_USED)
|
||||
specular_light = mix(specular_light, custom_radiance.rgb, custom_radiance.a);
|
||||
|
@ -1431,6 +1458,61 @@ void main() {
|
|||
ambient_light = mix(ambient_light, custom_irradiance.rgb, custom_irradiance.a);
|
||||
#endif // CUSTOM_IRRADIANCE_USED
|
||||
|
||||
#ifndef DISABLE_LIGHTMAP
|
||||
#ifdef USE_LIGHTMAP_CAPTURE
|
||||
{
|
||||
vec3 wnormal = mat3(scene_data.inv_view_matrix) * normal;
|
||||
const float c1 = 0.429043;
|
||||
const float c2 = 0.511664;
|
||||
const float c3 = 0.743125;
|
||||
const float c4 = 0.886227;
|
||||
const float c5 = 0.247708;
|
||||
ambient_light += (c1 * lightmap_captures[8].rgb * (wnormal.x * wnormal.x - wnormal.y * wnormal.y) +
|
||||
c3 * lightmap_captures[6].rgb * wnormal.z * wnormal.z +
|
||||
c4 * lightmap_captures[0].rgb -
|
||||
c5 * lightmap_captures[6].rgb +
|
||||
2.0 * c1 * lightmap_captures[4].rgb * wnormal.x * wnormal.y +
|
||||
2.0 * c1 * lightmap_captures[7].rgb * wnormal.x * wnormal.z +
|
||||
2.0 * c1 * lightmap_captures[5].rgb * wnormal.y * wnormal.z +
|
||||
2.0 * c2 * lightmap_captures[3].rgb * wnormal.x +
|
||||
2.0 * c2 * lightmap_captures[1].rgb * wnormal.y +
|
||||
2.0 * c2 * lightmap_captures[2].rgb * wnormal.z) *
|
||||
scene_data.emissive_exposure_normalization;
|
||||
}
|
||||
#else
|
||||
#ifdef USE_LIGHTMAP
|
||||
{
|
||||
vec3 uvw;
|
||||
uvw.xy = uv2 * lightmap_uv_scale.zw + lightmap_uv_scale.xy;
|
||||
uvw.z = float(lightmap_slice);
|
||||
|
||||
#ifdef USE_SH_LIGHTMAP
|
||||
uvw.z *= 4.0; // SH textures use 4 times more data.
|
||||
vec3 lm_light_l0 = textureLod(lightmap_textures, uvw + vec3(0.0, 0.0, 0.0), 0.0).rgb;
|
||||
vec3 lm_light_l1n1 = textureLod(lightmap_textures, uvw + vec3(0.0, 0.0, 1.0), 0.0).rgb;
|
||||
vec3 lm_light_l1_0 = textureLod(lightmap_textures, uvw + vec3(0.0, 0.0, 2.0), 0.0).rgb;
|
||||
vec3 lm_light_l1p1 = textureLod(lightmap_textures, uvw + vec3(0.0, 0.0, 3.0), 0.0).rgb;
|
||||
|
||||
vec3 n = normalize(lightmap_normal_xform * normal);
|
||||
|
||||
ambient_light += lm_light_l0 * 0.282095f;
|
||||
ambient_light += lm_light_l1n1 * 0.32573 * n.y * lightmap_exposure_normalization;
|
||||
ambient_light += lm_light_l1_0 * 0.32573 * n.z * lightmap_exposure_normalization;
|
||||
ambient_light += lm_light_l1p1 * 0.32573 * n.x * lightmap_exposure_normalization;
|
||||
if (metallic > 0.01) { // Since the more direct bounced light is lost, we can kind of fake it with this trick.
|
||||
vec3 r = reflect(normalize(-vertex), normal);
|
||||
specular_light += lm_light_l1n1 * 0.32573 * r.y * lightmap_exposure_normalization;
|
||||
specular_light += lm_light_l1_0 * 0.32573 * r.z * lightmap_exposure_normalization;
|
||||
specular_light += lm_light_l1p1 * 0.32573 * r.x * lightmap_exposure_normalization;
|
||||
}
|
||||
#else
|
||||
ambient_light += textureLod(lightmap_textures, uvw, 0.0).rgb * lightmap_exposure_normalization;
|
||||
#endif
|
||||
}
|
||||
#endif // USE_LIGHTMAP
|
||||
#endif // USE_LIGHTMAP_CAPTURE
|
||||
#endif // !DISABLE_LIGHTMAP
|
||||
|
||||
{
|
||||
#if defined(AMBIENT_LIGHT_DISABLED)
|
||||
ambient_light = vec3(0.0, 0.0, 0.0);
|
||||
|
@ -1466,6 +1548,11 @@ void main() {
|
|||
|
||||
#ifndef DISABLE_LIGHT_DIRECTIONAL
|
||||
for (uint i = uint(0); i < scene_data.directional_light_count; i++) {
|
||||
#if defined(USE_LIGHTMAP) && !defined(DISABLE_LIGHTMAP)
|
||||
if (directional_lights[i].bake_mode == LIGHT_BAKE_STATIC) {
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
light_compute(normal, normalize(directional_lights[i].direction), normalize(view), directional_lights[i].size, directional_lights[i].color * directional_lights[i].energy, true, 1.0, f0, roughness, metallic, 1.0, albedo, alpha,
|
||||
#ifdef LIGHT_BACKLIGHT_USED
|
||||
backlight,
|
||||
|
@ -1490,6 +1577,11 @@ void main() {
|
|||
if (i >= omni_light_count) {
|
||||
break;
|
||||
}
|
||||
#if defined(USE_LIGHTMAP) && !defined(DISABLE_LIGHTMAP)
|
||||
if (omni_lights[omni_light_indices[i]].bake_mode == LIGHT_BAKE_STATIC) {
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
light_process_omni(omni_light_indices[i], vertex, view, normal, f0, roughness, metallic, 1.0, albedo, alpha,
|
||||
#ifdef LIGHT_BACKLIGHT_USED
|
||||
backlight,
|
||||
|
@ -1513,6 +1605,11 @@ void main() {
|
|||
if (i >= spot_light_count) {
|
||||
break;
|
||||
}
|
||||
#if defined(USE_LIGHTMAP) && !defined(DISABLE_LIGHTMAP)
|
||||
if (spot_lights[spot_light_indices[i]].bake_mode == LIGHT_BAKE_STATIC) {
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
light_process_spot(spot_light_indices[i], vertex, view, normal, f0, roughness, metallic, 1.0, albedo, alpha,
|
||||
#ifdef LIGHT_BACKLIGHT_USED
|
||||
backlight,
|
||||
|
|
|
@ -569,69 +569,171 @@ void LightStorage::lightmap_initialize(RID p_rid) {
|
|||
|
||||
void LightStorage::lightmap_free(RID p_rid) {
|
||||
Lightmap *lightmap = lightmap_owner.get_or_null(p_rid);
|
||||
ERR_FAIL_NULL(lightmap);
|
||||
lightmap->dependency.deleted_notify(p_rid);
|
||||
lightmap_owner.free(p_rid);
|
||||
}
|
||||
|
||||
void LightStorage::lightmap_set_textures(RID p_lightmap, RID p_light, bool p_uses_spherical_haromics) {
|
||||
Lightmap *lightmap = lightmap_owner.get_or_null(p_lightmap);
|
||||
ERR_FAIL_NULL(lightmap);
|
||||
lightmap->light_texture = p_light;
|
||||
lightmap->uses_spherical_harmonics = p_uses_spherical_haromics;
|
||||
|
||||
GLuint tex = GLES3::TextureStorage::get_singleton()->texture_get_texid(lightmap->light_texture);
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, tex);
|
||||
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, 0);
|
||||
}
|
||||
|
||||
void LightStorage::lightmap_set_probe_bounds(RID p_lightmap, const AABB &p_bounds) {
|
||||
Lightmap *lightmap = lightmap_owner.get_or_null(p_lightmap);
|
||||
ERR_FAIL_NULL(lightmap);
|
||||
lightmap->bounds = p_bounds;
|
||||
}
|
||||
|
||||
void LightStorage::lightmap_set_probe_interior(RID p_lightmap, bool p_interior) {
|
||||
Lightmap *lightmap = lightmap_owner.get_or_null(p_lightmap);
|
||||
ERR_FAIL_NULL(lightmap);
|
||||
lightmap->interior = p_interior;
|
||||
}
|
||||
|
||||
void LightStorage::lightmap_set_probe_capture_data(RID p_lightmap, const PackedVector3Array &p_points, const PackedColorArray &p_point_sh, const PackedInt32Array &p_tetrahedra, const PackedInt32Array &p_bsp_tree) {
|
||||
Lightmap *lightmap = lightmap_owner.get_or_null(p_lightmap);
|
||||
ERR_FAIL_NULL(lightmap);
|
||||
|
||||
if (p_points.size()) {
|
||||
ERR_FAIL_COND(p_points.size() * 9 != p_point_sh.size());
|
||||
ERR_FAIL_COND((p_tetrahedra.size() % 4) != 0);
|
||||
ERR_FAIL_COND((p_bsp_tree.size() % 6) != 0);
|
||||
}
|
||||
|
||||
lightmap->points = p_points;
|
||||
lightmap->point_sh = p_point_sh;
|
||||
lightmap->tetrahedra = p_tetrahedra;
|
||||
lightmap->bsp_tree = p_bsp_tree;
|
||||
}
|
||||
|
||||
void LightStorage::lightmap_set_baked_exposure_normalization(RID p_lightmap, float p_exposure) {
|
||||
Lightmap *lightmap = lightmap_owner.get_or_null(p_lightmap);
|
||||
ERR_FAIL_NULL(lightmap);
|
||||
|
||||
lightmap->baked_exposure = p_exposure;
|
||||
}
|
||||
|
||||
PackedVector3Array LightStorage::lightmap_get_probe_capture_points(RID p_lightmap) const {
|
||||
return PackedVector3Array();
|
||||
Lightmap *lightmap = lightmap_owner.get_or_null(p_lightmap);
|
||||
ERR_FAIL_NULL_V(lightmap, PackedVector3Array());
|
||||
return lightmap->points;
|
||||
}
|
||||
|
||||
PackedColorArray LightStorage::lightmap_get_probe_capture_sh(RID p_lightmap) const {
|
||||
return PackedColorArray();
|
||||
Lightmap *lightmap = lightmap_owner.get_or_null(p_lightmap);
|
||||
ERR_FAIL_NULL_V(lightmap, PackedColorArray());
|
||||
return lightmap->point_sh;
|
||||
}
|
||||
|
||||
PackedInt32Array LightStorage::lightmap_get_probe_capture_tetrahedra(RID p_lightmap) const {
|
||||
return PackedInt32Array();
|
||||
Lightmap *lightmap = lightmap_owner.get_or_null(p_lightmap);
|
||||
ERR_FAIL_NULL_V(lightmap, PackedInt32Array());
|
||||
return lightmap->tetrahedra;
|
||||
}
|
||||
|
||||
PackedInt32Array LightStorage::lightmap_get_probe_capture_bsp_tree(RID p_lightmap) const {
|
||||
return PackedInt32Array();
|
||||
Lightmap *lightmap = lightmap_owner.get_or_null(p_lightmap);
|
||||
ERR_FAIL_NULL_V(lightmap, PackedInt32Array());
|
||||
return lightmap->bsp_tree;
|
||||
}
|
||||
|
||||
AABB LightStorage::lightmap_get_aabb(RID p_lightmap) const {
|
||||
return AABB();
|
||||
Lightmap *lightmap = lightmap_owner.get_or_null(p_lightmap);
|
||||
ERR_FAIL_NULL_V(lightmap, AABB());
|
||||
return lightmap->bounds;
|
||||
}
|
||||
|
||||
void LightStorage::lightmap_tap_sh_light(RID p_lightmap, const Vector3 &p_point, Color *r_sh) {
|
||||
Lightmap *lm = lightmap_owner.get_or_null(p_lightmap);
|
||||
ERR_FAIL_NULL(lm);
|
||||
|
||||
for (int i = 0; i < 9; i++) {
|
||||
r_sh[i] = Color(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
if (!lm->points.size() || !lm->bsp_tree.size() || !lm->tetrahedra.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
static_assert(sizeof(Lightmap::BSP) == 24);
|
||||
|
||||
const Lightmap::BSP *bsp = (const Lightmap::BSP *)lm->bsp_tree.ptr();
|
||||
int32_t node = 0;
|
||||
while (node >= 0) {
|
||||
if (Plane(bsp[node].plane[0], bsp[node].plane[1], bsp[node].plane[2], bsp[node].plane[3]).is_point_over(p_point)) {
|
||||
#ifdef DEBUG_ENABLED
|
||||
ERR_FAIL_COND(bsp[node].over >= 0 && bsp[node].over < node);
|
||||
#endif
|
||||
|
||||
node = bsp[node].over;
|
||||
} else {
|
||||
#ifdef DEBUG_ENABLED
|
||||
ERR_FAIL_COND(bsp[node].under >= 0 && bsp[node].under < node);
|
||||
#endif
|
||||
node = bsp[node].under;
|
||||
}
|
||||
}
|
||||
|
||||
if (node == Lightmap::BSP::EMPTY_LEAF) {
|
||||
return; // Nothing could be done.
|
||||
}
|
||||
|
||||
node = ABS(node) - 1;
|
||||
|
||||
uint32_t *tetrahedron = (uint32_t *)&lm->tetrahedra[node * 4];
|
||||
Vector3 points[4] = { lm->points[tetrahedron[0]], lm->points[tetrahedron[1]], lm->points[tetrahedron[2]], lm->points[tetrahedron[3]] };
|
||||
const Color *sh_colors[4]{ &lm->point_sh[tetrahedron[0] * 9], &lm->point_sh[tetrahedron[1] * 9], &lm->point_sh[tetrahedron[2] * 9], &lm->point_sh[tetrahedron[3] * 9] };
|
||||
Color barycentric = Geometry3D::tetrahedron_get_barycentric_coords(points[0], points[1], points[2], points[3], p_point);
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
float c = CLAMP(barycentric[i], 0.0, 1.0);
|
||||
for (int j = 0; j < 9; j++) {
|
||||
r_sh[j] += sh_colors[i][j] * c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool LightStorage::lightmap_is_interior(RID p_lightmap) const {
|
||||
return false;
|
||||
Lightmap *lightmap = lightmap_owner.get_or_null(p_lightmap);
|
||||
ERR_FAIL_NULL_V(lightmap, false);
|
||||
return lightmap->interior;
|
||||
}
|
||||
|
||||
void LightStorage::lightmap_set_probe_capture_update_speed(float p_speed) {
|
||||
lightmap_probe_capture_update_speed = p_speed;
|
||||
}
|
||||
|
||||
float LightStorage::lightmap_get_probe_capture_update_speed() const {
|
||||
return 0;
|
||||
return lightmap_probe_capture_update_speed;
|
||||
}
|
||||
|
||||
/* LIGHTMAP INSTANCE */
|
||||
|
||||
RID LightStorage::lightmap_instance_create(RID p_lightmap) {
|
||||
return RID();
|
||||
LightmapInstance li;
|
||||
li.lightmap = p_lightmap;
|
||||
return lightmap_instance_owner.make_rid(li);
|
||||
}
|
||||
|
||||
void LightStorage::lightmap_instance_free(RID p_lightmap) {
|
||||
lightmap_instance_owner.free(p_lightmap);
|
||||
}
|
||||
|
||||
void LightStorage::lightmap_instance_set_transform(RID p_lightmap, const Transform3D &p_transform) {
|
||||
LightmapInstance *li = lightmap_instance_owner.get_or_null(p_lightmap);
|
||||
ERR_FAIL_NULL(li);
|
||||
li->transform = p_transform;
|
||||
}
|
||||
|
||||
/* SHADOW ATLAS API */
|
||||
|
|
|
@ -155,6 +155,11 @@ struct Lightmap {
|
|||
Dependency dependency;
|
||||
};
|
||||
|
||||
struct LightmapInstance {
|
||||
RID lightmap;
|
||||
Transform3D transform;
|
||||
};
|
||||
|
||||
class LightStorage : public RendererLightStorage {
|
||||
public:
|
||||
enum ShadowAtlastQuadrant {
|
||||
|
@ -179,9 +184,14 @@ private:
|
|||
/* LIGHTMAP */
|
||||
|
||||
Vector<RID> lightmap_textures;
|
||||
float lightmap_probe_capture_update_speed = 4;
|
||||
|
||||
mutable RID_Owner<Lightmap, true> lightmap_owner;
|
||||
|
||||
/* LIGHTMAP INSTANCE */
|
||||
|
||||
mutable RID_Owner<LightmapInstance> lightmap_instance_owner;
|
||||
|
||||
/* SHADOW ATLAS */
|
||||
|
||||
// Note: The ShadowAtlas in the OpenGL is virtual. Each light gets assigned its
|
||||
|
@ -622,6 +632,9 @@ public:
|
|||
|
||||
/* LIGHTMAP INSTANCE */
|
||||
|
||||
LightmapInstance *get_lightmap_instance(RID p_rid) { return lightmap_instance_owner.get_or_null(p_rid); };
|
||||
bool owns_lightmap_instance(RID p_rid) { return lightmap_instance_owner.owns(p_rid); };
|
||||
|
||||
virtual RID lightmap_instance_create(RID p_lightmap) override;
|
||||
virtual void lightmap_instance_free(RID p_lightmap) override;
|
||||
virtual void lightmap_instance_set_transform(RID p_lightmap, const Transform3D &p_transform) override;
|
||||
|
|
|
@ -1523,7 +1523,7 @@ PackedStringArray LightmapGI::get_configuration_warnings() const {
|
|||
PackedStringArray warnings = Node::get_configuration_warnings();
|
||||
|
||||
if (OS::get_singleton()->get_current_rendering_method() == "gl_compatibility") {
|
||||
warnings.push_back(RTR("LightmapGI nodes are not supported when using the GL Compatibility backend yet. Support will be added in a future release."));
|
||||
warnings.push_back(RTR("Lightmap cannot be baked when using the GL Compatibility backend yet. Support will be added in a future release."));
|
||||
return warnings;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue