Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OpenGL: Implement rendering of lightmaps #85120

Merged
merged 1 commit into from Dec 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
127 changes: 113 additions & 14 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 @@ -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);
Expand Down Expand Up @@ -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) {
Expand All @@ -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]) {
Expand Down Expand Up @@ -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;
}
}
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