GLES3 fix normal map flipping with nvidia workaround
When not using TEXTURE_RECT path, flips have to sent via another method to the shader, to ensure that normal maps are correctly adjusted for direction. This PR adds an extra vertex attribute, LIGHT_ANGLE. For nvidia workarounds, where the shader still has access to the final transform and extra matrix, the LIGHT_ANGLE can be 0 (no adjustment), 180 degrees for a horizontal flip, and negative indicates a vertical flip. For batching path, the LIGHT_ANGLE can be used to directly specify the light angle for normal mapping, even when the final transform and extra matrix have been baked into vertex positions, so the same shader can be used for both.
This commit is contained in:
parent
dbb0ad3b59
commit
674327b78f
5 changed files with 153 additions and 12 deletions
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue