summaryrefslogtreecommitdiff
path: root/servers/visual/rasterizer
diff options
context:
space:
mode:
Diffstat (limited to 'servers/visual/rasterizer')
-rw-r--r--servers/visual/rasterizer/rasterizer.h43
-rw-r--r--servers/visual/rasterizer/rasterizer_canvas_rd.cpp621
-rw-r--r--servers/visual/rasterizer/rasterizer_canvas_rd.h142
-rw-r--r--servers/visual/rasterizer/rasterizer_storage_rd.h9
-rw-r--r--servers/visual/rasterizer/shaders/SCsub2
-rw-r--r--servers/visual/rasterizer/shaders/canvas.glsl141
-rw-r--r--servers/visual/rasterizer/shaders/canvas_occlusion.glsl38
-rw-r--r--servers/visual/rasterizer/shaders/canvas_occlusion_fix.glsl56
-rw-r--r--servers/visual/rasterizer/shaders/canvas_uniforms_inc.glsl48
9 files changed, 973 insertions, 127 deletions
diff --git a/servers/visual/rasterizer/rasterizer.h b/servers/visual/rasterizer/rasterizer.h
index 853d02f7c3..88520a6a79 100644
--- a/servers/visual/rasterizer/rasterizer.h
+++ b/servers/visual/rasterizer/rasterizer.h
@@ -562,15 +562,6 @@ public:
virtual void render_target_disable_clear_request(RID p_render_target) = 0;
virtual void render_target_do_clear_request(RID p_render_target) = 0;
- /* CANVAS SHADOW */
-
- virtual RID canvas_light_shadow_buffer_create(int p_width) = 0;
-
- /* LIGHT SHADOW MAPPING */
-
- virtual RID canvas_light_occluder_create() = 0;
- virtual void canvas_light_occluder_set_polylines(RID p_occluder, const PoolVector<Vector2> &p_lines) = 0;
-
virtual VS::InstanceType get_base_type(RID p_rid) const = 0;
virtual bool free(RID p_rid) = 0;
@@ -625,21 +616,21 @@ public:
RID texture;
Vector2 texture_offset;
RID canvas;
- RID shadow_buffer;
+ bool use_shadow;
int shadow_buffer_size;
float shadow_gradient_length;
VS::CanvasLightShadowFilter shadow_filter;
Color shadow_color;
float shadow_smooth;
- void *texture_cache; // implementation dependent
+ //void *texture_cache; // implementation dependent
Rect2 rect_cache;
Transform2D xform_cache;
float radius_cache; //used for shadow far plane
- CameraMatrix shadow_matrix_cache;
+ //CameraMatrix shadow_matrix_cache;
Transform2D light_shader_xform;
- Vector2 light_shader_pos;
+ //Vector2 light_shader_pos;
Light *shadows_next_ptr;
Light *filter_next_ptr;
@@ -648,6 +639,8 @@ public:
RID light_internal;
+ int32_t render_index_cache;
+
Light() {
enabled = true;
color = Color(1, 1, 1);
@@ -662,21 +655,19 @@ public:
energy = 1.0;
item_shadow_mask = -1;
mode = VS::CANVAS_LIGHT_MODE_ADD;
- texture_cache = NULL;
+ // texture_cache = NULL;
next_ptr = NULL;
mask_next_ptr = NULL;
filter_next_ptr = NULL;
+ use_shadow = false;
shadow_buffer_size = 2048;
shadow_gradient_length = 0;
shadow_filter = VS::CANVAS_LIGHT_FILTER_NONE;
shadow_smooth = 0.0;
+ render_index_cache = -1;
}
};
- virtual RID light_internal_create() = 0;
- virtual void light_internal_update(RID p_rid, Light *p_light) = 0;
- virtual void light_internal_free(RID p_rid) = 0;
-
typedef uint64_t TextureBindingID;
virtual TextureBindingID request_texture_binding(RID p_texture, RID p_normalmap, RID p_specular, VS::CanvasItemTextureFilter p_filter, VS::CanvasItemTextureRepeat p_repeat, RID p_multimesh) = 0;
@@ -957,7 +948,7 @@ public:
case Item::Command::TYPE_PRIMITIVE: {
const Item::CommandPrimitive *primitive = static_cast<const Item::CommandPrimitive *>(c);
- for (int j = 0; j < primitive->point_count; j++) {
+ for (uint32_t j = 0; j < primitive->point_count; j++) {
if (j == 0) {
r.position = primitive->points[0];
} else {
@@ -1085,7 +1076,7 @@ public:
c = n;
}
{
- uint32_t cbc = MIN((current_block + 1), blocks.size());
+ uint32_t cbc = MIN((current_block + 1), (uint32_t)blocks.size());
CommandBlock *blockptr = blocks.ptrw();
for (uint32_t i = 0; i < cbc; i++) {
blockptr[i].usage = 0;
@@ -1139,7 +1130,7 @@ public:
bool enabled;
RID canvas;
RID polygon;
- RID polygon_buffer;
+ RID occluder;
Rect2 aabb_cache;
Transform2D xform;
Transform2D xform_cache;
@@ -1156,12 +1147,18 @@ public:
}
};
- virtual void canvas_light_shadow_buffer_update(RID p_buffer, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders, CameraMatrix *p_xform_cache) = 0;
+ virtual RID light_create() = 0;
+ virtual void light_set_texture(RID p_rid, RID p_texture) = 0;
+ virtual void light_set_use_shadow(RID p_rid, bool p_enable, int p_resolution) = 0;
+ virtual void light_update_shadow(RID p_rid, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders) = 0;
- virtual void reset_canvas() = 0;
+ virtual RID occluder_polygon_create() = 0;
+ virtual void occluder_polygon_set_shape_as_lines(RID p_occluder, const PoolVector<Vector2> &p_lines) = 0;
+ virtual void occluder_polygon_set_cull_mode(RID p_occluder, VS::CanvasOccluderPolygonCullMode p_mode) = 0;
virtual void draw_window_margins(int *p_margins, RID *p_margin_textures) = 0;
+ virtual bool free(RID p_rid) = 0;
virtual void update() = 0;
RasterizerCanvas() { singleton = this; }
diff --git a/servers/visual/rasterizer/rasterizer_canvas_rd.cpp b/servers/visual/rasterizer/rasterizer_canvas_rd.cpp
index d72be70677..838e26ac73 100644
--- a/servers/visual/rasterizer/rasterizer_canvas_rd.cpp
+++ b/servers/visual/rasterizer/rasterizer_canvas_rd.cpp
@@ -63,6 +63,14 @@ void RasterizerCanvasRD::_update_transform_to_mat4(const Transform &p_transform,
p_mat4[15] = 1;
}
+void RasterizerCanvasRD::_update_specular_shininess(const Color &p_transform, uint32_t *r_ss) {
+
+ *r_ss = uint32_t(CLAMP(p_transform.a * 255.0, 0, 255)) << 24;
+ *r_ss |= uint32_t(CLAMP(p_transform.b * 255.0, 0, 255)) << 16;
+ *r_ss |= uint32_t(CLAMP(p_transform.g * 255.0, 0, 255)) << 8;
+ *r_ss |= uint32_t(CLAMP(p_transform.r * 255.0, 0, 255));
+}
+
RID RasterizerCanvasRD::_create_texture_binding(RID p_texture, RID p_normalmap, RID p_specular, VisualServer::CanvasItemTextureFilter p_filter, VisualServer::CanvasItemTextureRepeat p_repeat, RID p_multimesh) {
Vector<RD::Uniform> uniform_set;
@@ -472,12 +480,19 @@ void RasterizerCanvasRD::free_polygon(PolygonID p_polygon) {
polygon_buffers.polygons.erase(p_polygon);
}
-Size2i RasterizerCanvasRD::_bind_texture_binding(TextureBindingID p_binding, RD::DrawListID p_draw_list) {
+Size2i RasterizerCanvasRD::_bind_texture_binding(TextureBindingID p_binding, RD::DrawListID p_draw_list, uint32_t &flags) {
TextureBinding **texture_binding_ptr = bindings.texture_bindings.getptr(p_binding);
ERR_FAIL_COND_V(!texture_binding_ptr, Size2i());
TextureBinding *texture_binding = *texture_binding_ptr;
+ if (texture_binding->key.normalmap.is_valid()) {
+ flags |= FLAGS_DEFAULT_NORMAL_MAP_USED;
+ }
+ if (texture_binding->key.specular.is_valid()) {
+ flags |= FLAGS_DEFAULT_SPECULAR_MAP_USED;
+ }
+
if (!RD::get_singleton()->uniform_set_is_valid(texture_binding->uniform_set)) {
//texture may have changed (erased or replaced, see if we can fix)
texture_binding->uniform_set = _create_texture_binding(texture_binding->key.texture, texture_binding->key.normalmap, texture_binding->key.specular, texture_binding->key.texture_filter, texture_binding->key.texture_repeat, texture_binding->key.multimesh);
@@ -493,12 +508,15 @@ Size2i RasterizerCanvasRD::_bind_texture_binding(TextureBindingID p_binding, RD:
}
////////////////////
-void RasterizerCanvasRD::_render_item(RD::DrawListID p_draw_list, const Item *p_item, RenderTargetFormat p_render_target_format, RD::TextureSamples p_samples, const Color &p_modulate, const Transform2D &p_canvas_transform_inverse, Item *&current_clip) {
+void RasterizerCanvasRD::_render_item(RD::DrawListID p_draw_list, const Item *p_item, RenderTargetFormat p_render_target_format, RD::TextureSamples p_samples, const Transform2D &p_canvas_transform_inverse, Item *&current_clip, Light *p_lights) {
//create an empty push constant
PushConstant push_constant;
Transform2D base_transform = p_canvas_transform_inverse * p_item->final_transform;
_update_transform_2d_to_mat2x3(base_transform, push_constant.world);
+
+ Color base_color = p_item->final_modulate;
+
for (int i = 0; i < 4; i++) {
push_constant.modulation[i] = 0;
push_constant.ninepatch_margins[i] = 0;
@@ -506,7 +524,6 @@ void RasterizerCanvasRD::_render_item(RD::DrawListID p_draw_list, const Item *p_
push_constant.dst_rect[i] = 0;
}
push_constant.flags = 0;
- push_constant.specular_shininess = 0xFFFFFFFF;
push_constant.color_texture_pixel_size[0] = 0;
push_constant.color_texture_pixel_size[1] = 0;
@@ -514,6 +531,37 @@ void RasterizerCanvasRD::_render_item(RD::DrawListID p_draw_list, const Item *p_
push_constant.pad[2] = 0;
push_constant.pad[3] = 0;
+ push_constant.lights[0] = 0;
+ push_constant.lights[1] = 0;
+ push_constant.lights[2] = 0;
+ push_constant.lights[3] = 0;
+
+ uint32_t base_flags = 0;
+
+ {
+ Light *light = p_lights;
+
+ uint16_t light_count = 0;
+ while (light) {
+
+ if (light->render_index_cache >= 0 && p_item->light_mask & light->item_mask && p_item->z_final >= light->z_min && p_item->z_final <= light->z_max && p_item->global_rect_cache.intersects_transformed(light->xform_cache, light->rect_cache)) {
+
+ uint32_t light_index = light->render_index_cache;
+ push_constant.lights[light_count >> 2] |= light_index << ((light_count & 3) * 8);
+ light_count++;
+ if (light->mode == VS::CANVAS_LIGHT_MODE_MASK) {
+ base_flags |= FLAGS_USING_LIGHT_MASK;
+ }
+ if (light_count == MAX_LIGHTS_PER_ITEM) {
+ break;
+ }
+ }
+ light = light->next_ptr;
+ }
+
+ base_flags |= light_count << FLAGS_LIGHT_COUNT_SHIFT;
+ }
+
PipelineVariants *pipeline_variants = &shader.pipeline_variants;
bool reclip = false;
@@ -521,7 +569,8 @@ void RasterizerCanvasRD::_render_item(RD::DrawListID p_draw_list, const Item *p_
const Item::Command *c = p_item->commands;
while (c) {
- push_constant.flags = 0; //reset on each command for sanity
+ push_constant.flags = base_flags; //reset on each command for sanity
+ push_constant.specular_shininess = 0xFFFFFFFF;
switch (c->type) {
case Item::Command::TYPE_RECT: {
@@ -538,11 +587,17 @@ void RasterizerCanvasRD::_render_item(RD::DrawListID p_draw_list, const Item *p_
Size2 texpixel_size;
{
- texpixel_size = _bind_texture_binding(rect->texture_binding.binding_id, p_draw_list);
+ texpixel_size = _bind_texture_binding(rect->texture_binding.binding_id, p_draw_list, push_constant.flags);
texpixel_size.x = 1.0 / texpixel_size.x;
texpixel_size.y = 1.0 / texpixel_size.y;
}
+ if (rect->specular_shininess.a < 0.999) {
+ push_constant.flags |= FLAGS_DEFAULT_SPECULAR_MAP_USED;
+ }
+
+ _update_specular_shininess(rect->specular_shininess, &push_constant.specular_shininess);
+
Rect2 src_rect;
Rect2 dst_rect;
@@ -594,10 +649,10 @@ void RasterizerCanvasRD::_render_item(RD::DrawListID p_draw_list, const Item *p_
texpixel_size = Vector2(1, 1);
}
- push_constant.modulation[0] = rect->modulate.r * p_modulate.r;
- push_constant.modulation[1] = rect->modulate.g * p_modulate.g;
- push_constant.modulation[2] = rect->modulate.b * p_modulate.b;
- push_constant.modulation[3] = rect->modulate.a;
+ push_constant.modulation[0] = rect->modulate.r * base_color.r;
+ push_constant.modulation[1] = rect->modulate.g * base_color.g;
+ push_constant.modulation[2] = rect->modulate.b * base_color.b;
+ push_constant.modulation[3] = rect->modulate.a * base_color.a;
push_constant.src_rect[0] = src_rect.position.x;
push_constant.src_rect[1] = src_rect.position.y;
@@ -632,11 +687,17 @@ void RasterizerCanvasRD::_render_item(RD::DrawListID p_draw_list, const Item *p_
Size2 texpixel_size;
{
- texpixel_size = _bind_texture_binding(np->texture_binding.binding_id, p_draw_list);
+ texpixel_size = _bind_texture_binding(np->texture_binding.binding_id, p_draw_list, push_constant.flags);
texpixel_size.x = 1.0 / texpixel_size.x;
texpixel_size.y = 1.0 / texpixel_size.y;
}
+ if (np->specular_shininess.a < 0.999) {
+ push_constant.flags |= FLAGS_DEFAULT_SPECULAR_MAP_USED;
+ }
+
+ _update_specular_shininess(np->specular_shininess, &push_constant.specular_shininess);
+
Rect2 src_rect;
Rect2 dst_rect(np->rect.position.x, np->rect.position.y, np->rect.size.x, np->rect.size.y);
@@ -655,10 +716,10 @@ void RasterizerCanvasRD::_render_item(RD::DrawListID p_draw_list, const Item *p_
}
}
- push_constant.modulation[0] = np->color.r * p_modulate.r;
- push_constant.modulation[1] = np->color.g * p_modulate.g;
- push_constant.modulation[2] = np->color.b * p_modulate.b;
- push_constant.modulation[3] = np->color.a * p_modulate.a;
+ push_constant.modulation[0] = np->color.r * base_color.r;
+ push_constant.modulation[1] = np->color.g * base_color.g;
+ push_constant.modulation[2] = np->color.b * base_color.b;
+ push_constant.modulation[3] = np->color.a * base_color.a;
push_constant.src_rect[0] = src_rect.position.x;
push_constant.src_rect[1] = src_rect.position.y;
@@ -713,15 +774,21 @@ void RasterizerCanvasRD::_render_item(RD::DrawListID p_draw_list, const Item *p_
Size2 texpixel_size;
{
- texpixel_size = _bind_texture_binding(polygon->texture_binding.binding_id, p_draw_list);
+ texpixel_size = _bind_texture_binding(polygon->texture_binding.binding_id, p_draw_list, push_constant.flags);
texpixel_size.x = 1.0 / texpixel_size.x;
texpixel_size.y = 1.0 / texpixel_size.y;
}
- push_constant.modulation[0] = p_modulate.r;
- push_constant.modulation[1] = p_modulate.g;
- push_constant.modulation[2] = p_modulate.b;
- push_constant.modulation[3] = p_modulate.a;
+ if (polygon->specular_shininess.a < 0.999) {
+ push_constant.flags |= FLAGS_DEFAULT_SPECULAR_MAP_USED;
+ }
+
+ _update_specular_shininess(polygon->specular_shininess, &push_constant.specular_shininess);
+
+ push_constant.modulation[0] = base_color.r;
+ push_constant.modulation[1] = base_color.g;
+ push_constant.modulation[2] = base_color.b;
+ push_constant.modulation[3] = base_color.a;
for (int j = 0; j < 4; j++) {
push_constant.src_rect[j] = 0;
@@ -755,9 +822,15 @@ void RasterizerCanvasRD::_render_item(RD::DrawListID p_draw_list, const Item *p_
//bind textures
{
- _bind_texture_binding(primitive->texture_binding.binding_id, p_draw_list);
+ _bind_texture_binding(primitive->texture_binding.binding_id, p_draw_list, push_constant.flags);
}
+ if (primitive->specular_shininess.a < 0.999) {
+ push_constant.flags |= FLAGS_DEFAULT_SPECULAR_MAP_USED;
+ }
+
+ _update_specular_shininess(primitive->specular_shininess, &push_constant.specular_shininess);
+
RD::get_singleton()->draw_list_bind_index_array(p_draw_list, primitive_arrays.index_array[MIN(3, primitive->point_count) - 1]);
for (uint32_t j = 0; j < MIN(3, primitive->point_count); j++) {
@@ -765,7 +838,7 @@ void RasterizerCanvasRD::_render_item(RD::DrawListID p_draw_list, const Item *p_
push_constant.points[j * 2 + 1] = primitive->points[j].y;
push_constant.uvs[j * 2 + 0] = primitive->uvs[j].x;
push_constant.uvs[j * 2 + 1] = primitive->uvs[j].y;
- Color col = primitive->colors[j] * p_modulate;
+ Color col = primitive->colors[j] * base_color;
push_constant.colors[j * 2 + 0] = (uint32_t(Math::make_half_float(col.g)) << 16) | Math::make_half_float(col.r);
push_constant.colors[j * 2 + 1] = (uint32_t(Math::make_half_float(col.a)) << 16) | Math::make_half_float(col.b);
}
@@ -779,7 +852,7 @@ void RasterizerCanvasRD::_render_item(RD::DrawListID p_draw_list, const Item *p_
push_constant.points[j * 2 + 1] = primitive->points[j + 1].y;
push_constant.uvs[j * 2 + 0] = primitive->uvs[j + 1].x;
push_constant.uvs[j * 2 + 1] = primitive->uvs[j + 1].y;
- Color col = primitive->colors[j + 1] * p_modulate;
+ Color col = primitive->colors[j + 1] * base_color;
push_constant.colors[j * 2 + 0] = (uint32_t(Math::make_half_float(col.g)) << 16) | Math::make_half_float(col.r);
push_constant.colors[j * 2 + 1] = (uint32_t(Math::make_half_float(col.a)) << 16) | Math::make_half_float(col.b);
}
@@ -1107,12 +1180,12 @@ void RasterizerCanvasRD::_render_item(RD::DrawListID p_draw_list, const Item *p_
}
}
-void RasterizerCanvasRD::_render_items(RID p_to_render_target, int p_item_count, const Color &p_modulate, const Transform2D &p_transform) {
+void RasterizerCanvasRD::_render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights) {
Item *current_clip = NULL;
RenderTargetFormat render_target_format = RENDER_TARGET_FORMAT_8_BIT_INT;
- Transform2D canvas_transform_inverse = p_transform.affine_inverse();
+ Transform2D canvas_transform_inverse = p_canvas_transform_inverse;
RID framebuffer = storage->render_target_get_rd_framebuffer(p_to_render_target);
@@ -1153,7 +1226,7 @@ void RasterizerCanvasRD::_render_items(RID p_to_render_target, int p_item_count,
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, shader.default_skeleton_uniform_set, 1);
}
- _render_item(draw_list, ci, render_target_format, texture_samples, p_modulate, canvas_transform_inverse, current_clip);
+ _render_item(draw_list, ci, render_target_format, texture_samples, canvas_transform_inverse, current_clip, p_lights);
}
RD::get_singleton()->draw_list_end();
@@ -1167,11 +1240,67 @@ void RasterizerCanvasRD::_update_canvas_state_uniform_set() {
Vector<RD::Uniform> uniforms;
- RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_UNIFORM_BUFFER;
- u.binding = 0;
- u.ids.push_back(state.canvas_state_buffer);
- uniforms.push_back(u);
+ {
+ RD::Uniform u;
+ u.type = RD::UNIFORM_TYPE_UNIFORM_BUFFER;
+ u.binding = 0;
+ u.ids.push_back(state.canvas_state_buffer);
+ uniforms.push_back(u);
+ }
+
+ {
+ RD::Uniform u;
+ u.type = RD::UNIFORM_TYPE_UNIFORM_BUFFER;
+ u.binding = 1;
+ u.ids.push_back(state.lights_uniform_buffer);
+ uniforms.push_back(u);
+ }
+
+ {
+
+ RD::Uniform u_lights;
+ u_lights.type = RD::UNIFORM_TYPE_TEXTURE;
+ u_lights.binding = 2;
+
+ RD::Uniform u_shadows;
+ u_shadows.type = RD::UNIFORM_TYPE_TEXTURE;
+ u_shadows.binding = 3;
+
+ //lights
+ for (uint32_t i = 0; i < MAX_LIGHT_TEXTURES; i++) {
+ if (i < canvas_light_owner.get_rid_count()) {
+ CanvasLight *cl = canvas_light_owner.get_rid_by_index(i);
+ cl->texture_index = i;
+ RID rd_texture;
+
+ if (cl->texture.is_valid()) {
+ rd_texture = storage->texture_get_rd_texture(cl->texture);
+ }
+ if (rd_texture.is_valid()) {
+ u_lights.ids.push_back(rd_texture);
+ } else {
+ u_lights.ids.push_back(default_textures.white_texture);
+ }
+ if (cl->shadow.texture.is_valid()) {
+ u_shadows.ids.push_back(cl->shadow.texture);
+ } else {
+ u_shadows.ids.push_back(default_textures.black_texture);
+ }
+ } else {
+ u_lights.ids.push_back(default_textures.white_texture);
+ u_shadows.ids.push_back(default_textures.black_texture);
+ }
+ }
+
+ //in case there are more
+ for (uint32_t i = MAX_LIGHT_TEXTURES; i < canvas_light_owner.get_rid_count(); i++) {
+ CanvasLight *cl = canvas_light_owner.get_rid_by_index(i);
+ cl->texture_index = -1; //make invalid (no texture)
+ }
+
+ uniforms.push_back(u_lights);
+ uniforms.push_back(u_shadows);
+ }
state.canvas_state_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, shader.default_version_rd_shader, 3); // uses index 3
}
@@ -1182,6 +1311,7 @@ void RasterizerCanvasRD::canvas_render_items(RID p_to_render_target, Item *p_ite
//setup canvas state uniforms if needed
_update_canvas_state_uniform_set();
+ Transform2D canvas_transform_inverse = p_canvas_transform.affine_inverse();
{
//update canvas state uniform buffer
@@ -1194,9 +1324,77 @@ void RasterizerCanvasRD::canvas_render_items(RID p_to_render_target, Item *p_ite
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;
+
RD::get_singleton()->buffer_update(state.canvas_state_buffer, 0, sizeof(State::Buffer), &state_buffer, true);
}
+ //setup lights if exist
+
+ {
+
+ Light *l = p_light_list;
+ uint32_t index = 0;
+
+ while (l) {
+
+ if (index == MAX_RENDER_LIGHTS) {
+ l->render_index_cache = -1;
+ l = l->next_ptr;
+ continue;
+ }
+
+ CanvasLight *clight = canvas_light_owner.getornull(l->light_internal);
+ if (!clight || clight->texture_index < 0) { //unused or invalid texture
+ l->render_index_cache = -1;
+ l = l->next_ptr;
+ ERR_CONTINUE(!clight);
+ }
+ Transform2D to_light_xform = (p_canvas_transform * l->light_shader_xform).affine_inverse();
+
+ Vector2 canvas_light_pos = p_canvas_transform.xform(l->xform.get_origin()); //convert light position to canvas coordinates, as all computation is done in canvas coords to avoid precision loss
+ state.light_uniforms[index].position[0] = canvas_light_pos.x;
+ state.light_uniforms[index].position[1] = canvas_light_pos.y;
+
+ _update_transform_2d_to_mat2x4(to_light_xform, state.light_uniforms[index].matrix);
+
+ state.light_uniforms[index].height = l->height * (p_canvas_transform.elements[0].length() + p_canvas_transform.elements[1].length()) * 0.5; //approximate height conversion to the canvas size, since all calculations are done in canvas coords to avoid precision loss
+ for (int i = 0; i < 4; i++) {
+ state.light_uniforms[index].shadow_color[i] = l->shadow_color[i];
+ 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 (clight->shadow.texture.is_valid()) {
+ state.light_uniforms[index].shadow_pixel_size = 1.0 / clight->shadow.size;
+ } else {
+ state.light_uniforms[index].shadow_pixel_size = 1.0;
+ }
+ state.light_uniforms[index].flags = clight->texture_index;
+ state.light_uniforms[index].flags |= l->mode << LIGHT_FLAGS_BLEND_SHIFT;
+
+ l->render_index_cache = index;
+
+ index++;
+ 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);
+ }
+ }
+
//fill the list until rendering is possible.
Item *ci = p_item_list;
while (ci) {
@@ -1205,7 +1403,7 @@ void RasterizerCanvasRD::canvas_render_items(RID p_to_render_target, Item *p_ite
bool backbuffer_copy = ci->copy_back_buffer; // || shader uses SCREEN_TEXTURE
if (!ci->next || backbuffer_copy || item_count == MAX_RENDER_ITEMS - 1) {
- _render_items(p_to_render_target, item_count, p_modulate, p_canvas_transform);
+ _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list);
//then reset
item_count = 0;
}
@@ -1224,6 +1422,292 @@ void RasterizerCanvasRD::canvas_render_items(RID p_to_render_target, Item *p_ite
}
}
+RID RasterizerCanvasRD::light_create() {
+
+ CanvasLight canvas_light;
+ canvas_light.shadow.size = 0;
+ canvas_light.texture_index = -1;
+ return canvas_light_owner.make_rid(canvas_light);
+}
+
+void RasterizerCanvasRD::light_set_texture(RID p_rid, RID p_texture) {
+ CanvasLight *cl = canvas_light_owner.getornull(p_rid);
+ ERR_FAIL_COND(!cl);
+ if (cl->texture == p_texture) {
+ return;
+ }
+
+ cl->texture = p_texture;
+
+ //canvas state uniform set needs updating
+ if (state.canvas_state_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(state.canvas_state_uniform_set)) {
+ RD::get_singleton()->free(state.canvas_state_uniform_set);
+ }
+}
+void RasterizerCanvasRD::light_set_use_shadow(RID p_rid, bool p_enable, int p_resolution) {
+ CanvasLight *cl = canvas_light_owner.getornull(p_rid);
+ ERR_FAIL_COND(!cl);
+ ERR_FAIL_COND(p_resolution < 64);
+ if (cl->shadow.texture.is_valid() == p_enable && p_resolution == cl->shadow.size) {
+ return;
+ }
+
+ if (cl->shadow.texture.is_valid()) {
+
+ RD::get_singleton()->free(cl->shadow.uniform_set);
+ cl->shadow.uniform_set = RID();
+
+ for (int i = 0; i < 4; i++) {
+ RD::get_singleton()->free(cl->shadow.render_fb[i]);
+ RD::get_singleton()->free(cl->shadow.render_textures[i]);
+ cl->shadow.render_fb[i] = RID();
+ cl->shadow.render_textures[i] = RID();
+ }
+ RD::get_singleton()->free(cl->shadow.fix_fb);
+ RD::get_singleton()->free(cl->shadow.texture);
+
+ cl->shadow.fix_fb = RID();
+ cl->shadow.texture = RID();
+ }
+
+ if (p_enable) {
+
+ { //texture
+ RD::TextureFormat tf;
+ tf.type = RD::TEXTURE_TYPE_2D;
+ tf.width = p_resolution;
+ tf.height = 1;
+ tf.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT;
+ tf.format = RD::DATA_FORMAT_R32_SFLOAT;
+
+ cl->shadow.texture = RD::get_singleton()->texture_create(tf, RD::TextureView());
+
+ Vector<RID> fb_textures;
+ fb_textures.push_back(cl->shadow.texture);
+ cl->shadow.fix_fb = RD::get_singleton()->framebuffer_create(fb_textures);
+ }
+ {
+ RD::TextureFormat tf;
+ tf.type = RD::TEXTURE_TYPE_2D;
+ tf.width = p_resolution / 2;
+ tf.height = 1;
+ tf.usage_bits = RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
+ tf.format = RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_X8_D24_UNORM_PACK32, RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) ? RD::DATA_FORMAT_X8_D24_UNORM_PACK32 : RD::DATA_FORMAT_D32_SFLOAT;
+ //chunks to write
+ cl->shadow.render_depth = RD::get_singleton()->texture_create(tf, RD::TextureView());
+
+ RD::Uniform tex_uniforms;
+ tex_uniforms.type = RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE;
+ tex_uniforms.binding = 0;
+
+ for (int i = 0; i < 4; i++) {
+ tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
+ tf.format = RD::DATA_FORMAT_R32_SFLOAT;
+ cl->shadow.render_textures[i] = RD::get_singleton()->texture_create(tf, RD::TextureView());
+ Vector<RID> textures;
+ textures.push_back(cl->shadow.render_textures[i]);
+ textures.push_back(cl->shadow.render_depth);
+ cl->shadow.render_fb[i] = RD::get_singleton()->framebuffer_create(textures);
+
+ tex_uniforms.ids.push_back(default_samplers.samplers[VS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST][VS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED]);
+ tex_uniforms.ids.push_back(cl->shadow.render_textures[i]);
+ }
+
+ Vector<RD::Uniform> tex_uniforms_set;
+ tex_uniforms_set.push_back(tex_uniforms);
+ cl->shadow.uniform_set = RD::get_singleton()->uniform_set_create(tex_uniforms_set, shadow_render.shader_fix.version_get_shader(shadow_render.shader_fix_version, 0), 0);
+ }
+ }
+
+ //canvas state uniform set needs updating
+ if (state.canvas_state_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(state.canvas_state_uniform_set)) {
+ RD::get_singleton()->free(state.canvas_state_uniform_set);
+ }
+}
+
+void RasterizerCanvasRD::light_update_shadow(RID p_rid, 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.texture.is_null());
+
+ for (int i = 0; i < 4; i++) {
+
+ //make sure it remains orthogonal, makes easy to read angle later
+
+ //light.basis.scale(Vector3(to_light.elements[0].length(),to_light.elements[1].length(),1));
+
+ Vector<Color> cc;
+ cc.push_back(Color(p_far, p_far, p_far, 1.0));
+ RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(cl->shadow.render_fb[i], RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ_COLOR_DISCARD_DEPTH, cc);
+
+ CameraMatrix projection;
+ {
+ real_t fov = 90;
+ real_t nearp = p_near;
+ real_t farp = p_far;
+ real_t aspect = 1.0;
+
+ real_t ymax = nearp * Math::tan(Math::deg2rad(fov * 0.5));
+ real_t ymin = -ymax;
+ real_t xmin = ymin * aspect;
+ real_t xmax = ymax * aspect;
+
+ projection.set_frustum(xmin, xmax, ymin, ymax, nearp, farp);
+ }
+
+ Vector3 cam_target = Basis(Vector3(0, 0, Math_PI * 2 * (i / 4.0))).xform(Vector3(0, 1, 0));
+ projection = projection * CameraMatrix(Transform().looking_at(cam_target, 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];
+ }
+ }
+
+ /*if (i == 0)
+ *p_xform_cache = projection;*/
+
+ LightOccluderInstance *instance = p_occluders;
+
+ while (instance) {
+
+ OccluderPolygon *co = occluder_polygon_owner.getornull(instance->polygon);
+
+ if (!co || co->index_array.is_null() || !(p_light_mask & instance->light_mask)) {
+
+ instance = instance->next;
+ continue;
+ }
+
+ _update_transform_2d_to_mat4(p_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();
+ }
+
+ Vector<Color> cc;
+ cc.push_back(Color(p_far, p_far, p_far, 1.0));
+ RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(cl->shadow.fix_fb, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ_COLOR_DISCARD_DEPTH, cc);
+ RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, shadow_render.shader_fix_pipeline);
+ RD::get_singleton()->draw_list_bind_index_array(draw_list, primitive_arrays.index_array[3]);
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, cl->shadow.uniform_set, 0);
+ RD::get_singleton()->draw_list_draw(draw_list, true);
+ RD::get_singleton()->draw_list_end();
+}
+
+RID RasterizerCanvasRD::occluder_polygon_create() {
+
+ OccluderPolygon occluder;
+ occluder.point_count = 0;
+ occluder.cull_mode = VS::CANVAS_OCCLUDER_POLYGON_CULL_DISABLED;
+ return occluder_polygon_owner.make_rid(occluder);
+}
+
+void RasterizerCanvasRD::occluder_polygon_set_shape_as_lines(RID p_occluder, const PoolVector<Vector2> &p_lines) {
+
+ OccluderPolygon *oc = occluder_polygon_owner.getornull(p_occluder);
+ ERR_FAIL_COND(!oc);
+
+ if (oc->point_count != p_lines.size() && oc->vertex_array.is_valid()) {
+
+ RD::get_singleton()->free(oc->vertex_array);
+ RD::get_singleton()->free(oc->vertex_buffer);
+ RD::get_singleton()->free(oc->index_array);
+ RD::get_singleton()->free(oc->index_buffer);
+
+ oc->vertex_array = RID();
+ oc->vertex_buffer = RID();
+ oc->index_array = RID();
+ oc->index_buffer = RID();
+ }
+
+ if (p_lines.size()) {
+
+ PoolVector<uint8_t> geometry;
+ PoolVector<uint8_t> indices;
+ int lc = p_lines.size();
+
+ geometry.resize(lc * 6 * sizeof(float));
+ indices.resize(lc * 3 * sizeof(uint16_t));
+
+ {
+ PoolVector<uint8_t>::Write vw = geometry.write();
+ float *vwptr = (float *)vw.ptr();
+ PoolVector<uint8_t>::Write iw = indices.write();
+ uint16_t *iwptr = (uint16_t *)iw.ptr();
+
+ PoolVector<Vector2>::Read lr = p_lines.read();
+
+ const int POLY_HEIGHT = 16384;
+
+ for (int i = 0; i < lc / 2; i++) {
+
+ vwptr[i * 12 + 0] = lr[i * 2 + 0].x;
+ vwptr[i * 12 + 1] = lr[i * 2 + 0].y;
+ vwptr[i * 12 + 2] = POLY_HEIGHT;
+
+ vwptr[i * 12 + 3] = lr[i * 2 + 1].x;
+ vwptr[i * 12 + 4] = lr[i * 2 + 1].y;
+ vwptr[i * 12 + 5] = POLY_HEIGHT;
+
+ vwptr[i * 12 + 6] = lr[i * 2 + 1].x;
+ vwptr[i * 12 + 7] = lr[i * 2 + 1].y;
+ vwptr[i * 12 + 8] = -POLY_HEIGHT;
+
+ vwptr[i * 12 + 9] = lr[i * 2 + 0].x;
+ vwptr[i * 12 + 10] = lr[i * 2 + 0].y;
+ vwptr[i * 12 + 11] = -POLY_HEIGHT;
+
+ iwptr[i * 6 + 0] = i * 4 + 0;
+ iwptr[i * 6 + 1] = i * 4 + 1;
+ iwptr[i * 6 + 2] = i * 4 + 2;
+
+ iwptr[i * 6 + 3] = i * 4 + 2;
+ iwptr[i * 6 + 4] = i * 4 + 3;
+ iwptr[i * 6 + 5] = i * 4 + 0;
+ }
+ }
+
+ //if same buffer len is being set, just use BufferSubData to avoid a pipeline flush
+
+ if (oc->vertex_array.is_null()) {
+ //create from scratch
+ //vertices
+ oc->vertex_buffer = RD::get_singleton()->vertex_buffer_create(lc * 6 * sizeof(real_t), geometry);
+
+ Vector<RID> buffer;
+ buffer.push_back(oc->vertex_buffer);
+ oc->vertex_array = RD::get_singleton()->vertex_array_create(4 * lc / 2, shadow_render.vertex_format, buffer);
+ //indices
+
+ oc->index_buffer = RD::get_singleton()->index_buffer_create(3 * lc, RD::INDEX_BUFFER_FORMAT_UINT16, indices);
+ oc->index_array = RD::get_singleton()->index_array_create(oc->index_buffer, 0, 3 * lc);
+
+ } else {
+ //update existing
+ PoolVector<uint8_t>::Read vr = geometry.read();
+ RD::get_singleton()->buffer_update(oc->vertex_buffer, 0, geometry.size(), vr.ptr());
+ PoolVector<uint8_t>::Read ir = indices.read();
+ RD::get_singleton()->buffer_update(oc->index_buffer, 0, indices.size(), ir.ptr());
+ }
+ }
+}
+void RasterizerCanvasRD::occluder_polygon_set_cull_mode(RID p_occluder, VS::CanvasOccluderPolygonCullMode p_mode) {
+ OccluderPolygon *oc = occluder_polygon_owner.getornull(p_occluder);
+ ERR_FAIL_COND(!oc);
+ oc->cull_mode = p_mode;
+}
+
void RasterizerCanvasRD::update() {
_dispose_bindings();
}
@@ -1422,6 +1906,59 @@ RasterizerCanvasRD::RasterizerCanvasRD(RasterizerStorageRD *p_storage) {
shader.default_version_rd_shader = shader.canvas_shader.version_get_shader(shader.default_version, 0);
}
+ { //shadow rendering
+ Vector<String> versions;
+ versions.push_back(String()); //no versions
+ shadow_render.shader.initialize(versions);
+
+ {
+ Vector<RD::AttachmentFormat> attachments;
+
+ RD::AttachmentFormat af_color;
+ af_color.format = RD::DATA_FORMAT_R32_SFLOAT;
+ af_color.usage_flags = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
+
+ attachments.push_back(af_color);
+
+ shadow_render.framebuffer_fix_format = RD::get_singleton()->framebuffer_format_create(attachments);
+
+ RD::AttachmentFormat af_depth;
+ af_depth.format = RD::DATA_FORMAT_D24_UNORM_S8_UINT;
+ af_depth.format = RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_X8_D24_UNORM_PACK32, RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) ? RD::DATA_FORMAT_X8_D24_UNORM_PACK32 : RD::DATA_FORMAT_D32_SFLOAT;
+ af_depth.usage_flags = RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
+
+ attachments.push_back(af_depth);
+
+ shadow_render.framebuffer_format = RD::get_singleton()->framebuffer_format_create(attachments);
+ }
+
+ //pipelines
+ Vector<RD::VertexDescription> vf;
+ RD::VertexDescription vd;
+ vd.format = RD::DATA_FORMAT_R32G32B32_SFLOAT;
+ vd.location = 0;
+ vd.offset = 0;
+ vd.stride = sizeof(float) * 3;
+ vf.push_back(vd);
+ shadow_render.vertex_format = RD::get_singleton()->vertex_format_create(vf);
+
+ shadow_render.shader_version = shadow_render.shader.version_create();
+
+ for (int i = 0; i < 3; i++) {
+ RD::PipelineRasterizationState rs;
+ rs.cull_mode = i == 0 ? RD::POLYGON_CULL_DISABLED : (i == 1 ? RD::POLYGON_CULL_FRONT : RD::POLYGON_CULL_BACK);
+ RD::PipelineDepthStencilState ds;
+ ds.enable_depth_write = true;
+ ds.enable_depth_test = true;
+ ds.depth_compare_operator = RD::COMPARE_OP_LESS;
+ shadow_render.render_pipelines[i] = RD::get_singleton()->render_pipeline_create(shadow_render.shader.version_get_shader(shadow_render.shader_version, 0), shadow_render.framebuffer_format, shadow_render.vertex_format, RD::RENDER_PRIMITIVE_TRIANGLES, rs, RD::PipelineMultisampleState(), ds, RD::PipelineColorBlendState::create_disabled(), 0);
+ }
+
+ shadow_render.shader_fix.initialize(versions);
+ shadow_render.shader_fix_version = shadow_render.shader_fix.version_create();
+ shadow_render.shader_fix_pipeline = RD::get_singleton()->render_pipeline_create(shadow_render.shader_fix.version_get_shader(shadow_render.shader_fix_version, 0), shadow_render.framebuffer_fix_format, RD::INVALID_ID, RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState::create_disabled(), 0);
+ }
+
{ //bindings
bindings.id_generator = 0;
//generate for 0
@@ -1429,6 +1966,7 @@ RasterizerCanvasRD::RasterizerCanvasRD(RasterizerStorageRD *p_storage) {
{ //state allocate
state.canvas_state_buffer = RD::get_singleton()->uniform_buffer_create(sizeof(State::Buffer));
+ state.lights_uniform_buffer = RD::get_singleton()->uniform_buffer_create(sizeof(LightUniform) * MAX_RENDER_LIGHTS);
}
}
@@ -1491,6 +2029,27 @@ RasterizerCanvasRD::RasterizerCanvasRD(RasterizerStorageRD *p_storage) {
ERR_FAIL_COND(sizeof(PushConstant) != 128);
}
+bool RasterizerCanvasRD::free(RID p_rid) {
+
+ if (canvas_light_owner.owns(p_rid)) {
+ CanvasLight *cl = canvas_light_owner.getornull(p_rid);
+ ERR_FAIL_COND_V(!cl, false);
+ light_set_use_shadow(p_rid, false, 64);
+ canvas_light_owner.free(p_rid);
+ //canvas state uniform set needs updating
+ if (state.canvas_state_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(state.canvas_state_uniform_set)) {
+ RD::get_singleton()->free(state.canvas_state_uniform_set);
+ }
+ } else if (occluder_polygon_owner.owns(p_rid)) {
+ occluder_polygon_set_shape_as_lines(p_rid, PoolVector<Vector2>());
+ occluder_polygon_owner.free(p_rid);
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
RasterizerCanvasRD::~RasterizerCanvasRD() {
//canvas state
diff --git a/servers/visual/rasterizer/rasterizer_canvas_rd.h b/servers/visual/rasterizer/rasterizer_canvas_rd.h
index 91870a20fd..59193f2a68 100644
--- a/servers/visual/rasterizer/rasterizer_canvas_rd.h
+++ b/servers/visual/rasterizer/rasterizer_canvas_rd.h
@@ -5,6 +5,8 @@
#include "servers/visual/rasterizer/rasterizer_storage_rd.h"
#include "servers/visual/rasterizer/render_pipeline_vertex_format_cache_rd.h"
#include "servers/visual/rasterizer/shaders/canvas.glsl.gen.h"
+#include "servers/visual/rasterizer/shaders/canvas_occlusion.glsl.gen.h"
+#include "servers/visual/rasterizer/shaders/canvas_occlusion_fix.glsl.gen.h"
#include "servers/visual/rendering_device.h"
class RasterizerCanvasRD : public RasterizerCanvas {
@@ -37,13 +39,37 @@ 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),
FLAGS_USE_PIXEL_SNAP = (1 << 14),
FLAGS_USE_SKELETON = (1 << 15),
FLAGS_NINEPATCH_H_MODE_SHIFT = 16,
- FLAGS_NINEPATCH_V_MODE_SHIFT = 18
+ FLAGS_NINEPATCH_V_MODE_SHIFT = 18,
+ FLAGS_LIGHT_COUNT_SHIFT = 20,
+
+ FLAGS_DEFAULT_NORMAL_MAP_USED = (1 << 26),
+ FLAGS_DEFAULT_SPECULAR_MAP_USED = (1 << 27)
+
+ };
+
+ enum {
+ LIGHT_FLAGS_TEXTURE_MASK = 0xFFFF,
+ LIGHT_FLAGS_BLEND_SHIFT = 16,
+ LIGHT_FLAGS_BLEND_MASK = (3 << 16),
+ LIGHT_FLAGS_BLEND_MODE_ADD = (0 << 16),
+ LIGHT_FLAGS_BLEND_MODE_SUB = (1 << 16),
+ LIGHT_FLAGS_BLEND_MODE_MIX = (2 << 16),
+ LIGHT_FLAGS_BLEND_MODE_MASK = (3 << 16)
+ };
+
+ enum {
+ MAX_RENDER_ITEMS = 256 * 1024,
+ MAX_LIGHT_TEXTURES = 1024,
+ MAX_LIGHTS_PER_ITEM = 16,
+ MAX_RENDER_LIGHTS = 256
};
/****************/
@@ -183,18 +209,66 @@ class RasterizerCanvasRD : public RasterizerCanvas {
/**** LIGHTING ****/
/******************/
- enum {
- LIGHT_GRID_WIDTH = 16,
- LIGHT_GRID_HEIGHT = 16,
- MAX_LIGHTS = 128
+ struct CanvasLight {
+
+ int32_t texture_index;
+ RID texture;
+ struct {
+ int size;
+ RID texture;
+
+ RID render_depth;
+ RID render_fb[4];
+ RID render_textures[4];
+ RID fix_fb;
+ RID uniform_set;
+
+ } shadow;
};
+ RID_Owner<CanvasLight> canvas_light_owner;
+
+ struct ShadowRenderPushConstant {
+ float projection[16];
+ float modelview[16];
+ };
+
+ struct OccluderPolygon {
+
+ VS::CanvasOccluderPolygonCullMode cull_mode;
+ int point_count;
+ RID vertex_buffer;
+ RID vertex_array;
+ RID index_buffer;
+ RID index_array;
+ };
+
+ struct LightUniform {
+ float matrix[8]; //light to texture coordinate matrix
+ float color[4];
+ float shadow_color[4];
+ float position[2];
+ uint32_t flags; //index to light texture
+ float height;
+ float shadow_softness;
+ float shadow_pixel_size;
+ float pad[2];
+ };
+
+ RID_Owner<OccluderPolygon> occluder_polygon_owner;
+
struct {
- RID grid_texture;
- RID grid_buffer;
- PoolVector<uint8_t> grid_texture_data;
- PoolVector<uint8_t> grid_buffer_data;
- } lighting;
+ CanvasOcclusionShaderRD shader;
+ RID shader_version;
+ RID render_pipelines[3];
+ RD::VertexFormatID vertex_format;
+ RD::FramebufferFormatID framebuffer_format;
+
+ CanvasOcclusionFixShaderRD shader_fix;
+ RD::FramebufferFormatID framebuffer_fix_format;
+ RID shader_fix_version;
+ RID shader_fix_pipeline;
+ } shadow_render;
/***************/
/**** STATE ****/
@@ -208,12 +282,16 @@ class RasterizerCanvasRD : public RasterizerCanvas {
struct Buffer {
float canvas_transform[16];
float screen_transform[16];
+ float canvas_normal_transform[16];
+ float canvas_modulate[4];
//uint32_t light_count;
//uint32_t pad[3];
};
+
+ LightUniform light_uniforms[MAX_RENDER_LIGHTS];
+
+ RID lights_uniform_buffer;
RID canvas_state_buffer;
- //light buffer
- RID canvas_state_light_buffer;
//uniform set for all the above
RID canvas_state_uniform_set;
@@ -234,8 +312,8 @@ class RasterizerCanvasRD : public RasterizerCanvas {
};
//primitive
struct {
- float points[6]; // vec2 points[4]
- float uvs[6]; // vec2 points[4]
+ float points[6]; // vec2 points[3]
+ float uvs[6]; // vec2 points[3]
uint32_t colors[6]; // colors encoded as half
};
};
@@ -248,21 +326,19 @@ class RasterizerCanvasRD : public RasterizerCanvas {
float skeleton_inverse[16];
};
- enum {
- MAX_RENDER_ITEMS = 256 * 1024
- };
-
Item *items[MAX_RENDER_ITEMS];
- Size2i _bind_texture_binding(TextureBindingID p_binding, RenderingDevice::DrawListID p_draw_list);
- void _render_item(RenderingDevice::DrawListID p_draw_list, const Item *p_item, RenderTargetFormat p_render_target_format, RenderingDevice::TextureSamples p_samples, const Color &p_modulate, const Transform2D &p_canvas_transform_inverse, Item *&current_clip);
- void _render_items(RID p_to_render_target, int p_item_count, const Color &p_modulate, const Transform2D &p_transform);
+ Size2i _bind_texture_binding(TextureBindingID p_binding, RenderingDevice::DrawListID p_draw_list, uint32_t &flags);
+ void _render_item(RenderingDevice::DrawListID p_draw_list, const Item *p_item, RenderTargetFormat p_render_target_format, RenderingDevice::TextureSamples p_samples, const Transform2D &p_canvas_transform_inverse, Item *&current_clip, Light *p_lights);
+ void _render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights);
- void _update_transform_2d_to_mat2x4(const Transform2D &p_transform, float *p_mat2x4);
- void _update_transform_2d_to_mat2x3(const Transform2D &p_transform, float *p_mat2x3);
+ _FORCE_INLINE_ void _update_transform_2d_to_mat2x4(const Transform2D &p_transform, float *p_mat2x4);
+ _FORCE_INLINE_ void _update_transform_2d_to_mat2x3(const Transform2D &p_transform, float *p_mat2x3);
- void _update_transform_2d_to_mat4(const Transform2D &p_transform, float *p_mat4);
- void _update_transform_to_mat4(const Transform &p_transform, float *p_mat4);
+ _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);
+
+ _FORCE_INLINE_ void _update_specular_shininess(const Color &p_transform, uint32_t *r_ss);
void _update_canvas_state_uniform_set();
@@ -273,21 +349,23 @@ 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);
- RID light_internal_create() { return RID(); }
- void light_internal_update(RID p_rid, Light *p_light) {}
- void light_internal_free(RID p_rid) {}
+ RID light_create();
+ void light_set_texture(RID p_rid, RID p_texture);
+ void light_set_use_shadow(RID p_rid, bool p_enable, int p_resolution);
+ void light_update_shadow(RID p_rid, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders);
+
+ RID occluder_polygon_create();
+ void occluder_polygon_set_shape_as_lines(RID p_occluder, const PoolVector<Vector2> &p_lines);
+ void occluder_polygon_set_cull_mode(RID p_occluder, VS::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);
void canvas_debug_viewport_shadows(Light *p_lights_with_shadow){};
- void canvas_light_shadow_buffer_update(RID p_buffer, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders, CameraMatrix *p_xform_cache) {}
-
- void reset_canvas() {}
-
void draw_window_margins(int *p_margins, RID *p_margin_textures) {}
void update();
+ bool free(RID p_rid);
RasterizerCanvasRD(RasterizerStorageRD *p_storage);
~RasterizerCanvasRD();
};
diff --git a/servers/visual/rasterizer/rasterizer_storage_rd.h b/servers/visual/rasterizer/rasterizer_storage_rd.h
index 4f81a4cce9..bb9e20861f 100644
--- a/servers/visual/rasterizer/rasterizer_storage_rd.h
+++ b/servers/visual/rasterizer/rasterizer_storage_rd.h
@@ -647,15 +647,6 @@ public:
Size2 render_target_get_size(RID p_render_target);
RID render_target_get_rd_framebuffer(RID p_render_target);
- /* CANVAS SHADOW */
-
- RID canvas_light_shadow_buffer_create(int p_width) { return RID(); }
-
- /* LIGHT SHADOW MAPPING */
-
- RID canvas_light_occluder_create() { return RID(); }
- void canvas_light_occluder_set_polylines(RID p_occluder, const PoolVector<Vector2> &p_lines) {}
-
VS::InstanceType get_base_type(RID p_rid) const {
if (mesh_owner.owns(p_rid)) {
return VS::INSTANCE_MESH;
diff --git a/servers/visual/rasterizer/shaders/SCsub b/servers/visual/rasterizer/shaders/SCsub
index 62a4c2a0ba..ef1df0c203 100644
--- a/servers/visual/rasterizer/shaders/SCsub
+++ b/servers/visual/rasterizer/shaders/SCsub
@@ -4,3 +4,5 @@ Import('env')
if 'RD_GLSL' in env['BUILDERS']:
env.RD_GLSL('canvas.glsl');
+ env.RD_GLSL('canvas_occlusion.glsl');
+ env.RD_GLSL('canvas_occlusion_fix.glsl');
diff --git a/servers/visual/rasterizer/shaders/canvas.glsl b/servers/visual/rasterizer/shaders/canvas.glsl
index 734974ba81..6b1485c86b 100644
--- a/servers/visual/rasterizer/shaders/canvas.glsl
+++ b/servers/visual/rasterizer/shaders/canvas.glsl
@@ -23,6 +23,7 @@ layout(location = 7) in vec4 bone_weights_attrib;
layout(location=0) out vec2 uv_interp;
layout(location=1) out vec4 color_interp;
+layout(location=2) out vec2 vertex_interp;
#ifdef USE_NINEPATCH
@@ -206,12 +207,12 @@ VERTEX_SHADER_CODE
#endif
#endif
+ vertex = (canvas_data.canvas_transform * vec4(vertex,0.0,1.0)).xy;
+
+ vertex_interp = vertex;
uv_interp = uv;
-#if !defined(SKIP_TRANSFORM_USED)
- gl_Position = (canvas_data.screen_transform * canvas_data.canvas_transform) * vec4(vertex,0.0,1.0);
-#else
- gl_Position = vec4(vertex,0.0,1.0);
-#endif
+
+ gl_Position = canvas_data.screen_transform * vec4(vertex,0.0,1.0);
#ifdef USE_POINT_SIZE
gl_PointSize=point_size;
@@ -232,6 +233,7 @@ VERSION_DEFINES
layout(location=0) in vec2 uv_interp;
layout(location=1) in vec4 color_interp;
+layout(location=2) in vec2 vertex_interp;
#ifdef USE_NINEPATCH
@@ -315,6 +317,7 @@ void main() {
vec4 color = color_interp;
vec2 uv = uv_interp;
+ vec2 vertex = vertex_interp;
#if !defined(USE_ATTRIBUTES) && !defined(USE_PRIMITIVE)
@@ -347,6 +350,8 @@ void main() {
#endif
+ uint light_count = (draw_data.flags>>FLAGS_LIGHT_COUNT_SHIFT)&0xF; //max 16 lights
+
vec3 normal;
@@ -357,18 +362,33 @@ void main() {
bool normal_used = false;
#endif
-#if 0
- if (false /*normal_used || canvas_data.light_count > 0*/ ) {
- normal.xy = texture(sampler2D(normal_texture,texture_sampler ), uv).xy * 2.0 - 1.0;
+
+ if (normal_used || (light_count > 0 && 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;
} else {
-#endif
normal = vec3(0.0, 0.0, 1.0);
-#if 0
}
+
+ vec4 specular_shininess;
+
+#if defined(SPECULAR_SHININESS_USED)
+
+ bool specular_shininess_used = true;
+#else
+ bool specular_shininess_used = false;
#endif
+ if (specular_shininess_used || (light_count > 0 && 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;
+ } else {
+ specular_shininess = vec4(1.0);
+ }
+
+
#if defined(SCREEN_UV_USED)
vec2 screen_uv = gl_FragCoord.xy * screen_pixel_size;
#endif
@@ -392,13 +412,104 @@ FRAGMENT_SHADER_CODE
#endif
}
-#if 0
- if (canvas_data.light_count > 0 ) {
- //do lighting
+ if (normal_used) {
+ //convert by item transform
+ normal.xy = mat2(normalize(draw_data.world_x), normalize(draw_data.world_y)) * normal.xy;
+ //convert by canvas transform
+ normal = normalize((canvas_data.canvas_normal_transform * vec4(normal,0.0)).xyz);
+ }
+
+ vec4 base_color=color;
+ if (bool(draw_data.flags&FLAGS_USING_LIGHT_MASK)) {
+ color=vec4(0.0); //inivisible by default due to using light mask
}
-#endif
- //color.rgb *= color.a;
+
+ color*=canvas_data.canvas_modulation;
+
+ for(uint i=0;i<light_count;i++) {
+ uint light_base;
+ if (i<8) {
+ if (i<4) {
+ light_base=draw_data.lights[0];
+ } else {
+ light_base=draw_data.lights[1];
+ }
+ } else {
+ if (i<12) {
+ light_base=draw_data.lights[2];
+ } else {
+ light_base=draw_data.lights[3];
+ }
+ }
+ light_base>>=(i&3)*8;
+ light_base&=0xFF;
+
+#define LIGHT_FLAGS_BLEND_MASK (3<<16)
+#define LIGHT_FLAGS_BLEND_MODE_ADD (0<<16)
+#define LIGHT_FLAGS_BLEND_MODE_SUB (1<<16)
+#define LIGHT_FLAGS_BLEND_MODE_MIX (2<<16)
+#define LIGHT_FLAGS_BLEND_MODE_MASK (3<<16)
+
+
+ vec2 tex_uv = (vec4(vertex,0.0,1.0) * mat4(light_array.data[light_base].matrix[0],light_array.data[light_base].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.
+ uint texture_idx = light_array.data[light_base].flags&LIGHT_FLAGS_TEXTURE_MASK;
+ vec4 light_color = texture(sampler2D(light_textures[texture_idx],texture_sampler),tex_uv);
+ vec4 light_base_color = light_array.data[light_base].color;
+ light_color.rgb*=light_base_color.rgb*light_base_color.a;
+
+ if (normal_used) {
+
+ vec3 light_pos = vec3(light_array.data[light_base].position,light_array.data[light_base].height);
+ vec3 pos = vec3(vertex,0.0);
+ 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;
+ }
+
+ }
+
+ if (any(lessThan(tex_uv, vec2(0.0, 0.0))) || any(greaterThanEqual(tex_uv, vec2(1.0, 1.0)))) {
+ //if outside the light texture, light color is zero
+ light_color.a = 0.0;
+ }
+
+ 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;
+ }
+ }
+
frag_color = color;
}
diff --git a/servers/visual/rasterizer/shaders/canvas_occlusion.glsl b/servers/visual/rasterizer/shaders/canvas_occlusion.glsl
new file mode 100644
index 0000000000..fb35f16971
--- /dev/null
+++ b/servers/visual/rasterizer/shaders/canvas_occlusion.glsl
@@ -0,0 +1,38 @@
+/* clang-format off */
+[vertex]
+/* clang-format on */
+
+#version 450
+
+layout(location = 0) in highp vec3 vertex;
+
+layout(push_constant, binding = 0, std430) uniform Constants {
+
+ mat4 modelview;
+ mat4 projection;
+} constants;
+
+layout(location = 0) out highp float depth;
+
+void main() {
+
+ highp vec4 vtx = (constants.modelview * vec4(vertex, 1.0));
+ depth = length(vtx.xy);
+
+ gl_Position = constants.projection * vtx;
+
+}
+
+/* clang-format off */
+[fragment]
+/* clang-format on */
+
+#version 450
+
+layout(location = 0) in highp float depth;
+layout(location = 0) out highp float distance_buf;
+
+void main() {
+
+ distance_buf=depth;
+}
diff --git a/servers/visual/rasterizer/shaders/canvas_occlusion_fix.glsl b/servers/visual/rasterizer/shaders/canvas_occlusion_fix.glsl
new file mode 100644
index 0000000000..48757bb68a
--- /dev/null
+++ b/servers/visual/rasterizer/shaders/canvas_occlusion_fix.glsl
@@ -0,0 +1,56 @@
+/* clang-format off */
+[vertex]
+/* clang-format on */
+
+#version 450
+
+layout(location = 0) out highp float u;
+
+void main() {
+
+ if (gl_VertexIndex==0) {
+ u=0.0;
+ gl_Position=vec4(-1.0,-1.0,0.0,1.0);
+ } else if (gl_VertexIndex==1) {
+ u=0.0;
+ gl_Position=vec4(-1.0,1.0,0.0,1.0);
+ } else if (gl_VertexIndex==2) {
+ u=1.0;
+ gl_Position=vec4(1.0,1.0,0.0,1.0);
+ } else {
+ u=1.0;
+ gl_Position=vec4(1.0,-1.0,0.0,1.0);
+ }
+}
+
+/* clang-format off */
+[fragment]
+/* clang-format on */
+
+#version 450
+
+#define PI 3.14159265359
+
+layout(set=0, binding=0) uniform sampler2D textures[4];
+layout(location = 0) in highp float u;
+layout(location = 0) out highp float distance;
+
+void main() {
+
+ //0-1 in the texture we are writing to represents a circle, 0-2PI)
+ //obtain the quarter circle from the source textures
+ highp float sub_angle = ((mod(u,0.25)/0.25)*2.0-1.0)*(PI/4.0);
+ highp float x=tan(sub_angle)*0.5+0.5;
+
+ float depth;
+ if (u<0.25) {
+ depth=texture(textures[0],vec2(x,0.0)).x;
+ } else if (u<0.50) {
+ depth=texture(textures[1],vec2(x,0.0)).x;
+ } else if (u<0.75) {
+ depth=texture(textures[2],vec2(x,0.0)).x;
+ } else {
+ depth=texture(textures[3],vec2(x,0.0)).x;
+ }
+ distance=depth;
+}
diff --git a/servers/visual/rasterizer/shaders/canvas_uniforms_inc.glsl b/servers/visual/rasterizer/shaders/canvas_uniforms_inc.glsl
index e205170292..cf8a17ce1e 100644
--- a/servers/visual/rasterizer/shaders/canvas_uniforms_inc.glsl
+++ b/servers/visual/rasterizer/shaders/canvas_uniforms_inc.glsl
@@ -1,8 +1,10 @@
/* SET0: Per draw primitive settings */
+#define M_PI 3.14159265359
-#define MAX_LIGHTS 128
+#define MAX_LIGHT_TEXTURES 1024
+#define MAX_RENDER_LIGHTS 256
#define FLAGS_INSTANCING_STRIDE_MASK 0xF
#define FLAGS_INSTANCING_ENABLED (1<<4)
@@ -13,16 +15,19 @@
#define FLAGS_CLIP_RECT_UV (1 << 9)
#define FLAGS_TRANSPOSE_RECT (1 << 10)
+#define FLAGS_USING_LIGHT_MASK (1 << 11)
#define FLAGS_NINEPACH_DRAW_CENTER (1 << 12)
#define FLAGS_USING_PARTICLES (1 << 13)
#define FLAGS_USE_PIXEL_SNAP (1 << 14)
-
#define FLAGS_NINEPATCH_H_MODE_SHIFT 16
#define FLAGS_NINEPATCH_V_MODE_SHIFT 18
#define FLAGS_LIGHT_COUNT_SHIFT 20
+#define FLAGS_DEFAULT_NORMAL_MAP_USED (1 << 26)
+#define FLAGS_DEFAULT_SPECULAR_MAP_USED (1 << 27)
+
layout(push_constant, binding = 0, std430) uniform DrawData {
vec2 world_x;
vec2 world_y;
@@ -77,26 +82,35 @@ layout(set = 2, binding = 1, std140) uniform SkeletonData {
layout(set = 3, binding = 0, std140) uniform CanvasData {
mat4 canvas_transform;
mat4 screen_transform;
+ mat4 canvas_normal_transform;
+ vec4 canvas_modulation;
//uint light_count;
} canvas_data;
+#define LIGHT_FLAGS_TEXTURE_MASK 0xFFFF
+#define LIGHT_FLAGS_BLEND_MASK (3<<16)
+#define LIGHT_FLAGS_BLEND_MODE_ADD (0<<16)
+#define LIGHT_FLAGS_BLEND_MODE_SUB (1<<16)
+#define LIGHT_FLAGS_BLEND_MODE_MIX (2<<16)
+#define LIGHT_FLAGS_BLEND_MODE_MASK (3<<16)
+
+
struct Light {
- // light matrices
- mat4 light_matrix;
- mat4 light_local_matrix;
- mat4 shadow_matrix;
- vec4 light_color;
- vec4 light_shadow_color;
- vec2 light_pos;
- float shadowpixel_size;
- float shadow_gradient;
- float light_height;
- float light_outside_alpha;
- float shadow_distance_mult;
+ mat2x4 matrix; //light to texture coordinate matrix
+ vec4 color;
+ vec4 shadow_color;
+ vec2 position;
+ uint flags; //index to light texture
+ float height;
+ float shadow_softness;
+ float shadow_pixel_size;
+ float pad0;
+ float pad1;
};
layout(set = 3, binding = 1, std140) uniform LightData {
- Light lights[MAX_LIGHTS];
-} light_data;
+ Light data[MAX_RENDER_LIGHTS];
+} light_array;
-layout(set = 3, binding = 2) uniform texture2D light_textures[MAX_LIGHTS];
+layout(set = 3, binding = 2) uniform texture2D light_textures[MAX_LIGHT_TEXTURES];
+layout(set = 3, binding = 3) uniform texture2D shadow_textures[MAX_LIGHT_TEXTURES];