summaryrefslogtreecommitdiff
path: root/servers/rendering
diff options
context:
space:
mode:
authorreduz <reduzio@gmail.com>2020-11-03 16:51:53 -0300
committerreduz <reduzio@gmail.com>2020-11-04 10:03:01 -0300
commitf123981a96c8ab7d7cb3e77ecbcf0048cb3b79e6 (patch)
tree9759e240a4043c1e5878a0a431ab6ae366792340 /servers/rendering
parent3ec10bb07cb4a1ce1170c1d189475c8147576b7e (diff)
Implement DirectionalLight2D
Also separated Light2D in PointLight2D and DirectionalLight2D. Used PointLight2D because its more of a point, and it does not work the same as OmniLight (as shape depends on texture). Added a few utility methods to Rect2D I needed.
Diffstat (limited to 'servers/rendering')
-rw-r--r--servers/rendering/rasterizer.h13
-rw-r--r--servers/rendering/rasterizer_rd/rasterizer_canvas_rd.cpp224
-rw-r--r--servers/rendering/rasterizer_rd/rasterizer_canvas_rd.h14
-rw-r--r--servers/rendering/rasterizer_rd/shaders/canvas.glsl224
-rw-r--r--servers/rendering/rasterizer_rd/shaders/canvas_uniforms_inc.glsl5
-rw-r--r--servers/rendering/rendering_server_canvas.cpp83
-rw-r--r--servers/rendering/rendering_server_canvas.h12
-rw-r--r--servers/rendering/rendering_server_raster.h8
-rw-r--r--servers/rendering/rendering_server_viewport.cpp126
-rw-r--r--servers/rendering/rendering_server_wrap_mt.h8
10 files changed, 541 insertions, 176 deletions
diff --git a/servers/rendering/rasterizer.h b/servers/rendering/rasterizer.h
index b2b2b4977b..02aa329b07 100644
--- a/servers/rendering/rasterizer.h
+++ b/servers/rendering/rasterizer.h
@@ -832,7 +832,9 @@ public:
int layer_max;
int item_mask;
int item_shadow_mask;
+ float directional_distance;
RS::CanvasLightMode mode;
+ RS::CanvasLightBlendMode blend_mode;
RID texture;
Vector2 texture_offset;
RID canvas;
@@ -854,7 +856,7 @@ public:
Light *shadows_next_ptr;
Light *filter_next_ptr;
Light *next_ptr;
- Light *mask_next_ptr;
+ Light *directional_next_ptr;
RID light_internal;
uint64_t version;
@@ -875,16 +877,18 @@ public:
scale = 1.0;
energy = 1.0;
item_shadow_mask = 1;
- mode = RS::CANVAS_LIGHT_MODE_ADD;
+ mode = RS::CANVAS_LIGHT_MODE_POINT;
+ blend_mode = RS::CANVAS_LIGHT_BLEND_MODE_ADD;
// texture_cache = nullptr;
next_ptr = nullptr;
- mask_next_ptr = nullptr;
+ directional_next_ptr = nullptr;
filter_next_ptr = nullptr;
use_shadow = false;
shadow_buffer_size = 2048;
shadow_filter = RS::CANVAS_LIGHT_FILTER_NONE;
shadow_smooth = 0.0;
render_index_cache = -1;
+ directional_distance = 10000.0;
}
};
@@ -1322,7 +1326,7 @@ public:
}
};
- virtual void canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, const Transform2D &p_canvas_transform, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel) = 0;
+ virtual void canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, Light *p_directional_list, const Transform2D &p_canvas_transform, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel) = 0;
virtual void canvas_debug_viewport_shadows(Light *p_lights_with_shadow) = 0;
struct LightOccluderInstance {
@@ -1350,6 +1354,7 @@ public:
virtual void light_set_texture(RID p_rid, RID p_texture) = 0;
virtual void light_set_use_shadow(RID p_rid, bool p_enable) = 0;
virtual void light_update_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders) = 0;
+ virtual void light_update_directional_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_cull_distance, const Rect2 &p_clip_rect, LightOccluderInstance *p_occluders) = 0;
virtual RID occluder_polygon_create() = 0;
virtual void occluder_polygon_set_shape_as_lines(RID p_occluder, const Vector<Vector2> &p_lines) = 0;
diff --git a/servers/rendering/rasterizer_rd/rasterizer_canvas_rd.cpp b/servers/rendering/rasterizer_rd/rasterizer_canvas_rd.cpp
index da30291353..b33940b22c 100644
--- a/servers/rendering/rasterizer_rd/rasterizer_canvas_rd.cpp
+++ b/servers/rendering/rasterizer_rd/rasterizer_canvas_rd.cpp
@@ -416,10 +416,6 @@ void RasterizerCanvasRD::_render_item(RD::DrawListID p_draw_list, const Item *p_
light_count++;
- if (light->mode == RS::CANVAS_LIGHT_MODE_MASK) {
- base_flags |= FLAGS_USING_LIGHT_MASK;
- }
-
if (light_count == MAX_LIGHTS_PER_ITEM) {
break;
}
@@ -430,7 +426,7 @@ void RasterizerCanvasRD::_render_item(RD::DrawListID p_draw_list, const Item *p_
base_flags |= light_count << FLAGS_LIGHT_COUNT_SHIFT;
}
- light_mode = light_count > 0 ? PIPELINE_LIGHT_MODE_ENABLED : PIPELINE_LIGHT_MODE_DISABLED;
+ light_mode = (light_count > 0 || using_directional_lights) ? PIPELINE_LIGHT_MODE_ENABLED : PIPELINE_LIGHT_MODE_DISABLED;
PipelineVariants *pipeline_variants = p_pipeline_variants;
@@ -1194,51 +1190,83 @@ void RasterizerCanvasRD::_render_items(RID p_to_render_target, int p_item_count,
RD::get_singleton()->draw_list_end();
}
-void RasterizerCanvasRD::canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, const Transform2D &p_canvas_transform, RenderingServer::CanvasItemTextureFilter p_default_filter, RenderingServer::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel) {
+void RasterizerCanvasRD::canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, Light *p_directional_light_list, const Transform2D &p_canvas_transform, RenderingServer::CanvasItemTextureFilter p_default_filter, RenderingServer::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel) {
int item_count = 0;
//setup canvas state uniforms if needed
Transform2D canvas_transform_inverse = p_canvas_transform.affine_inverse();
+ //setup directional lights if exist
+
+ uint32_t light_count = 0;
+ uint32_t directional_light_count = 0;
{
- //update canvas state uniform buffer
- State::Buffer state_buffer;
+ Light *l = p_directional_light_list;
+ uint32_t index = 0;
- Size2i ssize = storage->render_target_get_size(p_to_render_target);
+ while (l) {
+ if (index == state.max_lights_per_render) {
+ l->render_index_cache = -1;
+ l = l->next_ptr;
+ continue;
+ }
- Transform screen_transform;
- screen_transform.translate(-(ssize.width / 2.0f), -(ssize.height / 2.0f), 0.0f);
- screen_transform.scale(Vector3(2.0f / ssize.width, 2.0f / ssize.height, 1.0f));
- _update_transform_to_mat4(screen_transform, state_buffer.screen_transform);
- _update_transform_2d_to_mat4(p_canvas_transform, state_buffer.canvas_transform);
+ CanvasLight *clight = canvas_light_owner.getornull(l->light_internal);
+ if (!clight) { //unused or invalid texture
+ l->render_index_cache = -1;
+ l = l->next_ptr;
+ ERR_CONTINUE(!clight);
+ }
- Transform2D normal_transform = p_canvas_transform;
- normal_transform.elements[0].normalize();
- normal_transform.elements[1].normalize();
- normal_transform.elements[2] = Vector2();
- _update_transform_2d_to_mat4(normal_transform, state_buffer.canvas_normal_transform);
+ Vector2 canvas_light_dir = l->xform_cache.elements[1].normalized();
- state_buffer.canvas_modulate[0] = p_modulate.r;
- state_buffer.canvas_modulate[1] = p_modulate.g;
- state_buffer.canvas_modulate[2] = p_modulate.b;
- state_buffer.canvas_modulate[3] = p_modulate.a;
+ state.light_uniforms[index].position[0] = -canvas_light_dir.x;
+ state.light_uniforms[index].position[1] = -canvas_light_dir.y;
- Size2 render_target_size = storage->render_target_get_size(p_to_render_target);
- state_buffer.screen_pixel_size[0] = 1.0 / render_target_size.x;
- state_buffer.screen_pixel_size[1] = 1.0 / render_target_size.y;
+ _update_transform_2d_to_mat2x4(clight->shadow.directional_xform, state.light_uniforms[index].shadow_matrix);
- state_buffer.time = state.time;
- state_buffer.use_pixel_snap = p_snap_2d_vertices_to_pixel;
+ state.light_uniforms[index].height = l->height; //0..1 here
- RD::get_singleton()->buffer_update(state.canvas_state_buffer, 0, sizeof(State::Buffer), &state_buffer, true);
+ for (int i = 0; i < 4; i++) {
+ state.light_uniforms[index].shadow_color[i] = uint8_t(CLAMP(int32_t(l->shadow_color[i] * 255.0), 0, 255));
+ state.light_uniforms[index].color[i] = l->color[i];
+ }
+
+ state.light_uniforms[index].color[3] = l->energy; //use alpha for energy, so base color can go separate
+
+ if (state.shadow_fb.is_valid()) {
+ state.light_uniforms[index].shadow_pixel_size = (1.0 / state.shadow_texture_size) * (1.0 + l->shadow_smooth);
+ state.light_uniforms[index].shadow_z_far_inv = 1.0 / clight->shadow.z_far;
+ state.light_uniforms[index].shadow_y_ofs = clight->shadow.y_offset;
+ } else {
+ state.light_uniforms[index].shadow_pixel_size = 1.0;
+ state.light_uniforms[index].shadow_z_far_inv = 1.0;
+ state.light_uniforms[index].shadow_y_ofs = 0;
+ }
+
+ state.light_uniforms[index].flags = l->blend_mode << LIGHT_FLAGS_BLEND_SHIFT;
+ state.light_uniforms[index].flags |= l->shadow_filter << LIGHT_FLAGS_FILTER_SHIFT;
+ if (clight->shadow.enabled) {
+ state.light_uniforms[index].flags |= LIGHT_FLAGS_HAS_SHADOW;
+ }
+
+ l->render_index_cache = index;
+
+ index++;
+ l = l->next_ptr;
+ }
+
+ light_count = index;
+ directional_light_count = light_count;
+ using_directional_lights = directional_light_count > 0;
}
//setup lights if exist
{
Light *l = p_light_list;
- uint32_t index = 0;
+ uint32_t index = light_count;
while (l) {
if (index == state.max_lights_per_render) {
@@ -1280,7 +1308,7 @@ void RasterizerCanvasRD::canvas_render_items(RID p_to_render_target, Item *p_ite
state.light_uniforms[index].shadow_y_ofs = 0;
}
- state.light_uniforms[index].flags |= l->mode << LIGHT_FLAGS_BLEND_SHIFT;
+ state.light_uniforms[index].flags = l->blend_mode << LIGHT_FLAGS_BLEND_SHIFT;
state.light_uniforms[index].flags |= l->shadow_filter << LIGHT_FLAGS_FILTER_SHIFT;
if (clight->shadow.enabled) {
state.light_uniforms[index].flags |= LIGHT_FLAGS_HAS_SHADOW;
@@ -1306,9 +1334,46 @@ void RasterizerCanvasRD::canvas_render_items(RID p_to_render_target, Item *p_ite
l = l->next_ptr;
}
- if (index > 0) {
- RD::get_singleton()->buffer_update(state.lights_uniform_buffer, 0, sizeof(LightUniform) * index, &state.light_uniforms[0], true);
- }
+ light_count = index;
+ }
+
+ if (light_count > 0) {
+ RD::get_singleton()->buffer_update(state.lights_uniform_buffer, 0, sizeof(LightUniform) * light_count, &state.light_uniforms[0], true);
+ }
+
+ {
+ //update canvas state uniform buffer
+ State::Buffer state_buffer;
+
+ Size2i ssize = storage->render_target_get_size(p_to_render_target);
+
+ Transform screen_transform;
+ screen_transform.translate(-(ssize.width / 2.0f), -(ssize.height / 2.0f), 0.0f);
+ screen_transform.scale(Vector3(2.0f / ssize.width, 2.0f / ssize.height, 1.0f));
+ _update_transform_to_mat4(screen_transform, state_buffer.screen_transform);
+ _update_transform_2d_to_mat4(p_canvas_transform, state_buffer.canvas_transform);
+
+ Transform2D normal_transform = p_canvas_transform;
+ normal_transform.elements[0].normalize();
+ normal_transform.elements[1].normalize();
+ normal_transform.elements[2] = Vector2();
+ _update_transform_2d_to_mat4(normal_transform, state_buffer.canvas_normal_transform);
+
+ state_buffer.canvas_modulate[0] = p_modulate.r;
+ state_buffer.canvas_modulate[1] = p_modulate.g;
+ state_buffer.canvas_modulate[2] = p_modulate.b;
+ state_buffer.canvas_modulate[3] = p_modulate.a;
+
+ Size2 render_target_size = storage->render_target_get_size(p_to_render_target);
+ state_buffer.screen_pixel_size[0] = 1.0 / render_target_size.x;
+ state_buffer.screen_pixel_size[1] = 1.0 / render_target_size.y;
+
+ state_buffer.time = state.time;
+ state_buffer.use_pixel_snap = p_snap_2d_vertices_to_pixel;
+
+ state_buffer.directional_light_count = directional_light_count;
+
+ RD::get_singleton()->buffer_update(state.canvas_state_buffer, 0, sizeof(State::Buffer), &state_buffer, true);
}
{ //default filter/repeat
@@ -1439,10 +1504,7 @@ void RasterizerCanvasRD::light_set_use_shadow(RID p_rid, bool p_enable) {
cl->shadow.enabled = p_enable;
}
-void RasterizerCanvasRD::light_update_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders) {
- CanvasLight *cl = canvas_light_owner.getornull(p_rid);
- ERR_FAIL_COND(!cl->shadow.enabled);
-
+void RasterizerCanvasRD::_update_shadow_atlas() {
if (state.shadow_fb == RID()) {
//ah, we lack the shadow texture..
RD::get_singleton()->free(state.shadow_texture); //erase placeholder
@@ -1474,6 +1536,12 @@ void RasterizerCanvasRD::light_update_shadow(RID p_rid, int p_shadow_index, cons
state.shadow_fb = RD::get_singleton()->framebuffer_create(fb_textures);
}
+}
+void RasterizerCanvasRD::light_update_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders) {
+ CanvasLight *cl = canvas_light_owner.getornull(p_rid);
+ ERR_FAIL_COND(!cl->shadow.enabled);
+
+ _update_shadow_atlas();
cl->shadow.z_far = p_far;
cl->shadow.y_offset = float(p_shadow_index * 2 + 1) / float(state.max_lights_per_render * 2);
@@ -1547,6 +1615,86 @@ void RasterizerCanvasRD::light_update_shadow(RID p_rid, int p_shadow_index, cons
}
}
+void RasterizerCanvasRD::light_update_directional_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_cull_distance, const Rect2 &p_clip_rect, LightOccluderInstance *p_occluders) {
+ CanvasLight *cl = canvas_light_owner.getornull(p_rid);
+ ERR_FAIL_COND(!cl->shadow.enabled);
+
+ _update_shadow_atlas();
+
+ Vector2 light_dir = p_light_xform.elements[1].normalized();
+
+ Vector2 center = p_clip_rect.position + p_clip_rect.size * 0.5;
+
+ float to_edge_distance = ABS(light_dir.dot(p_clip_rect.get_support(light_dir)) - light_dir.dot(center));
+
+ Vector2 from_pos = center - light_dir * (to_edge_distance + p_cull_distance);
+ float distance = to_edge_distance * 2.0 + p_cull_distance;
+ float half_size = p_clip_rect.size.length() * 0.5; //shadow length, must keep this no matter the angle
+
+ cl->shadow.z_far = distance;
+ cl->shadow.y_offset = float(p_shadow_index * 2 + 1) / float(state.max_lights_per_render * 2);
+
+ Transform2D to_light_xform;
+
+ to_light_xform[2] = from_pos;
+ to_light_xform[1] = light_dir;
+ to_light_xform[0] = -light_dir.tangent();
+
+ to_light_xform.invert();
+
+ Vector<Color> cc;
+ cc.push_back(Color(1, 1, 1, 1));
+
+ Rect2i rect(0, p_shadow_index * 2, state.shadow_texture_size, 2);
+ RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(state.shadow_fb, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, cc, 1.0, 0, rect);
+
+ CameraMatrix projection;
+ projection.set_orthogonal(-half_size, half_size, -0.5, 0.5, 0.0, distance);
+ projection = projection * CameraMatrix(Transform().looking_at(Vector3(0, 1, 0), Vector3(0, 0, -1)).affine_inverse());
+
+ ShadowRenderPushConstant push_constant;
+ for (int y = 0; y < 4; y++) {
+ for (int x = 0; x < 4; x++) {
+ push_constant.projection[y * 4 + x] = projection.matrix[y][x];
+ }
+ }
+
+ push_constant.direction[0] = 0.0;
+ push_constant.direction[1] = 1.0;
+ push_constant.z_far = distance;
+ push_constant.pad = 0;
+
+ LightOccluderInstance *instance = p_occluders;
+
+ while (instance) {
+ OccluderPolygon *co = occluder_polygon_owner.getornull(instance->occluder);
+
+ if (!co || co->index_array.is_null() || !(p_light_mask & instance->light_mask)) {
+ instance = instance->next;
+ continue;
+ }
+
+ _update_transform_2d_to_mat2x4(to_light_xform * instance->xform_cache, push_constant.modelview);
+
+ RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, shadow_render.render_pipelines[co->cull_mode]);
+ RD::get_singleton()->draw_list_bind_vertex_array(draw_list, co->vertex_array);
+ RD::get_singleton()->draw_list_bind_index_array(draw_list, co->index_array);
+ RD::get_singleton()->draw_list_set_push_constant(draw_list, &push_constant, sizeof(ShadowRenderPushConstant));
+
+ RD::get_singleton()->draw_list_draw(draw_list, true);
+
+ instance = instance->next;
+ }
+
+ RD::get_singleton()->draw_list_end();
+
+ Transform2D to_shadow;
+ to_shadow.elements[0].x = 1.0 / -(half_size * 2.0);
+ to_shadow.elements[2].x = 0.5;
+
+ cl->shadow.directional_xform = to_shadow * to_light_xform;
+}
+
RID RasterizerCanvasRD::occluder_polygon_create() {
OccluderPolygon occluder;
occluder.point_count = 0;
diff --git a/servers/rendering/rasterizer_rd/rasterizer_canvas_rd.h b/servers/rendering/rasterizer_rd/rasterizer_canvas_rd.h
index 3412435367..b516f63cbf 100644
--- a/servers/rendering/rasterizer_rd/rasterizer_canvas_rd.h
+++ b/servers/rendering/rasterizer_rd/rasterizer_canvas_rd.h
@@ -75,7 +75,6 @@ class RasterizerCanvasRD : public RasterizerCanvas {
FLAGS_CLIP_RECT_UV = (1 << 9),
FLAGS_TRANSPOSE_RECT = (1 << 10),
- FLAGS_USING_LIGHT_MASK = (1 << 11),
FLAGS_NINEPACH_DRAW_CENTER = (1 << 12),
FLAGS_USING_PARTICLES = (1 << 13),
@@ -269,6 +268,7 @@ class RasterizerCanvasRD : public RasterizerCanvas {
bool enabled = false;
float z_far;
float y_offset;
+ Transform2D directional_xform;
} shadow;
};
@@ -331,12 +331,13 @@ class RasterizerCanvasRD : public RasterizerCanvas {
float screen_transform[16];
float canvas_normal_transform[16];
float canvas_modulate[4];
+
float screen_pixel_size[2];
float time;
uint32_t use_pixel_snap;
- //uint32_t light_count;
- //uint32_t pad[3];
+ uint32_t directional_light_count;
+ uint32_t pad[3];
};
LightUniform *light_uniforms;
@@ -355,6 +356,7 @@ class RasterizerCanvasRD : public RasterizerCanvas {
uint32_t max_lights_per_item;
double time;
+
} state;
struct PushConstant {
@@ -388,6 +390,7 @@ class RasterizerCanvasRD : public RasterizerCanvas {
Item *items[MAX_RENDER_ITEMS];
+ bool using_directional_lights = false;
RID default_canvas_texture;
RID default_canvas_group_shader;
@@ -408,6 +411,8 @@ class RasterizerCanvasRD : public RasterizerCanvas {
_FORCE_INLINE_ void _update_transform_2d_to_mat4(const Transform2D &p_transform, float *p_mat4);
_FORCE_INLINE_ void _update_transform_to_mat4(const Transform &p_transform, float *p_mat4);
+ void _update_shadow_atlas();
+
public:
PolygonID request_polygon(const Vector<int> &p_indices, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), const Vector<int> &p_bones = Vector<int>(), const Vector<float> &p_weights = Vector<float>());
void free_polygon(PolygonID p_polygon);
@@ -416,12 +421,13 @@ public:
void light_set_texture(RID p_rid, RID p_texture);
void light_set_use_shadow(RID p_rid, bool p_enable);
void light_update_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders);
+ void light_update_directional_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_cull_distance, const Rect2 &p_clip_rect, LightOccluderInstance *p_occluders);
RID occluder_polygon_create();
void occluder_polygon_set_shape_as_lines(RID p_occluder, const Vector<Vector2> &p_lines);
void occluder_polygon_set_cull_mode(RID p_occluder, RS::CanvasOccluderPolygonCullMode p_mode);
- void canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, const Transform2D &p_canvas_transform, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel);
+ void canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, Light *p_directional_light_list, const Transform2D &p_canvas_transform, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel);
void canvas_debug_viewport_shadows(Light *p_lights_with_shadow) {}
diff --git a/servers/rendering/rasterizer_rd/shaders/canvas.glsl b/servers/rendering/rasterizer_rd/shaders/canvas.glsl
index b382c0d85f..2a0f94e733 100644
--- a/servers/rendering/rasterizer_rd/shaders/canvas.glsl
+++ b/servers/rendering/rasterizer_rd/shaders/canvas.glsl
@@ -249,7 +249,7 @@ vec4 light_compute(
inout vec4 shadow_modulate,
vec2 screen_uv,
vec2 uv,
- vec4 color) {
+ vec4 color, bool is_directional) {
vec4 light = vec4(0.0);
/* clang-format off */
LIGHT_SHADER_CODE
@@ -302,6 +302,99 @@ float map_ninepatch_axis(float pixel, float draw_size, float tex_pixel_size, flo
#endif
+#ifdef USE_LIGHTING
+
+vec3 light_normal_compute(vec3 light_vec, vec3 normal, vec3 base_color, vec3 light_color, vec4 specular_shininess, bool specular_shininess_used) {
+ float cNdotL = max(0.0, dot(normal, light_vec));
+
+ if (specular_shininess_used) {
+ //blinn
+ vec3 view = vec3(0.0, 0.0, 1.0); // not great but good enough
+ vec3 half_vec = normalize(view + light_vec);
+
+ float cNdotV = max(dot(normal, view), 0.0);
+ float cNdotH = max(dot(normal, half_vec), 0.0);
+ float cVdotH = max(dot(view, half_vec), 0.0);
+ float cLdotH = max(dot(light_vec, half_vec), 0.0);
+ float shininess = exp2(15.0 * specular_shininess.a + 1.0) * 0.25;
+ float blinn = pow(cNdotH, shininess);
+ blinn *= (shininess + 8.0) * (1.0 / (8.0 * M_PI));
+ float s = (blinn) / max(4.0 * cNdotV * cNdotL, 0.75);
+
+ return specular_shininess.rgb * light_color * s + light_color * base_color * cNdotL;
+ } else {
+ return light_color * base_color * cNdotL;
+ }
+}
+
+//float distance = length(shadow_pos);
+vec4 light_shadow_compute(uint light_base, vec4 light_color, vec4 shadow_uv
+#ifdef LIGHT_SHADER_CODE_USED
+ ,
+ vec3 shadow_modulate
+#endif
+) {
+ float shadow;
+ uint shadow_mode = light_array.data[light_base].flags & LIGHT_FLAGS_FILTER_MASK;
+
+ if (shadow_mode == LIGHT_FLAGS_SHADOW_NEAREST) {
+ shadow = textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv, 0.0).x;
+ } else if (shadow_mode == LIGHT_FLAGS_SHADOW_PCF5) {
+ vec4 shadow_pixel_size = vec4(light_array.data[light_base].shadow_pixel_size, 0.0, 0.0, 0.0);
+ shadow = 0.0;
+ shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv - shadow_pixel_size * 2.0, 0.0).x;
+ shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv - shadow_pixel_size, 0.0).x;
+ shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv, 0.0).x;
+ shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv + shadow_pixel_size, 0.0).x;
+ shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv + shadow_pixel_size * 2.0, 0.0).x;
+ shadow /= 5.0;
+ } else { //PCF13
+ vec4 shadow_pixel_size = vec4(light_array.data[light_base].shadow_pixel_size, 0.0, 0.0, 0.0);
+ shadow = 0.0;
+ shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv - shadow_pixel_size * 6.0, 0.0).x;
+ shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv - shadow_pixel_size * 5.0, 0.0).x;
+ shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv - shadow_pixel_size * 4.0, 0.0).x;
+ shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv - shadow_pixel_size * 3.0, 0.0).x;
+ shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv - shadow_pixel_size * 2.0, 0.0).x;
+ shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv - shadow_pixel_size, 0.0).x;
+ shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv, 0.0).x;
+ shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv + shadow_pixel_size, 0.0).x;
+ shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv + shadow_pixel_size * 2.0, 0.0).x;
+ shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv + shadow_pixel_size * 3.0, 0.0).x;
+ shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv + shadow_pixel_size * 4.0, 0.0).x;
+ shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv + shadow_pixel_size * 5.0, 0.0).x;
+ shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv + shadow_pixel_size * 6.0, 0.0).x;
+ shadow /= 13.0;
+ }
+
+ vec4 shadow_color = unpackUnorm4x8(light_array.data[light_base].shadow_color);
+#ifdef LIGHT_SHADER_CODE_USED
+ shadow_color *= shadow_modulate;
+#endif
+
+ shadow_color.a *= light_color.a; //respect light alpha
+
+ return mix(light_color, shadow_color, shadow);
+}
+
+void light_blend_compute(uint light_base, vec4 light_color, inout vec3 color) {
+ uint blend_mode = light_array.data[light_base].flags & LIGHT_FLAGS_BLEND_MASK;
+
+ switch (blend_mode) {
+ case LIGHT_FLAGS_BLEND_MODE_ADD: {
+ color.rgb += light_color.rgb * light_color.a;
+ } break;
+ case LIGHT_FLAGS_BLEND_MODE_SUB: {
+ color.rgb -= light_color.rgb * light_color.a;
+ } break;
+ case LIGHT_FLAGS_BLEND_MODE_MIX: {
+ color.rgb = mix(color.rgb, light_color.rgb, light_color.a);
+ } break;
+ }
+}
+
+#endif
+
void main() {
vec4 color = color_interp;
vec2 uv = uv_interp;
@@ -332,6 +425,7 @@ void main() {
color *= texture(sampler2D(color_texture, texture_sampler), uv);
uint light_count = (draw_data.flags >> FLAGS_LIGHT_COUNT_SHIFT) & 0xF; //max 16 lights
+ bool using_light = light_count > 0 || canvas_data.directional_light_count > 0;
vec3 normal;
@@ -341,7 +435,7 @@ void main() {
bool normal_used = false;
#endif
- if (normal_used || (light_count > 0 && bool(draw_data.flags & FLAGS_DEFAULT_NORMAL_MAP_USED))) {
+ if (normal_used || (using_light && bool(draw_data.flags & FLAGS_DEFAULT_NORMAL_MAP_USED))) {
normal.xy = texture(sampler2D(normal_texture, texture_sampler), uv).xy * vec2(2.0, -2.0) - vec2(1.0, -1.0);
normal.z = sqrt(1.0 - dot(normal.xy, normal.xy));
normal_used = true;
@@ -358,7 +452,7 @@ void main() {
bool specular_shininess_used = false;
#endif
- if (specular_shininess_used || (light_count > 0 && normal_used && bool(draw_data.flags & FLAGS_DEFAULT_SPECULAR_MAP_USED))) {
+ if (specular_shininess_used || (using_light && normal_used && bool(draw_data.flags & FLAGS_DEFAULT_SPECULAR_MAP_USED))) {
specular_shininess = texture(sampler2D(specular_texture, texture_sampler), uv);
specular_shininess *= unpackUnorm4x8(draw_data.specular_shininess);
specular_shininess_used = true;
@@ -401,13 +495,52 @@ FRAGMENT_SHADER_CODE
normal = normalize((canvas_data.canvas_normal_transform * vec4(normal, 0.0)).xyz);
}
- vec4 base_color = color;
+ vec3 base_color = color.rgb;
if (bool(draw_data.flags & FLAGS_USING_LIGHT_MASK)) {
color = vec4(0.0); //invisible by default due to using light mask
}
color *= canvas_data.canvas_modulation;
#ifdef USE_LIGHTING
+
+ // Directional Lights
+
+ for (uint i = 0; i < canvas_data.directional_light_count; i++) {
+ uint light_base = i;
+
+ vec2 direction = light_array.data[light_base].position;
+ vec4 light_color = light_array.data[light_base].color;
+
+#ifdef LIGHT_SHADER_CODE_USED
+
+ vec4 shadow_modulate = vec4(1.0);
+ light_color = light_compute(light_vertex, direction, normal, light_color, light_color.a, specular_shininess, shadow_modulate, screen_uv, color, uv, true);
+#else
+
+ if (normal_used) {
+ vec3 light_vec = normalize(mix(vec3(direction, 0.0), vec3(0, 0, 1), light_array.data[light_base].height));
+ light_color.rgb = light_normal_compute(light_vec, normal, base_color, light_color.rgb, specular_shininess, specular_shininess_used);
+ }
+#endif
+
+ if (bool(light_array.data[light_base].flags & LIGHT_FLAGS_HAS_SHADOW)) {
+ vec2 shadow_pos = (vec4(shadow_vertex, 0.0, 1.0) * mat4(light_array.data[light_base].shadow_matrix[0], light_array.data[light_base].shadow_matrix[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy; //multiply inverse given its transposed. Optimizer removes useless operations.
+
+ vec4 shadow_uv = vec4(shadow_pos.x, light_array.data[light_base].shadow_y_ofs, shadow_pos.y * light_array.data[light_base].shadow_zfar_inv, 1.0);
+
+ light_color = light_shadow_compute(light_base, light_color, shadow_uv
+#ifdef LIGHT_SHADER_CODE_USED
+ ,
+ shadow_modulate
+#endif
+ );
+ }
+
+ light_blend_compute(light_base, light_color, color.rgb);
+ }
+
+ // Positional Lights
+
for (uint i = 0; i < MAX_LIGHTS_PER_ITEM; i++) {
if (i >= light_count) {
break;
@@ -440,7 +573,7 @@ FRAGMENT_SHADER_CODE
vec3 light_position = vec3(light_array.data[light_base].position, light_array.data[light_base].height);
light_color.rgb *= light_base_color.rgb;
- light_color = light_compute(light_vertex, light_position, normal, light_color, light_base_color.a, specular_shininess, shadow_modulate, screen_uv, color, uv);
+ light_color = light_compute(light_vertex, light_position, normal, light_color, light_base_color.a, specular_shininess, shadow_modulate, screen_uv, color, uv, false);
#else
light_color.rgb *= light_base_color.rgb * light_base_color.a;
@@ -451,24 +584,7 @@ FRAGMENT_SHADER_CODE
vec3 light_vec = normalize(light_pos - pos);
float cNdotL = max(0.0, dot(normal, light_vec));
- if (specular_shininess_used) {
- //blinn
- vec3 view = vec3(0.0, 0.0, 1.0); // not great but good enough
- vec3 half_vec = normalize(view + light_vec);
-
- float cNdotV = max(dot(normal, view), 0.0);
- float cNdotH = max(dot(normal, half_vec), 0.0);
- float cVdotH = max(dot(view, half_vec), 0.0);
- float cLdotH = max(dot(light_vec, half_vec), 0.0);
- float shininess = exp2(15.0 * specular_shininess.a + 1.0) * 0.25;
- float blinn = pow(cNdotH, shininess);
- blinn *= (shininess + 8.0) * (1.0 / (8.0 * M_PI));
- float s = (blinn) / max(4.0 * cNdotV * cNdotL, 0.75);
-
- light_color.rgb = specular_shininess.rgb * light_base_color.rgb * s + light_color.rgb * cNdotL;
- } else {
- light_color.rgb *= cNdotL;
- }
+ light_color.rgb = light_normal_compute(light_vec, normal, base_color, light_color.rgb, specular_shininess, specular_shininess_used);
}
#endif
if (any(lessThan(tex_uv, vec2(0.0, 0.0))) || any(greaterThanEqual(tex_uv, vec2(1.0, 1.0)))) {
@@ -506,69 +622,17 @@ FRAGMENT_SHADER_CODE
distance *= light_array.data[light_base].shadow_zfar_inv;
//float distance = length(shadow_pos);
- float shadow;
- uint shadow_mode = light_array.data[light_base].flags & LIGHT_FLAGS_FILTER_MASK;
-
vec4 shadow_uv = vec4(tex_ofs, light_array.data[light_base].shadow_y_ofs, distance, 1.0);
- if (shadow_mode == LIGHT_FLAGS_SHADOW_NEAREST) {
- shadow = textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv, 0.0).x;
- } else if (shadow_mode == LIGHT_FLAGS_SHADOW_PCF5) {
- vec4 shadow_pixel_size = vec4(light_array.data[light_base].shadow_pixel_size, 0.0, 0.0, 0.0);
- shadow = 0.0;
- shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv - shadow_pixel_size * 2.0, 0.0).x;
- shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv - shadow_pixel_size, 0.0).x;
- shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv, 0.0).x;
- shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv + shadow_pixel_size, 0.0).x;
- shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv + shadow_pixel_size * 2.0, 0.0).x;
- shadow /= 5.0;
- } else { //PCF13
- vec4 shadow_pixel_size = vec4(light_array.data[light_base].shadow_pixel_size, 0.0, 0.0, 0.0);
- shadow = 0.0;
- shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv - shadow_pixel_size * 6.0, 0.0).x;
- shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv - shadow_pixel_size * 5.0, 0.0).x;
- shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv - shadow_pixel_size * 4.0, 0.0).x;
- shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv - shadow_pixel_size * 3.0, 0.0).x;
- shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv - shadow_pixel_size * 2.0, 0.0).x;
- shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv - shadow_pixel_size, 0.0).x;
- shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv, 0.0).x;
- shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv + shadow_pixel_size, 0.0).x;
- shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv + shadow_pixel_size * 2.0, 0.0).x;
- shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv + shadow_pixel_size * 3.0, 0.0).x;
- shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv + shadow_pixel_size * 4.0, 0.0).x;
- shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv + shadow_pixel_size * 5.0, 0.0).x;
- shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv + shadow_pixel_size * 6.0, 0.0).x;
- shadow /= 13.0;
- }
-
- vec4 shadow_color = unpackUnorm4x8(light_array.data[light_base].shadow_color);
+ light_color = light_shadow_compute(light_base, light_color, shadow_uv
#ifdef LIGHT_SHADER_CODE_USED
- shadow_color *= shadow_modulate;
+ ,
+ shadow_modulate
#endif
-
- shadow_color.a *= light_color.a; //respect light alpha
-
- light_color = mix(light_color, shadow_color, shadow);
- //light_color = mix(light_color, shadow_color, shadow);
+ );
}
- uint blend_mode = light_array.data[light_base].flags & LIGHT_FLAGS_BLEND_MASK;
-
- switch (blend_mode) {
- case LIGHT_FLAGS_BLEND_MODE_ADD: {
- color.rgb += light_color.rgb * light_color.a;
- } break;
- case LIGHT_FLAGS_BLEND_MODE_SUB: {
- color.rgb -= light_color.rgb * light_color.a;
- } break;
- case LIGHT_FLAGS_BLEND_MODE_MIX: {
- color.rgb = mix(color.rgb, light_color.rgb, light_color.a);
- } break;
- case LIGHT_FLAGS_BLEND_MODE_MASK: {
- light_color.a *= base_color.a;
- color.rgb = mix(color.rgb, light_color.rgb, light_color.a);
- } break;
- }
+ light_blend_compute(light_base, light_color, color.rgb);
}
#endif
diff --git a/servers/rendering/rasterizer_rd/shaders/canvas_uniforms_inc.glsl b/servers/rendering/rasterizer_rd/shaders/canvas_uniforms_inc.glsl
index 1a226a87d3..bb39584cbb 100644
--- a/servers/rendering/rasterizer_rd/shaders/canvas_uniforms_inc.glsl
+++ b/servers/rendering/rasterizer_rd/shaders/canvas_uniforms_inc.glsl
@@ -68,7 +68,10 @@ layout(set = 0, binding = 1, std140) uniform CanvasData {
float time;
bool use_pixel_snap;
- //uint light_count;
+ uint directional_light_count;
+ uint pad0;
+ uint pad1;
+ uint pad2;
}
canvas_data;
diff --git a/servers/rendering/rendering_server_canvas.cpp b/servers/rendering/rendering_server_canvas.cpp
index fd7c022d62..4480b79f75 100644
--- a/servers/rendering/rendering_server_canvas.cpp
+++ b/servers/rendering/rendering_server_canvas.cpp
@@ -37,7 +37,7 @@
static const int z_range = RS::CANVAS_ITEM_Z_MAX - RS::CANVAS_ITEM_Z_MIN + 1;
-void RenderingServerCanvas::_render_canvas_item_tree(RID p_to_render_target, Canvas::ChildItem *p_child_items, int p_child_item_count, Item *p_canvas_item, const Transform2D &p_transform, const Rect2 &p_clip_rect, const Color &p_modulate, RasterizerCanvas::Light *p_lights, RenderingServer::CanvasItemTextureFilter p_default_filter, RenderingServer::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel) {
+void RenderingServerCanvas::_render_canvas_item_tree(RID p_to_render_target, Canvas::ChildItem *p_child_items, int p_child_item_count, Item *p_canvas_item, const Transform2D &p_transform, const Rect2 &p_clip_rect, const Color &p_modulate, RasterizerCanvas::Light *p_lights, RasterizerCanvas::Light *p_directional_lights, RenderingServer::CanvasItemTextureFilter p_default_filter, RenderingServer::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel) {
RENDER_TIMESTAMP("Cull CanvasItem Tree");
memset(z_list, 0, z_range * sizeof(RasterizerCanvas::Item *));
@@ -68,7 +68,7 @@ void RenderingServerCanvas::_render_canvas_item_tree(RID p_to_render_target, Can
RENDER_TIMESTAMP("Render Canvas Items");
- RSG::canvas_render->canvas_render_items(p_to_render_target, list, p_modulate, p_lights, p_transform, p_default_filter, p_default_repeat, p_snap_2d_vertices_to_pixel);
+ RSG::canvas_render->canvas_render_items(p_to_render_target, list, p_modulate, p_lights, p_directional_lights, p_transform, p_default_filter, p_default_repeat, p_snap_2d_vertices_to_pixel);
}
void _collect_ysort_children(RenderingServerCanvas::Item *p_canvas_item, Transform2D p_transform, RenderingServerCanvas::Item *p_material_owner, RenderingServerCanvas::Item **r_items, int &r_index) {
@@ -298,28 +298,7 @@ void RenderingServerCanvas::_cull_canvas_item(Item *p_canvas_item, const Transfo
}
}
-void RenderingServerCanvas::_light_mask_canvas_items(int p_z, RasterizerCanvas::Item *p_canvas_item, RasterizerCanvas::Light *p_masked_lights) {
- if (!p_masked_lights) {
- return;
- }
-
- RasterizerCanvas::Item *ci = p_canvas_item;
-
- while (ci) {
- RasterizerCanvas::Light *light = p_masked_lights;
- while (light) {
- if (ci->light_mask & light->item_mask && p_z >= light->z_min && p_z <= light->z_max && ci->global_rect_cache.intersects_transformed(light->xform_cache, light->rect_cache)) {
- ci->light_masked = true;
- }
-
- light = light->mask_next_ptr;
- }
-
- ci = ci->next;
- }
-}
-
-void RenderingServerCanvas::render_canvas(RID p_render_target, Canvas *p_canvas, const Transform2D &p_transform, RasterizerCanvas::Light *p_lights, RasterizerCanvas::Light *p_masked_lights, const Rect2 &p_clip_rect, RenderingServer::CanvasItemTextureFilter p_default_filter, RenderingServer::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_transforms_to_pixel, bool p_snap_2d_vertices_to_pixel) {
+void RenderingServerCanvas::render_canvas(RID p_render_target, Canvas *p_canvas, const Transform2D &p_transform, RasterizerCanvas::Light *p_lights, RasterizerCanvas::Light *p_directional_lights, const Rect2 &p_clip_rect, RenderingServer::CanvasItemTextureFilter p_default_filter, RenderingServer::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_transforms_to_pixel, bool p_snap_2d_vertices_to_pixel) {
RENDER_TIMESTAMP(">Render Canvas");
snapping_2d_transforms_to_pixel = p_snap_2d_transforms_to_pixel;
@@ -341,26 +320,26 @@ void RenderingServerCanvas::render_canvas(RID p_render_target, Canvas *p_canvas,
}
if (!has_mirror) {
- _render_canvas_item_tree(p_render_target, ci, l, nullptr, p_transform, p_clip_rect, p_canvas->modulate, p_lights, p_default_filter, p_default_repeat, p_snap_2d_vertices_to_pixel);
+ _render_canvas_item_tree(p_render_target, ci, l, nullptr, p_transform, p_clip_rect, p_canvas->modulate, p_lights, p_directional_lights, p_default_filter, p_default_repeat, p_snap_2d_vertices_to_pixel);
} else {
//used for parallaxlayer mirroring
for (int i = 0; i < l; i++) {
const Canvas::ChildItem &ci2 = p_canvas->child_items[i];
- _render_canvas_item_tree(p_render_target, nullptr, 0, ci2.item, p_transform, p_clip_rect, p_canvas->modulate, p_lights, p_default_filter, p_default_repeat, p_snap_2d_vertices_to_pixel);
+ _render_canvas_item_tree(p_render_target, nullptr, 0, ci2.item, p_transform, p_clip_rect, p_canvas->modulate, p_lights, p_directional_lights, p_default_filter, p_default_repeat, p_snap_2d_vertices_to_pixel);
//mirroring (useful for scrolling backgrounds)
if (ci2.mirror.x != 0) {
Transform2D xform2 = p_transform * Transform2D(0, Vector2(ci2.mirror.x, 0));
- _render_canvas_item_tree(p_render_target, nullptr, 0, ci2.item, xform2, p_clip_rect, p_canvas->modulate, p_lights, p_default_filter, p_default_repeat, p_snap_2d_vertices_to_pixel);
+ _render_canvas_item_tree(p_render_target, nullptr, 0, ci2.item, xform2, p_clip_rect, p_canvas->modulate, p_lights, p_directional_lights, p_default_filter, p_default_repeat, p_snap_2d_vertices_to_pixel);
}
if (ci2.mirror.y != 0) {
Transform2D xform2 = p_transform * Transform2D(0, Vector2(0, ci2.mirror.y));
- _render_canvas_item_tree(p_render_target, nullptr, 0, ci2.item, xform2, p_clip_rect, p_canvas->modulate, p_lights, p_default_filter, p_default_repeat, p_snap_2d_vertices_to_pixel);
+ _render_canvas_item_tree(p_render_target, nullptr, 0, ci2.item, xform2, p_clip_rect, p_canvas->modulate, p_lights, p_directional_lights, p_default_filter, p_default_repeat, p_snap_2d_vertices_to_pixel);
}
if (ci2.mirror.y != 0 && ci2.mirror.x != 0) {
Transform2D xform2 = p_transform * Transform2D(0, ci2.mirror);
- _render_canvas_item_tree(p_render_target, nullptr, 0, ci2.item, xform2, p_clip_rect, p_canvas->modulate, p_lights, p_default_filter, p_default_repeat, p_snap_2d_vertices_to_pixel);
+ _render_canvas_item_tree(p_render_target, nullptr, 0, ci2.item, xform2, p_clip_rect, p_canvas->modulate, p_lights, p_directional_lights, p_default_filter, p_default_repeat, p_snap_2d_vertices_to_pixel);
}
}
}
@@ -1040,13 +1019,38 @@ RID RenderingServerCanvas::canvas_light_create() {
return canvas_light_owner.make_rid(clight);
}
+void RenderingServerCanvas::canvas_light_set_mode(RID p_light, RS::CanvasLightMode p_mode) {
+ RasterizerCanvas::Light *clight = canvas_light_owner.getornull(p_light);
+ ERR_FAIL_COND(!clight);
+
+ if (clight->mode == p_mode) {
+ return;
+ }
+
+ RID canvas = clight->canvas;
+
+ if (canvas.is_valid()) {
+ canvas_light_attach_to_canvas(p_light, RID());
+ }
+
+ clight->mode = p_mode;
+
+ if (canvas.is_valid()) {
+ canvas_light_attach_to_canvas(p_light, canvas);
+ }
+}
+
void RenderingServerCanvas::canvas_light_attach_to_canvas(RID p_light, RID p_canvas) {
RasterizerCanvas::Light *clight = canvas_light_owner.getornull(p_light);
ERR_FAIL_COND(!clight);
if (clight->canvas.is_valid()) {
Canvas *canvas = canvas_owner.getornull(clight->canvas);
- canvas->lights.erase(clight);
+ if (clight->mode == RS::CANVAS_LIGHT_MODE_POINT) {
+ canvas->lights.erase(clight);
+ } else {
+ canvas->directional_lights.erase(clight);
+ }
}
if (!canvas_owner.owns(p_canvas)) {
@@ -1057,7 +1061,11 @@ void RenderingServerCanvas::canvas_light_attach_to_canvas(RID p_light, RID p_can
if (clight->canvas.is_valid()) {
Canvas *canvas = canvas_owner.getornull(clight->canvas);
- canvas->lights.insert(clight);
+ if (clight->mode == RS::CANVAS_LIGHT_MODE_POINT) {
+ canvas->lights.insert(clight);
+ } else {
+ canvas->directional_lights.insert(clight);
+ }
}
}
@@ -1068,7 +1076,7 @@ void RenderingServerCanvas::canvas_light_set_enabled(RID p_light, bool p_enabled
clight->enabled = p_enabled;
}
-void RenderingServerCanvas::canvas_light_set_scale(RID p_light, float p_scale) {
+void RenderingServerCanvas::canvas_light_set_texture_scale(RID p_light, float p_scale) {
RasterizerCanvas::Light *clight = canvas_light_owner.getornull(p_light);
ERR_FAIL_COND(!clight);
@@ -1152,11 +1160,18 @@ void RenderingServerCanvas::canvas_light_set_item_shadow_cull_mask(RID p_light,
clight->item_shadow_mask = p_mask;
}
-void RenderingServerCanvas::canvas_light_set_mode(RID p_light, RS::CanvasLightMode p_mode) {
+void RenderingServerCanvas::canvas_light_set_directional_distance(RID p_light, float p_distance) {
RasterizerCanvas::Light *clight = canvas_light_owner.getornull(p_light);
ERR_FAIL_COND(!clight);
- clight->mode = p_mode;
+ clight->directional_distance = p_distance;
+}
+
+void RenderingServerCanvas::canvas_light_set_blend_mode(RID p_light, RS::CanvasLightBlendMode p_mode) {
+ RasterizerCanvas::Light *clight = canvas_light_owner.getornull(p_light);
+ ERR_FAIL_COND(!clight);
+
+ clight->blend_mode = p_mode;
}
void RenderingServerCanvas::canvas_light_set_shadow_enabled(RID p_light, bool p_enabled) {
diff --git a/servers/rendering/rendering_server_canvas.h b/servers/rendering/rendering_server_canvas.h
index a9a81888aa..36e2f77e95 100644
--- a/servers/rendering/rendering_server_canvas.h
+++ b/servers/rendering/rendering_server_canvas.h
@@ -116,6 +116,7 @@ public:
};
Set<RasterizerCanvas::Light *> lights;
+ Set<RasterizerCanvas::Light *> directional_lights;
Set<RasterizerCanvas::LightOccluderInstance *> occluders;
@@ -155,15 +156,14 @@ public:
bool snapping_2d_transforms_to_pixel = false;
private:
- void _render_canvas_item_tree(RID p_to_render_target, Canvas::ChildItem *p_child_items, int p_child_item_count, Item *p_canvas_item, const Transform2D &p_transform, const Rect2 &p_clip_rect, const Color &p_modulate, RasterizerCanvas::Light *p_lights, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel);
+ void _render_canvas_item_tree(RID p_to_render_target, Canvas::ChildItem *p_child_items, int p_child_item_count, Item *p_canvas_item, const Transform2D &p_transform, const Rect2 &p_clip_rect, const Color &p_modulate, RasterizerCanvas::Light *p_lights, RasterizerCanvas::Light *p_directional_lights, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel);
void _cull_canvas_item(Item *p_canvas_item, const Transform2D &p_transform, const Rect2 &p_clip_rect, const Color &p_modulate, int p_z, RasterizerCanvas::Item **z_list, RasterizerCanvas::Item **z_last_list, Item *p_canvas_clip, Item *p_material_owner);
- void _light_mask_canvas_items(int p_z, RasterizerCanvas::Item *p_canvas_item, RasterizerCanvas::Light *p_masked_lights);
RasterizerCanvas::Item **z_list;
RasterizerCanvas::Item **z_last_list;
public:
- void render_canvas(RID p_render_target, Canvas *p_canvas, const Transform2D &p_transform, RasterizerCanvas::Light *p_lights, RasterizerCanvas::Light *p_masked_lights, const Rect2 &p_clip_rect, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_transforms_to_pixel, bool p_snap_2d_vertices_to_pixel);
+ void render_canvas(RID p_render_target, Canvas *p_canvas, const Transform2D &p_transform, RasterizerCanvas::Light *p_lights, RasterizerCanvas::Light *p_directional_lights, const Rect2 &p_clip_rect, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_transforms_to_pixel, bool p_snap_2d_vertices_to_pixel);
RID canvas_create();
void canvas_set_item_mirroring(RID p_canvas, RID p_item, const Point2 &p_mirroring);
@@ -220,9 +220,10 @@ public:
void canvas_item_set_canvas_group_mode(RID p_item, RS::CanvasGroupMode p_mode, float p_clear_margin = 5.0, bool p_fit_empty = false, float p_fit_margin = 0.0, bool p_blur_mipmaps = false);
RID canvas_light_create();
+ void canvas_light_set_mode(RID p_light, RS::CanvasLightMode p_mode);
void canvas_light_attach_to_canvas(RID p_light, RID p_canvas);
void canvas_light_set_enabled(RID p_light, bool p_enabled);
- void canvas_light_set_scale(RID p_light, float p_scale);
+ void canvas_light_set_texture_scale(RID p_light, float p_scale);
void canvas_light_set_transform(RID p_light, const Transform2D &p_transform);
void canvas_light_set_texture(RID p_light, RID p_texture);
void canvas_light_set_texture_offset(RID p_light, const Vector2 &p_offset);
@@ -233,8 +234,9 @@ public:
void canvas_light_set_layer_range(RID p_light, int p_min_layer, int p_max_layer);
void canvas_light_set_item_cull_mask(RID p_light, int p_mask);
void canvas_light_set_item_shadow_cull_mask(RID p_light, int p_mask);
+ void canvas_light_set_directional_distance(RID p_light, float p_distance);
- void canvas_light_set_mode(RID p_light, RS::CanvasLightMode p_mode);
+ void canvas_light_set_blend_mode(RID p_light, RS::CanvasLightBlendMode p_mode);
void canvas_light_set_shadow_enabled(RID p_light, bool p_enabled);
void canvas_light_set_shadow_filter(RID p_light, RS::CanvasLightShadowFilter p_filter);
diff --git a/servers/rendering/rendering_server_raster.h b/servers/rendering/rendering_server_raster.h
index a89d4db1f8..daad706f8e 100644
--- a/servers/rendering/rendering_server_raster.h
+++ b/servers/rendering/rendering_server_raster.h
@@ -748,9 +748,12 @@ public:
BIND6(canvas_item_set_canvas_group_mode, RID, CanvasGroupMode, float, bool, float, bool)
BIND0R(RID, canvas_light_create)
+
+ BIND2(canvas_light_set_mode, RID, CanvasLightMode)
+
BIND2(canvas_light_attach_to_canvas, RID, RID)
BIND2(canvas_light_set_enabled, RID, bool)
- BIND2(canvas_light_set_scale, RID, float)
+ BIND2(canvas_light_set_texture_scale, RID, float)
BIND2(canvas_light_set_transform, RID, const Transform2D &)
BIND2(canvas_light_set_texture, RID, RID)
BIND2(canvas_light_set_texture_offset, RID, const Vector2 &)
@@ -761,8 +764,9 @@ public:
BIND3(canvas_light_set_layer_range, RID, int, int)
BIND2(canvas_light_set_item_cull_mask, RID, int)
BIND2(canvas_light_set_item_shadow_cull_mask, RID, int)
+ BIND2(canvas_light_set_directional_distance, RID, float)
- BIND2(canvas_light_set_mode, RID, CanvasLightMode)
+ BIND2(canvas_light_set_blend_mode, RID, CanvasLightBlendMode)
BIND2(canvas_light_set_shadow_enabled, RID, bool)
BIND2(canvas_light_set_shadow_filter, RID, CanvasLightShadowFilter)
diff --git a/servers/rendering/rendering_server_viewport.cpp b/servers/rendering/rendering_server_viewport.cpp
index b5d8f0da40..23911faa5d 100644
--- a/servers/rendering/rendering_server_viewport.cpp
+++ b/servers/rendering/rendering_server_viewport.cpp
@@ -142,11 +142,15 @@ void RenderingServerViewport::_draw_viewport(Viewport *p_viewport, XRInterface::
Rect2 clip_rect(0, 0, p_viewport->size.x, p_viewport->size.y);
RasterizerCanvas::Light *lights = nullptr;
RasterizerCanvas::Light *lights_with_shadow = nullptr;
- RasterizerCanvas::Light *lights_with_mask = nullptr;
+
+ RasterizerCanvas::Light *directional_lights = nullptr;
+ RasterizerCanvas::Light *directional_lights_with_shadow = nullptr;
+
Rect2 shadow_rect;
int light_count = 0;
int shadow_count = 0;
+ int directional_light_count = 0;
RENDER_TIMESTAMP("Cull Canvas Lights");
for (Map<RID, Viewport::CanvasData>::Element *E = p_viewport->canvas_map.front(); E; E = E->next()) {
@@ -186,10 +190,6 @@ void RenderingServerViewport::_draw_viewport(Viewport *p_viewport, XRInterface::
lights_with_shadow = cl;
cl->radius_cache = cl->rect_cache.size.length();
}
- if (cl->mode == RS::CANVAS_LIGHT_MODE_MASK) {
- cl->mask_next_ptr = lights_with_mask;
- lights_with_mask = cl;
- }
light_count++;
}
@@ -199,6 +199,26 @@ void RenderingServerViewport::_draw_viewport(Viewport *p_viewport, XRInterface::
}
}
+ for (Set<RasterizerCanvas::Light *>::Element *F = canvas->directional_lights.front(); F; F = F->next()) {
+ RasterizerCanvas::Light *cl = F->get();
+ if (cl->enabled) {
+ cl->filter_next_ptr = directional_lights;
+ directional_lights = cl;
+ cl->xform_cache = xf * cl->xform;
+ cl->xform_cache.elements[2] = Vector2(); //translation is pointless
+ if (cl->use_shadow) {
+ cl->shadows_next_ptr = directional_lights_with_shadow;
+ directional_lights_with_shadow = cl;
+ }
+
+ directional_light_count++;
+
+ if (directional_light_count == RS::MAX_2D_DIRECTIONAL_LIGHTS) {
+ break;
+ }
+ }
+ }
+
canvas_map[Viewport::CanvasKey(E->key(), E->get().layer, E->get().sublayer)] = &E->get();
}
@@ -240,6 +260,90 @@ void RenderingServerViewport::_draw_viewport(Viewport *p_viewport, XRInterface::
RENDER_TIMESTAMP("<End rendering 2D Shadows");
}
+ if (directional_lights_with_shadow) {
+ //update shadows if any
+ RasterizerCanvas::Light *light = directional_lights_with_shadow;
+ while (light) {
+ Vector2 light_dir = -light->xform_cache.elements[1].normalized(); // Y is light direction
+ float cull_distance = light->directional_distance;
+
+ Vector2 light_dir_sign;
+ light_dir_sign.x = (ABS(light_dir.x) < CMP_EPSILON) ? 0.0 : ((light_dir.x > 0.0) ? 1.0 : -1.0);
+ light_dir_sign.y = (ABS(light_dir.y) < CMP_EPSILON) ? 0.0 : ((light_dir.y > 0.0) ? 1.0 : -1.0);
+
+ Vector2 points[6];
+ int point_count = 0;
+
+ for (int j = 0; j < 4; j++) {
+ static const Vector2 signs[4] = { Vector2(1, 1), Vector2(1, 0), Vector2(0, 0), Vector2(0, 1) };
+ Vector2 sign_cmp = signs[j] * 2.0 - Vector2(1.0, 1.0);
+ Vector2 point = clip_rect.position + clip_rect.size * signs[j];
+
+ if (sign_cmp == light_dir_sign) {
+ //both point in same direction, plot offseted
+ points[point_count++] = point + light_dir * cull_distance;
+ } else if (sign_cmp.x == light_dir_sign.x || sign_cmp.y == light_dir_sign.y) {
+ int next_j = (j + 1) % 4;
+ Vector2 next_sign_cmp = signs[next_j] * 2.0 - Vector2(1.0, 1.0);
+
+ //one point in the same direction, plot segment
+
+ if (next_sign_cmp.x == light_dir_sign.x || next_sign_cmp.y == light_dir_sign.y) {
+ if (light_dir_sign.x != 0.0 || light_dir_sign.y != 0.0) {
+ points[point_count++] = point;
+ }
+ points[point_count++] = point + light_dir * cull_distance;
+ } else {
+ points[point_count++] = point + light_dir * cull_distance;
+ if (light_dir_sign.x != 0.0 || light_dir_sign.y != 0.0) {
+ points[point_count++] = point;
+ }
+ }
+ } else {
+ //plot normally
+ points[point_count++] = point;
+ }
+ }
+
+ Vector2 xf_points[6];
+
+ RasterizerCanvas::LightOccluderInstance *occluders = nullptr;
+
+ RENDER_TIMESTAMP(">Render Directional 2D Shadows");
+
+ //make list of occluders
+ int occ_cullded = 0;
+ for (Map<RID, Viewport::CanvasData>::Element *E = p_viewport->canvas_map.front(); E; E = E->next()) {
+ RenderingServerCanvas::Canvas *canvas = static_cast<RenderingServerCanvas::Canvas *>(E->get().canvas);
+ Transform2D xf = _canvas_get_transform(p_viewport, canvas, &E->get(), clip_rect.size);
+
+ for (Set<RasterizerCanvas::LightOccluderInstance *>::Element *F = canvas->occluders.front(); F; F = F->next()) {
+ if (!F->get()->enabled) {
+ continue;
+ }
+ F->get()->xform_cache = xf * F->get()->xform;
+ Transform2D localizer = F->get()->xform_cache.affine_inverse();
+
+ for (int j = 0; j < point_count; j++) {
+ xf_points[j] = localizer.xform(points[j]);
+ }
+ if (F->get()->aabb_cache.intersects_filled_polygon(xf_points, point_count)) {
+ F->get()->next = occluders;
+ occluders = F->get();
+ occ_cullded++;
+ }
+ }
+ }
+
+ RSG::canvas_render->light_update_directional_shadow(light->light_internal, shadow_count++, light->xform_cache, light->item_shadow_mask, cull_distance, clip_rect, occluders);
+
+ light = light->shadows_next_ptr;
+ }
+
+ //RSG::canvas_render->reset_canvas();
+ RENDER_TIMESTAMP("<Render Directional 2D Shadows");
+ }
+
if (scenario_draw_canvas_bg && canvas_map.front() && canvas_map.front()->key().get_layer() > scenario_canvas_max_layer) {
if (!can_draw_3d) {
RSG::scene->render_empty_scene(p_viewport->render_buffers, p_viewport->scenario, p_viewport->shadow_atlas);
@@ -255,6 +359,7 @@ void RenderingServerViewport::_draw_viewport(Viewport *p_viewport, XRInterface::
Transform2D xform = _canvas_get_transform(p_viewport, canvas, E->get(), clip_rect.size);
RasterizerCanvas::Light *canvas_lights = nullptr;
+ RasterizerCanvas::Light *canvas_directional_lights = nullptr;
RasterizerCanvas::Light *ptr = lights;
while (ptr) {
@@ -265,7 +370,16 @@ void RenderingServerViewport::_draw_viewport(Viewport *p_viewport, XRInterface::
ptr = ptr->filter_next_ptr;
}
- RSG::canvas->render_canvas(p_viewport->render_target, canvas, xform, canvas_lights, lights_with_mask, clip_rect, p_viewport->texture_filter, p_viewport->texture_repeat, p_viewport->snap_2d_transforms_to_pixel, p_viewport->snap_2d_vertices_to_pixel);
+ ptr = directional_lights;
+ while (ptr) {
+ if (E->get()->layer >= ptr->layer_min && E->get()->layer <= ptr->layer_max) {
+ ptr->next_ptr = canvas_directional_lights;
+ canvas_directional_lights = ptr;
+ }
+ ptr = ptr->filter_next_ptr;
+ }
+
+ RSG::canvas->render_canvas(p_viewport->render_target, canvas, xform, canvas_lights, canvas_directional_lights, clip_rect, p_viewport->texture_filter, p_viewport->texture_repeat, p_viewport->snap_2d_transforms_to_pixel, p_viewport->snap_2d_vertices_to_pixel);
i++;
if (scenario_draw_canvas_bg && E->key().get_layer() >= scenario_canvas_max_layer) {
diff --git a/servers/rendering/rendering_server_wrap_mt.h b/servers/rendering/rendering_server_wrap_mt.h
index 42f1b5bcc8..237ba012fa 100644
--- a/servers/rendering/rendering_server_wrap_mt.h
+++ b/servers/rendering/rendering_server_wrap_mt.h
@@ -647,9 +647,12 @@ public:
FUNC6(canvas_item_set_canvas_group_mode, RID, CanvasGroupMode, float, bool, float, bool)
FUNC0R(RID, canvas_light_create)
+
+ FUNC2(canvas_light_set_mode, RID, CanvasLightMode)
+
FUNC2(canvas_light_attach_to_canvas, RID, RID)
FUNC2(canvas_light_set_enabled, RID, bool)
- FUNC2(canvas_light_set_scale, RID, float)
+ FUNC2(canvas_light_set_texture_scale, RID, float)
FUNC2(canvas_light_set_transform, RID, const Transform2D &)
FUNC2(canvas_light_set_texture, RID, RID)
FUNC2(canvas_light_set_texture_offset, RID, const Vector2 &)
@@ -660,8 +663,9 @@ public:
FUNC3(canvas_light_set_layer_range, RID, int, int)
FUNC2(canvas_light_set_item_cull_mask, RID, int)
FUNC2(canvas_light_set_item_shadow_cull_mask, RID, int)
+ FUNC2(canvas_light_set_directional_distance, RID, float)
- FUNC2(canvas_light_set_mode, RID, CanvasLightMode)
+ FUNC2(canvas_light_set_blend_mode, RID, CanvasLightBlendMode)
FUNC2(canvas_light_set_shadow_enabled, RID, bool)
FUNC2(canvas_light_set_shadow_filter, RID, CanvasLightShadowFilter)