Merge pull request #41254 from lawnjelly/gles3_nvidia_normalrotation

GLES3 fix normal map flipping with nvidia workaround
This commit is contained in:
Rémi Verschelde 2020-09-28 10:40:22 +02:00 committed by GitHub
commit cd05197fb3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 153 additions and 12 deletions

View file

@ -170,6 +170,7 @@ void RasterizerCanvasGLES3::canvas_begin() {
state.canvas_shader.set_conditional(CanvasShaderGLES3::SHADOW_FILTER_PCF13, false);
state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_DISTANCE_FIELD, false);
state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_NINEPATCH, false);
state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_LIGHT_ANGLE, false);
state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_SKELETON, false);
state.canvas_shader.set_custom_shader(0);
@ -191,6 +192,7 @@ void RasterizerCanvasGLES3::canvas_begin() {
glBindVertexArray(data.canvas_quad_array);
state.using_texture_rect = true;
state.using_ninepatch = false;
state.using_light_angle = false;
state.using_skeleton = false;
}
@ -204,6 +206,7 @@ void RasterizerCanvasGLES3::canvas_end() {
state.using_texture_rect = false;
state.using_ninepatch = false;
state.using_light_angle = false;
}
RasterizerStorageGLES3::Texture *RasterizerCanvasGLES3::_bind_canvas_texture(const RID &p_texture, const RID &p_normal_map, bool p_force) {
@ -288,9 +291,9 @@ RasterizerStorageGLES3::Texture *RasterizerCanvasGLES3::_bind_canvas_texture(con
return tex_return;
}
void RasterizerCanvasGLES3::_set_texture_rect_mode(bool p_enable, bool p_ninepatch) {
void RasterizerCanvasGLES3::_set_texture_rect_mode(bool p_enable, bool p_ninepatch, bool p_light_angle) {
if (state.using_texture_rect == p_enable && state.using_ninepatch == p_ninepatch)
if (state.using_texture_rect == p_enable && state.using_ninepatch == p_ninepatch && state.using_light_angle == p_light_angle)
return;
if (p_enable) {
@ -304,6 +307,7 @@ void RasterizerCanvasGLES3::_set_texture_rect_mode(bool p_enable, bool p_ninepat
state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_NINEPATCH, p_ninepatch && p_enable);
state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_TEXTURE_RECT, p_enable);
state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_LIGHT_ANGLE, p_light_angle);
state.canvas_shader.bind();
state.canvas_shader.set_uniform(CanvasShaderGLES3::FINAL_MODULATE, state.canvas_item_modulate);
state.canvas_shader.set_uniform(CanvasShaderGLES3::MODELVIEW_MATRIX, state.final_transform);
@ -319,6 +323,7 @@ void RasterizerCanvasGLES3::_set_texture_rect_mode(bool p_enable, bool p_ninepat
}
state.using_texture_rect = p_enable;
state.using_ninepatch = p_ninepatch;
state.using_light_angle = p_light_angle;
}
void RasterizerCanvasGLES3::_draw_polygon(const int *p_indices, int p_index_count, int p_vertex_count, const Vector2 *p_vertices, const Vector2 *p_uvs, const Color *p_colors, bool p_singlecolor, const int *p_bones, const float *p_weights) {
@ -548,7 +553,7 @@ void RasterizerCanvasGLES3::_draw_generic_indices(GLuint p_primitive, const int
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
void RasterizerCanvasGLES3::_draw_gui_primitive(int p_points, const Vector2 *p_vertices, const Color *p_colors, const Vector2 *p_uvs) {
void RasterizerCanvasGLES3::_draw_gui_primitive(int p_points, const Vector2 *p_vertices, const Color *p_colors, const Vector2 *p_uvs, const float *p_light_angles) {
static const GLenum prim[5] = { GL_POINTS, GL_POINTS, GL_LINES, GL_TRIANGLES, GL_TRIANGLE_FAN };
@ -557,6 +562,7 @@ void RasterizerCanvasGLES3::_draw_gui_primitive(int p_points, const Vector2 *p_v
int version = 0;
int color_ofs = 0;
int uv_ofs = 0;
int light_angle_ofs = 0;
int stride = 2;
if (p_colors) { //color
@ -571,7 +577,13 @@ void RasterizerCanvasGLES3::_draw_gui_primitive(int p_points, const Vector2 *p_v
stride += 2;
}
float b[(2 + 2 + 4) * 4];
if (p_light_angles) { //light_angles
version |= 4;
light_angle_ofs = stride;
stride += 1;
}
float b[(2 + 2 + 4 + 1) * 4];
for (int i = 0; i < p_points; i++) {
b[stride * i + 0] = p_vertices[i].x;
@ -596,6 +608,13 @@ void RasterizerCanvasGLES3::_draw_gui_primitive(int p_points, const Vector2 *p_v
}
}
if (p_light_angles) {
for (int i = 0; i < p_points; i++) {
b[stride * i + light_angle_ofs] = p_light_angles[i];
}
}
glBindBuffer(GL_ARRAY_BUFFER, data.polygon_buffer);
#ifndef GLES_OVER_GL
// Orphan the buffer to avoid CPU/GPU sync points caused by glBufferSubData
@ -623,10 +642,19 @@ static const GLenum gl_primitive[] = {
void RasterizerCanvasGLES3::render_rect_nvidia_workaround(const Item::CommandRect *p_rect, const RasterizerStorageGLES3::Texture *p_texture) {
_set_texture_rect_mode(false);
if (p_texture) {
bool send_light_angles = false;
// only need to use light angles when normal mapping
// otherwise we can use the default shader
if (state.current_normal != RID()) {
send_light_angles = true;
}
// we don't want to use texture rect, and we want to send light angles if we are using normal mapping
_set_texture_rect_mode(false, false, send_light_angles);
bool untile = false;
if (p_rect->flags & CANVAS_RECT_TILE && !(p_texture->flags & VS::TEXTURE_FLAG_REPEAT)) {
@ -663,6 +691,10 @@ void RasterizerCanvasGLES3::render_rect_nvidia_workaround(const Item::CommandRec
src_rect.position + Vector2(0.0, src_rect.size.y),
};
// for encoding in light angle
bool flip_h = false;
bool flip_v = false;
if (p_rect->flags & CANVAS_RECT_TRANSPOSE) {
SWAP(uvs[1], uvs[3]);
}
@ -670,13 +702,42 @@ void RasterizerCanvasGLES3::render_rect_nvidia_workaround(const Item::CommandRec
if (p_rect->flags & CANVAS_RECT_FLIP_H) {
SWAP(uvs[0], uvs[1]);
SWAP(uvs[2], uvs[3]);
flip_h = true;
flip_v = !flip_v;
}
if (p_rect->flags & CANVAS_RECT_FLIP_V) {
SWAP(uvs[0], uvs[3]);
SWAP(uvs[1], uvs[2]);
flip_v = !flip_v;
}
_draw_gui_primitive(4, points, NULL, uvs);
if (send_light_angles) {
// for single rects, there is no need to fully utilize the light angle,
// we only need it to encode flips (horz and vert). But the shader can be reused with
// batching in which case the angle encodes the transform as well as
// the flips.
// Note transpose is NYI. I don't think it worked either with the non-nvidia method.
// if horizontal flip, angle is 180
float angle = 0.0f;
if (flip_h)
angle = Math_PI;
// add 1 (to take care of zero floating point error with sign)
angle += 1.0f;
// flip if necessary
if (flip_v)
angle *= -1.0f;
// light angle must be sent for each vert, instead as a single uniform in the uniform draw method
// this has the benefit of enabling batching with light angles.
float light_angles[4] = { angle, angle, angle, angle };
_draw_gui_primitive(4, points, NULL, uvs, light_angles);
} else {
_draw_gui_primitive(4, points, NULL, uvs);
}
if (untile) {
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
@ -684,6 +745,8 @@ void RasterizerCanvasGLES3::render_rect_nvidia_workaround(const Item::CommandRec
}
} else {
_set_texture_rect_mode(false);
state.canvas_shader.set_uniform(CanvasShaderGLES3::CLIP_RECT_UV, false);
Vector2 points[4] = {
@ -2262,7 +2325,7 @@ void RasterizerCanvasGLES3::initialize() {
uint32_t poly_size = GLOBAL_DEF_RST("rendering/limits/buffers/canvas_polygon_buffer_size_kb", 128);
ProjectSettings::get_singleton()->set_custom_property_info("rendering/limits/buffers/canvas_polygon_buffer_size_kb", PropertyInfo(Variant::INT, "rendering/limits/buffers/canvas_polygon_buffer_size_kb", PROPERTY_HINT_RANGE, "0,256,1,or_greater"));
poly_size *= 1024; //kb
poly_size = MAX(poly_size, (2 + 2 + 4) * 4 * sizeof(float));
poly_size = MAX(poly_size, (2 + 2 + 4 + 1) * 4 * sizeof(float));
glGenBuffers(1, &data.polygon_buffer);
glBindBuffer(GL_ARRAY_BUFFER, data.polygon_buffer);
glBufferData(GL_ARRAY_BUFFER, poly_size, NULL, GL_DYNAMIC_DRAW); //allocate max size
@ -2270,13 +2333,14 @@ void RasterizerCanvasGLES3::initialize() {
data.polygon_buffer_size = poly_size;
//quad arrays
for (int i = 0; i < 4; i++) {
for (int i = 0; i < Data::NUM_QUAD_ARRAY_VARIATIONS; i++) {
glGenVertexArrays(1, &data.polygon_buffer_quad_arrays[i]);
glBindVertexArray(data.polygon_buffer_quad_arrays[i]);
glBindBuffer(GL_ARRAY_BUFFER, data.polygon_buffer);
int uv_ofs = 0;
int color_ofs = 0;
int light_angle_ofs = 0;
int stride = 2 * 4;
if (i & 1) { //color
@ -2289,6 +2353,11 @@ void RasterizerCanvasGLES3::initialize() {
stride += 2 * 4;
}
if (i & 4) { //light_angle
light_angle_ofs = stride;
stride += 1 * 4;
}
glEnableVertexAttribArray(VS::ARRAY_VERTEX);
glVertexAttribPointer(VS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, stride, NULL);
@ -2302,6 +2371,12 @@ void RasterizerCanvasGLES3::initialize() {
glVertexAttribPointer(VS::ARRAY_TEX_UV, 2, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(uv_ofs));
}
if (i & 4) {
// reusing tangent for light_angle
glEnableVertexAttribArray(VS::ARRAY_TANGENT);
glVertexAttribPointer(VS::ARRAY_TANGENT, 1, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(light_angle_ofs));
}
glBindVertexArray(0);
}

View file

@ -52,11 +52,13 @@ public:
struct Data {
enum { NUM_QUAD_ARRAY_VARIATIONS = 8 };
GLuint canvas_quad_vertices;
GLuint canvas_quad_array;
GLuint polygon_buffer;
GLuint polygon_buffer_quad_arrays[4];
GLuint polygon_buffer_quad_arrays[NUM_QUAD_ARRAY_VARIATIONS];
GLuint polygon_buffer_pointer_array;
GLuint polygon_index_buffer;
@ -78,6 +80,7 @@ public:
bool using_texture_rect;
bool using_ninepatch;
bool using_light_angle;
RID current_tex;
RID current_normal;
@ -127,10 +130,10 @@ public:
virtual void canvas_begin();
virtual void canvas_end();
_FORCE_INLINE_ void _set_texture_rect_mode(bool p_enable, bool p_ninepatch = false);
_FORCE_INLINE_ void _set_texture_rect_mode(bool p_enable, bool p_ninepatch = false, bool p_light_angle = false);
_FORCE_INLINE_ RasterizerStorageGLES3::Texture *_bind_canvas_texture(const RID &p_texture, const RID &p_normal_map, bool p_force = false);
_FORCE_INLINE_ void _draw_gui_primitive(int p_points, const Vector2 *p_vertices, const Color *p_colors, const Vector2 *p_uvs);
_FORCE_INLINE_ void _draw_gui_primitive(int p_points, const Vector2 *p_vertices, const Color *p_colors, const Vector2 *p_uvs, const float *p_light_angles = nullptr);
_FORCE_INLINE_ void _draw_polygon(const int *p_indices, int p_index_count, int p_vertex_count, const Vector2 *p_vertices, const Vector2 *p_uvs, const Color *p_colors, bool p_singlecolor, const int *p_bones, const float *p_weights);
_FORCE_INLINE_ void _draw_generic(GLuint p_primitive, int p_vertex_count, const Vector2 *p_vertices, const Vector2 *p_uvs, const Color *p_colors, bool p_singlecolor);
_FORCE_INLINE_ void _draw_generic_indices(GLuint p_primitive, const int *p_indices, int p_index_count, int p_vertex_count, const Vector2 *p_vertices, const Vector2 *p_uvs, const Color *p_colors, bool p_singlecolor);

View file

@ -413,6 +413,42 @@ void RasterizerGLES3::register_config() {
ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/filters/anisotropic_filter_level", PropertyInfo(Variant::INT, "rendering/quality/filters/anisotropic_filter_level", PROPERTY_HINT_RANGE, "1,16,1"));
}
// returns NULL if no error, or an error string
const char *RasterizerGLES3::gl_check_for_error(bool p_print_error) {
GLenum err = glGetError();
const char *err_string = nullptr;
switch (err) {
default: {
// not recognised
} break;
case GL_NO_ERROR: {
} break;
case GL_INVALID_ENUM: {
err_string = "GL_INVALID_ENUM";
} break;
case GL_INVALID_VALUE: {
err_string = "GL_INVALID_VALUE";
} break;
case GL_INVALID_OPERATION: {
err_string = "GL_INVALID_OPERATION";
} break;
case GL_INVALID_FRAMEBUFFER_OPERATION: {
err_string = "GL_INVALID_FRAMEBUFFER_OPERATION";
} break;
case GL_OUT_OF_MEMORY: {
err_string = "GL_OUT_OF_MEMORY";
} break;
}
if (p_print_error && err_string) {
print_line(err_string);
}
return err_string;
}
RasterizerGLES3::RasterizerGLES3() {
storage = memnew(RasterizerStorageGLES3);

View file

@ -71,6 +71,8 @@ public:
virtual bool is_low_end() const { return false; }
const char *gl_check_for_error(bool p_print_error = true);
RasterizerGLES3();
~RasterizerGLES3();
};

View file

@ -2,6 +2,11 @@
[vertex]
layout(location = 0) in highp vec2 vertex;
#ifdef USE_LIGHT_ANGLE
layout(location = 2) in highp float light_angle;
#endif
/* clang-format on */
layout(location = 3) in vec4 color_attrib;
@ -248,12 +253,32 @@ VERTEX_SHADER_CODE
pos = outvec.xy;
#endif
#ifdef USE_LIGHT_ANGLE
// we add a fixed offset because we are using the sign later,
// and don't want floating point error around 0.0
float la = abs(light_angle) - 1.0;
// vector light angle
vec4 vla;
vla.xy = vec2(cos(la), sin(la));
vla.zw = vec2(-vla.y, vla.x);
vla.zw *= sign(light_angle);
// apply the transform matrix.
// The rotate will be encoded in the transform matrix for single rects,
// and just the flips in the light angle.
// For batching we will encode the rotation and the flips
// in the light angle, and can use the same shader.
local_rot.xy = normalize((modelview_matrix * (extra_matrix_instance * vec4(vla.xy, 0.0, 0.0))).xy);
local_rot.zw = normalize((modelview_matrix * (extra_matrix_instance * vec4(vla.zw, 0.0, 0.0))).xy);
#else
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 // not using light angle
#endif
}