Several fixes to make GLES2 on HTML5 work much better.

Changed math class error reporting to be a bit less paranoid.
This commit is contained in:
Juan Linietsky 2019-02-25 21:46:24 -03:00
parent 51c1d55cf9
commit a32b26dfa2
16 changed files with 315 additions and 100 deletions

View File

@ -141,6 +141,7 @@ opts.Add(EnumVariable('target', "Compilation target", 'debug', ('debug', 'releas
opts.Add(EnumVariable('optimize', "Optimization type", 'speed', ('speed', 'size')))
opts.Add(BoolVariable('tools', "Build the tools (a.k.a. the Godot editor)", True))
opts.Add(BoolVariable('use_lto', 'Use link-time optimization', False))
opts.Add(BoolVariable('use_precise_math_checks', 'Math checks use very precise epsilon (useful to debug the engine)', False))
# Components
opts.Add(BoolVariable('deprecated', "Enable deprecated features", True))
@ -224,6 +225,9 @@ env_base.Append(CPPPATH=['#editor', '#'])
env_base.platform_exporters = platform_exporters
env_base.platform_apis = platform_apis
if (env_base["use_precise_math_checks"]):
env_base.Append(CPPDEFINES=['PRECISE_MATH_CHECKS'])
if (env_base['target'] == 'debug'):
env_base.Append(CPPDEFINES=['DEBUG_MEMORY_ALLOC','DISABLE_FORCED_INLINE'])

View File

@ -76,15 +76,23 @@ void Basis::invert() {
}
void Basis::orthonormalize() {
/* this check is undesired, the matrix could be wrong but we still may want to generate a valid one
* for practical purposes
#ifdef MATH_CHECKS
ERR_FAIL_COND(determinant() == 0);
#endif
*/
// Gram-Schmidt Process
Vector3 x = get_axis(0);
Vector3 y = get_axis(1);
Vector3 z = get_axis(2);
#ifdef MATH_CHECKS
ERR_FAIL_COND(x.length_squared() == 0);
ERR_FAIL_COND(y.length_squared() == 0);
ERR_FAIL_COND(z.length_squared() == 0);
#endif
x.normalize();
y = (y - x * (x.dot(y)));
y.normalize();
@ -118,16 +126,16 @@ bool Basis::is_diagonal() const {
}
bool Basis::is_rotation() const {
return Math::is_equal_approx(determinant(), 1) && is_orthogonal();
return Math::is_equal_approx(determinant(), 1, UNIT_EPSILON) && is_orthogonal();
}
bool Basis::is_symmetric() const {
if (!Math::is_equal_approx(elements[0][1], elements[1][0]))
if (!Math::is_equal_approx_ratio(elements[0][1], elements[1][0], UNIT_EPSILON))
return false;
if (!Math::is_equal_approx(elements[0][2], elements[2][0]))
if (!Math::is_equal_approx_ratio(elements[0][2], elements[2][0], UNIT_EPSILON))
return false;
if (!Math::is_equal_approx(elements[1][2], elements[2][1]))
if (!Math::is_equal_approx_ratio(elements[1][2], elements[2][1], UNIT_EPSILON))
return false;
return true;
@ -488,6 +496,11 @@ void Basis::set_euler_xyz(const Vector3 &p_euler) {
// as the x, y, and z components of a Vector3 respectively.
Vector3 Basis::get_euler_yxz() const {
/* checking this is a bad idea, because obtaining from scaled transform is a valid use case
#ifdef MATH_CHECKS
ERR_FAIL_COND(!is_rotation());
#endif
*/
// Euler angles in YXZ convention.
// See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix
//
@ -496,9 +509,7 @@ Vector3 Basis::get_euler_yxz() const {
// cy*sx*sz-cz*sy cy*cz*sx+sy*sz cy*cx
Vector3 euler;
#ifdef MATH_CHECKS
ERR_FAIL_COND_V(!is_rotation(), euler);
#endif
real_t m12 = elements[1][2];
if (m12 < 1) {
@ -556,7 +567,7 @@ bool Basis::is_equal_approx(const Basis &a, const Basis &b) const {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (!Math::is_equal_approx(a.elements[i][j], b.elements[i][j]))
if (!Math::is_equal_approx_ratio(a.elements[i][j], b.elements[i][j], UNIT_EPSILON))
return false;
}
}
@ -599,10 +610,14 @@ Basis::operator String() const {
}
Quat Basis::get_quat() const {
#ifdef MATH_CHECKS
ERR_FAIL_COND_V(!is_rotation(), Quat());
#endif
real_t trace = elements[0][0] + elements[1][1] + elements[2][2];
/* Allow getting a quaternion from an unnormalized transform */
Basis m = *this;
m.elements[0].normalize();
m.elements[1].normalize();
m.elements[2].normalize();
real_t trace = m.elements[0][0] + m.elements[1][1] + m.elements[2][2];
real_t temp[4];
if (trace > 0.0) {
@ -610,23 +625,23 @@ Quat Basis::get_quat() const {
temp[3] = (s * 0.5);
s = 0.5 / s;
temp[0] = ((elements[2][1] - elements[1][2]) * s);
temp[1] = ((elements[0][2] - elements[2][0]) * s);
temp[2] = ((elements[1][0] - elements[0][1]) * s);
temp[0] = ((m.elements[2][1] - m.elements[1][2]) * s);
temp[1] = ((m.elements[0][2] - m.elements[2][0]) * s);
temp[2] = ((m.elements[1][0] - m.elements[0][1]) * s);
} else {
int i = elements[0][0] < elements[1][1] ?
(elements[1][1] < elements[2][2] ? 2 : 1) :
(elements[0][0] < elements[2][2] ? 2 : 0);
int i = m.elements[0][0] < m.elements[1][1] ?
(m.elements[1][1] < m.elements[2][2] ? 2 : 1) :
(m.elements[0][0] < m.elements[2][2] ? 2 : 0);
int j = (i + 1) % 3;
int k = (i + 2) % 3;
real_t s = Math::sqrt(elements[i][i] - elements[j][j] - elements[k][k] + 1.0);
real_t s = Math::sqrt(m.elements[i][i] - m.elements[j][j] - m.elements[k][k] + 1.0);
temp[i] = s * 0.5;
s = 0.5 / s;
temp[3] = (elements[k][j] - elements[j][k]) * s;
temp[j] = (elements[j][i] + elements[i][j]) * s;
temp[k] = (elements[k][i] + elements[i][k]) * s;
temp[3] = (m.elements[k][j] - m.elements[j][k]) * s;
temp[j] = (m.elements[j][i] + m.elements[i][j]) * s;
temp[k] = (m.elements[k][i] + m.elements[i][k]) * s;
}
return Quat(temp[0], temp[1], temp[2], temp[3]);
@ -696,9 +711,11 @@ void Basis::set_orthogonal_index(int p_index) {
}
void Basis::get_axis_angle(Vector3 &r_axis, real_t &r_angle) const {
/* checking this is a bad idea, because obtaining from scaled transform is a valid use case
#ifdef MATH_CHECKS
ERR_FAIL_COND(!is_rotation());
#endif
*/
real_t angle, x, y, z; // variables for result
real_t epsilon = 0.01; // margin to allow for rounding errors
real_t epsilon2 = 0.1; // margin to distinguish between 0 and 180 degrees
@ -835,14 +852,15 @@ void Basis::set_diagonal(const Vector3 p_diag) {
}
Basis Basis::slerp(const Basis &target, const real_t &t) const {
// TODO: implement this directly without using quaternions to make it more efficient
#ifdef MATH_CHECKS
ERR_FAIL_COND_V(!is_rotation(), Basis());
ERR_FAIL_COND_V(!target.is_rotation(), Basis());
#endif
//consider scale
Quat from(*this);
Quat to(target);
return Basis(from.slerp(to, t));
Basis b(from.slerp(to, t));
b.elements[0] *= Math::lerp(elements[0].length(), target.elements[0].length(), t);
b.elements[1] *= Math::lerp(elements[1].length(), target.elements[1].length(), t);
b.elements[2] *= Math::lerp(elements[2].length(), target.elements[2].length(), t);
return b;
}

View File

@ -33,6 +33,7 @@
#define CMP_EPSILON 0.00001
#define CMP_EPSILON2 (CMP_EPSILON * CMP_EPSILON)
#define CMP_NORMALIZE_TOLERANCE 0.000001
#define CMP_POINT_IN_PLANE_EPSILON 0.00001
@ -49,6 +50,14 @@
#define MATH_CHECKS
#endif
//this epsilon is for values related to a unit size (scalar or vector len)
#ifdef PRECISE_MATH_CHECKS
#define UNIT_EPSILON 0.00001
#else
//tolerate some more floating point error normally
#define UNIT_EPSILON 0.001
#endif
#define USEC_TO_SEC(m_usec) ((m_usec) / 1000000.0)
enum ClockDirection {

View File

@ -249,13 +249,25 @@ public:
static float random(float from, float to);
static real_t random(int from, int to) { return (real_t)random((real_t)from, (real_t)to); }
static _ALWAYS_INLINE_ bool is_equal_approx(real_t a, real_t b) {
static _ALWAYS_INLINE_ bool is_equal_approx_ratio(real_t a, real_t b, real_t epsilon = CMP_EPSILON) {
// this is an approximate way to check that numbers are close, as a ratio of their average size
// helps compare approximate numbers that may be very big or very small
real_t diff = abs(a - b);
if (diff == 0.0) {
return true;
}
real_t avg_size = (abs(a) + abs(b)) / 2.0;
diff /= avg_size;
return diff < epsilon;
}
static _ALWAYS_INLINE_ bool is_equal_approx(real_t a, real_t b, real_t epsilon = CMP_EPSILON) {
// TODO: Comparing floats for approximate-equality is non-trivial.
// Using epsilon should cover the typical cases in Godot (where a == b is used to compare two reals), such as matrix and vector comparison operators.
// A proper implementation in terms of ULPs should eventually replace the contents of this function.
// See https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ for details.
return abs(a - b) < CMP_EPSILON;
return abs(a - b) < epsilon;
}
static _ALWAYS_INLINE_ float absf(float g) {

View File

@ -135,7 +135,7 @@ Quat Quat::normalized() const {
}
bool Quat::is_normalized() const {
return Math::is_equal_approx(length_squared(), 1.0);
return Math::is_equal_approx(length_squared(), 1.0, UNIT_EPSILON); //use less epsilon
}
Quat Quat::inverse() const {

View File

@ -65,7 +65,7 @@ Vector2 Vector2::normalized() const {
bool Vector2::is_normalized() const {
// use length_squared() instead of length() to avoid sqrt(), makes it more stringent.
return Math::is_equal_approx(length_squared(), 1.0);
return Math::is_equal_approx(length_squared(), 1.0, UNIT_EPSILON);
}
real_t Vector2::distance_to(const Vector2 &p_vector2) const {

View File

@ -414,7 +414,7 @@ Vector3 Vector3::normalized() const {
bool Vector3::is_normalized() const {
// use length_squared() instead of length() to avoid sqrt(), makes it more stringent.
return Math::is_equal_approx(length_squared(), 1.0);
return Math::is_equal_approx(length_squared(), 1.0, UNIT_EPSILON);
}
Vector3 Vector3::inverse() const {

View File

@ -2019,6 +2019,7 @@ void RasterizerCanvasGLES2::initialize() {
state.canvas_shader.init();
state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT, true);
state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_RGBA_SHADOWS, storage->config.use_rgba_2d_shadows);
state.canvas_shader.bind();

View File

@ -62,6 +62,7 @@ RID RasterizerSceneGLES2::shadow_atlas_create() {
ShadowAtlas *shadow_atlas = memnew(ShadowAtlas);
shadow_atlas->fbo = 0;
shadow_atlas->depth = 0;
shadow_atlas->color = 0;
shadow_atlas->size = 0;
shadow_atlas->smallest_subdiv = 0;
@ -86,9 +87,13 @@ void RasterizerSceneGLES2::shadow_atlas_set_size(RID p_atlas, int p_size) {
if (shadow_atlas->fbo) {
glDeleteTextures(1, &shadow_atlas->depth);
glDeleteFramebuffers(1, &shadow_atlas->fbo);
if (shadow_atlas->color) {
glDeleteTextures(1, &shadow_atlas->color);
}
shadow_atlas->fbo = 0;
shadow_atlas->depth = 0;
shadow_atlas->color = 0;
}
// erase shadow atlast references from lights
@ -119,6 +124,16 @@ void RasterizerSceneGLES2::shadow_atlas_set_size(RID p_atlas, int p_size) {
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, shadow_atlas->depth, 0);
if (storage->config.use_rgba_3d_shadows) {
glGenTextures(1, &shadow_atlas->color);
glBindTexture(GL_TEXTURE_2D, shadow_atlas->color);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, shadow_atlas->size, shadow_atlas->size, 0, GL_RGBA, GL_UNSIGNED_BYTE, 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, shadow_atlas->color, 0);
}
glViewport(0, 0, shadow_atlas->size, shadow_atlas->size);
glDepthMask(GL_TRUE);
@ -459,10 +474,10 @@ RID RasterizerSceneGLES2::reflection_probe_instance_create(RID p_probe) {
for (int i = 0; i < 6; i++) {
glGenFramebuffers(1, &rpi->fbo[i]);
glGenTextures(1, &rpi->color[i]);
}
glGenFramebuffers(1, &rpi->fbo_blur);
glGenRenderbuffers(1, &rpi->depth);
glGenTextures(1, &rpi->depth);
rpi->cubemap = 0;
//glGenTextures(1, &rpi->cubemap);
@ -510,9 +525,14 @@ bool RasterizerSceneGLES2::reflection_probe_instance_begin_render(RID p_instance
GLenum type = GL_UNSIGNED_BYTE;
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, rpi->depth);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, size, size, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL);
if (rpi->cubemap != 0) {
glDeleteTextures(1, &rpi->cubemap);
}
glGenTextures(1, &rpi->cubemap);
glBindTexture(GL_TEXTURE_CUBE_MAP, rpi->cubemap);
#if 1
@ -523,17 +543,15 @@ bool RasterizerSceneGLES2::reflection_probe_instance_begin_render(RID p_instance
glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
glBindRenderbuffer(GL_RENDERBUFFER, rpi->depth); //resize depth buffer
#ifdef JAVASCRIPT_ENABLED
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, size, size);
#else
glRenderbufferStorage(GL_RENDERBUFFER, _DEPTH_COMPONENT24_OES, size, size);
#endif
//Generate framebuffers for rendering
for (int i = 0; i < 6; i++) {
glBindFramebuffer(GL_FRAMEBUFFER, rpi->fbo[i]);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, _cube_side_enum[i], rpi->cubemap, 0);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rpi->depth);
glBindTexture(GL_TEXTURE_2D, rpi->color[i]);
glTexImage2D(GL_TEXTURE_2D, 0, internal_format, size, size, 0, format, type, NULL);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rpi->color[i], 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, rpi->depth, 0);
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
ERR_CONTINUE(status != GL_FRAMEBUFFER_COMPLETE);
}
#else
@ -570,6 +588,8 @@ bool RasterizerSceneGLES2::reflection_probe_instance_begin_render(RID p_instance
glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glBindFramebuffer(GL_FRAMEBUFFER, RasterizerStorageGLES2::system_fbo);
}
return true;
@ -579,6 +599,7 @@ bool RasterizerSceneGLES2::reflection_probe_instance_postprocess_step(RID p_inst
ReflectionProbeInstance *rpi = reflection_probe_instance_owner.getornull(p_instance);
ERR_FAIL_COND_V(!rpi, false);
ERR_FAIL_COND_V(rpi->current_resolution == 0, false);
int size = rpi->probe_ptr->resolution;
@ -596,16 +617,23 @@ bool RasterizerSceneGLES2::reflection_probe_instance_postprocess_step(RID p_inst
}
}
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_CUBE_MAP, rpi->cubemap);
glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); //use linear, no mipmaps so it does not read from what is being written to
//first of all, copy rendered textures to cubemap
for (int i = 0; i < 6; i++) {
glBindFramebuffer(GL_FRAMEBUFFER, rpi->fbo[i]);
glViewport(0, 0, size, size);
glCopyTexImage2D(_cube_side_enum[i], 0, GL_RGB, 0, 0, size, size, 0);
}
//do filtering
//vdc cache
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, storage->resources.radical_inverse_vdc_cache_tex);
glBindFramebuffer(GL_FRAMEBUFFER, rpi->fbo_blur);
// now render to the framebuffer, mipmap level for mipmap level
int lod = 1;
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_CUBE_MAP, rpi->cubemap);
glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); //use linear, no mipmaps so it does not read from what is being written to
size >>= 1;
int mipmaps = 6;
@ -613,13 +641,20 @@ bool RasterizerSceneGLES2::reflection_probe_instance_postprocess_step(RID p_inst
storage->shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES2::USE_SOURCE_PANORAMA, false);
storage->shaders.cubemap_filter.bind();
glBindFramebuffer(GL_FRAMEBUFFER, storage->resources.mipmap_blur_fbo);
//blur
while (size >= 1) {
for (int i = 0; i < 6; i++) {
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, _cube_side_enum[i], rpi->cubemap, lod);
glActiveTexture(GL_TEXTURE3);
glBindTexture(GL_TEXTURE_2D, storage->resources.mipmap_blur_color);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, size, size, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, storage->resources.mipmap_blur_color, 0);
glViewport(0, 0, size, size);
glActiveTexture(GL_TEXTURE0);
for (int i = 0; i < 6; i++) {
storage->bind_quad_array();
storage->shaders.cubemap_filter.set_uniform(CubemapFilterShaderGLES2::FACE_ID, i);
float roughness = CLAMP(lod / (float)(mipmaps - 1), 0, 1);
@ -627,6 +662,7 @@ bool RasterizerSceneGLES2::reflection_probe_instance_postprocess_step(RID p_inst
storage->shaders.cubemap_filter.set_uniform(CubemapFilterShaderGLES2::Z_FLIP, false);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glCopyTexImage2D(_cube_side_enum[i], lod, GL_RGB, 0, 0, size, size, 0);
}
size >>= 1;
@ -635,9 +671,14 @@ bool RasterizerSceneGLES2::reflection_probe_instance_postprocess_step(RID p_inst
}
// restore ranges
glActiveTexture(GL_TEXTURE0);
glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glBindTexture(GL_TEXTURE_2D, 0);
glActiveTexture(GL_TEXTURE3); //back to panorama
glBindTexture(GL_TEXTURE_2D, 0);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, 0);
glBindFramebuffer(GL_FRAMEBUFFER, RasterizerStorageGLES2::system_fbo);
return true;
}
@ -1751,7 +1792,11 @@ void RasterizerSceneGLES2::_setup_light_type(LightInstance *p_light, ShadowAtlas
if (!state.render_no_shadows && p_light->light_ptr->shadow) {
state.scene_shader.set_conditional(SceneShaderGLES2::USE_SHADOW, true);
glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 3);
if (storage->config.use_rgba_3d_shadows) {
glBindTexture(GL_TEXTURE_2D, directional_shadow.color);
} else {
glBindTexture(GL_TEXTURE_2D, directional_shadow.depth);
}
state.scene_shader.set_conditional(SceneShaderGLES2::SHADOW_MODE_PCF_5, shadow_filter_mode == SHADOW_FILTER_PCF5);
state.scene_shader.set_conditional(SceneShaderGLES2::SHADOW_MODE_PCF_13, shadow_filter_mode == SHADOW_FILTER_PCF13);
}
@ -1763,7 +1808,11 @@ void RasterizerSceneGLES2::_setup_light_type(LightInstance *p_light, ShadowAtlas
if (!state.render_no_shadows && shadow_atlas && p_light->light_ptr->shadow) {
state.scene_shader.set_conditional(SceneShaderGLES2::USE_SHADOW, true);
glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 3);
if (storage->config.use_rgba_3d_shadows) {
glBindTexture(GL_TEXTURE_2D, shadow_atlas->color);
} else {
glBindTexture(GL_TEXTURE_2D, shadow_atlas->depth);
}
state.scene_shader.set_conditional(SceneShaderGLES2::SHADOW_MODE_PCF_5, shadow_filter_mode == SHADOW_FILTER_PCF5);
state.scene_shader.set_conditional(SceneShaderGLES2::SHADOW_MODE_PCF_13, shadow_filter_mode == SHADOW_FILTER_PCF13);
}
@ -1774,7 +1823,11 @@ void RasterizerSceneGLES2::_setup_light_type(LightInstance *p_light, ShadowAtlas
if (!state.render_no_shadows && shadow_atlas && p_light->light_ptr->shadow) {
state.scene_shader.set_conditional(SceneShaderGLES2::USE_SHADOW, true);
glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 3);
if (storage->config.use_rgba_3d_shadows) {
glBindTexture(GL_TEXTURE_2D, shadow_atlas->color);
} else {
glBindTexture(GL_TEXTURE_2D, shadow_atlas->depth);
}
state.scene_shader.set_conditional(SceneShaderGLES2::SHADOW_MODE_PCF_5, shadow_filter_mode == SHADOW_FILTER_PCF5);
state.scene_shader.set_conditional(SceneShaderGLES2::SHADOW_MODE_PCF_13, shadow_filter_mode == SHADOW_FILTER_PCF13);
}
@ -2041,7 +2094,7 @@ void RasterizerSceneGLES2::_setup_refprobes(ReflectionProbeInstance *p_refprobe1
state.scene_shader.set_uniform(SceneShaderGLES2::REFPROBE2_USE_BOX_PROJECT, p_refprobe2->probe_ptr->box_projection);
state.scene_shader.set_uniform(SceneShaderGLES2::REFPROBE2_BOX_EXTENTS, p_refprobe2->probe_ptr->extents);
state.scene_shader.set_uniform(SceneShaderGLES2::REFPROBE2_BOX_OFFSET, p_refprobe2->probe_ptr->origin_offset);
state.scene_shader.set_uniform(SceneShaderGLES2::REFPROBE2_EXTERIOR, !p_refprobe2->probe_ptr->interior);
state.scene_shader.set_uniform(SceneShaderGLES2::REFPROBE2_EXTERIOR, p_refprobe2->probe_ptr->interior);
state.scene_shader.set_uniform(SceneShaderGLES2::REFPROBE2_INTENSITY, p_refprobe2->probe_ptr->intensity);
Color ambient;
@ -2737,6 +2790,7 @@ void RasterizerSceneGLES2::render_scene(const Transform &p_cam_transform, const
if (probe_interior) {
env_radiance_tex = 0; //do not use radiance texture on interiors
state.default_ambient = Color(0, 0, 0, 1); //black as default ambient for interior
}
// render opaque things first
@ -2994,7 +3048,9 @@ void RasterizerSceneGLES2::render_shadow(RID p_light, RID p_shadow_atlas, int p_
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glDepthMask(GL_TRUE);
if (!storage->config.use_rgba_3d_shadows) {
glColorMask(0, 0, 0, 0);
}
if (custom_vp_size) {
glViewport(0, 0, custom_vp_size, custom_vp_size);
@ -3070,7 +3126,9 @@ void RasterizerSceneGLES2::render_shadow(RID p_light, RID p_shadow_atlas, int p_
if (storage->frame.current_rt) {
glViewport(0, 0, storage->frame.current_rt->width, storage->frame.current_rt->height);
}
if (!storage->config.use_rgba_3d_shadows) {
glColorMask(1, 1, 1, 1);
}
}
void RasterizerSceneGLES2::set_scene_pass(uint64_t p_pass) {
@ -3108,6 +3166,16 @@ bool RasterizerSceneGLES2::free(RID p_rid) {
ReflectionProbeInstance *reflection_instance = reflection_probe_instance_owner.get(p_rid);
for (int i = 0; i < 6; i++) {
glDeleteFramebuffers(1, &reflection_instance->fbo[i]);
glDeleteTextures(1, &reflection_instance->color[i]);
}
if (reflection_instance->cubemap != 0) {
glDeleteTextures(1, &reflection_instance->cubemap);
}
glDeleteTextures(1, &reflection_instance->depth);
reflection_probe_release_atlas_index(p_rid);
reflection_probe_instance_owner.free(p_rid);
memdelete(reflection_instance);
@ -3124,6 +3192,8 @@ void RasterizerSceneGLES2::set_debug_draw_mode(VS::ViewportDebugDraw p_debug_dra
void RasterizerSceneGLES2::initialize() {
state.scene_shader.init();
state.scene_shader.set_conditional(SceneShaderGLES2::USE_RGBA_SHADOWS, storage->config.use_rgba_3d_shadows);
state.cube_to_dp_shader.init();
render_list.init();
@ -3202,6 +3272,7 @@ void RasterizerSceneGLES2::initialize() {
glBindTexture(GL_TEXTURE_CUBE_MAP, cube.cubemap);
for (int i = 0; i < 6; i++) {
glTexImage2D(_cube_side_enum[i], 0, GL_DEPTH_COMPONENT, cube_size, cube_size, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL);
}
@ -3245,6 +3316,17 @@ void RasterizerSceneGLES2::initialize() {
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, directional_shadow.depth, 0);
if (storage->config.use_rgba_3d_shadows) {
glGenTextures(1, &directional_shadow.color);
glBindTexture(GL_TEXTURE_2D, directional_shadow.color);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, directional_shadow.size, directional_shadow.size, 0, GL_RGBA, GL_UNSIGNED_BYTE, 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, directional_shadow.color, 0);
}
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) {
ERR_PRINT("Directional shadow framebuffer status invalid");

View File

@ -256,6 +256,7 @@ public:
GLuint fbo;
GLuint depth;
GLuint color;
Map<RID, uint32_t> shadow_owners;
};
@ -279,6 +280,7 @@ public:
struct DirectionalShadow {
GLuint fbo;
GLuint depth;
GLuint color;
int light_count;
int size;
@ -311,10 +313,9 @@ public:
int reflection_index;
GLuint fbo[6];
GLuint cubemap;
GLuint color[6];
GLuint depth;
GLuint fbo_blur;
GLuint cubemap;
int current_resolution;
mutable bool dirty;

View File

@ -1026,18 +1026,8 @@ void RasterizerStorageGLES2::sky_set_texture(RID p_sky, RID p_panorama, int p_ra
glGenTextures(1, &sky->radiance);
glBindTexture(GL_TEXTURE_CUBE_MAP, sky->radiance);
// Now we create a new framebuffer. The new cubemap images will be used as
// attachements for it, so we can fill them by issuing draw calls.
GLuint tmp_fb;
int size = p_radiance_size / 4; //divide by four because its a cubemap (this is an approximation because GLES3 uses a dual paraboloid)
int lod = 0;
int mipmaps = 6;
int mm_level = mipmaps;
GLenum internal_format = GL_RGB;
GLenum format = GL_RGB;
GLenum type = GL_UNSIGNED_BYTE;
@ -1050,6 +1040,12 @@ void RasterizerStorageGLES2::sky_set_texture(RID p_sky, RID p_panorama, int p_ra
}
glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
//no filters for now
glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
#else
while (size >= 1) {
@ -1063,39 +1059,50 @@ void RasterizerStorageGLES2::sky_set_texture(RID p_sky, RID p_panorama, int p_ra
}
#endif
//framebuffer
glGenFramebuffers(1, &tmp_fb);
glBindFramebuffer(GL_FRAMEBUFFER, tmp_fb);
shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES2::USE_SOURCE_PANORAMA, texture->target == GL_TEXTURE_2D);
shaders.cubemap_filter.bind();
lod = 0;
mm_level = mipmaps;
glBindFramebuffer(GL_FRAMEBUFFER, resources.mipmap_blur_fbo);
int mipmaps = 6;
int lod = 0;
int mm_level = mipmaps;
size = p_radiance_size;
shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES2::USE_SOURCE_PANORAMA, true);
shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES2::USE_DIRECT_WRITE, true);
shaders.cubemap_filter.bind();
// now render to the framebuffer, mipmap level for mipmap level
// third, render to the framebuffer using separate textures, then copy to mipmaps
while (size >= 1) {
//make framebuffer size the texture size, need to use a separate texture for compatibility
glActiveTexture(GL_TEXTURE3);
glBindTexture(GL_TEXTURE_2D, resources.mipmap_blur_color);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, size, size, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, resources.mipmap_blur_color, 0);
if (lod == 1) {
//bind panorama for smaller lods
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_CUBE_MAP, sky->radiance);
shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES2::USE_SOURCE_PANORAMA, false);
shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES2::USE_DIRECT_WRITE, false);
shaders.cubemap_filter.bind();
}
glViewport(0, 0, size, size);
bind_quad_array();
glActiveTexture(GL_TEXTURE2); //back to panorama
for (int i = 0; i < 6; i++) {
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, _cube_side_enum[i], sky->radiance, lod);
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) {
break; //may be too small
}
glViewport(0, 0, size, size);
bind_quad_array();
shaders.cubemap_filter.set_uniform(CubemapFilterShaderGLES2::FACE_ID, i);
float roughness = mm_level ? lod / (float)(mipmaps - 1) : 1;
float roughness = mm_level >= 0 ? lod / (float)(mipmaps - 1) : 1;
roughness = MIN(1.0, roughness); //keep max at 1
shaders.cubemap_filter.set_uniform(CubemapFilterShaderGLES2::ROUGHNESS, roughness);
shaders.cubemap_filter.set_uniform(CubemapFilterShaderGLES2::Z_FLIP, false);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glCopyTexImage2D(_cube_side_enum[i], lod, GL_RGB, 0, 0, size, size, 0);
}
size >>= 1;
@ -1105,16 +1112,28 @@ void RasterizerStorageGLES2::sky_set_texture(RID p_sky, RID p_panorama, int p_ra
lod++;
}
shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES2::USE_SOURCE_PANORAMA, false);
shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES2::USE_DIRECT_WRITE, false);
// restore ranges
glActiveTexture(GL_TEXTURE2); //back to panorama
glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glBindTexture(GL_TEXTURE_2D, 0);
glActiveTexture(GL_TEXTURE3); //back to panorama
glBindTexture(GL_TEXTURE_2D, 0);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, 0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, 0);
// Framebuffer did its job. thank mr framebuffer
glActiveTexture(GL_TEXTURE0); //back to panorama
glBindFramebuffer(GL_FRAMEBUFFER, RasterizerStorageGLES2::system_fbo);
glDeleteFramebuffers(1, &tmp_fb);
}
/* SHADER API */
@ -2438,7 +2457,6 @@ AABB RasterizerStorageGLES2::mesh_surface_get_aabb(RID p_mesh, int p_surface) co
}
Vector<PoolVector<uint8_t> > RasterizerStorageGLES2::mesh_surface_get_blend_shapes(RID p_mesh, int p_surface) const {
WARN_PRINT("GLES2 mesh_surface_get_blend_shapes is not implemented");
return Vector<PoolVector<uint8_t> >();
}
Vector<AABB> RasterizerStorageGLES2::mesh_surface_get_skeleton_aabb(RID p_mesh, int p_surface) const {
@ -3662,6 +3680,7 @@ RID RasterizerStorageGLES2::reflection_probe_create() {
reflection_probe->intensity = 1.0;
reflection_probe->interior_ambient = Color();
reflection_probe->interior_ambient_energy = 1.0;
reflection_probe->interior_ambient_probe_contrib = 0.0;
reflection_probe->max_distance = 0;
reflection_probe->extents = Vector3(1, 1, 1);
reflection_probe->origin_offset = Vector3(0, 0, 0);
@ -3747,6 +3766,7 @@ void RasterizerStorageGLES2::reflection_probe_set_as_interior(RID p_probe, bool
ERR_FAIL_COND(!reflection_probe);
reflection_probe->interior = p_enable;
reflection_probe->instance_change_notify(true, false);
}
void RasterizerStorageGLES2::reflection_probe_set_enable_box_projection(RID p_probe, bool p_enable) {
@ -4962,8 +4982,10 @@ void RasterizerStorageGLES2::initialize() {
#endif
#ifdef GLES_OVER_GL
config.use_rgba_2d_shadows = false;
config.use_rgba_3d_shadows = false;
#else
config.use_rgba_2d_shadows = !(config.float_texture_supported && config.extensions.has("GL_EXT_texture_rg"));
config.use_rgba_3d_shadows = config.extensions.has("GL_OES_depth_texture");
#endif
#ifdef GLES_OVER_GL
@ -4974,6 +4996,8 @@ void RasterizerStorageGLES2::initialize() {
#ifdef GLES_OVER_GL
config.support_write_depth = true;
#elif defined(JAVASCRIPT_ENABLED)
config.support_write_depth = false;
#else
config.support_write_depth = config.extensions.has("GL_EXT_frag_depth");
#endif
@ -4995,7 +5019,7 @@ void RasterizerStorageGLES2::initialize() {
shaders.copy.init();
shaders.cubemap_filter.init();
bool ggx_hq = GLOBAL_GET("rendering/quality/reflections/high_quality_ggx.mobile");
bool ggx_hq = GLOBAL_GET("rendering/quality/reflections/high_quality_ggx");
shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES2::LOW_QUALITY, !ggx_hq);
{
@ -5115,10 +5139,20 @@ void RasterizerStorageGLES2::initialize() {
}
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, 512, 1, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, radical_inverse);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); //need this for proper sampling
glBindTexture(GL_TEXTURE_2D, 0);
}
{
glGenFramebuffers(1, &resources.mipmap_blur_fbo);
glGenTextures(1, &resources.mipmap_blur_color);
}
#ifdef GLES_OVER_GL
//this needs to be enabled manually in OpenGL 2.1

View File

@ -78,6 +78,7 @@ public:
bool force_vertex_shading;
bool use_rgba_2d_shadows;
bool use_rgba_3d_shadows;
bool support_32_bits_indices;
bool support_write_depth;
@ -92,6 +93,9 @@ public:
GLuint normal_tex;
GLuint aniso_tex;
GLuint mipmap_blur_fbo;
GLuint mipmap_blur_color;
GLuint radical_inverse_vdc_cache_tex;
bool use_rgba_2d_shadows;

View File

@ -187,6 +187,18 @@ void main() {
vec2 uv = (uv_interp * 2.0) - 1.0;
vec3 N = texelCoordToVec(uv, face_id);
#ifdef USE_DIRECT_WRITE
#ifdef USE_SOURCE_PANORAMA
gl_FragColor = vec4(texturePanorama(source_panorama, N).rgb, 1.0);
#else
gl_FragColor = vec4(textureCube(source_cube,N).rgb, 1.0);
#endif //USE_SOURCE_PANORAMA
#else
vec4 sum = vec4(0.0);
for (int sample_num = 0; sample_num < SAMPLE_COUNT; sample_num++) {
@ -221,4 +233,5 @@ void main() {
sum.rgb = mix((vec3(1.0) + a) * pow(sum.rgb, vec3(1.0 / 2.4)) - a, 12.92 * sum.rgb, vec3(lessThan(sum.rgb, vec3(0.0031308))));
gl_FragColor = vec4(sum.rgb, 1.0);
#endif
}

View File

@ -96,6 +96,10 @@ uniform float light_normal_bias;
// varyings
//
#if defined(RENDER_DEPTH) && defined(USE_RGBA_SHADOWS)
varying highp vec4 position_interp;
#endif
varying highp vec3 vertex_interp;
varying vec3 normal_interp;
@ -355,7 +359,7 @@ void main() {
uv2_interp = uv2_attrib;
#endif
#ifdef OVERRIDE_POSITION
#if defined(OVERRIDE_POSITION)
highp vec4 position;
#endif
@ -647,11 +651,16 @@ VERTEX_SHADER_CODE
#endif //use vertex lighting
#ifdef OVERRIDE_POSITION
#if defined(OVERRIDE_POSITION)
gl_Position = position;
#else
gl_Position = projection_matrix * vec4(vertex_interp, 1.0);
#endif
#if defined(RENDER_DEPTH) && defined(USE_RGBA_SHADOWS)
position_interp = gl_Position;
#endif
}
/* clang-format off */
@ -975,6 +984,10 @@ uniform vec4 light_clamp;
// varyings
//
#if defined(RENDER_DEPTH) && defined(USE_RGBA_SHADOWS)
varying highp vec4 position_interp;
#endif
varying highp vec3 vertex_interp;
varying vec3 normal_interp;
@ -1335,8 +1348,21 @@ LIGHT_SHADER_CODE
#ifdef USE_SHADOW
#define SAMPLE_SHADOW_TEXEL(p_shadow, p_pos, p_depth) step(p_depth, texture2D(p_shadow, p_pos).r)
#define SAMPLE_SHADOW_TEXEL_PROJ(p_shadow, p_pos) step(p_pos.z, texture2DProj(p_shadow, p_pos).r)
#ifdef USE_RGBA_SHADOWS
#define SHADOW_DEPTH(m_val) dot(m_val, vec4(1.0 / (256.0 * 256.0 * 256.0), 1.0 / (256.0 * 256.0), 1.0 / 256.0, 1.0))
#else
#define SHADOW_DEPTH(m_val) (m_val).r
#endif
#define SAMPLE_SHADOW_TEXEL(p_shadow, p_pos, p_depth) step(p_depth, SHADOW_DEPTH(texture2D(p_shadow, p_pos)))
#define SAMPLE_SHADOW_TEXEL_PROJ(p_shadow, p_pos) step(p_pos.z, SHADOW_DEPTH(texture2DProj(p_shadow, p_pos)))
float sample_shadow(highp sampler2D shadow, highp vec4 spos) {
@ -2105,5 +2131,15 @@ FRAGMENT_SHADER_CODE
#endif // defined(FOG_DEPTH_ENABLED) || defined(FOG_HEIGHT_ENABLED)
#endif // not RENDER_DEPTH
#else // not RENDER_DEPTH
//depth render
#ifdef USE_RGBA_SHADOWS
highp float depth = ((position_interp.z / position_interp.w) + 1.0) * 0.5 + 0.0; // bias
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.0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0);
gl_FragColor = comp;
#endif
#endif
}

View File

@ -5522,6 +5522,7 @@ void RasterizerStorageGLES3::reflection_probe_set_as_interior(RID p_probe, bool
ERR_FAIL_COND(!reflection_probe);
reflection_probe->interior = p_enable;
reflection_probe->instance_change_notify(true, false);
}
void RasterizerStorageGLES3::reflection_probe_set_enable_box_projection(RID p_probe, bool p_enable) {

View File

@ -721,7 +721,7 @@ void SpatialMaterial::_update_shader() {
code += "\tvec2 base_uv2 = UV2;\n";
}
if (features[FEATURE_DEPTH_MAPPING] && !flags[FLAG_UV1_USE_TRIPLANAR]) { //depthmap not supported with triplanar
if (!VisualServer::get_singleton()->is_low_end() && features[FEATURE_DEPTH_MAPPING] && !flags[FLAG_UV1_USE_TRIPLANAR]) { //depthmap not supported with triplanar
code += "\t{\n";
code += "\t\tvec3 view_dir = normalize(normalize(-VERTEX)*mat3(TANGENT*depth_flip.x,-BINORMAL*depth_flip.y,NORMAL));\n"; // binormal is negative due to mikktspace, flip 'unflips' it ;-)