diff --git a/Data/Sys/Shaders/Anaglyph/dubois.glsl b/Data/Sys/Shaders/Anaglyph/dubois.glsl new file mode 100644 index 0000000000..66f9e8382f --- /dev/null +++ b/Data/Sys/Shaders/Anaglyph/dubois.glsl @@ -0,0 +1,18 @@ +// Anaglyph Red-Cyan shader based on Dubois algorithm +// Constants taken from the paper: +// "Conversion of a Stereo Pair to Anaglyph with +// the Least-Squares Projection Method" +// Eric Dubois, March 2009 + +void main() +{ + float4 c0 = SampleLayer(0); + float4 c1 = SampleLayer(1); + mat3 l = mat3( 0.437, 0.449, 0.164, + -0.062,-0.062,-0.024, + -0.048,-0.050,-0.017); + mat3 r = mat3(-0.011,-0.032,-0.007, + 0.377, 0.761, 0.009, + -0.026,-0.093, 1.234); + SetOutput(float4(c0.rgb * l + c1.rgb * r, c0.a)); +} diff --git a/Data/Sys/Shaders/Anaglyph/fullcolor.glsl b/Data/Sys/Shaders/Anaglyph/fullcolor.glsl new file mode 100644 index 0000000000..8f033aedf1 --- /dev/null +++ b/Data/Sys/Shaders/Anaglyph/fullcolor.glsl @@ -0,0 +1,8 @@ +// Anaglyph Red-Cyan shader without compensation + +void main() +{ + float4 c0 = SampleLayer(0); + float4 c1 = SampleLayer(1); + SetOutput(float4(c0.r, c1.gb, c0.a)); +} diff --git a/Data/Sys/Shaders/Anaglyph/grayscale.glsl b/Data/Sys/Shaders/Anaglyph/grayscale.glsl new file mode 100644 index 0000000000..a1be73da28 --- /dev/null +++ b/Data/Sys/Shaders/Anaglyph/grayscale.glsl @@ -0,0 +1,10 @@ +// Anaglyph Red-Cyan grayscale shader + +void main() +{ + float4 c0 = SampleLayer(0); + float avg0 = (c0.r + c0.g + c0.b) / 3.0; + float4 c1 = SampleLayer(1); + float avg1 = (c1.r + c1.g + c1.b) / 3.0; + SetOutput(float4(avg0, avg1, avg1, c0.a)); +} diff --git a/Data/Sys/Shaders/Anaglyph/grayscale2.glsl b/Data/Sys/Shaders/Anaglyph/grayscale2.glsl new file mode 100644 index 0000000000..1746396fd8 --- /dev/null +++ b/Data/Sys/Shaders/Anaglyph/grayscale2.glsl @@ -0,0 +1,12 @@ +// Anaglyph Red-Cyan luma grayscale shader +// Info: http://www.oreillynet.com/cs/user/view/cs_msg/8691 + +void main() +{ + float3 luma = float3(0.222, 0.707, 0.071); + float4 c0 = SampleLayer(0); + float avg0 = dot(c0.rgb, luma); + float4 c1 = SampleLayer(1); + float avg1 = dot(c1.rgb, luma); + SetOutput(float4(avg0, avg1, avg1, c0.a)); +} diff --git a/Source/Core/Common/CommonPaths.h b/Source/Core/Common/CommonPaths.h index 62d0ea3db3..4292befa9a 100644 --- a/Source/Core/Common/CommonPaths.h +++ b/Source/Core/Common/CommonPaths.h @@ -74,6 +74,7 @@ #define WII_SYSCONF_DIR "shared2" DIR_SEP "sys" #define WII_WC24CONF_DIR "shared2" DIR_SEP "wc24" #define THEMES_DIR "Themes" +#define ANAGLYPH_DIR "Anaglyph" // Filenames // Files in the directory returned by GetUserPath(D_CONFIG_IDX) diff --git a/Source/Core/DolphinWX/VideoConfigDiag.cpp b/Source/Core/DolphinWX/VideoConfigDiag.cpp index 93cf7b1078..09d0aa9d1d 100644 --- a/Source/Core/DolphinWX/VideoConfigDiag.cpp +++ b/Source/Core/DolphinWX/VideoConfigDiag.cpp @@ -395,29 +395,14 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string &title, con } // postproc shader - if (vconfig.backend_info.PPShaders.size()) + if (vconfig.backend_info.bSupportsPostProcessing) { wxFlexGridSizer* const szr_pp = new wxFlexGridSizer(3, 5, 5); choice_ppshader = new wxChoice(page_enh, wxID_ANY); RegisterControl(choice_ppshader, wxGetTranslation(ppshader_desc)); - choice_ppshader->AppendString(_("(off)")); - button_config_pp = new wxButton(page_enh, wxID_ANY, _("Config")); - for (const std::string& shader : vconfig.backend_info.PPShaders) - { - choice_ppshader->AppendString(StrToWxStr(shader)); - } - - if (vconfig.sPostProcessingShader.empty() || vconfig.iStereoMode == STEREO_ANAGLYPH) - choice_ppshader->Select(0); - else - choice_ppshader->SetStringSelection(StrToWxStr(vconfig.sPostProcessingShader)); - - // Should the configuration button be loaded by default? - PostProcessingShaderConfiguration postprocessing_shader; - postprocessing_shader.LoadShader(vconfig.sPostProcessingShader); - button_config_pp->Enable(postprocessing_shader.HasOptions()); + PopulatePostProcessingShaders(); choice_ppshader->Bind(wxEVT_CHOICE, &VideoConfigDiag::Event_PPShader, this); button_config_pp->Bind(wxEVT_BUTTON, &VideoConfigDiag::Event_ConfigurePPShader, this); @@ -745,3 +730,36 @@ void VideoConfigDiag::CreateDescriptionArea(wxPanel* const page, wxBoxSizer* con // Store description text object for later lookup desc_texts.insert(std::pair(page, desc_text)); } + +void VideoConfigDiag::PopulatePostProcessingShaders() +{ + std::vector &shaders = (vconfig.iStereoMode == STEREO_ANAGLYPH) ? + vconfig.backend_info.AnaglyphShaders : vconfig.backend_info.PPShaders; + + if (shaders.empty()) + return; + + if (vconfig.iStereoMode != STEREO_ANAGLYPH) + choice_ppshader->AppendString(_("(off)")); + + for (const std::string& shader : shaders) + { + choice_ppshader->AppendString(StrToWxStr(shader)); + } + + if (!choice_ppshader->SetStringSelection(StrToWxStr(vconfig.sPostProcessingShader))) + { + // Invalid shader, reset it to default + choice_ppshader->Select(0); + + if (vconfig.iStereoMode == STEREO_ANAGLYPH) + vconfig.sPostProcessingShader = shaders[0]; + else + vconfig.sPostProcessingShader.clear(); + } + + // Should the configuration button be loaded by default? + PostProcessingShaderConfiguration postprocessing_shader; + postprocessing_shader.LoadShader(vconfig.sPostProcessingShader); + button_config_pp->Enable(postprocessing_shader.HasOptions()); +} diff --git a/Source/Core/DolphinWX/VideoConfigDiag.h b/Source/Core/DolphinWX/VideoConfigDiag.h index d3224d9e2a..10eb8c0385 100644 --- a/Source/Core/DolphinWX/VideoConfigDiag.h +++ b/Source/Core/DolphinWX/VideoConfigDiag.h @@ -144,7 +144,7 @@ protected: void Event_PPShader(wxCommandEvent &ev) { const int sel = ev.GetInt(); - if (sel) + if (sel || vconfig.iStereoMode == STEREO_ANAGLYPH) vconfig.sPostProcessingShader = WxStrToStr(ev.GetString()); else vconfig.sPostProcessingShader.clear(); @@ -152,7 +152,7 @@ protected: // Should we enable the configuration button? PostProcessingShaderConfiguration postprocessing_shader; postprocessing_shader.LoadShader(vconfig.sPostProcessingShader); - button_config_pp->Enable(postprocessing_shader.HasOptions() && vconfig.iStereoMode != STEREO_ANAGLYPH); + button_config_pp->Enable(postprocessing_shader.HasOptions()); ev.Skip(); } @@ -181,15 +181,10 @@ protected: void Event_StereoMode(wxCommandEvent &ev) { - if (ev.GetInt() == STEREO_ANAGLYPH && vconfig.backend_info.PPShaders.size()) + if (vconfig.backend_info.bSupportsPostProcessing) { // Anaglyph overrides post-processing shaders - choice_ppshader->Select(0); - choice_ppshader->Enable(false); - } - else if (vconfig.backend_info.PPShaders.size()) - { - choice_ppshader->Enable(true); + choice_ppshader->Clear(); } ev.Skip(); @@ -214,6 +209,10 @@ protected: virtual_xfb->Enable(vconfig.bUseXFB); real_xfb->Enable(vconfig.bUseXFB); + // Repopulating the post-processing shaders can't be done from an event + if (choice_ppshader && choice_ppshader->IsEmpty()) + PopulatePostProcessingShaders(); + // Things which shouldn't be changed during emulation if (Core::IsRunning()) { @@ -251,6 +250,7 @@ protected: void Evt_EnterControl(wxMouseEvent& ev); void Evt_LeaveControl(wxMouseEvent& ev); void CreateDescriptionArea(wxPanel* const page, wxBoxSizer* const sizer); + void PopulatePostProcessingShaders(); wxChoice* choice_backend; wxChoice* choice_adapter; diff --git a/Source/Core/UICommon/UICommon.cpp b/Source/Core/UICommon/UICommon.cpp index 8e260f582d..735487306a 100644 --- a/Source/Core/UICommon/UICommon.cpp +++ b/Source/Core/UICommon/UICommon.cpp @@ -65,6 +65,7 @@ void CreateDirectories() File::CreateFullPath(File::GetUserPath(D_MAPS_IDX)); File::CreateFullPath(File::GetUserPath(D_SCREENSHOTS_IDX)); File::CreateFullPath(File::GetUserPath(D_SHADERS_IDX)); + File::CreateFullPath(File::GetUserPath(D_SHADERS_IDX) + ANAGLYPH_DIR DIR_SEP); File::CreateFullPath(File::GetUserPath(D_STATESAVES_IDX)); File::CreateFullPath(File::GetUserPath(D_THEMES_IDX)); } diff --git a/Source/Core/VideoBackends/D3D/main.cpp b/Source/Core/VideoBackends/D3D/main.cpp index 9e8d0661ed..57e47fd629 100644 --- a/Source/Core/VideoBackends/D3D/main.cpp +++ b/Source/Core/VideoBackends/D3D/main.cpp @@ -81,6 +81,7 @@ void InitBackendInfo() g_Config.backend_info.bSupportsOversizedViewports = false; g_Config.backend_info.bSupportsGeometryShaders = true; g_Config.backend_info.bSupports3DVision = true; + g_Config.backend_info.bSupportsPostProcessing = false; IDXGIFactory* factory; IDXGIAdapter* ad; @@ -133,6 +134,7 @@ void InitBackendInfo() // Clear ppshaders string vector g_Config.backend_info.PPShaders.clear(); + g_Config.backend_info.AnaglyphShaders.clear(); DX11::D3D::UnloadDXGI(); DX11::D3D::UnloadD3D(); diff --git a/Source/Core/VideoBackends/OGL/PostProcessing.cpp b/Source/Core/VideoBackends/OGL/PostProcessing.cpp index 3c1bb386bf..3fa6f26de9 100644 --- a/Source/Core/VideoBackends/OGL/PostProcessing.cpp +++ b/Source/Core/VideoBackends/OGL/PostProcessing.cpp @@ -36,29 +36,10 @@ static const char s_vertex_shader[] = " uv0 = rawpos * src_rect.zw + src_rect.xy;\n" "}\n"; -// Anaglyph Red-Cyan shader based on Dubois algorithm -// Constants taken from the paper: -// "Conversion of a Stereo Pair to Anaglyph with -// the Least-Squares Projection Method" -// Eric Dubois, March 2009 -static const char s_anaglyph_shader[] = - "void main() {\n" - " vec4 c0 = SampleLayer(0);\n" - " vec4 c1 = SampleLayer(1);\n" - " mat3 l = mat3( 0.437, 0.449, 0.164,\n" - " -0.062,-0.062,-0.024,\n" - " -0.048,-0.050,-0.017);\n" - " mat3 r = mat3(-0.011,-0.032,-0.007,\n" - " 0.377, 0.761, 0.009,\n" - " -0.026,-0.093, 1.234);\n" - " SetOutput(vec4(c0.rgb * l + c1.rgb * r, c0.a));\n" - "}\n"; - static const char s_default_shader[] = "void main() { SetOutput(Sample()); }\n"; OpenGLPostProcessing::OpenGLPostProcessing() : m_initialized(false) - , m_anaglyph(false) { CreateHeader(); @@ -183,8 +164,7 @@ void OpenGLPostProcessing::BlitFromTexture(TargetRectangle src, TargetRectangle void OpenGLPostProcessing::ApplyShader() { // shader didn't changed - if (m_initialized && m_config.GetShader() == g_ActiveConfig.sPostProcessingShader && - ((g_ActiveConfig.iStereoMode == STEREO_ANAGLYPH) == m_anaglyph)) + if (m_initialized && m_config.GetShader() == g_ActiveConfig.sPostProcessingShader) return; m_shader.Destroy(); @@ -192,9 +172,7 @@ void OpenGLPostProcessing::ApplyShader() // load shader code std::string code = ""; - if (g_ActiveConfig.iStereoMode == STEREO_ANAGLYPH) - code = s_anaglyph_shader; - else if (g_ActiveConfig.sPostProcessingShader != "") + if (g_ActiveConfig.sPostProcessingShader != "") code = m_config.LoadShader(); if (code == "") @@ -244,7 +222,6 @@ void OpenGLPostProcessing::ApplyShader() std::string glsl_name = "option_" + it.first; m_uniform_bindings[it.first] = glGetUniformLocation(m_shader.glprogid, glsl_name.c_str()); } - m_anaglyph = g_ActiveConfig.iStereoMode == STEREO_ANAGLYPH; m_initialized = true; } diff --git a/Source/Core/VideoBackends/OGL/PostProcessing.h b/Source/Core/VideoBackends/OGL/PostProcessing.h index e2be0bdc7d..4c67eca839 100644 --- a/Source/Core/VideoBackends/OGL/PostProcessing.h +++ b/Source/Core/VideoBackends/OGL/PostProcessing.h @@ -28,7 +28,6 @@ public: private: bool m_initialized; - bool m_anaglyph; SHADER m_shader; GLuint m_uniform_resolution; GLuint m_uniform_src_rect; diff --git a/Source/Core/VideoBackends/OGL/main.cpp b/Source/Core/VideoBackends/OGL/main.cpp index f549fed5d6..68565e50fd 100644 --- a/Source/Core/VideoBackends/OGL/main.cpp +++ b/Source/Core/VideoBackends/OGL/main.cpp @@ -95,14 +95,14 @@ std::string VideoBackend::GetDisplayName() const return "OpenGL"; } -static void GetShaders(std::vector &shaders) +static void GetShaders(std::vector &shaders, const std::string &sub_dir = "") { std::set already_found; shaders.clear(); - static const std::string directories[] = { - File::GetUserPath(D_SHADERS_IDX), - File::GetSysDirectory() + SHADERS_DIR DIR_SEP, + const std::string directories[] = { + File::GetUserPath(D_SHADERS_IDX) + sub_dir, + File::GetSysDirectory() + SHADERS_DIR DIR_SEP + sub_dir, }; for (auto& directory : directories) { @@ -139,6 +139,7 @@ static void InitBackendInfo() g_Config.backend_info.bSupportsOversizedViewports = true; g_Config.backend_info.bSupportsGeometryShaders = true; g_Config.backend_info.bSupports3DVision = false; + g_Config.backend_info.bSupportsPostProcessing = true; g_Config.backend_info.Adapters.clear(); @@ -148,6 +149,7 @@ static void InitBackendInfo() // pp shaders GetShaders(g_Config.backend_info.PPShaders); + GetShaders(g_Config.backend_info.AnaglyphShaders, std::string(ANAGLYPH_DIR DIR_SEP)); } void VideoBackend::ShowConfig(void *_hParent) diff --git a/Source/Core/VideoCommon/PostProcessing.cpp b/Source/Core/VideoCommon/PostProcessing.cpp index b4ee1e8702..c27b0f89fc 100644 --- a/Source/Core/VideoCommon/PostProcessing.cpp +++ b/Source/Core/VideoCommon/PostProcessing.cpp @@ -30,14 +30,16 @@ std::string PostProcessingShaderConfiguration::LoadShader(std::string shader) shader = g_ActiveConfig.sPostProcessingShader; m_current_shader = shader; + const std::string sub_dir = (g_Config.iStereoMode == STEREO_ANAGLYPH) ? ANAGLYPH_DIR DIR_SEP : ""; + // loading shader code std::string code; - std::string path = File::GetUserPath(D_SHADERS_IDX) + shader + ".glsl"; + std::string path = File::GetUserPath(D_SHADERS_IDX) + sub_dir + shader + ".glsl"; if (!File::Exists(path)) { // Fallback to shared user dir - path = File::GetSysDirectory() + SHADERS_DIR DIR_SEP + shader + ".glsl"; + path = File::GetSysDirectory() + SHADERS_DIR DIR_SEP + sub_dir + shader + ".glsl"; } if (!File::ReadFileToString(path, code)) diff --git a/Source/Core/VideoCommon/VideoConfig.h b/Source/Core/VideoCommon/VideoConfig.h index cf6ea92b32..69684557e8 100644 --- a/Source/Core/VideoCommon/VideoConfig.h +++ b/Source/Core/VideoCommon/VideoConfig.h @@ -147,6 +147,7 @@ struct VideoConfig final std::vector Adapters; // for D3D std::vector AAModes; std::vector PPShaders; // post-processing shaders + std::vector AnaglyphShaders; // anaglyph shaders bool bSupportsExclusiveFullscreen; bool bSupportsDualSourceBlend; @@ -158,6 +159,7 @@ struct VideoConfig final bool bSupportsBindingLayout; // Needed by ShaderGen, so must stay in VideoCommon bool bSupportsBBox; bool bSupportsGSInstancing; // Needed by GeometryShaderGen, so must stay in VideoCommon + bool bSupportsPostProcessing; } backend_info; // Utility