Merge pull request #1817 from Armada651/custom-anaglyph

PostProcessing: Add support for user-supplied anaglyph shaders.
This commit is contained in:
Markus Wick 2015-01-26 11:08:29 +01:00
commit 53a9df10f9
14 changed files with 128 additions and 73 deletions

View File

@ -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));
}

View File

@ -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));
}

View File

@ -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));
}

View File

@ -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));
}

View File

@ -74,6 +74,7 @@
#define WII_SYSCONF_DIR "shared2" DIR_SEP "sys" #define WII_SYSCONF_DIR "shared2" DIR_SEP "sys"
#define WII_WC24CONF_DIR "shared2" DIR_SEP "wc24" #define WII_WC24CONF_DIR "shared2" DIR_SEP "wc24"
#define THEMES_DIR "Themes" #define THEMES_DIR "Themes"
#define ANAGLYPH_DIR "Anaglyph"
// Filenames // Filenames
// Files in the directory returned by GetUserPath(D_CONFIG_IDX) // Files in the directory returned by GetUserPath(D_CONFIG_IDX)

View File

@ -395,29 +395,14 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string &title, con
} }
// postproc shader // postproc shader
if (vconfig.backend_info.PPShaders.size()) if (vconfig.backend_info.bSupportsPostProcessing)
{ {
wxFlexGridSizer* const szr_pp = new wxFlexGridSizer(3, 5, 5); wxFlexGridSizer* const szr_pp = new wxFlexGridSizer(3, 5, 5);
choice_ppshader = new wxChoice(page_enh, wxID_ANY); choice_ppshader = new wxChoice(page_enh, wxID_ANY);
RegisterControl(choice_ppshader, wxGetTranslation(ppshader_desc)); RegisterControl(choice_ppshader, wxGetTranslation(ppshader_desc));
choice_ppshader->AppendString(_("(off)"));
button_config_pp = new wxButton(page_enh, wxID_ANY, _("Config")); button_config_pp = new wxButton(page_enh, wxID_ANY, _("Config"));
for (const std::string& shader : vconfig.backend_info.PPShaders) PopulatePostProcessingShaders();
{
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());
choice_ppshader->Bind(wxEVT_CHOICE, &VideoConfigDiag::Event_PPShader, this); choice_ppshader->Bind(wxEVT_CHOICE, &VideoConfigDiag::Event_PPShader, this);
button_config_pp->Bind(wxEVT_BUTTON, &VideoConfigDiag::Event_ConfigurePPShader, this); button_config_pp->Bind(wxEVT_BUTTON, &VideoConfigDiag::Event_ConfigurePPShader, this);
@ -745,3 +730,38 @@ void VideoConfigDiag::CreateDescriptionArea(wxPanel* const page, wxBoxSizer* con
// Store description text object for later lookup // Store description text object for later lookup
desc_texts.insert(std::pair<wxWindow*,wxStaticText*>(page, desc_text)); desc_texts.insert(std::pair<wxWindow*,wxStaticText*>(page, desc_text));
} }
void VideoConfigDiag::PopulatePostProcessingShaders()
{
std::vector<std::string> &shaders = (vconfig.iStereoMode == STEREO_ANAGLYPH) ?
vconfig.backend_info.AnaglyphShaders : vconfig.backend_info.PPShaders;
if (shaders.empty())
return;
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 = "dubois";
choice_ppshader->SetStringSelection(StrToWxStr(vconfig.sPostProcessingShader));
}
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());
}

View File

@ -152,7 +152,7 @@ protected:
// Should we enable the configuration button? // Should we enable the configuration button?
PostProcessingShaderConfiguration postprocessing_shader; PostProcessingShaderConfiguration postprocessing_shader;
postprocessing_shader.LoadShader(vconfig.sPostProcessingShader); 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(); ev.Skip();
} }
@ -181,15 +181,10 @@ protected:
void Event_StereoMode(wxCommandEvent &ev) 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 // Anaglyph overrides post-processing shaders
choice_ppshader->Select(0); choice_ppshader->Clear();
choice_ppshader->Enable(false);
}
else if (vconfig.backend_info.PPShaders.size())
{
choice_ppshader->Enable(true);
} }
ev.Skip(); ev.Skip();
@ -214,6 +209,10 @@ protected:
virtual_xfb->Enable(vconfig.bUseXFB); virtual_xfb->Enable(vconfig.bUseXFB);
real_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 // Things which shouldn't be changed during emulation
if (Core::IsRunning()) if (Core::IsRunning())
{ {
@ -251,6 +250,7 @@ protected:
void Evt_EnterControl(wxMouseEvent& ev); void Evt_EnterControl(wxMouseEvent& ev);
void Evt_LeaveControl(wxMouseEvent& ev); void Evt_LeaveControl(wxMouseEvent& ev);
void CreateDescriptionArea(wxPanel* const page, wxBoxSizer* const sizer); void CreateDescriptionArea(wxPanel* const page, wxBoxSizer* const sizer);
void PopulatePostProcessingShaders();
wxChoice* choice_backend; wxChoice* choice_backend;
wxChoice* choice_adapter; wxChoice* choice_adapter;

View File

@ -65,6 +65,7 @@ void CreateDirectories()
File::CreateFullPath(File::GetUserPath(D_MAPS_IDX)); File::CreateFullPath(File::GetUserPath(D_MAPS_IDX));
File::CreateFullPath(File::GetUserPath(D_SCREENSHOTS_IDX)); File::CreateFullPath(File::GetUserPath(D_SCREENSHOTS_IDX));
File::CreateFullPath(File::GetUserPath(D_SHADERS_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_STATESAVES_IDX));
File::CreateFullPath(File::GetUserPath(D_THEMES_IDX)); File::CreateFullPath(File::GetUserPath(D_THEMES_IDX));
} }

View File

@ -81,6 +81,7 @@ void InitBackendInfo()
g_Config.backend_info.bSupportsOversizedViewports = false; g_Config.backend_info.bSupportsOversizedViewports = false;
g_Config.backend_info.bSupportsGeometryShaders = true; g_Config.backend_info.bSupportsGeometryShaders = true;
g_Config.backend_info.bSupports3DVision = true; g_Config.backend_info.bSupports3DVision = true;
g_Config.backend_info.bSupportsPostProcessing = false;
IDXGIFactory* factory; IDXGIFactory* factory;
IDXGIAdapter* ad; IDXGIAdapter* ad;
@ -133,6 +134,7 @@ void InitBackendInfo()
// Clear ppshaders string vector // Clear ppshaders string vector
g_Config.backend_info.PPShaders.clear(); g_Config.backend_info.PPShaders.clear();
g_Config.backend_info.AnaglyphShaders.clear();
DX11::D3D::UnloadDXGI(); DX11::D3D::UnloadDXGI();
DX11::D3D::UnloadD3D(); DX11::D3D::UnloadD3D();

View File

@ -36,29 +36,8 @@ static const char s_vertex_shader[] =
" uv0 = rawpos * src_rect.zw + src_rect.xy;\n" " uv0 = rawpos * src_rect.zw + src_rect.xy;\n"
"}\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() OpenGLPostProcessing::OpenGLPostProcessing()
: m_initialized(false) : m_initialized(false)
, m_anaglyph(false)
{ {
CreateHeader(); CreateHeader();
@ -183,23 +162,14 @@ void OpenGLPostProcessing::BlitFromTexture(TargetRectangle src, TargetRectangle
void OpenGLPostProcessing::ApplyShader() void OpenGLPostProcessing::ApplyShader()
{ {
// shader didn't changed // shader didn't changed
if (m_initialized && m_config.GetShader() == g_ActiveConfig.sPostProcessingShader && if (m_initialized && m_config.GetShader() == g_ActiveConfig.sPostProcessingShader)
((g_ActiveConfig.iStereoMode == STEREO_ANAGLYPH) == m_anaglyph))
return; return;
m_shader.Destroy(); m_shader.Destroy();
m_uniform_bindings.clear(); m_uniform_bindings.clear();
// load shader code // load shader code
std::string code = ""; std::string code = m_config.LoadShader();
if (g_ActiveConfig.iStereoMode == STEREO_ANAGLYPH)
code = s_anaglyph_shader;
else if (g_ActiveConfig.sPostProcessingShader != "")
code = m_config.LoadShader();
if (code == "")
code = s_default_shader;
code = LoadShaderOptions(code); code = LoadShaderOptions(code);
const char* vertex_shader = s_vertex_shader; const char* vertex_shader = s_vertex_shader;
@ -211,8 +181,8 @@ void OpenGLPostProcessing::ApplyShader()
if (!ProgramShaderCache::CompileShader(m_shader, vertex_shader, code.c_str())) if (!ProgramShaderCache::CompileShader(m_shader, vertex_shader, code.c_str()))
{ {
ERROR_LOG(VIDEO, "Failed to compile post-processing shader %s", m_config.GetShader().c_str()); ERROR_LOG(VIDEO, "Failed to compile post-processing shader %s", m_config.GetShader().c_str());
g_ActiveConfig.sPostProcessingShader.clear();
code = LoadShaderOptions(s_default_shader); code = m_config.LoadShader();
ProgramShaderCache::CompileShader(m_shader, vertex_shader, code.c_str()); ProgramShaderCache::CompileShader(m_shader, vertex_shader, code.c_str());
} }
@ -244,7 +214,6 @@ void OpenGLPostProcessing::ApplyShader()
std::string glsl_name = "option_" + it.first; std::string glsl_name = "option_" + it.first;
m_uniform_bindings[it.first] = glGetUniformLocation(m_shader.glprogid, glsl_name.c_str()); m_uniform_bindings[it.first] = glGetUniformLocation(m_shader.glprogid, glsl_name.c_str());
} }
m_anaglyph = g_ActiveConfig.iStereoMode == STEREO_ANAGLYPH;
m_initialized = true; m_initialized = true;
} }

View File

@ -28,7 +28,6 @@ public:
private: private:
bool m_initialized; bool m_initialized;
bool m_anaglyph;
SHADER m_shader; SHADER m_shader;
GLuint m_uniform_resolution; GLuint m_uniform_resolution;
GLuint m_uniform_src_rect; GLuint m_uniform_src_rect;

View File

@ -95,14 +95,14 @@ std::string VideoBackend::GetDisplayName() const
return "OpenGL"; return "OpenGL";
} }
static void GetShaders(std::vector<std::string> &shaders) static void GetShaders(std::vector<std::string> &shaders, const std::string &sub_dir = "")
{ {
std::set<std::string> already_found; std::set<std::string> already_found;
shaders.clear(); shaders.clear();
static const std::string directories[] = { const std::string directories[] = {
File::GetUserPath(D_SHADERS_IDX), File::GetUserPath(D_SHADERS_IDX) + sub_dir,
File::GetSysDirectory() + SHADERS_DIR DIR_SEP, File::GetSysDirectory() + SHADERS_DIR DIR_SEP + sub_dir,
}; };
for (auto& directory : directories) for (auto& directory : directories)
{ {
@ -139,6 +139,7 @@ static void InitBackendInfo()
g_Config.backend_info.bSupportsOversizedViewports = true; g_Config.backend_info.bSupportsOversizedViewports = true;
g_Config.backend_info.bSupportsGeometryShaders = true; g_Config.backend_info.bSupportsGeometryShaders = true;
g_Config.backend_info.bSupports3DVision = false; g_Config.backend_info.bSupports3DVision = false;
g_Config.backend_info.bSupportsPostProcessing = true;
g_Config.backend_info.Adapters.clear(); g_Config.backend_info.Adapters.clear();
@ -148,6 +149,7 @@ static void InitBackendInfo()
// pp shaders // pp shaders
GetShaders(g_Config.backend_info.PPShaders); GetShaders(g_Config.backend_info.PPShaders);
GetShaders(g_Config.backend_info.AnaglyphShaders, std::string(ANAGLYPH_DIR DIR_SEP));
} }
void VideoBackend::ShowConfig(void *_hParent) void VideoBackend::ShowConfig(void *_hParent)

View File

@ -13,6 +13,8 @@
#include "VideoCommon/VideoConfig.h" #include "VideoCommon/VideoConfig.h"
static const char s_default_shader[] = "void main() { SetOutput(Sample()); }\n";
PostProcessingShaderImplementation::PostProcessingShaderImplementation() PostProcessingShaderImplementation::PostProcessingShaderImplementation()
{ {
m_timer.Start(); m_timer.Start();
@ -30,20 +32,29 @@ std::string PostProcessingShaderConfiguration::LoadShader(std::string shader)
shader = g_ActiveConfig.sPostProcessingShader; shader = g_ActiveConfig.sPostProcessingShader;
m_current_shader = shader; m_current_shader = shader;
const std::string sub_dir = (g_Config.iStereoMode == STEREO_ANAGLYPH) ? ANAGLYPH_DIR DIR_SEP : "";
// loading shader code // loading shader code
std::string 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 (shader == "")
{
code = s_default_shader;
}
else
{
if (!File::Exists(path)) if (!File::Exists(path))
{ {
// Fallback to shared user dir // 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)) if (!File::ReadFileToString(path, code))
{ {
ERROR_LOG(VIDEO, "Post-processing shader not found: %s", path.c_str()); ERROR_LOG(VIDEO, "Post-processing shader not found: %s", path.c_str());
return ""; code = s_default_shader;
}
} }
LoadOptions(code); LoadOptions(code);

View File

@ -147,6 +147,7 @@ struct VideoConfig final
std::vector<std::string> Adapters; // for D3D std::vector<std::string> Adapters; // for D3D
std::vector<std::string> AAModes; std::vector<std::string> AAModes;
std::vector<std::string> PPShaders; // post-processing shaders std::vector<std::string> PPShaders; // post-processing shaders
std::vector<std::string> AnaglyphShaders; // anaglyph shaders
bool bSupportsExclusiveFullscreen; bool bSupportsExclusiveFullscreen;
bool bSupportsDualSourceBlend; bool bSupportsDualSourceBlend;
@ -158,6 +159,7 @@ struct VideoConfig final
bool bSupportsBindingLayout; // Needed by ShaderGen, so must stay in VideoCommon bool bSupportsBindingLayout; // Needed by ShaderGen, so must stay in VideoCommon
bool bSupportsBBox; bool bSupportsBBox;
bool bSupportsGSInstancing; // Needed by GeometryShaderGen, so must stay in VideoCommon bool bSupportsGSInstancing; // Needed by GeometryShaderGen, so must stay in VideoCommon
bool bSupportsPostProcessing;
} backend_info; } backend_info;
// Utility // Utility