forked from Ports/perfect_dark
Decals fix
This commit is contained in:
1 parent
256323a109
commit
60989596d5
3 files changed
+193
-1
No files matched your search
+157
-1
@@ -33,6 +33,10 @@ struct ShaderProgram {
|
||||
GLint frame_count_location;
|
||||
GLint noise_scale_location;
|
||||
GLint three_point_filter_locations[2];
|
||||
GLint decal_mode_location;
|
||||
GLint decal_depth_location;
|
||||
int decal_mode;
|
||||
float decal_depth[2];
|
||||
};
|
||||
|
||||
struct Framebuffer {
|
||||
@@ -40,6 +44,8 @@ struct Framebuffer {
|
||||
bool has_depth_buffer;
|
||||
uint32_t msaa_level;
|
||||
bool invert_y;
|
||||
bool depth_copy_checked;
|
||||
bool depth_copy_failed;
|
||||
|
||||
GLuint fbo, clrbuf, clrbuf_msaa, rbo;
|
||||
};
|
||||
@@ -58,6 +64,19 @@ static int current_anisotropy_level;
|
||||
static FilteringMode current_filter_mode = FILTER_LINEAR;
|
||||
static MipmapFilteringMode current_mipmap_filter_mode = MIPMAP_LINEAR;
|
||||
static bool current_textures_linear_filter[2] = {false, false};
|
||||
static struct ShaderProgram* current_shader_program;
|
||||
|
||||
// Decal depth proximity: decal pixels are discarded if the surface below them is much further away
|
||||
#define DECAL_DEPTH_TEXTURE_UNIT 2
|
||||
static bool depth_copy_supported;
|
||||
static bool depth_copy_dirty = true;
|
||||
static size_t depth_copy_source;
|
||||
static GLuint depth_copy_fbo, depth_copy_tex;
|
||||
static uint32_t depth_copy_width, depth_copy_height;
|
||||
static bool current_depth_test;
|
||||
static bool current_depth_decal;
|
||||
static bool decal_projection_valid;
|
||||
static float decal_depth_params[2];
|
||||
|
||||
static int gl_glsl_version = 130;
|
||||
static char gl_glsl_version_str[16] = "130";
|
||||
@@ -120,6 +139,7 @@ static void gfx_opengl_unload_shader(struct ShaderProgram* old_prg) {
|
||||
|
||||
static void gfx_opengl_load_shader(struct ShaderProgram* new_prg) {
|
||||
// if (!new_prg) return;
|
||||
current_shader_program = new_prg;
|
||||
glUseProgram(new_prg->opengl_program_id);
|
||||
gfx_opengl_vertex_array_set_attribs(new_prg);
|
||||
gfx_opengl_set_uniforms(new_prg);
|
||||
@@ -382,6 +402,9 @@ static struct ShaderProgram* gfx_opengl_create_and_load_new_shader(uint64_t shad
|
||||
|
||||
append_line(fs_buf, &fs_len, "uniform int frame_count;");
|
||||
append_line(fs_buf, &fs_len, "uniform float noise_scale;");
|
||||
append_line(fs_buf, &fs_len, "uniform int uDecalMode;");
|
||||
append_line(fs_buf, &fs_len, "uniform highp vec2 uDecalDepth;");
|
||||
append_line(fs_buf, &fs_len, "uniform highp sampler2D uDepthTex;");
|
||||
|
||||
append_line(fs_buf, &fs_len, "float random(in vec3 value) {");
|
||||
append_line(fs_buf, &fs_len, " float random = dot(sin(value), vec3(12.9898, 78.233, 37.719));");
|
||||
@@ -442,6 +465,15 @@ static struct ShaderProgram* gfx_opengl_create_and_load_new_shader(uint64_t shad
|
||||
|
||||
append_line(fs_buf, &fs_len, "void main() {");
|
||||
|
||||
// Window depth is c0 + c1 / w, so the stored depth can be converted back to the clip-space W of the surface.
|
||||
// Reject decal pixels that hang past an edge over a surface that is further away than a small tolerance.
|
||||
append_line(fs_buf, &fs_len, " if (uDecalMode != 0) {");
|
||||
append_line(fs_buf, &fs_len, " highp float fragW = 1.0 / gl_FragCoord.w;");
|
||||
append_line(fs_buf, &fs_len, " highp float surfaceZ = texelFetch(uDepthTex, ivec2(gl_FragCoord.xy), 0).r;");
|
||||
append_line(fs_buf, &fs_len, " highp float surfaceW = uDecalDepth.y / (surfaceZ - uDecalDepth.x);");
|
||||
append_line(fs_buf, &fs_len, " if (surfaceW - fragW > fragW * 0.03 + 2.0 * fwidth(fragW)) discard;");
|
||||
append_line(fs_buf, &fs_len, " }");
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
if (cc_features.used_textures[i]) {
|
||||
bool s = cc_features.clamp[i][0], t = cc_features.clamp[i][1];
|
||||
@@ -652,6 +684,14 @@ static struct ShaderProgram* gfx_opengl_create_and_load_new_shader(uint64_t shad
|
||||
prg->noise_scale_location = glGetUniformLocation(shader_program, "noise_scale");
|
||||
prg->three_point_filter_locations[0] = glGetUniformLocation(shader_program, "three_point_filter0");
|
||||
prg->three_point_filter_locations[1] = glGetUniformLocation(shader_program, "three_point_filter1");
|
||||
prg->decal_mode_location = glGetUniformLocation(shader_program, "uDecalMode");
|
||||
prg->decal_depth_location = glGetUniformLocation(shader_program, "uDecalDepth");
|
||||
prg->decal_mode = 0;
|
||||
prg->decal_depth[0] = prg->decal_depth[1] = 0.0f;
|
||||
if (prg->decal_mode_location >= 0) {
|
||||
glUniform1i(prg->decal_mode_location, 0);
|
||||
glUniform1i(glGetUniformLocation(shader_program, "uDepthTex"), DECAL_DEPTH_TEXTURE_UNIT);
|
||||
}
|
||||
|
||||
gfx_opengl_load_shader(prg);
|
||||
|
||||
@@ -671,6 +711,7 @@ static void gfx_opengl_shader_get_info(struct ShaderProgram* prg, uint8_t* num_i
|
||||
|
||||
static void gfx_opengl_clear_shaders(void) {
|
||||
glUseProgram(0);
|
||||
current_shader_program = nullptr;
|
||||
for (auto& pair : shader_program_pool) {
|
||||
glDeleteProgram(pair.second.opengl_program_id);
|
||||
}
|
||||
@@ -741,6 +782,9 @@ static void gfx_opengl_set_sampler_parameters(int tile, bool linear_filter, uint
|
||||
}
|
||||
|
||||
static void gfx_opengl_set_depth_mode(bool depth_test, bool depth_update, bool depth_compare, bool depth_source_prim, uint16_t zmode) {
|
||||
current_depth_test = depth_test;
|
||||
current_depth_decal = depth_test && depth_compare && zmode == ZMODE_DEC;
|
||||
|
||||
if (depth_test) {
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glDepthMask(depth_update ? GL_TRUE : GL_FALSE);
|
||||
@@ -810,10 +854,113 @@ static void gfx_opengl_set_use_alpha(bool use_alpha, bool modulate) {
|
||||
}
|
||||
}
|
||||
|
||||
static void gfx_opengl_set_decal_projection(bool valid, float alpha, float beta) {
|
||||
// Clip-space Z is alpha * W + beta; convert that to window depth, including the no-depth-clamp Z scale hack
|
||||
const float zscale = GLAD_GL_ARB_depth_clamp ? 1.0f : 0.3f;
|
||||
decal_projection_valid = valid;
|
||||
decal_depth_params[0] = 0.5f + 0.5f * zscale * alpha;
|
||||
decal_depth_params[1] = 0.5f * zscale * beta;
|
||||
}
|
||||
|
||||
static bool gfx_opengl_update_depth_copy(void) {
|
||||
if (!depth_copy_supported) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Framebuffer& fb = framebuffers[current_framebuffer];
|
||||
if ((current_framebuffer != 0 && !fb.has_depth_buffer) || fb.depth_copy_failed) {
|
||||
return false;
|
||||
}
|
||||
if (!depth_copy_dirty && depth_copy_source == current_framebuffer && depth_copy_width == fb.width &&
|
||||
depth_copy_height == fb.height) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (depth_copy_tex == 0 || depth_copy_width != fb.width || depth_copy_height != fb.height) {
|
||||
if (depth_copy_tex == 0) {
|
||||
glGenTextures(1, &depth_copy_tex);
|
||||
glGenFramebuffers(1, &depth_copy_fbo);
|
||||
}
|
||||
|
||||
GLint active_texture;
|
||||
glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);
|
||||
glActiveTexture(GL_TEXTURE0 + DECAL_DEPTH_TEXTURE_UNIT);
|
||||
glBindTexture(GL_TEXTURE_2D, depth_copy_tex);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, fb.width, fb.height, 0, GL_DEPTH_STENCIL,
|
||||
GL_UNSIGNED_INT_24_8, NULL);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glActiveTexture(active_texture);
|
||||
|
||||
const GLenum no_draw_buffer = GL_NONE;
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, depth_copy_fbo);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, depth_copy_tex, 0);
|
||||
glDrawBuffers(1, &no_draw_buffer);
|
||||
glReadBuffer(GL_NONE);
|
||||
|
||||
depth_copy_width = fb.width;
|
||||
depth_copy_height = fb.height;
|
||||
}
|
||||
|
||||
const bool check_errors = !fb.depth_copy_checked;
|
||||
if (check_errors) {
|
||||
for (int i = 0; i < 16 && glGetError() != GL_NO_ERROR; i++) {
|
||||
}
|
||||
}
|
||||
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, fb.fbo);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, depth_copy_fbo);
|
||||
glBlitFramebuffer(0, 0, fb.width, fb.height, 0, 0, fb.width, fb.height, GL_DEPTH_BUFFER_BIT, GL_NEAREST);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fb.fbo);
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
|
||||
if (check_errors) {
|
||||
fb.depth_copy_checked = true;
|
||||
if (glGetError() != GL_NO_ERROR) {
|
||||
fb.depth_copy_failed = true;
|
||||
sysLogPrintf(LOG_WARNING, "GL: could not copy depth buffer of framebuffer %u, decal edges will not be clipped",
|
||||
(unsigned)current_framebuffer);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
depth_copy_dirty = false;
|
||||
depth_copy_source = current_framebuffer;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void gfx_opengl_set_decal_uniforms(struct ShaderProgram* prg, bool enabled) {
|
||||
if (prg == nullptr || prg->decal_mode_location < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int mode = enabled ? 1 : 0;
|
||||
if (prg->decal_mode != mode) {
|
||||
glUniform1i(prg->decal_mode_location, mode);
|
||||
prg->decal_mode = mode;
|
||||
}
|
||||
if (enabled &&
|
||||
(prg->decal_depth[0] != decal_depth_params[0] || prg->decal_depth[1] != decal_depth_params[1])) {
|
||||
glUniform2f(prg->decal_depth_location, decal_depth_params[0], decal_depth_params[1]);
|
||||
prg->decal_depth[0] = decal_depth_params[0];
|
||||
prg->decal_depth[1] = decal_depth_params[1];
|
||||
}
|
||||
}
|
||||
|
||||
static void gfx_opengl_draw_triangles(float buf_vbo[], size_t buf_vbo_len, size_t buf_vbo_num_tris) {
|
||||
// printf("flushing %d tris\n", buf_vbo_num_tris);
|
||||
const bool decal = current_depth_decal && decal_projection_valid && gfx_opengl_update_depth_copy();
|
||||
gfx_opengl_set_decal_uniforms(current_shader_program, decal);
|
||||
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * buf_vbo_len, buf_vbo, GL_STREAM_DRAW);
|
||||
glDrawArrays(GL_TRIANGLES, 0, 3 * buf_vbo_num_tris);
|
||||
|
||||
if (current_depth_test && current_depth_mask) {
|
||||
depth_copy_dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
typedef void (APIENTRY *DEBUGPROC)(GLenum source,
|
||||
@@ -990,6 +1137,11 @@ static void gfx_opengl_init(void) {
|
||||
gfx_framebuffers_enabled = false;
|
||||
}
|
||||
|
||||
depth_copy_supported = GLVersion.major >= 3 && gfx_opengl_supports_framebuffers() && glDrawBuffers && glReadBuffer;
|
||||
if (!depth_copy_supported) {
|
||||
sysLogPrintf(LOG_WARNING, "GL: depth buffer copies unsupported, decal edges will not be clipped");
|
||||
}
|
||||
|
||||
if ((GLVersion.major < 4 || GLVersion.minor < 4) && !GLAD_GL_ARB_texture_mirror_clamp_to_edge) {
|
||||
// GL_MIRROR_CLAMP_TO_EDGE unsupported
|
||||
gl_mirror_clamp = GL_MIRRORED_REPEAT;
|
||||
@@ -1138,6 +1290,7 @@ static void gfx_opengl_update_framebuffer_parameters(int fb_id, uint32_t width,
|
||||
fb.has_depth_buffer = has_depth_buffer;
|
||||
fb.msaa_level = msaa_level;
|
||||
fb.invert_y = opengl_invert_y;
|
||||
depth_copy_dirty = true;
|
||||
}
|
||||
|
||||
bool gfx_opengl_start_draw_to_framebuffer(int fb_id, float noise_scale) {
|
||||
@@ -1148,6 +1301,7 @@ bool gfx_opengl_start_draw_to_framebuffer(int fb_id, float noise_scale) {
|
||||
}
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fb.fbo);
|
||||
current_framebuffer = fb_id;
|
||||
depth_copy_dirty = true;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
@@ -1165,6 +1319,7 @@ void gfx_opengl_clear_framebuffer(bool clear_color, bool clear_depth) {
|
||||
if (clear_depth) {
|
||||
glDepthMask(GL_TRUE);
|
||||
mask |= GL_DEPTH_BUFFER_BIT;
|
||||
depth_copy_dirty = true;
|
||||
}
|
||||
glClear(mask);
|
||||
if (clear_depth) {
|
||||
@@ -1317,5 +1472,6 @@ struct GfxRenderingAPI gfx_opengl_api = {
|
||||
gfx_opengl_get_texture_filter,
|
||||
gfx_opengl_set_mipmap_filter,
|
||||
gfx_opengl_set_anisotropy_level,
|
||||
gfx_opengl_get_max_anisotropy_level
|
||||
gfx_opengl_get_max_anisotropy_level,
|
||||
gfx_opengl_set_decal_projection
|
||||
};
|
||||
@@ -204,6 +204,8 @@ static struct RenderingState {
|
||||
struct XYWidthHeight viewport, scissor;
|
||||
struct ShaderProgram* shader_program;
|
||||
TextureCacheNode* textures[SHADER_MAX_TEXTURES];
|
||||
bool decal_projection_valid;
|
||||
float decal_projection[2];
|
||||
} rendering_state;
|
||||
|
||||
struct GfxDimensions gfx_current_window_dimensions;
|
||||
@@ -1274,6 +1276,39 @@ static void gfx_sp_tri1(uint8_t vtx1_idx, uint8_t vtx2_idx, uint8_t vtx3_idx, bo
|
||||
rendering_state.depth_mode = depth_mode;
|
||||
}
|
||||
|
||||
if (depth_test && depth_compare && zmode == ZMODE_DEC) {
|
||||
// For perspective projections the clip-space Z column is alpha * W column + beta,
|
||||
// which lets the renderer turn stored depth back into distance for decal proximity tests
|
||||
int k = 0;
|
||||
for (int i = 1; i < 3; i++) {
|
||||
if (fabsf(rsp.P_matrix[i][3]) > fabsf(rsp.P_matrix[k][3])) {
|
||||
k = i;
|
||||
}
|
||||
}
|
||||
|
||||
bool valid = fabsf(rsp.P_matrix[k][3]) > 1e-6f;
|
||||
float alpha = 0.0f, beta = 0.0f;
|
||||
if (valid) {
|
||||
alpha = rsp.P_matrix[k][2] / rsp.P_matrix[k][3];
|
||||
beta = rsp.P_matrix[3][2] - alpha * rsp.P_matrix[3][3];
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (fabsf(rsp.P_matrix[i][2] - alpha * rsp.P_matrix[i][3]) > 1e-4f * (1.0f + fabsf(rsp.P_matrix[i][2]))) {
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
valid = valid && fabsf(beta) > 1e-6f;
|
||||
}
|
||||
|
||||
if (valid != rendering_state.decal_projection_valid || alpha != rendering_state.decal_projection[0] ||
|
||||
beta != rendering_state.decal_projection[1]) {
|
||||
gfx_flush();
|
||||
gfx_rapi->set_decal_projection(valid, alpha, beta);
|
||||
rendering_state.decal_projection_valid = valid;
|
||||
rendering_state.decal_projection[0] = alpha;
|
||||
rendering_state.decal_projection[1] = beta;
|
||||
}
|
||||
}
|
||||
|
||||
if (rdp.viewport_or_scissor_changed) {
|
||||
if (memcmp(&rdp.viewport, &rendering_state.viewport, sizeof(rdp.viewport)) != 0) {
|
||||
gfx_flush();
|
||||
|
||||
@@ -56,6 +56,7 @@ struct GfxRenderingAPI {
|
||||
void (*set_mipmap_filter)(enum MipmapFilteringMode mode);
|
||||
void (*set_anisotropy_level)(int);
|
||||
int (*get_max_anisotropy_level)(void);
|
||||
void (*set_decal_projection)(bool valid, float alpha, float beta);
|
||||
};
|
||||
|
||||
#endif
|
||||
Reference in new issue
Block a user