Skip to content

Commit

Permalink
Merge pull request #37887 from reduz/implement-projectors
Browse files Browse the repository at this point in the history
Add support for projectors in spot and omni lights.
  • Loading branch information
akien-mga authored Apr 14, 2020
2 parents 84142f6 + 6f293ed commit 9db5259
Show file tree
Hide file tree
Showing 12 changed files with 253 additions and 36 deletions.
42 changes: 41 additions & 1 deletion scene/3d/light_3d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ void Light3D::set_shadow(bool p_enable) {
shadow = p_enable;
RS::get_singleton()->light_set_shadow(light, p_enable);

if (type == RenderingServer::LIGHT_SPOT) {
if (type == RenderingServer::LIGHT_SPOT || type == RenderingServer::LIGHT_OMNI) {
update_configuration_warning();
}
}
Expand Down Expand Up @@ -166,6 +166,18 @@ Light3D::BakeMode Light3D::get_bake_mode() const {
return bake_mode;
}

void Light3D::set_projector(const Ref<Texture> &p_texture) {

projector = p_texture;
RID tex_id = projector.is_valid() ? projector->get_rid() : RID();
RS::get_singleton()->light_set_projector(light, tex_id);
update_configuration_warning();
}

Ref<Texture2D> Light3D::get_projector() const {
return projector;
}

void Light3D::_update_visibility() {

if (!is_inside_tree())
Expand Down Expand Up @@ -221,6 +233,10 @@ void Light3D::_validate_property(PropertyInfo &property) const {
property.usage = 0;
}

if (get_light_type() == RS::LIGHT_DIRECTIONAL && property.name == "light_projector") {
property.usage = 0;
}

if (get_light_type() != RS::LIGHT_DIRECTIONAL && property.name == "light_angular_distance") {
property.usage = 0;
}
Expand Down Expand Up @@ -255,10 +271,14 @@ void Light3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_bake_mode", "bake_mode"), &Light3D::set_bake_mode);
ClassDB::bind_method(D_METHOD("get_bake_mode"), &Light3D::get_bake_mode);

ClassDB::bind_method(D_METHOD("set_projector", "projector"), &Light3D::set_projector);
ClassDB::bind_method(D_METHOD("get_projector"), &Light3D::get_projector);

ADD_GROUP("Light", "light_");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "light_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_color", "get_color");
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_energy", PROPERTY_HINT_RANGE, "0,16,0.01,or_greater"), "set_param", "get_param", PARAM_ENERGY);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_indirect_energy", PROPERTY_HINT_RANGE, "0,16,0.01,or_greater"), "set_param", "get_param", PARAM_INDIRECT_ENERGY);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "light_projector", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_projector", "get_projector");
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_size", PROPERTY_HINT_RANGE, "0,64,0.01,or_greater"), "set_param", "get_param", PARAM_SIZE);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_angular_distance", PROPERTY_HINT_RANGE, "0,90,0.01"), "set_param", "get_param", PARAM_SIZE);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "light_negative"), "set_negative", "is_negative");
Expand Down Expand Up @@ -444,6 +464,19 @@ OmniLight3D::ShadowMode OmniLight3D::get_shadow_mode() const {
return shadow_mode;
}

String OmniLight3D::get_configuration_warning() const {
String warning = Light3D::get_configuration_warning();

if (!has_shadow() && get_projector().is_valid()) {
if (warning != String()) {
warning += "\n\n";
}
warning += TTR("Projector texture only works with shadows active.");
}

return warning;
}

void OmniLight3D::_bind_methods() {

ClassDB::bind_method(D_METHOD("set_shadow_mode", "mode"), &OmniLight3D::set_shadow_mode);
Expand Down Expand Up @@ -475,6 +508,13 @@ String SpotLight3D::get_configuration_warning() const {
warning += TTR("A SpotLight3D with an angle wider than 90 degrees cannot cast shadows.");
}

if (!has_shadow() && get_projector().is_valid()) {
if (warning != String()) {
warning += "\n\n";
}
warning += TTR("Projector texture only works with shadows active.");
}

return warning;
}

Expand Down
6 changes: 6 additions & 0 deletions scene/3d/light_3d.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ class Light3D : public VisualInstance3D {
bool editor_only;
void _update_visibility();
BakeMode bake_mode;
Ref<Texture2D> projector;

// bind helpers

Expand Down Expand Up @@ -125,6 +126,9 @@ class Light3D : public VisualInstance3D {
void set_bake_mode(BakeMode p_mode);
BakeMode get_bake_mode() const;

void set_projector(const Ref<Texture> &p_texture);
Ref<Texture2D> get_projector() const;

virtual AABB get_aabb() const;
virtual Vector<Face3> get_faces(uint32_t p_usage_flags) const;

Expand Down Expand Up @@ -196,6 +200,8 @@ class OmniLight3D : public Light3D {
void set_shadow_mode(ShadowMode p_mode);
ShadowMode get_shadow_mode() const;

virtual String get_configuration_warning() const;

OmniLight3D();
};

Expand Down
4 changes: 2 additions & 2 deletions servers/rendering/rasterizer.h
Original file line number Diff line number Diff line change
Expand Up @@ -327,8 +327,8 @@ class RasterizerStorage {

virtual Size2 texture_size_with_proxy(RID p_proxy) = 0;

virtual void texture_add_to_decal_atlas(RID p_texture) = 0;
virtual void texture_remove_from_decal_atlas(RID p_texture) = 0;
virtual void texture_add_to_decal_atlas(RID p_texture, bool p_panorama_to_dp = false) = 0;
virtual void texture_remove_from_decal_atlas(RID p_texture, bool p_panorama_to_dp = false) = 0;

/* SHADER API */

Expand Down
5 changes: 3 additions & 2 deletions servers/rendering/rasterizer_rd/rasterizer_effects_rd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ RID RasterizerEffectsRD::_get_compute_uniform_set_from_image_pair(RID p_texture1
return uniform_set;
}

void RasterizerEffectsRD::copy_to_atlas_fb(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2 &p_uv_rect, RD::DrawListID p_draw_list, bool p_flip_y) {
void RasterizerEffectsRD::copy_to_atlas_fb(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2 &p_uv_rect, RD::DrawListID p_draw_list, bool p_flip_y, bool p_panorama) {

zeromem(&copy_to_fb.push_constant, sizeof(CopyToFbPushConstant));

Expand All @@ -219,7 +219,7 @@ void RasterizerEffectsRD::copy_to_atlas_fb(RID p_source_rd_texture, RID p_dest_f
}

RD::DrawListID draw_list = p_draw_list;
RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, copy_to_fb.pipelines[COPY_TO_FB_COPY].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer)));
RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, copy_to_fb.pipelines[p_panorama ? COPY_TO_FB_COPY_PANORAMA_TO_DP : COPY_TO_FB_COPY].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer)));
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_source_rd_texture), 0);
RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array);
RD::get_singleton()->draw_list_set_push_constant(draw_list, &copy_to_fb.push_constant, sizeof(CopyToFbPushConstant));
Expand Down Expand Up @@ -1238,6 +1238,7 @@ RasterizerEffectsRD::RasterizerEffectsRD() {
{
Vector<String> copy_modes;
copy_modes.push_back("\n");
copy_modes.push_back("\n#define MODE_PANORAMA_TO_DP\n");

copy_to_fb.shader.initialize(copy_modes);

Expand Down
3 changes: 2 additions & 1 deletion servers/rendering/rasterizer_rd/rasterizer_effects_rd.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ class RasterizerEffectsRD {

enum CopyToFBMode {
COPY_TO_FB_COPY,
COPY_TO_FB_COPY_PANORAMA_TO_DP,
COPY_TO_FB_MAX,

};
Expand Down Expand Up @@ -565,7 +566,7 @@ class RasterizerEffectsRD {
void copy_to_rect(RID p_source_rd_texture, RID p_dest_texture, const Rect2i &p_rect, bool p_flip_y = false, bool p_force_luminance = false, bool p_all_source = false, bool p_8_bit_dst = false);
void copy_depth_to_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2i &p_rect, bool p_flip_y = false);
void copy_depth_to_rect_and_linearize(RID p_source_rd_texture, RID p_dest_texture, const Rect2i &p_rect, bool p_flip_y, float p_z_near, float p_z_far);
void copy_to_atlas_fb(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2 &p_uv_rect, RD::DrawListID p_draw_list, bool p_flip_y = false);
void copy_to_atlas_fb(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2 &p_uv_rect, RD::DrawListID p_draw_list, bool p_flip_y = false, bool p_panorama = false);
void gaussian_blur(RID p_source_rd_texture, RID p_texture, RID p_back_texture, const Rect2i &p_region, bool p_8bit_dst = false);
void gaussian_glow(RID p_source_rd_texture, RID p_texture, RID p_back_texture, const Size2i &p_size, float p_strength = 1.0, bool p_first_pass = false, float p_luminance_cap = 16.0, float p_exposure = 1.0, float p_bloom = 0.0, float p_hdr_bleed_treshold = 1.0, float p_hdr_bleed_scale = 1.0, RID p_auto_exposure = RID(), float p_auto_exposure_grey = 1.0);

Expand Down
32 changes: 25 additions & 7 deletions servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1841,6 +1841,30 @@ void RasterizerSceneHighEndRD::_setup_lights(RID *p_light_cull_result, int p_lig
light_data.atlas_rect[2] = 0;
light_data.atlas_rect[3] = 0;

RID projector = storage->light_get_projector(base);

if (projector.is_valid()) {
Rect2 rect = storage->decal_atlas_get_texture_rect(projector);

if (type == RS::LIGHT_SPOT) {

light_data.projector_rect[0] = rect.position.x;
light_data.projector_rect[1] = rect.position.y + rect.size.height; //flip because shadow is flipped
light_data.projector_rect[2] = rect.size.width;
light_data.projector_rect[3] = -rect.size.height;
} else {
light_data.projector_rect[0] = rect.position.x;
light_data.projector_rect[1] = rect.position.y;
light_data.projector_rect[2] = rect.size.width;
light_data.projector_rect[3] = rect.size.height * 0.5; //used by dp, so needs to be half
}
} else {
light_data.projector_rect[0] = 0;
light_data.projector_rect[1] = 0;
light_data.projector_rect[2] = 0;
light_data.projector_rect[3] = 0;
}

if (p_using_shadows && p_shadow_atlas.is_valid() && shadow_atlas_owns_light_instance(p_shadow_atlas, li)) {
// fill in the shadow information

Expand Down Expand Up @@ -1892,17 +1916,11 @@ void RasterizerSceneHighEndRD::_setup_lights(RID *p_light_cull_result, int p_lig

} else if (type == RS::LIGHT_SPOT) {

//used for clamping in this light type
light_data.atlas_rect[2] += light_data.atlas_rect[0];
light_data.atlas_rect[3] += light_data.atlas_rect[1];

Transform modelview = (p_camera_inverse_transform * light_transform).inverse();
CameraMatrix bias;
bias.set_light_bias();
CameraMatrix rectm;
rectm.set_light_atlas_rect(rect);

CameraMatrix shadow_mtx = rectm * bias * light_instance_get_shadow_camera(li, 0) * modelview;
CameraMatrix shadow_mtx = bias * light_instance_get_shadow_camera(li, 0) * modelview;
store_camera(shadow_mtx, light_data.shadow_matrix);

if (size > 0.0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,7 @@ class RasterizerSceneHighEndRD : public RasterizerSceneRD {
float soft_shadow_scale;
uint32_t mask;
uint32_t pad[2];
float projector_rect[4];
};

struct DirectionalLightData {
Expand Down
27 changes: 24 additions & 3 deletions servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3180,7 +3180,19 @@ void RasterizerStorageRD::light_set_projector(RID p_light, RID p_texture) {
Light *light = light_owner.getornull(p_light);
ERR_FAIL_COND(!light);

if (light->projector == p_texture) {
return;
}

if (light->type != RS::LIGHT_DIRECTIONAL && light->projector.is_valid()) {
texture_remove_from_decal_atlas(light->projector, light->type == RS::LIGHT_OMNI);
}

light->projector = p_texture;

if (light->type != RS::LIGHT_DIRECTIONAL && light->projector.is_valid()) {
texture_add_to_decal_atlas(light->projector, light->type == RS::LIGHT_OMNI);
}
}

void RasterizerStorageRD::light_set_negative(RID p_light, bool p_enable) {
Expand Down Expand Up @@ -4381,22 +4393,30 @@ RS::InstanceType RasterizerStorageRD::get_base_type(RID p_rid) const {
return RS::INSTANCE_NONE;
}

void RasterizerStorageRD::texture_add_to_decal_atlas(RID p_texture) {
void RasterizerStorageRD::texture_add_to_decal_atlas(RID p_texture, bool p_panorama_to_dp) {
if (!decal_atlas.textures.has(p_texture)) {
DecalAtlas::Texture t;
t.users = 1;
t.panorama_to_dp_users = p_panorama_to_dp ? 1 : 0;
decal_atlas.textures[p_texture] = t;
decal_atlas.dirty = true;
} else {
DecalAtlas::Texture *t = decal_atlas.textures.getptr(p_texture);
t->users++;
if (p_panorama_to_dp) {
t->panorama_to_dp_users++;
}
}
}

void RasterizerStorageRD::texture_remove_from_decal_atlas(RID p_texture) {
void RasterizerStorageRD::texture_remove_from_decal_atlas(RID p_texture, bool p_panorama_to_dp) {
DecalAtlas::Texture *t = decal_atlas.textures.getptr(p_texture);
ERR_FAIL_COND(!t);
t->users--;
if (p_panorama_to_dp) {
ERR_FAIL_COND(t->panorama_to_dp_users == 0);
t->panorama_to_dp_users--;
}
if (t->users == 0) {
decal_atlas.textures.erase(p_texture);
//do not mark it dirty, there is no need to since it remains working
Expand Down Expand Up @@ -4590,7 +4610,7 @@ void RasterizerStorageRD::_update_decal_atlas() {
while ((K = decal_atlas.textures.next(K))) {
DecalAtlas::Texture *t = decal_atlas.textures.getptr(*K);
Texture *src_tex = texture_owner.getornull(*K);
effects.copy_to_atlas_fb(src_tex->rd_texture, mm.fb, t->uv_rect, draw_list);
effects.copy_to_atlas_fb(src_tex->rd_texture, mm.fb, t->uv_rect, draw_list, false, t->panorama_to_dp_users > 0);
}

RD::get_singleton()->draw_list_end();
Expand Down Expand Up @@ -4732,6 +4752,7 @@ bool RasterizerStorageRD::free(RID p_rid) {

} else if (light_owner.owns(p_rid)) {

light_set_projector(p_rid, RID()); //clear projector
// delete the texture
Light *light = light_owner.getornull(p_rid);
light->instance_dependency.instance_notify_deleted(p_rid);
Expand Down
13 changes: 11 additions & 2 deletions servers/rendering/rasterizer_rd/rasterizer_storage_rd.h
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ class RasterizerStorageRD : public RasterizerStorage {
struct DecalAtlas {
struct Texture {

int panorama_to_dp_users;
int users;
Rect2 uv_rect;
};
Expand Down Expand Up @@ -599,8 +600,8 @@ class RasterizerStorageRD : public RasterizerStorage {

virtual Size2 texture_size_with_proxy(RID p_proxy);

virtual void texture_add_to_decal_atlas(RID p_texture);
virtual void texture_remove_from_decal_atlas(RID p_texture);
virtual void texture_add_to_decal_atlas(RID p_texture, bool p_panorama_to_dp = false);
virtual void texture_remove_from_decal_atlas(RID p_texture, bool p_panorama_to_dp = false);

RID decal_atlas_get_texture() const;
RID decal_atlas_get_texture_srgb() const;
Expand Down Expand Up @@ -964,6 +965,14 @@ class RasterizerStorageRD : public RasterizerStorage {
return light->param[p_param];
}

_FORCE_INLINE_ RID light_get_projector(RID p_light) {

const Light *light = light_owner.getornull(p_light);
ERR_FAIL_COND_V(!light, RID());

return light->projector;
}

_FORCE_INLINE_ Color light_get_color(RID p_light) {

const Light *light = light_owner.getornull(p_light);
Expand Down
28 changes: 28 additions & 0 deletions servers/rendering/rasterizer_rd/shaders/copy_to_fb.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,34 @@ layout(location = 0) out vec4 frag_color;
void main() {

vec2 uv = uv_interp;

#ifdef MODE_PANORAMA_TO_DP

//obtain normal from dual paraboloid uv
#define M_PI 3.14159265359

float side;
uv.y = modf(uv.y * 2.0, side);
side = side * 2.0 - 1.0;
vec3 normal = vec3(uv * 2.0 - 1.0, 0.0);
normal.z = 0.5 - 0.5 * ((normal.x * normal.x) + (normal.y * normal.y));
normal *= -side;
normal = normalize(normal);

//now convert normal to panorama uv

vec2 st = vec2(atan(normal.x, normal.z), acos(normal.y));

if (st.x < 0.0)
st.x += M_PI * 2.0;

uv = st / vec2(M_PI * 2.0, M_PI);

if (side < 0.0) {
//uv.y = 1.0 - uv.y;
uv = 1.0 - uv;
}
#endif
vec4 color = textureLod(source_color, uv, 0.0);
if (params.force_luminance) {
color.rgb = vec3(max(max(color.r, color.g), color.b));
Expand Down
Loading

0 comments on commit 9db5259

Please sign in to comment.