Skip to content

Commit

Permalink
OpenGL: Implement rendering of lightmaps
Browse files Browse the repository at this point in the history
  • Loading branch information
dsnopek committed Dec 5, 2023
1 parent 80de898 commit 79dba3c
Show file tree
Hide file tree
Showing 6 changed files with 343 additions and 24 deletions.
117 changes: 106 additions & 11 deletions drivers/gles3/rasterizer_scene_gles3.cpp
Expand Up @@ -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() {
Expand Down Expand Up @@ -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));
Expand All @@ -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));
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
}
}
}

Expand Down Expand Up @@ -2803,12 +2838,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) {
Expand All @@ -2831,6 +2882,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]) {
Expand Down Expand Up @@ -2920,6 +2975,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;
}
}
Expand Down
14 changes: 13 additions & 1 deletion drivers/gles3/rasterizer_scene_gles3.h
Expand Up @@ -170,6 +170,9 @@ class RasterizerSceneGLES3 : public RendererSceneRender {
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");

Expand All @@ -181,7 +184,7 @@ class RasterizerSceneGLES3 : public RendererSceneRender {
float size;

uint32_t enabled; // For use by SkyShaders
float pad;
uint32_t bake_mode;
float shadow_opacity;
float specular;
};
Expand Down Expand Up @@ -269,6 +272,10 @@ class RasterizerSceneGLES3 : public RendererSceneRender {
GeometryInstanceGLES3 *owner = nullptr;
};

struct GeometryInstanceLightmapSH {
Color sh[9];
};

class GeometryInstanceGLES3 : public RenderGeometryInstanceBase {
public:
//used during rendering
Expand Down Expand Up @@ -296,6 +303,11 @@ class RasterizerSceneGLES3 : public RendererSceneRender {
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;
Expand Down

0 comments on commit 79dba3c

Please sign in to comment.