diff --git a/src/graphic/Fast3D/gfx_direct3d11.cpp b/src/graphic/Fast3D/gfx_direct3d11.cpp index 811f07733..a0cdfbdd9 100644 --- a/src/graphic/Fast3D/gfx_direct3d11.cpp +++ b/src/graphic/Fast3D/gfx_direct3d11.cpp @@ -42,6 +42,7 @@ namespace { struct PerFrameCB { uint32_t noise_frame; float noise_scale; + float alpha_test_value; uint32_t padding[2]; // constant buffers must be multiples of 16 bytes in size }; @@ -920,7 +921,7 @@ static void gfx_d3d11_update_framebuffer_parameters(int fb_id, uint32_t width, u fb.msaa_level = msaa_level; } -void gfx_d3d11_start_draw_to_framebuffer(int fb_id, float noise_scale) { +void gfx_d3d11_start_draw_to_framebuffer(int fb_id, float noise_scale, float alpha_test_value) { Framebuffer& fb = d3d.framebuffers[fb_id]; d3d.render_target_height = d3d.textures[fb.texture_id].height; @@ -933,6 +934,10 @@ void gfx_d3d11_start_draw_to_framebuffer(int fb_id, float noise_scale) { d3d.per_frame_cb_data.noise_scale = 1.0f / noise_scale; } + if (alpha_test_value != 0.0f) { + d3d.per_frame_cb_data.alpha_test_value = alpha_test_value; + } + D3D11_MAPPED_SUBRESOURCE ms; ZeroMemory(&ms, sizeof(D3D11_MAPPED_SUBRESOURCE)); d3d.context->Map(d3d.per_frame_cb.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &ms); diff --git a/src/graphic/Fast3D/gfx_direct3d_common.cpp b/src/graphic/Fast3D/gfx_direct3d_common.cpp index 003b44c27..b0bb3d16b 100644 --- a/src/graphic/Fast3D/gfx_direct3d_common.cpp +++ b/src/graphic/Fast3D/gfx_direct3d_common.cpp @@ -194,6 +194,24 @@ void gfx_direct3d_common_build_shader(char buf[8192], size_t& len, size_t& num_f append_line(buf, &len, "cbuffer PerFrameCB : register(b0) {"); append_line(buf, &len, " uint noise_frame;"); append_line(buf, &len, " float noise_scale;"); + append_line(buf, &len, " float alpha_test_value;"); + append_line(buf, &len, "}"); + + append_line(buf, &len, "float3 colorDither(in float _noise_val, in float3 _color)"); + append_line(buf, &len, "{"); + append_line(buf, &len, " float3 _noise = float3(_noise_val, _noise_val, _noise_val);"); + append_line(buf, &len, " float3 threshold = 7.0 / 255.0 * (_noise - 0.5);"); + append_line(buf, &len, " _color = clamp(_color + threshold, 0.0, 1.0);"); + append_line(buf, &len, " _color.rgb = round(_color.rgb * 32.0) / 32.0;"); + append_line(buf, &len, " return _color;"); + append_line(buf, &len, "}"); + + append_line(buf, &len, "float alphaDither(in float _noise, in float _alpha)"); + append_line(buf, &len, "{"); + append_line(buf, &len, " float threshold = 7.0 / 255.0 * (_noise - 0.5);"); + append_line(buf, &len, " _alpha = clamp(_alpha + threshold, 0.0, 1.0);"); + append_line(buf, &len, " _alpha = round(_alpha * 32.0) / 32.0;"); + append_line(buf, &len, " return _alpha;"); append_line(buf, &len, "}"); append_line(buf, &len, "float random(in float3 value) {"); @@ -428,14 +446,22 @@ void gfx_direct3d_common_build_shader(char buf[8192], size_t& len, size_t& num_f } if (cc_features.opt_alpha && cc_features.opt_noise) { - append_line(buf, &len, " float2 coords = screenSpace.xy * noise_scale;"); append_line(buf, &len, - " texel.a *= round(saturate(random(float3(floor(coords), noise_frame)) + texel.a - 0.5));"); + " texel.a = alphaDither(random(float3(floor(screenSpace.xy * noise_scale), " + "float(noise_frame))), texel.a);"); + } else if (cc_features.opt_noise) { + append_line( + buf, &len, + " texel.rgb = colorDither(random(float3(floor(screenSpace.xy * noise_scale), float(frame_count))), " + " texel.rgb);"); } if (cc_features.opt_alpha) { + append_line(buf, &len, " float alphaValue = texel.a;"); + if (cc_features.opt_alpha_threshold) { - append_line(buf, &len, " if (texel.a < 8.0 / 256.0) discard;"); + append_line(buf, &len, "alphaValue = clamp(alphaValue, 0.0, 1.0);"); + append_line(buf, &len, "if (alphaValue < alpha_test_value) discard;"); } if (cc_features.opt_invisible) { append_line(buf, &len, " texel.a = 0.0;"); diff --git a/src/graphic/Fast3D/gfx_metal.cpp b/src/graphic/Fast3D/gfx_metal.cpp index 4c1a287f6..bc8803f9a 100644 --- a/src/graphic/Fast3D/gfx_metal.cpp +++ b/src/graphic/Fast3D/gfx_metal.cpp @@ -114,6 +114,7 @@ struct FramebufferMetal { struct FrameUniforms { simd::int1 frameCount; simd::float1 noiseScale; + simd::float1 alphaTestValue; }; struct CoordUniforms { @@ -274,7 +275,7 @@ static void gfx_metal_init(void) { struct CoordUniforms { uint2 coords[1024]; }; - + kernel void depthKernel(depth2d depth_texture [[ texture(0) ]], constant CoordUniforms& query_coords [[ buffer(0) ]], device float* output_values [[ buffer(1) ]], @@ -914,7 +915,7 @@ static void gfx_metal_update_framebuffer_parameters(int fb_id, uint32_t width, u autorelease_pool->release(); } -void gfx_metal_start_draw_to_framebuffer(int fb_id, float noise_scale) { +void gfx_metal_start_draw_to_framebuffer(int fb_id, float noise_scale, float alpha_test_value) { FramebufferMetal& fb = mctx.framebuffers[fb_id]; mctx.render_target_height = mctx.textures[fb.texture_id].height; @@ -939,6 +940,10 @@ void gfx_metal_start_draw_to_framebuffer(int fb_id, float noise_scale) { mctx.frame_uniforms.noiseScale = 1.0f / noise_scale; } + if (alpha_test_value != 0.0f) { + mctx.frame_uniforms.alphaTestValue = alpha_test_value; + } + memcpy(mctx.frame_uniform_buffer->contents(), &mctx.frame_uniforms, sizeof(FrameUniforms)); } diff --git a/src/graphic/Fast3D/gfx_metal_shader.cpp b/src/graphic/Fast3D/gfx_metal_shader.cpp index 8757ab586..8fb488792 100644 --- a/src/graphic/Fast3D/gfx_metal_shader.cpp +++ b/src/graphic/Fast3D/gfx_metal_shader.cpp @@ -140,6 +140,7 @@ MTL::VertexDescriptor* gfx_metal_build_shader(char buf[8192], size_t& num_floats append_line(buf, &len, "struct FrameUniforms {"); append_line(buf, &len, " int frameCount;"); append_line(buf, &len, " float noiseScale;"); + append_line(buf, &len, " float alphaTestValue;"); append_line(buf, &len, "};"); // end uniforms struct @@ -293,6 +294,21 @@ MTL::VertexDescriptor* gfx_metal_build_shader(char buf[8192], size_t& num_floats append_line(buf, &len, "}"); } + append_line(buf, &len, "float3 colorDither(float _noise_val, float3 _color) {"); + append_line(buf, &len, " float3 _noise = float3(_noise_val, _noise_val, _noise_val);"); + append_line(buf, &len, " float3 threshold = 7.0 / 255.0 * (_noise - 0.5);"); + append_line(buf, &len, " _color = clamp(_color + threshold, 0.0, 1.0);"); + append_line(buf, &len, " _color.rgb = round(_color.rgb * 32.0) / 32.0;"); + append_line(buf, &len, " return _color;"); + append_line(buf, &len, "}"); + + append_line(buf, &len, "float alphaDither(float _noise, float _alpha) {"); + append_line(buf, &len, " float threshold = 7.0 / 255.0 * (_noise - 0.5);"); + append_line(buf, &len, " _alpha = clamp(_alpha + threshold, 0.0, 1.0);"); + append_line(buf, &len, " _alpha = round(_alpha * 32.0) / 32.0;"); + append_line(buf, &len, " return _alpha;"); + append_line(buf, &len, "}"); + append_line(buf, &len, "float random(float3 value) {"); append_line(buf, &len, " float random = dot(sin(value), float3(12.9898, 78.233, 37.719));"); append_line(buf, &len, " return fract(sin(random) * 143758.5453);"); @@ -415,22 +431,29 @@ MTL::VertexDescriptor* gfx_metal_build_shader(char buf[8192], size_t& num_floats append_line(buf, &len, " if (texel.w > 0.19) texel.w = 1.0; else discard_fragment();"); } - if (cc_features.opt_alpha && cc_features.opt_noise) { - append_line(buf, &len, " float2 coords = in.position.xy * frameUniforms.noiseScale;"); - append_line(buf, &len, - " texel.w *= floor(fast::clamp(random(float3(floor(coords), float(frameUniforms.frameCount))) + " - "texel.w, 0.0, 1.0));"); - } - if (cc_features.opt_grayscale) { append_line(buf, &len, " float intensity = (texel.x + texel.y + texel.z) / 3.0;"); append_line(buf, &len, " float3 new_texel = in.grayscale.xyz * intensity;"); append_line(buf, &len, " texel.xyz = mix(texel.xyz, new_texel, in.grayscale.w);"); } + if (cc_features.opt_alpha && cc_features.opt_noise) { + append_line(buf, &len, + " texel.w = alphaDither(random(float3(floor(in.position.xy * frameUniforms.noiseScale), " + "float(frameUniforms.frameCount))), texel.w);"); + } else if (cc_features.opt_noise) { + append_line(buf, &len, + " texel.rgb = colorDither(random(float3(floor(in.position.xy * frameUniforms.noiseScale), " + "float(frameUniforms.frameCount))), " + " texel.rgb);"); + } + if (cc_features.opt_alpha) { + append_line(buf, &len, " float alphaValue = texel.w;"); + if (cc_features.opt_alpha_threshold) { - append_line(buf, &len, " if (texel.w < 8.0 / 256.0) discard_fragment();"); + append_line(buf, &len, " alphaValue = clamp(alphaValue, 0.0, 1.0);"); + append_line(buf, &len, " if (alphaValue < frameUniforms.alphaTestValue) discard_fragment();"); } if (cc_features.opt_invisible) { append_line(buf, &len, " texel.w = 0.0;"); diff --git a/src/graphic/Fast3D/gfx_opengl.cpp b/src/graphic/Fast3D/gfx_opengl.cpp index f4cf0e89a..051893e7e 100644 --- a/src/graphic/Fast3D/gfx_opengl.cpp +++ b/src/graphic/Fast3D/gfx_opengl.cpp @@ -60,8 +60,10 @@ struct ShaderProgram { GLint attrib_locations[16]; uint8_t attrib_sizes[16]; uint8_t num_attribs; + bool used_alpha_threshold; GLint frame_count_location; GLint noise_scale_location; + GLint alpha_test_val_location; }; struct Framebuffer { @@ -85,6 +87,7 @@ static uint32_t frame_count; static vector framebuffers; static size_t current_framebuffer; static float current_noise_scale; +static float current_alpha_test_value = 0.0f; static FilteringMode current_filter_mode = FILTER_THREE_POINT; GLint max_msaa_level = 1; @@ -120,6 +123,9 @@ static void gfx_opengl_vertex_array_set_attribs(struct ShaderProgram* prg) { static void gfx_opengl_set_uniforms(struct ShaderProgram* prg) { glUniform1i(prg->frame_count_location, frame_count); glUniform1f(prg->noise_scale_location, current_noise_scale); + if (prg->used_alpha_threshold) { + glUniform1f(prg->alpha_test_val_location, current_alpha_test_value); + } } static void gfx_opengl_unload_shader(struct ShaderProgram* old_prg) { @@ -415,9 +421,30 @@ static struct ShaderProgram* gfx_opengl_create_and_load_new_shader(uint64_t shad append_line(fs_buf, &fs_len, "uniform sampler2D uTexBlend1;"); } + if (cc_features.opt_alpha_threshold) { + append_line(fs_buf, &fs_len, "uniform float alphaTestValue;"); + append_line(fs_buf, &fs_len, "#define WRAP(x, low, high) (mod((x)-(low), (high)-(low)) + (low));"); + } + 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, "vec3 colorDither(float _noise_val, vec3 _color)"); + append_line(fs_buf, &fs_len, "{"); + append_line(fs_buf, &fs_len, " vec3 _noise = vec3(_noise_val, _noise_val, _noise_val);"); + append_line(fs_buf, &fs_len, " vec3 threshold = 7.0 / 255.0 * (_noise - 0.5);"); + append_line(fs_buf, &fs_len, " _color = clamp(_color + threshold, 0.0, 1.0);"); + append_line(fs_buf, &fs_len, " _color.rgb = round(_color.rgb * 32.0) / 32.0;"); + append_line(fs_buf, &fs_len, " return _color;"); + append_line(fs_buf, &fs_len, "}"); + append_line(fs_buf, &fs_len, "float alphaDither(float _noise, float _alpha)"); + append_line(fs_buf, &fs_len, "{"); + append_line(fs_buf, &fs_len, " float threshold = 7.0 / 255.0 * (_noise - 0.5);"); + append_line(fs_buf, &fs_len, " _alpha = clamp(_alpha + threshold, 0.0, 1.0);"); + append_line(fs_buf, &fs_len, " _alpha = round(_alpha * 32.0) / 32.0;"); + append_line(fs_buf, &fs_len, " return _alpha;"); + append_line(fs_buf, &fs_len, "}"); + 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));"); append_line(fs_buf, &fs_len, " return fract(sin(random) * 143758.5453);"); @@ -554,9 +581,13 @@ static struct ShaderProgram* gfx_opengl_create_and_load_new_shader(uint64_t shad } if (cc_features.opt_alpha && cc_features.opt_noise) { + append_line( + fs_buf, &fs_len, + "texel.a = alphaDither(random(vec3(floor(gl_FragCoord.xy * noise_scale), float(frame_count))), texel.a);"); + } else if (cc_features.opt_noise) { append_line(fs_buf, &fs_len, - "texel.a *= floor(clamp(random(vec3(floor(gl_FragCoord.xy * noise_scale), float(frame_count))) + " - "texel.a, 0.0, 1.0));"); + "texel.rgb = colorDither(random(vec3(floor(gl_FragCoord.xy * noise_scale), float(frame_count))), " + "texel.rgb);"); } if (cc_features.opt_grayscale) { @@ -566,8 +597,11 @@ static struct ShaderProgram* gfx_opengl_create_and_load_new_shader(uint64_t shad } if (cc_features.opt_alpha) { + append_line(fs_buf, &fs_len, "float alphaValue = texel.a;"); + if (cc_features.opt_alpha_threshold) { - append_line(fs_buf, &fs_len, "if (texel.a < 8.0 / 256.0) discard;"); + append_line(fs_buf, &fs_len, "alphaValue = clamp(alphaValue, 0.0, 1.0);"); + append_line(fs_buf, &fs_len, "if (alphaValue < alphaTestValue) discard;"); } if (cc_features.opt_invisible) { append_line(fs_buf, &fs_len, "texel.a = 0.0;"); @@ -691,6 +725,7 @@ static struct ShaderProgram* gfx_opengl_create_and_load_new_shader(uint64_t shad prg->frame_count_location = glGetUniformLocation(shader_program, "frame_count"); prg->noise_scale_location = glGetUniformLocation(shader_program, "noise_scale"); + prg->alpha_test_val_location = glGetUniformLocation(shader_program, "alphaTestValue"); gfx_opengl_load_shader(prg); @@ -719,6 +754,13 @@ static struct ShaderProgram* gfx_opengl_create_and_load_new_shader(uint64_t shad glUniform1i(sampler_location, 5); } + if (cc_features.opt_alpha_threshold) { + prg->alpha_test_val_location = glGetUniformLocation(shader_program, "alphaTestValue"); + prg->used_alpha_threshold = true; + } else { + prg->used_alpha_threshold = false; + } + return prg; } @@ -979,9 +1021,10 @@ static void gfx_opengl_update_framebuffer_parameters(int fb_id, uint32_t width, fb.invert_y = opengl_invert_y; } -void gfx_opengl_start_draw_to_framebuffer(int fb_id, float noise_scale) { +void gfx_opengl_start_draw_to_framebuffer(int fb_id, float noise_scale, float alpha_test_value) { Framebuffer& fb = framebuffers[fb_id]; + current_alpha_test_value = alpha_test_value; if (noise_scale != 0.0f) { current_noise_scale = 1.0f / noise_scale; } diff --git a/src/graphic/Fast3D/gfx_pc.cpp b/src/graphic/Fast3D/gfx_pc.cpp index cae6ccf15..e510db7e6 100644 --- a/src/graphic/Fast3D/gfx_pc.cpp +++ b/src/graphic/Fast3D/gfx_pc.cpp @@ -1412,12 +1412,16 @@ static void gfx_sp_tri1(uint8_t vtx1_idx, uint8_t vtx2_idx, uint8_t vtx3_idx, bo (g_rdp.other_mode_l & (3 << 16)) == (G_BL_1MA << 16); bool use_fog = (g_rdp.other_mode_l >> 30) == G_BL_CLR_FOG; bool texture_edge = (g_rdp.other_mode_l & CVG_X_ALPHA) == CVG_X_ALPHA; - bool use_noise = (g_rdp.other_mode_l & (3U << G_MDSFT_ALPHACOMPARE)) == G_AC_DITHER; + bool use_noise = ((g_rdp.other_mode_h & (3U << G_MDSFT_CYCLETYPE)) < G_CYC_COPY) && + ((g_rdp.other_mode_l & (3U << G_MDSFT_ALPHACOMPARE)) == G_AC_DITHER || + (g_rdp.other_mode_h & (3U << G_MDSFT_ALPHADITHER)) == G_AD_NOISE || + (g_rdp.other_mode_h & (3U << G_MDSFT_COLORDITHER)) == G_CD_NOISE); bool use_2cyc = (g_rdp.other_mode_h & (3U << G_MDSFT_CYCLETYPE)) == G_CYC_2CYCLE; bool alpha_threshold = (g_rdp.other_mode_l & (3U << G_MDSFT_ALPHACOMPARE)) == G_AC_THRESHOLD; bool invisible = (g_rdp.other_mode_l & (3 << 24)) == (G_BL_0 << 24) && (g_rdp.other_mode_l & (3 << 20)) == (G_BL_CLR_MEM << 20); bool use_grayscale = g_rdp.grayscale; + g_rdp.alpha_test_value = 0.0f; if (texture_edge) { use_alpha = true; @@ -1440,6 +1444,7 @@ static void gfx_sp_tri1(uint8_t vtx1_idx, uint8_t vtx2_idx, uint8_t vtx3_idx, bo } if (alpha_threshold) { cc_options |= (uint64_t)SHADER_OPT_ALPHA_THRESHOLD; + g_rdp.alpha_test_value = g_rdp.blend_color.a; } if (invisible) { cc_options |= (uint64_t)SHADER_OPT_INVISIBLE; @@ -2184,7 +2189,10 @@ static void gfx_dp_set_fog_color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { } static void gfx_dp_set_blend_color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { - // TODO: Implement this command.. + g_rdp.blend_color.r = r; + g_rdp.blend_color.g = g; + g_rdp.blend_color.b = b; + g_rdp.blend_color.a = a; } static void gfx_dp_set_fill_color(uint32_t packed_color) { @@ -3283,11 +3291,15 @@ bool gfx_set_fb_handler_custom(Gfx** cmd0) { return false; } +float gfx_calculate_noise_scale() { + return ((float)gfx_current_dimensions.height / gfx_native_dimensions.height) * 0.5f; +} + bool gfx_reset_fb_handler_custom(Gfx** cmd0) { gfx_flush(); fbActive = 0; - gfx_rapi->start_draw_to_framebuffer(game_renders_to_framebuffer ? game_framebuffer : 0, - (float)gfx_current_dimensions.height / gfx_native_dimensions.height); + gfx_rapi->start_draw_to_framebuffer(game_renders_to_framebuffer ? game_framebuffer : 0, gfx_calculate_noise_scale(), + g_rdp.alpha_test_value); return false; } @@ -3885,8 +3897,8 @@ void gfx_run(Gfx* commands, const std::unordered_map& mtx_replacemen gfx_current_window_dimensions.height, 1, false, true, true, !game_renders_to_framebuffer); gfx_rapi->start_frame(); - gfx_rapi->start_draw_to_framebuffer(game_renders_to_framebuffer ? game_framebuffer : 0, - (float)gfx_current_dimensions.height / gfx_native_dimensions.height); + gfx_rapi->start_draw_to_framebuffer(game_renders_to_framebuffer ? game_framebuffer : 0, gfx_calculate_noise_scale(), + g_rdp.alpha_test_value); gfx_rapi->clear_framebuffer(); g_rdp.viewport_or_scissor_changed = true; rendering_state.viewport = {}; @@ -3911,7 +3923,7 @@ void gfx_run(Gfx* commands, const std::unordered_map& mtx_replacemen currentDir = std::stack(); if (game_renders_to_framebuffer) { - gfx_rapi->start_draw_to_framebuffer(0, 1); + gfx_rapi->start_draw_to_framebuffer(0, 1, 0); gfx_rapi->clear_framebuffer(); if (gfx_msaa_level > 1) { @@ -3967,7 +3979,7 @@ extern "C" int gfx_create_framebuffer(uint32_t width, uint32_t height, uint32_t } void gfx_set_framebuffer(int fb, float noise_scale) { - gfx_rapi->start_draw_to_framebuffer(fb, noise_scale); + gfx_rapi->start_draw_to_framebuffer(fb, noise_scale, g_rdp.alpha_test_value); gfx_rapi->clear_framebuffer(); } @@ -4015,7 +4027,7 @@ void gfx_copy_framebuffer(int fb_dst_id, int fb_src_id, bool copyOnce, bool* has } void gfx_reset_framebuffer() { - gfx_rapi->start_draw_to_framebuffer(0, (float)gfx_current_dimensions.height / gfx_native_dimensions.height); + gfx_rapi->start_draw_to_framebuffer(0, gfx_calculate_noise_scale(), g_rdp.alpha_test_value); gfx_rapi->clear_framebuffer(); } diff --git a/src/graphic/Fast3D/gfx_pc.h b/src/graphic/Fast3D/gfx_pc.h index 757952ef8..402241d53 100644 --- a/src/graphic/Fast3D/gfx_pc.h +++ b/src/graphic/Fast3D/gfx_pc.h @@ -185,8 +185,9 @@ struct RDP { bool grayscale; uint8_t prim_lod_fraction; - struct RGBA env_color, prim_color, fog_color, fill_color, grayscale_color; + struct RGBA env_color, blend_color, prim_color, fog_color, fill_color, grayscale_color; struct XYWidthHeight viewport, scissor; + float alpha_test_value; bool viewport_or_scissor_changed; void* z_buf_address; void* color_image_address; diff --git a/src/graphic/Fast3D/gfx_rendering_api.h b/src/graphic/Fast3D/gfx_rendering_api.h index 01959d2f4..add7c4508 100644 --- a/src/graphic/Fast3D/gfx_rendering_api.h +++ b/src/graphic/Fast3D/gfx_rendering_api.h @@ -57,7 +57,7 @@ struct GfxRenderingAPI { void (*update_framebuffer_parameters)(int fb_id, uint32_t width, uint32_t height, uint32_t msaa_level, bool opengl_invert_y, bool render_target, bool has_depth_buffer, bool can_extract_depth); - void (*start_draw_to_framebuffer)(int fb_id, float noise_scale); + void (*start_draw_to_framebuffer)(int fb_id, float noise_scale, float alpha_test_value); void (*copy_framebuffer)(int fb_dst_id, int fb_src_id, int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1); void (*clear_framebuffer)(void);