Support lights and shadows in GLES2, fixes #21853

This commit is contained in:
Juan Linietsky 2018-12-20 10:09:07 -03:00
parent 6cdcde7114
commit 3bdd1ff387
7 changed files with 852 additions and 30 deletions

View file

@ -186,6 +186,42 @@ RasterizerStorageGLES2::Texture *RasterizerCanvasGLES2::_bind_canvas_texture(con
glBindTexture(GL_TEXTURE_2D, storage->resources.white_tex);
}
if (p_normal_map == state.current_normal) {
//do none
state.canvas_shader.set_uniform(CanvasShaderGLES2::USE_DEFAULT_NORMAL, state.current_normal.is_valid());
} else if (p_normal_map.is_valid()) {
RasterizerStorageGLES2::Texture *normal_map = storage->texture_owner.getornull(p_normal_map);
if (!normal_map) {
state.current_normal = RID();
glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 2);
glBindTexture(GL_TEXTURE_2D, storage->resources.normal_tex);
state.canvas_shader.set_uniform(CanvasShaderGLES2::USE_DEFAULT_NORMAL, false);
} else {
normal_map = normal_map->get_ptr();
if (normal_map->redraw_if_visible) { //check before proxy, because this is usually used with proxies
VisualServerRaster::redraw_request();
}
glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 2);
glBindTexture(GL_TEXTURE_2D, normal_map->tex_id);
state.current_normal = p_normal_map;
state.canvas_shader.set_uniform(CanvasShaderGLES2::USE_DEFAULT_NORMAL, true);
}
} else {
state.current_normal = RID();
glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 2);
glBindTexture(GL_TEXTURE_2D, storage->resources.normal_tex);
state.canvas_shader.set_uniform(CanvasShaderGLES2::USE_DEFAULT_NORMAL, false);
}
return tex_return;
}
@ -1053,10 +1089,164 @@ void RasterizerCanvasGLES2::canvas_render_items(Item *p_item_list, int p_z, cons
_set_uniforms();
_canvas_item_render_commands(p_item_list, NULL, reclip, material_ptr);
if (unshaded || (state.uniforms.final_modulate.a > 0.001 && (!shader_cache || shader_cache->canvas_item.light_mode != RasterizerStorageGLES2::Shader::CanvasItem::LIGHT_MODE_LIGHT_ONLY) && !ci->light_masked))
_canvas_item_render_commands(p_item_list, NULL, reclip, material_ptr);
rebind_shader = true; // hacked in for now.
if ((blend_mode == RasterizerStorageGLES2::Shader::CanvasItem::BLEND_MODE_MIX || blend_mode == RasterizerStorageGLES2::Shader::CanvasItem::BLEND_MODE_PMALPHA) && p_light && !unshaded) {
Light *light = p_light;
bool light_used = false;
VS::CanvasLightMode mode = VS::CANVAS_LIGHT_MODE_ADD;
state.uniforms.final_modulate = ci->final_modulate; // remove the canvas modulate
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)) {
//intersects this light
if (!light_used || mode != light->mode) {
mode = light->mode;
switch (mode) {
case VS::CANVAS_LIGHT_MODE_ADD: {
glBlendEquation(GL_FUNC_ADD);
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
} break;
case VS::CANVAS_LIGHT_MODE_SUB: {
glBlendEquation(GL_FUNC_REVERSE_SUBTRACT);
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
} break;
case VS::CANVAS_LIGHT_MODE_MIX:
case VS::CANVAS_LIGHT_MODE_MASK: {
glBlendEquation(GL_FUNC_ADD);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
} break;
}
}
if (!light_used) {
state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_LIGHTING, true);
light_used = true;
}
bool has_shadow = light->shadow_buffer.is_valid() && ci->light_mask & light->item_shadow_mask;
state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_SHADOWS, has_shadow);
if (has_shadow) {
state.canvas_shader.set_conditional(CanvasShaderGLES2::SHADOW_USE_GRADIENT, light->shadow_gradient_length > 0);
switch (light->shadow_filter) {
case VS::CANVAS_LIGHT_FILTER_NONE: state.canvas_shader.set_conditional(CanvasShaderGLES2::SHADOW_FILTER_NEAREST, true); break;
case VS::CANVAS_LIGHT_FILTER_PCF3: state.canvas_shader.set_conditional(CanvasShaderGLES2::SHADOW_FILTER_PCF3, true); break;
case VS::CANVAS_LIGHT_FILTER_PCF5: state.canvas_shader.set_conditional(CanvasShaderGLES2::SHADOW_FILTER_PCF5, true); break;
case VS::CANVAS_LIGHT_FILTER_PCF7: state.canvas_shader.set_conditional(CanvasShaderGLES2::SHADOW_FILTER_PCF7, true); break;
case VS::CANVAS_LIGHT_FILTER_PCF9: state.canvas_shader.set_conditional(CanvasShaderGLES2::SHADOW_FILTER_PCF9, true); break;
case VS::CANVAS_LIGHT_FILTER_PCF13: state.canvas_shader.set_conditional(CanvasShaderGLES2::SHADOW_FILTER_PCF13, true); break;
}
}
bool light_rebind = state.canvas_shader.bind();
if (light_rebind) {
_set_uniforms();
}
state.canvas_shader.set_uniform(CanvasShaderGLES2::LIGHT_MATRIX, light->light_shader_xform);
Transform2D basis_inverse = light->light_shader_xform.affine_inverse().orthonormalized();
basis_inverse[2] = Vector2();
state.canvas_shader.set_uniform(CanvasShaderGLES2::LIGHT_MATRIX_INVERSE, basis_inverse);
state.canvas_shader.set_uniform(CanvasShaderGLES2::LIGHT_LOCAL_MATRIX, light->xform_cache.affine_inverse());
state.canvas_shader.set_uniform(CanvasShaderGLES2::LIGHT_COLOR, light->color * light->energy);
state.canvas_shader.set_uniform(CanvasShaderGLES2::LIGHT_POS, light->light_shader_pos);
state.canvas_shader.set_uniform(CanvasShaderGLES2::LIGHT_HEIGHT, light->height);
state.canvas_shader.set_uniform(CanvasShaderGLES2::LIGHT_OUTSIDE_ALPHA, light->mode == VS::CANVAS_LIGHT_MODE_MASK ? 1.0 : 0.0);
if (has_shadow) {
RasterizerStorageGLES2::CanvasLightShadow *cls = storage->canvas_light_shadow_owner.get(light->shadow_buffer);
glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 4);
glBindTexture(GL_TEXTURE_2D, cls->distance);
state.canvas_shader.set_uniform(CanvasShaderGLES2::SHADOW_MATRIX, light->shadow_matrix_cache);
state.canvas_shader.set_uniform(CanvasShaderGLES2::LIGHT_SHADOW_COLOR, light->shadow_color);
state.canvas_shader.set_uniform(CanvasShaderGLES2::SHADOWPIXEL_SIZE, (1.0 / light->shadow_buffer_size) * (1.0 + light->shadow_smooth));
if (light->radius_cache == 0) {
state.canvas_shader.set_uniform(CanvasShaderGLES2::SHADOW_GRADIENT, 0.0);
} else {
state.canvas_shader.set_uniform(CanvasShaderGLES2::SHADOW_GRADIENT, light->shadow_gradient_length / (light->radius_cache * 1.1));
}
state.canvas_shader.set_uniform(CanvasShaderGLES2::SHADOW_DISTANCE_MULT, light->radius_cache * 1.1);
/*canvas_shader.set_uniform(CanvasShaderGLES2::SHADOW_MATRIX,light->shadow_matrix_cache);
canvas_shader.set_uniform(CanvasShaderGLES2::SHADOW_ESM_MULTIPLIER,light->shadow_esm_mult);
canvas_shader.set_uniform(CanvasShaderGLES2::LIGHT_SHADOW_COLOR,light->shadow_color);*/
}
glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 3);
RasterizerStorageGLES2::Texture *t = storage->texture_owner.getornull(light->texture);
if (!t) {
glBindTexture(GL_TEXTURE_2D, storage->resources.white_tex);
} else {
t = t->get_ptr();
glBindTexture(t->target, t->tex_id);
}
glActiveTexture(GL_TEXTURE0);
_canvas_item_render_commands(p_item_list, NULL, reclip, material_ptr); //redraw using light
}
light = light->next_ptr;
}
if (light_used) {
state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_LIGHTING, false);
state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_SHADOWS, false);
state.canvas_shader.set_conditional(CanvasShaderGLES2::SHADOW_FILTER_NEAREST, false);
state.canvas_shader.set_conditional(CanvasShaderGLES2::SHADOW_FILTER_PCF3, false);
state.canvas_shader.set_conditional(CanvasShaderGLES2::SHADOW_FILTER_PCF5, false);
state.canvas_shader.set_conditional(CanvasShaderGLES2::SHADOW_FILTER_PCF7, false);
state.canvas_shader.set_conditional(CanvasShaderGLES2::SHADOW_FILTER_PCF9, false);
state.canvas_shader.set_conditional(CanvasShaderGLES2::SHADOW_FILTER_PCF13, false);
state.canvas_shader.bind();
last_blend_mode = -1;
/*
//this is set again, so it should not be needed anyway?
state.canvas_item_modulate = unshaded ? ci->final_modulate : Color(
ci->final_modulate.r * p_modulate.r,
ci->final_modulate.g * p_modulate.g,
ci->final_modulate.b * p_modulate.b,
ci->final_modulate.a * p_modulate.a );
state.canvas_shader.set_uniform(CanvasShaderGLES2::MODELVIEW_MATRIX,state.final_transform);
state.canvas_shader.set_uniform(CanvasShaderGLES2::EXTRA_MATRIX,Transform2D());
state.canvas_shader.set_uniform(CanvasShaderGLES2::FINAL_MODULATE,state.canvas_item_modulate);
glBlendEquation(GL_FUNC_ADD);
if (storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_TRANSPARENT]) {
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
} else {
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
//@TODO RESET canvas_blend_mode
*/
}
}
if (reclip) {
glEnable(GL_SCISSOR_TEST);
int y = storage->frame.current_rt->height - (current_clip->final_clip_rect.position.y + current_clip->final_clip_rect.size.y);
@ -1077,8 +1267,116 @@ void RasterizerCanvasGLES2::canvas_debug_viewport_shadows(Light *p_lights_with_s
}
void RasterizerCanvasGLES2::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) {
}
RasterizerStorageGLES2::CanvasLightShadow *cls = storage->canvas_light_shadow_owner.get(p_buffer);
ERR_FAIL_COND(!cls);
glDisable(GL_BLEND);
glDisable(GL_SCISSOR_TEST);
glDisable(GL_DITHER);
glDisable(GL_CULL_FACE);
glDepthFunc(GL_LEQUAL);
glEnable(GL_DEPTH_TEST);
glDepthMask(true);
glBindFramebuffer(GL_FRAMEBUFFER, cls->fbo);
state.canvas_shadow_shader.set_conditional(CanvasShadowShaderGLES2::USE_RGBA_SHADOWS, storage->config.use_rgba_2d_shadows);
state.canvas_shadow_shader.bind();
glViewport(0, 0, cls->size, cls->height);
glClearDepth(1.0f);
glClearColor(1, 1, 1, 1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
VS::CanvasOccluderPolygonCullMode cull = VS::CANVAS_OCCLUDER_POLYGON_CULL_DISABLED;
for (int i = 0; i < 4; i++) {
//make sure it remains orthogonal, makes easy to read angle later
Transform light;
light.origin[0] = p_light_xform[2][0];
light.origin[1] = p_light_xform[2][1];
light.basis[0][0] = p_light_xform[0][0];
light.basis[0][1] = p_light_xform[1][0];
light.basis[1][0] = p_light_xform[0][1];
light.basis[1][1] = p_light_xform[1][1];
//light.basis.scale(Vector3(to_light.elements[0].length(),to_light.elements[1].length(),1));
//p_near=1;
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());
state.canvas_shadow_shader.set_uniform(CanvasShadowShaderGLES2::PROJECTION_MATRIX, projection);
state.canvas_shadow_shader.set_uniform(CanvasShadowShaderGLES2::LIGHT_MATRIX, light);
state.canvas_shadow_shader.set_uniform(CanvasShadowShaderGLES2::DISTANCE_NORM, 1.0 / p_far);
if (i == 0)
*p_xform_cache = projection;
glViewport(0, (cls->height / 4) * i, cls->size, cls->height / 4);
LightOccluderInstance *instance = p_occluders;
while (instance) {
RasterizerStorageGLES2::CanvasOccluder *cc = storage->canvas_occluder_owner.get(instance->polygon_buffer);
if (!cc || cc->len == 0 || !(p_light_mask & instance->light_mask)) {
instance = instance->next;
continue;
}
state.canvas_shadow_shader.set_uniform(CanvasShadowShaderGLES2::WORLD_MATRIX, instance->xform_cache);
if (cull != instance->cull_cache) {
cull = instance->cull_cache;
switch (cull) {
case VS::CANVAS_OCCLUDER_POLYGON_CULL_DISABLED: {
glDisable(GL_CULL_FACE);
} break;
case VS::CANVAS_OCCLUDER_POLYGON_CULL_CLOCKWISE: {
glEnable(GL_CULL_FACE);
glCullFace(GL_FRONT);
} break;
case VS::CANVAS_OCCLUDER_POLYGON_CULL_COUNTER_CLOCKWISE: {
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
} break;
}
}
glBindVertexArray(cc->array_id);
glDrawElements(GL_TRIANGLES, cc->len * 3, GL_UNSIGNED_SHORT, 0);
instance = instance->next;
}
}
glBindVertexArray(0);
}
void RasterizerCanvasGLES2::reset_canvas() {
glDisable(GL_CULL_FACE);
@ -1094,7 +1392,7 @@ void RasterizerCanvasGLES2::reset_canvas() {
}
// bind the back buffer to a texture so shaders can use it.
// It should probably use texture unit -3 (as GLES3 does as well) but currently that's buggy.
// It should probably use texture unit -3 (as GLES2 does as well) but currently that's buggy.
// keeping this for now as there's nothing else that uses texture unit 2
// TODO ^
if (storage->frame.current_rt) {
@ -1264,6 +1562,8 @@ void RasterizerCanvasGLES2::initialize() {
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
state.canvas_shadow_shader.init();
state.canvas_shader.init();
state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT, true);

View file

@ -36,7 +36,7 @@
#include "shaders/canvas.glsl.gen.h"
#include "shaders/lens_distorted.glsl.gen.h"
// #include "shaders/canvas_shadow.glsl.gen.h"
#include "shaders/canvas_shadow.glsl.gen.h"
class RasterizerSceneGLES2;
@ -70,7 +70,7 @@ public:
Uniforms uniforms;
bool canvas_texscreen_used;
CanvasShaderGLES2 canvas_shader;
// CanvasShadowShaderGLES3 canvas_shadow_shader;
CanvasShadowShaderGLES2 canvas_shadow_shader;
LensDistortedShaderGLES2 lens_shader;
bool using_texture_rect;

View file

@ -1138,6 +1138,7 @@ void RasterizerStorageGLES2::_update_shader(Shader *p_shader) const {
case VS::SHADER_CANVAS_ITEM: {
p_shader->canvas_item.light_mode = Shader::CanvasItem::LIGHT_MODE_NORMAL;
p_shader->canvas_item.blend_mode = Shader::CanvasItem::BLEND_MODE_MIX;
p_shader->canvas_item.uses_screen_texture = false;
@ -1150,8 +1151,8 @@ void RasterizerStorageGLES2::_update_shader(Shader *p_shader) const {
shaders.actions_canvas.render_mode_values["blend_mul"] = Pair<int *, int>(&p_shader->canvas_item.blend_mode, Shader::CanvasItem::BLEND_MODE_MUL);
shaders.actions_canvas.render_mode_values["blend_premul_alpha"] = Pair<int *, int>(&p_shader->canvas_item.blend_mode, Shader::CanvasItem::BLEND_MODE_PMALPHA);
// shaders.actions_canvas.render_mode_values["unshaded"] = Pair<int *, int>(&p_shader->canvas_item.light_mode, Shader::CanvasItem::LIGHT_MODE_UNSHADED);
// shaders.actions_canvas.render_mode_values["light_only"] = Pair<int *, int>(&p_shader->canvas_item.light_mode, Shader::CanvasItem::LIGHT_MODE_LIGHT_ONLY);
shaders.actions_canvas.render_mode_values["unshaded"] = Pair<int *, int>(&p_shader->canvas_item.light_mode, Shader::CanvasItem::LIGHT_MODE_UNSHADED);
shaders.actions_canvas.render_mode_values["light_only"] = Pair<int *, int>(&p_shader->canvas_item.light_mode, Shader::CanvasItem::LIGHT_MODE_LIGHT_ONLY);
shaders.actions_canvas.usage_flag_pointers["SCREEN_UV"] = &p_shader->canvas_item.uses_screen_uv;
shaders.actions_canvas.usage_flag_pointers["SCREEN_PIXEL_SIZE"] = &p_shader->canvas_item.uses_screen_uv;
@ -4193,16 +4194,161 @@ void RasterizerStorageGLES2::render_target_set_msaa(RID p_render_target, VS::Vie
/* CANVAS SHADOW */
RID RasterizerStorageGLES2::canvas_light_shadow_buffer_create(int p_width) {
return RID();
CanvasLightShadow *cls = memnew(CanvasLightShadow);
if (p_width > config.max_texture_size)
p_width = config.max_texture_size;
cls->size = p_width;
cls->height = 16;
glActiveTexture(GL_TEXTURE0);
glGenFramebuffers(1, &cls->fbo);
glBindFramebuffer(GL_FRAMEBUFFER, cls->fbo);
glGenRenderbuffers(1, &cls->depth);
glBindRenderbuffer(GL_RENDERBUFFER, cls->depth);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, cls->size, cls->height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, cls->depth);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
glGenTextures(1, &cls->distance);
glBindTexture(GL_TEXTURE_2D, cls->distance);
if (config.use_rgba_2d_shadows) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, cls->size, cls->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
} else {
glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, cls->size, cls->height, 0, GL_RED, GL_FLOAT, NULL);
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, cls->distance, 0);
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
//printf("errnum: %x\n",status);
glBindFramebuffer(GL_FRAMEBUFFER, RasterizerStorageGLES2::system_fbo);
if (status != GL_FRAMEBUFFER_COMPLETE) {
memdelete(cls);
ERR_FAIL_COND_V(status != GL_FRAMEBUFFER_COMPLETE, RID());
}
return canvas_light_shadow_owner.make_rid(cls);
}
/* LIGHT SHADOW MAPPING */
RID RasterizerStorageGLES2::canvas_light_occluder_create() {
return RID();
CanvasOccluder *co = memnew(CanvasOccluder);
co->index_id = 0;
co->vertex_id = 0;
co->len = 0;
glGenVertexArrays(1, &co->array_id);
return canvas_occluder_owner.make_rid(co);
}
void RasterizerStorageGLES2::canvas_light_occluder_set_polylines(RID p_occluder, const PoolVector<Vector2> &p_lines) {
CanvasOccluder *co = canvas_occluder_owner.get(p_occluder);
ERR_FAIL_COND(!co);
co->lines = p_lines;
if (p_lines.size() != co->len) {
if (co->index_id)
glDeleteBuffers(1, &co->index_id);
if (co->vertex_id)
glDeleteBuffers(1, &co->vertex_id);
co->index_id = 0;
co->vertex_id = 0;
co->len = 0;
}
if (p_lines.size()) {
PoolVector<float> geometry;
PoolVector<uint16_t> indices;
int lc = p_lines.size();
geometry.resize(lc * 6);
indices.resize(lc * 3);
PoolVector<float>::Write vw = geometry.write();
PoolVector<uint16_t>::Write iw = indices.write();
PoolVector<Vector2>::Read lr = p_lines.read();
const int POLY_HEIGHT = 16384;
for (int i = 0; i < lc / 2; i++) {
vw[i * 12 + 0] = lr[i * 2 + 0].x;
vw[i * 12 + 1] = lr[i * 2 + 0].y;
vw[i * 12 + 2] = POLY_HEIGHT;
vw[i * 12 + 3] = lr[i * 2 + 1].x;
vw[i * 12 + 4] = lr[i * 2 + 1].y;
vw[i * 12 + 5] = POLY_HEIGHT;
vw[i * 12 + 6] = lr[i * 2 + 1].x;
vw[i * 12 + 7] = lr[i * 2 + 1].y;
vw[i * 12 + 8] = -POLY_HEIGHT;
vw[i * 12 + 9] = lr[i * 2 + 0].x;
vw[i * 12 + 10] = lr[i * 2 + 0].y;
vw[i * 12 + 11] = -POLY_HEIGHT;
iw[i * 6 + 0] = i * 4 + 0;
iw[i * 6 + 1] = i * 4 + 1;
iw[i * 6 + 2] = i * 4 + 2;
iw[i * 6 + 3] = i * 4 + 2;
iw[i * 6 + 4] = i * 4 + 3;
iw[i * 6 + 5] = i * 4 + 0;
}
//if same buffer len is being set, just use BufferSubData to avoid a pipeline flush
if (!co->vertex_id) {
glGenBuffers(1, &co->vertex_id);
glBindBuffer(GL_ARRAY_BUFFER, co->vertex_id);
glBufferData(GL_ARRAY_BUFFER, lc * 6 * sizeof(real_t), vw.ptr(), GL_STATIC_DRAW);
} else {
glBindBuffer(GL_ARRAY_BUFFER, co->vertex_id);
glBufferSubData(GL_ARRAY_BUFFER, 0, lc * 6 * sizeof(real_t), vw.ptr());
}
glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind
if (!co->index_id) {
glGenBuffers(1, &co->index_id);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, co->index_id);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, lc * 3 * sizeof(uint16_t), iw.ptr(), GL_DYNAMIC_DRAW);
} else {
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, co->index_id);
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, lc * 3 * sizeof(uint16_t), iw.ptr());
}
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); //unbind
co->len = lc;
glBindVertexArray(co->array_id);
glBindBuffer(GL_ARRAY_BUFFER, co->vertex_id);
glEnableVertexAttribArray(VS::ARRAY_VERTEX);
glVertexAttribPointer(VS::ARRAY_VERTEX, 3, GL_FLOAT, false, 0, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, co->index_id);
glBindVertexArray(0);
}
}
VS::InstanceType RasterizerStorageGLES2::get_base_type(RID p_rid) const {
@ -4415,6 +4561,32 @@ bool RasterizerStorageGLES2::free(RID p_rid) {
lightmap_capture_data_owner.free(p_rid);
memdelete(lightmap_capture);
return true;
} else if (canvas_occluder_owner.owns(p_rid)) {
CanvasOccluder *co = canvas_occluder_owner.get(p_rid);
if (co->index_id)
glDeleteBuffers(1, &co->index_id);
if (co->vertex_id)
glDeleteBuffers(1, &co->vertex_id);
glDeleteVertexArrays(1, &co->array_id);
canvas_occluder_owner.free(p_rid);
memdelete(co);
return true;
} else if (canvas_light_shadow_owner.owns(p_rid)) {
CanvasLightShadow *cls = canvas_light_shadow_owner.get(p_rid);
glDeleteFramebuffers(1, &cls->fbo);
glDeleteRenderbuffers(1, &cls->depth);
glDeleteTextures(1, &cls->distance);
canvas_light_shadow_owner.free(p_rid);
memdelete(cls);
return true;
} else {
return false;
}
@ -4471,6 +4643,7 @@ void RasterizerStorageGLES2::initialize() {
config.float_texture_supported = config.extensions.has("GL_ARB_texture_float") || config.extensions.has("GL_OES_texture_float");
config.s3tc_supported = config.extensions.has("GL_EXT_texture_compression_s3tc");
config.etc1_supported = config.extensions.has("GL_OES_compressed_ETC1_RGB8_texture");
config.use_rgba_2d_shadows = false;
frame.count = 0;
frame.delta = 0;

View file

@ -76,6 +76,8 @@ public:
bool keep_original_textures;
bool force_vertex_shading;
bool use_rgba_2d_shadows;
} config;
struct Resources {
@ -86,6 +88,7 @@ public:
GLuint aniso_tex;
GLuint radical_inverse_vdc_cache_tex;
bool use_rgba_2d_shadows;
GLuint quadie;
@ -400,7 +403,6 @@ public:
int blend_mode;
/*
enum LightMode {
LIGHT_MODE_NORMAL,
LIGHT_MODE_UNSHADED,
@ -408,7 +410,6 @@ public:
};
int light_mode;
*/
bool uses_screen_texture;
bool uses_screen_uv;
@ -1161,10 +1162,32 @@ public:
/* CANVAS SHADOW */
struct CanvasLightShadow : public RID_Data {
int size;
int height;
GLuint fbo;
GLuint depth;
GLuint distance; //for older devices
};
RID_Owner<CanvasLightShadow> canvas_light_shadow_owner;
virtual RID canvas_light_shadow_buffer_create(int p_width);
/* LIGHT SHADOW MAPPING */
struct CanvasOccluder : public RID_Data {
GLuint array_id; // 0 means, unconfigured
GLuint vertex_id; // 0 means, unconfigured
GLuint index_id; // 0 means, unconfigured
PoolVector<Vector2> lines;
int len;
};
RID_Owner<CanvasOccluder> canvas_occluder_owner;
virtual RID canvas_light_occluder_create();
virtual void canvas_light_occluder_set_polylines(RID p_occluder, const PoolVector<Vector2> &p_lines);

View file

@ -6,7 +6,7 @@ if 'GLES2_GLSL' in env['BUILDERS']:
env.GLES2_GLSL('copy.glsl');
# env.GLES2_GLSL('resolve.glsl');
env.GLES2_GLSL('canvas.glsl');
# env.GLES2_GLSL('canvas_shadow.glsl');
env.GLES2_GLSL('canvas_shadow.glsl');
env.GLES2_GLSL('scene.glsl');
env.GLES2_GLSL('cubemap_filter.glsl');
env.GLES2_GLSL('cube_to_dp.glsl');

View file

@ -31,6 +31,38 @@ uniform vec4 src_rect;
uniform highp float time;
#ifdef USE_LIGHTING
// light matrices
uniform highp mat4 light_matrix;
uniform highp mat4 light_matrix_inverse;
uniform highp mat4 light_local_matrix;
uniform highp mat4 shadow_matrix;
uniform highp vec4 light_color;
uniform highp vec4 light_shadow_color;
uniform highp vec2 light_pos;
uniform highp float shadowpixel_size;
uniform highp float shadow_gradient;
uniform highp float light_height;
uniform highp float light_outside_alpha;
uniform highp float shadow_distance_mult;
varying vec4 light_uv_interp;
varying vec2 transformed_light_uv;
varying vec4 local_rot;
#ifdef USE_SHADOWS
varying highp vec2 pos;
#endif
const bool at_light_pass = true;
#else
const bool at_light_pass = false;
#endif
/* clang-format off */
VERTEX_SHADER_GLOBALS
@ -48,8 +80,19 @@ vec2 select(vec2 a, vec2 b, bvec2 c) {
void main() {
vec4 color = color_attrib;
#ifdef USE_INSTANCING
mat4 extra_matrix_instance = extra_matrix * transpose(mat4(instance_xform0, instance_xform1, instance_xform2, vec4(0.0, 0.0, 0.0, 1.0)));
color *= instance_color;
vec4 instance_custom = instance_custom_data;
#else
mat4 extra_matrix_instance = extra_matrix;
vec4 instance_custom = vec4(0.0);
#endif
#ifdef USE_TEXTURE_RECT
if (dst_rect.z < 0.0) { // Transpose is encoded as negative dst_rect.z
@ -96,6 +139,27 @@ VERTEX_SHADER_CODE
#endif
gl_Position = projection_matrix * outvec;
#ifdef USE_LIGHTING
light_uv_interp.xy = (light_matrix * outvec).xy;
light_uv_interp.zw = (light_local_matrix * outvec).xy;
transformed_light_uv = (mat3(light_matrix_inverse) * vec3(light_uv_interp.zw, 0.0)).xy; //for normal mapping
#ifdef USE_SHADOWS
pos = outvec.xy;
#endif
local_rot.xy = normalize((modelview_matrix * (extra_matrix_instance * vec4(1.0, 0.0, 0.0, 0.0))).xy);
local_rot.zw = normalize((modelview_matrix * (extra_matrix_instance * vec4(0.0, 1.0, 0.0, 0.0))).xy);
#ifdef USE_TEXTURE_RECT
local_rot.xy *= sign(src_rect.z);
local_rot.zw *= sign(src_rect.w);
#endif
#endif
}
/* clang-format off */
@ -133,6 +197,41 @@ uniform vec2 screen_pixel_size;
#endif
#ifdef USE_LIGHTING
uniform highp mat4 light_matrix;
uniform highp mat4 light_local_matrix;
uniform highp mat4 shadow_matrix;
uniform highp vec4 light_color;
uniform highp vec4 light_shadow_color;
uniform highp vec2 light_pos;
uniform highp float shadowpixel_size;
uniform highp float shadow_gradient;
uniform highp float light_height;
uniform highp float light_outside_alpha;
uniform highp float shadow_distance_mult;
uniform lowp sampler2D light_texture; // texunit:-3
varying vec4 light_uv_interp;
varying vec2 transformed_light_uv;
varying vec4 local_rot;
#ifdef USE_SHADOWS
uniform highp sampler2D shadow_texture; // texunit:-4
varying highp vec2 pos;
#endif
const bool at_light_pass = true;
#else
const bool at_light_pass = false;
#endif
uniform bool use_default_normal;
/* clang-format off */
FRAGMENT_SHADER_GLOBALS
@ -151,15 +250,236 @@ void main() {
#ifdef SCREEN_UV_USED
vec2 screen_uv = gl_FragCoord.xy * screen_pixel_size;
#endif
vec3 normal;
#if defined(NORMAL_USED)
bool normal_used = true;
#else
bool normal_used = false;
#endif
if (use_default_normal) {
normal.xy = texture2D(normal_texture, uv_interp).xy * 2.0 - 1.0;
normal.z = sqrt(1.0 - dot(normal.xy, normal.xy));
normal_used = true;
} else {
normal = vec3(0.0, 0.0, 1.0);
}
{
float normal_depth = 1.0;
#if defined(NORMALMAP_USED)
vec3 normal_map = vec3(0.0, 0.0, 1.0);
normal_used = true;
#endif
/* clang-format off */
FRAGMENT_SHADER_CODE
/* clang-format on */
#if defined(NORMALMAP_USED)
normal = mix(vec3(0.0, 0.0, 1.0), normal_map * vec3(2.0, -2.0, 1.0) - vec3(1.0, -1.0, 0.0), normal_depth);
#endif
}
color *= final_modulate;
#ifdef USE_LIGHTING
vec2 light_vec = transformed_light_uv;
if (normal_used) {
normal.xy = mat2(local_rot.xy, local_rot.zw) * normal.xy;
}
color *= final_modulate;
float att = 1.0;
vec2 light_uv = light_uv_interp.xy;
vec4 light = texture2D(light_texture, light_uv);
if (any(lessThan(light_uv_interp.xy, vec2(0.0, 0.0))) || any(greaterThanEqual(light_uv_interp.xy, vec2(1.0, 1.0)))) {
color.a *= light_outside_alpha; //invisible
} else {
float real_light_height = light_height;
vec4 real_light_color = light_color;
vec4 real_light_shadow_color = light_shadow_color;
#if defined(USE_LIGHT_SHADER_CODE)
//light is written by the light shader
light_compute(
light,
light_vec,
real_light_height,
real_light_color,
light_uv,
real_light_shadow_color,
normal,
uv,
#if defined(SCREEN_UV_USED)
screen_uv,
#endif
color);
#endif
light *= real_light_color;
if (normal_used) {
vec3 light_normal = normalize(vec3(light_vec, -real_light_height));
light *= max(dot(-light_normal, normal), 0.0);
}
color *= light;
#ifdef USE_SHADOWS
light_vec = light_uv_interp.zw; //for shadows
float angle_to_light = -atan(light_vec.x, light_vec.y);
float PI = 3.14159265358979323846264;
/*int i = int(mod(floor((angle_to_light+7.0*PI/6.0)/(4.0*PI/6.0))+1.0, 3.0)); // +1 pq os indices estao em ordem 2,0,1 nos arrays
float ang*/
float su, sz;
float abs_angle = abs(angle_to_light);
vec2 point;
float sh;
if (abs_angle < 45.0 * PI / 180.0) {
point = light_vec;
sh = 0.0 + (1.0 / 8.0);
} else if (abs_angle > 135.0 * PI / 180.0) {
point = -light_vec;
sh = 0.5 + (1.0 / 8.0);
} else if (angle_to_light > 0.0) {
point = vec2(light_vec.y, -light_vec.x);
sh = 0.25 + (1.0 / 8.0);
} else {
point = vec2(-light_vec.y, light_vec.x);
sh = 0.75 + (1.0 / 8.0);
}
highp vec4 s = shadow_matrix * vec4(point, 0.0, 1.0);
s.xyz /= s.w;
su = s.x * 0.5 + 0.5;
sz = s.z * 0.5 + 0.5;
//sz=lightlength(light_vec);
highp float shadow_attenuation = 0.0;
#ifdef USE_RGBA_SHADOWS
#define SHADOW_DEPTH(m_tex, m_uv) dot(texture2D((m_tex), (m_uv)), vec4(1.0 / (256.0 * 256.0 * 256.0), 1.0 / (256.0 * 256.0), 1.0 / 256.0, 1))
#else
#define SHADOW_DEPTH(m_tex, m_uv) (texture2D((m_tex), (m_uv)).r)
#endif
#ifdef SHADOW_USE_GRADIENT
#define SHADOW_TEST(m_ofs) \
{ \
highp float sd = SHADOW_DEPTH(shadow_texture, vec2(m_ofs, sh)); \
shadow_attenuation += 1.0 - smoothstep(sd, sd + shadow_gradient, sz); \
}
#else
#define SHADOW_TEST(m_ofs) \
{ \
highp float sd = SHADOW_DEPTH(shadow_texture, vec2(m_ofs, sh)); \
shadow_attenuation += step(sz, sd); \
}
#endif
#ifdef SHADOW_FILTER_NEAREST
SHADOW_TEST(su);
#endif
#ifdef SHADOW_FILTER_PCF3
SHADOW_TEST(su + shadowpixel_size);
SHADOW_TEST(su);
SHADOW_TEST(su - shadowpixel_size);
shadow_attenuation /= 3.0;
#endif
#ifdef SHADOW_FILTER_PCF5
SHADOW_TEST(su + shadowpixel_size * 2.0);
SHADOW_TEST(su + shadowpixel_size);
SHADOW_TEST(su);
SHADOW_TEST(su - shadowpixel_size);
SHADOW_TEST(su - shadowpixel_size * 2.0);
shadow_attenuation /= 5.0;
#endif
#ifdef SHADOW_FILTER_PCF7
SHADOW_TEST(su + shadowpixel_size * 3.0);
SHADOW_TEST(su + shadowpixel_size * 2.0);
SHADOW_TEST(su + shadowpixel_size);
SHADOW_TEST(su);
SHADOW_TEST(su - shadowpixel_size);
SHADOW_TEST(su - shadowpixel_size * 2.0);
SHADOW_TEST(su - shadowpixel_size * 3.0);
shadow_attenuation /= 7.0;
#endif
#ifdef SHADOW_FILTER_PCF9
SHADOW_TEST(su + shadowpixel_size * 4.0);
SHADOW_TEST(su + shadowpixel_size * 3.0);
SHADOW_TEST(su + shadowpixel_size * 2.0);
SHADOW_TEST(su + shadowpixel_size);
SHADOW_TEST(su);
SHADOW_TEST(su - shadowpixel_size);
SHADOW_TEST(su - shadowpixel_size * 2.0);
SHADOW_TEST(su - shadowpixel_size * 3.0);
SHADOW_TEST(su - shadowpixel_size * 4.0);
shadow_attenuation /= 9.0;
#endif
#ifdef SHADOW_FILTER_PCF13
SHADOW_TEST(su + shadowpixel_size * 6.0);
SHADOW_TEST(su + shadowpixel_size * 5.0);
SHADOW_TEST(su + shadowpixel_size * 4.0);
SHADOW_TEST(su + shadowpixel_size * 3.0);
SHADOW_TEST(su + shadowpixel_size * 2.0);
SHADOW_TEST(su + shadowpixel_size);
SHADOW_TEST(su);
SHADOW_TEST(su - shadowpixel_size);
SHADOW_TEST(su - shadowpixel_size * 2.0);
SHADOW_TEST(su - shadowpixel_size * 3.0);
SHADOW_TEST(su - shadowpixel_size * 4.0);
SHADOW_TEST(su - shadowpixel_size * 5.0);
SHADOW_TEST(su - shadowpixel_size * 6.0);
shadow_attenuation /= 13.0;
#endif
//color *= shadow_attenuation;
color = mix(real_light_shadow_color, color, shadow_attenuation);
//use shadows
#endif
}
//use lighting
#endif
gl_FragColor = color;
}

View file

@ -1,15 +1,23 @@
/* clang-format off */
[vertex]
#ifdef USE_GLES_OVER_GL
#define mediump
#define highp
#else
precision highp float;
precision highp int;
#endif
attribute highp vec3 vertex; // attrib:0
uniform highp mat4 projection_matrix;
/* clang-format on */
uniform highp mat4 light_matrix;
uniform highp mat4 world_matrix;
uniform highp float distance_norm;
layout(location = 0) in highp vec3 vertex;
out highp vec4 position_interp;
varying highp vec4 position_interp;
void main() {
@ -20,31 +28,29 @@ void main() {
/* clang-format off */
[fragment]
in highp vec4 position_interp;
#ifdef USE_GLES_OVER_GL
#define mediump
#define highp
#else
precision mediump float;
precision mediump int;
#endif
varying highp vec4 position_interp;
/* clang-format on */
#ifdef USE_RGBA_SHADOWS
layout(location = 0) out lowp vec4 distance_buf;
#else
layout(location = 0) out highp float distance_buf;
#endif
void main() {
highp float depth = ((position_interp.z / position_interp.w) + 1.0) * 0.5 + 0.0; //bias;
highp float depth = ((position_interp.z / position_interp.w) + 1.0) * 0.5 + 0.0; // bias
#ifdef USE_RGBA_SHADOWS
highp vec4 comp = fract(depth * vec4(256.0 * 256.0 * 256.0, 256.0 * 256.0, 256.0, 1.0));
comp -= comp.xxyz * vec4(0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0);
distance_buf = comp;
gl_FragColor = comp;
#else
distance_buf = depth;
gl_FragColor = vec4(depth);
#endif
}