View Full Version : Anaglyph 3D Shader/Filter for MPC-HC
James Freeman
14th April 2014, 10:04
Anyone knows a way to show SBS or Top-Bottom 3D videos in Anaglyph (Red-Cyan) in MPC-HC using a simple shader/filter?
I have PowerDVD13 & Bino3D but I want to use madVR.
Thanks.
Ceremony
14th April 2014, 11:55
dunno about a shader/filter, but you could always use avisynth within ffdshow to achieve this.
here are a few websites:
http://www.pantarheon.org/AviSynth3DToolbox/
http://3dvision-blog.com/1220-2d-to-3d-realtime-video-conversion-script-for-avisynth-v0-3/
JanWillem32
14th April 2014, 13:27
That's actually an easy shader to write.
These shaders require the extended register layout (usage of register c2). These shaders are only prototypes using nearest neighbor resizing. I can add variants with better resizers if these prototypes seem to decently work. (Please tell me which resizer you would like to try.) The only note I have is that these shaders should actually be inserted in a display R'G'B' stage, and not just post-resize, but that's just a minor detail.// (C) 2014 Jan-Willem Krans (janwillem32 <at> hotmail.com)
// This file is part of Video pixel shader pack.
// This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
// You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
// prototype side-by-side to red-cyan anaglyph 3D stereoscopic view
// This shader should be run as a screen space pixel shader.
// This shader requires compiling with ps_2_0, but higher is better, see http://en.wikipedia.org/wiki/Pixel_shader to look up what PS version your video card supports.
// For this shader to work correctly, the video width in pixels on screen has to be an even amount.
sampler s0;
float2 c0;
float2 c1;
float4 c2;
float4 main(float2 tex : TEXCOORD0) : COLOR
{
float2 texr = tex;
if(all(float4(tex, -tex) >= float4(c2.xy, -c2.zw))) {// test for within video rect
float2 vr = c0.xx*c2.xz;
float2 CurPixel = tex*c0-.5;
float ox = CurPixel.x-vr.x;// video-relative x position
float PixelOffset = floor(-.5*ox);// the two images are half-width, compensate for that, round half-pixel offsets down
// adjust the x coordinate of the source position to the correct side of the video image using the pixel offset we just calculated
tex.x += PixelOffset*c1.x;
texr.x += (PixelOffset+.5*(vr.y-vr.x))*c1.x;}// adjust the pixel offset to the video frame on the right side of the video data
return float4(tex2D(s0, tex).r, tex2D(s0, texr).gbb);// sample from the left and right images and only pass-trough the red channel for the left, and the green and blue channels for the right view
}// (C) 2014 Jan-Willem Krans (janwillem32 <at> hotmail.com)
// This file is part of Video pixel shader pack.
// This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
// You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
// prototype top-and-bottom to red-cyan anaglyph 3D stereoscopic view
// This shader should be run as a screen space pixel shader.
// This shader requires compiling with ps_2_0, but higher is better, see http://en.wikipedia.org/wiki/Pixel_shader to look up what PS version your video card supports.
// For this shader to work correctly, the video height in pixels on screen has to be an even amount.
sampler s0;
float2 c0;
float2 c1;
float4 c2;
float4 main(float2 tex : TEXCOORD0) : COLOR
{
float2 texr = tex;
if(all(float4(tex, -tex) >= float4(c2.xy, -c2.zw))) {// test for within video rect
float2 vr = c0.yy*c2.yw;
float2 CurPixel = tex*c0-.5;
float oy = CurPixel.y-vr.x;// video-relative y position
float PixelOffset = floor(-.5*oy);// the two images are half-height, compensate for that, round half-pixel offsets down
// adjust the y coordinate of the source position to the correct side of the video image using the pixel offset we just calculated
tex.y += PixelOffset*c1.y;
texr.y += (PixelOffset+.5*(vr.y-vr.x))*c1.y;}// adjust the pixel offset to the video frame on the bottom side of the video data
return float4(tex2D(s0, tex).r, tex2D(s0, texr).gbb);// sample from the left and right images and only pass-trough the red channel for the left, and the green and blue channels for the right view
}
James Freeman
14th April 2014, 14:23
Thanks Jan,
They don't work.
I see only Red picture with the first, and distorted red picture with the second.
JanWillem32
14th April 2014, 15:40
My best guess is that the extended register layout is unavailable. For videos stretched to the entire screen area it's not a problem. I wrote these two simpler shaders. These shaders can't deal with any borders however. The information about the video rectangle is required to compensate for that.// (C) 2014 Jan-Willem Krans (janwillem32 <at> hotmail.com)
// This file is part of Video pixel shader pack.
// This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
// You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
// prototype basic fullscreen side-by-side to red-cyan anaglyph 3D stereoscopic view
// This shader should be run as a screen space pixel shader.
// This shader requires compiling with ps_2_0, but higher is better, see http://en.wikipedia.org/wiki/Pixel_shader to look up what PS version your video card supports.
// For this shader to work correctly, the video width in pixels on screen has to be an even amount.
sampler s0;
float2 c0;
float2 c1;
float4 main(float2 tex : TEXCOORD0) : COLOR
{
float2 texr = tex;
float2 vr = float2(0., c0.x);
float2 CurPixel = tex*c0-.5;
float ox = CurPixel.x-vr.x;// video-relative x position
float PixelOffset = floor(-.5*ox);// the two images are half-width, compensate for that, round half-pixel offsets down
// adjust the x coordinate of the source position to the correct side of the video image using the pixel offset we just calculated
tex.x += PixelOffset*c1.x;
texr.x += (PixelOffset+.5*(vr.y-vr.x))*c1.x;// adjust the pixel offset to the video frame on the right side of the video data
return float4(tex2D(s0, tex).r, tex2D(s0, texr).gbb);// sample from the left and right images and only pass-trough the red channel for the left, and the green and blue channels for the right view
}// (C) 2014 Jan-Willem Krans (janwillem32 <at> hotmail.com)
// This file is part of Video pixel shader pack.
// This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
// You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
// prototype basic fullscreen top-and-bottom to red-cyan anaglyph 3D stereoscopic view
// This shader should be run as a screen space pixel shader.
// This shader requires compiling with ps_2_0, but higher is better, see http://en.wikipedia.org/wiki/Pixel_shader to look up what PS version your video card supports.
// For this shader to work correctly, the video height in pixels on screen has to be an even amount.
sampler s0;
float2 c0;
float2 c1;
float4 main(float2 tex : TEXCOORD0) : COLOR
{
float2 texr = tex;
float2 vr = float2(0., c0.y);
float2 CurPixel = tex*c0-.5;
float oy = CurPixel.y-vr.x;// video-relative y position
float PixelOffset = floor(-.5*oy);// the two images are half-height, compensate for that, round half-pixel offsets down
// adjust the y coordinate of the source position to the correct side of the video image using the pixel offset we just calculated
tex.y += PixelOffset*c1.y;
texr.y += (PixelOffset+.5*(vr.y-vr.x))*c1.y;// adjust the pixel offset to the video frame on the bottom side of the video data
return float4(tex2D(s0, tex).r, tex2D(s0, texr).gbb);// sample from the left and right images and only pass-trough the red channel for the left, and the green and blue channels for the right view
}
James Freeman
14th April 2014, 15:59
It Works! Thank You.
Can you enable Lanczos resizer after the manipulation?
JanWillem32
15th April 2014, 00:56
Anything else?// (C) 2014 Jan-Willem Krans (janwillem32 <at> hotmail.com)
// This file is part of Video pixel shader pack.
// This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
// You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
// prototype side-by-side to red-cyan anaglyph 3D stereoscopic view with compensated Lanczos2 resizing
// This shader should be run as a screen space pixel shader.
// This shader requires compiling with ps_2_0, but higher is better, see http://en.wikipedia.org/wiki/Pixel_shader to look up what PS version your video card supports.
// For this shader to work correctly, the video width in pixels on screen has to be an even amount.
sampler s0;
float2 c0;
float2 c1;
float4 c2;
static const float PI = acos(-1.);
static const float4 wset = float4(1.25, .25, .75, 1.75)*PI;
static const float4 wsets = wset*.5;
static const float4 wp = sin(wset)*sin(wsets)/(wset*wsets);
static const float2 wc = (1.-dot(1., wp))*float2(.75, .25);// compensate truncated window factor by bilinear factoring on the two nearest samples
static const float4 w = wp+float4(0., wc, 0.);
float4 main(float2 tex : TEXCOORD0) : COLOR
{
if(any(float4(tex, -tex) < float4(c2.xy, -c2.zw))) return tex2D(s0, tex);// discard if outside video rect
else {
float ox = (tex.x-c2.x)*c0.x-.5;// video-relative x position
// detect half of the even or odd coordinates
ox = ox*.5+.25;// the two images are half-width, compensate for that
float n = frac(ox);
float dx = (n > .5)? -c1.x : c1.x;// even y positive, odd y negative
float pixeloffset = n-ox;// round half-pixel offsets down
// adjust the x coordinate of the source position to the correct side of the video image using the pixel offset we just calculated
tex.x += pixeloffset*c1.x;
float center = .5*(c2.z-c2.x);
float texr = tex.x+center-c1.x;// adjust the pixel offset to the video frame on the right side of the video data
// resolve position data and limit the outer samples to the active video rectangle
float2 outeroffsets = float2(-dx, dx+dx);
float4 outerpositions = float2(tex.x, texr).xxyy+outeroffsets.xyxy;
float4 limits = float3(c2.xz, center).xzzy+(c1.xx*float2(.5, -.5)).xyxy;
float4 clampedouterpositions = clamp(outerpositions, limits.xxzz, limits.yyww);
// sample from the left and right images and only pass-trough the red channel for the left, and the green and blue channels for the right view
float3 Q0 = float3(tex2D(s0, float2(clampedouterpositions.x, tex.y)).r, tex2D(s0, float2(clampedouterpositions.z, tex.y)).gb);
float3 Q1 = float3(tex2D(s0, tex).r, tex2D(s0, float2(texr, tex.y)).gb);
float3 Q2 = float3(tex2D(s0, float2(tex.x+dx, tex.y)).r, tex2D(s0, float2(texr+dx, tex.y)).gb);
float3 Q3 = float3(tex2D(s0, float2(clampedouterpositions.y, tex.y)).r, tex2D(s0, float2(clampedouterpositions.w, tex.y)).gb);
return (w.x*Q0+w.y*Q1+w.z*Q2+w.w*Q3).rgbb;}// interpolate and output
}// (C) 2014 Jan-Willem Krans (janwillem32 <at> hotmail.com)
// This file is part of Video pixel shader pack.
// This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
// You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
// prototype top-and-bottom to red-cyan anaglyph 3D stereoscopic view with compensated Lanczos2 resizing
// This shader should be run as a screen space pixel shader.
// This shader requires compiling with ps_2_0, but higher is better, see http://en.wikipedia.org/wiki/Pixel_shader to look up what PS version your video card supports.
// For this shader to work correctly, the video height in pixels on screen has to be an even amount.
sampler s0;
float2 c0;
float2 c1;
float4 c2;
static const float PI = acos(-1.);
static const float4 wset = float4(1.25, .25, .75, 1.75)*PI;
static const float4 wsets = wset*.5;
static const float4 wp = sin(wset)*sin(wsets)/(wset*wsets);
static const float2 wc = (1.-dot(1., wp))*float2(.75, .25);// compensate truncated window factor by bilinear factoring on the two nearest samples
static const float4 w = wp+float4(0., wc, 0.);
float4 main(float2 tex : TEXCOORD0) : COLOR
{
if(any(float4(tex, -tex) < float4(c2.xy, -c2.zw))) return tex2D(s0, tex);// discard if outside video rect
else {
float oy = (tex.y-c2.y)*c0.y-.5;// video-relative y position
// detect half of the even or odd coordinates
oy = oy*.5+.25;// the two images are half-width, compensate for that
float n = frac(oy);
float dy = (n > .5)? -c1.y : c1.y;// even y positive, odd y negative
float pixeloffset = n-oy;// round half-pixel offsets down
// adjust the y coordinate of the source position to the correct side of the video image using the pixel offset we just calculated
tex.y += pixeloffset*c1.y;
float center = .5*(c2.w-c2.y);
float texr = tex.y+center-c1.y;// adjust the pixel offset to the video frame on the bottom side of the video data
// resolve position data and limit the outer samples to the active video rectangle
float2 outeroffsets = float2(-dy, dy+dy);
float4 outerpositions = float2(tex.y, texr).xxyy+outeroffsets.xyxy;
float4 limits = float3(c2.yw, center).xzzy+(c1.yy*float2(.5, -.5)).xyxy;
float4 clampedouterpositions = clamp(outerpositions, limits.xxzz, limits.yyww);
// sample from the left and right images and only pass-trough the red channel for the left, and the green and blue channels for the right view
float3 Q0 = float3(tex2D(s0, float2(tex.x, clampedouterpositions.x)).r, tex2D(s0, float2(tex.x, clampedouterpositions.z)).gb);
float3 Q1 = float3(tex2D(s0, tex).r, tex2D(s0, float2(tex.x, texr)).gb);
float3 Q2 = float3(tex2D(s0, float2(tex.x, tex.y+dy)).r, tex2D(s0, float2(tex.x, texr+dy)).gb);
float3 Q3 = float3(tex2D(s0, float2(tex.x, clampedouterpositions.y)).r, tex2D(s0, float2(tex.x, clampedouterpositions.w)).gb);
return (w.x*Q0+w.y*Q1+w.z*Q2+w.w*Q3).rgbb;}// interpolate and output
}// (C) 2014 Jan-Willem Krans (janwillem32 <at> hotmail.com)
// This file is part of Video pixel shader pack.
// This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
// You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
// prototype basic fullscreen side-by-side to red-cyan anaglyph 3D stereoscopic view with compensated Lanczos2 resizing
// This shader should be run as a screen space pixel shader.
// This shader requires compiling with ps_2_0, but higher is better, see http://en.wikipedia.org/wiki/Pixel_shader to look up what PS version your video card supports.
// For this shader to work correctly, the video width in pixels on screen has to be an even amount.
sampler s0;
float2 c0;
float2 c1;
static const float PI = acos(-1.);
static const float4 wset = float4(1.25, .25, .75, 1.75)*PI;
static const float4 wsets = wset*.5;
static const float4 wp = sin(wset)*sin(wsets)/(wset*wsets);
static const float2 wc = (1.-dot(1., wp))*float2(.75, .25);// compensate truncated window factor by bilinear factoring on the two nearest samples
static const float4 w = wp+float4(0., wc, 0.);
float4 main(float2 tex : TEXCOORD0) : COLOR
{
float ox = tex.x*c0.x-.5;// video-relative x position
// detect half of the even or odd coordinates
ox = ox*.5+.25;// the two images are half-width, compensate for that
float n = frac(ox);
float dx = (n > .5)? -c1.x : c1.x;// even x positive, odd x negative
float pixeloffset = n-ox;// round half-pixel offsets down
// adjust the x coordinate of the source position to the correct side of the video image using the pixel offset we just calculated
tex.x += pixeloffset*c1.x;
float center = .5;
float texr = tex.x+center-c1.x;// adjust the pixel offset to the video frame on the right side of the video data
// sample from the left and right images and only pass-trough the red channel for the left, and the green and blue channels for the right view
float3 Q0 = float3(tex2D(s0, float2(tex.x-dx, tex.y)).r, tex2D(s0, float2(texr-dx, tex.y)).gb);
float3 Q1 = float3(tex2D(s0, tex).r, tex2D(s0, float2(texr, tex.y)).gb);
float3 Q2 = float3(tex2D(s0, float2(tex.x+dx, tex.y)).r, tex2D(s0, float2(texr+dx, tex.y)).gb);
float3 Q3 = float3(tex2D(s0, float2(tex.x+dx+dx, tex.y)).r, tex2D(s0, float2(texr+dx+dx, tex.y)).gb);
return (w.x*Q0+w.y*Q1+w.z*Q2+w.w*Q3).rgbb;// interpolate and output
}// (C) 2014 Jan-Willem Krans (janwillem32 <at> hotmail.com)
// This file is part of Video pixel shader pack.
// This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
// You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
// prototype basic fullscreen top-and-bottom to red-cyan anaglyph 3D stereoscopic view with compensated Lanczos2 resizing
// This shader should be run as a screen space pixel shader.
// This shader requires compiling with ps_2_0, but higher is better, see http://en.wikipedia.org/wiki/Pixel_shader to look up what PS version your video card supports.
// For this shader to work correctly, the video height in pixels on screen has to be an even amount.
sampler s0;
float2 c0;
float2 c1;
static const float PI = acos(-1.);
static const float4 wset = float4(1.25, .25, .75, 1.75)*PI;
static const float4 wsets = wset*.5;
static const float4 wp = sin(wset)*sin(wsets)/(wset*wsets);
static const float2 wc = (1.-dot(1., wp))*float2(.75, .25);// compensate truncated window factor by bilinear factoring on the two nearest samples
static const float4 w = wp+float4(0., wc, 0.);
float4 main(float2 tex : TEXCOORD0) : COLOR
{
float oy = tex.y*c0.y-.5;// video-relative y position
// detect half of the even or odd coordinates
oy = oy*.5+.25;// the two images are half-width, compensate for that
float n = frac(oy);
float dy = (n > .5)? -c1.y : c1.y;// even y positive, odd y negative
float pixeloffset = n-oy;// round half-pixel offsets down
// adjust the y coordinate of the source position to the correct side of the video image using the pixel offset we just calculated
tex.y += pixeloffset*c1.y;
float center = .5;
float texr = tex.y+center-c1.y;// adjust the pixel offset to the video frame on the bottom side of the video data
// sample from the left and right images and only pass-trough the red channel for the left, and the green and blue channels for the right view
float3 Q0 = float3(tex2D(s0, float2(tex.x, tex.y-dy)).r, tex2D(s0, float2(tex.x, texr-dy)).gb);
float3 Q1 = float3(tex2D(s0, tex).r, tex2D(s0, float2(tex.x, texr)).gb);
float3 Q2 = float3(tex2D(s0, float2(tex.x, tex.y+dy)).r, tex2D(s0, float2(tex.x, texr+dy)).gb);
float3 Q3 = float3(tex2D(s0, float2(tex.x, tex.y+dy+dy)).r, tex2D(s0, float2(tex.x, texr+dy+dy)).gb);
return (w.x*Q0+w.y*Q1+w.z*Q2+w.w*Q3).rgbb;// interpolate and output
}
James Freeman
15th April 2014, 10:25
Anything else?
Thank you very very much Jan.
I hope I didn't came too demanding... I was only asking for help.
It was you who offered the professional help. :p
I quote:
That's actually an easy shader to write.
But as always, you outdone yourself and my expectations.
kazuya2k8
5th May 2014, 17:09
Thanks for the prototype shader sample, JanWillen32
I would like to implement Dubois Anaglyph Algorithm since it's more comfy to my eyes (and I'm a bit colorblind). I tried doing it myself~ I'm not sure if I'm doing it right.
// (C) 2014 Jan-Willem Krans (janwillem32 <at> hotmail.com)
// This file is part of Video pixel shader pack.
// This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
// You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
// prototype basic fullscreen side-by-side to red-cyan anaglyph 3D stereoscopic view with compensated Lanczos2 resizing
// This shader should be run as a screen space pixel shader.
// This shader requires compiling with ps_2_0, but higher is better, see http://en.wikipedia.org/wiki/Pixel_shader to look up what PS version your video card supports.
// For this shader to work correctly, the video width in pixels on screen has to be an even amount.
sampler s0;
float2 c0;
float2 c1;
static const float PI = acos(-1.);
static const float4 wset = float4(1.25, .25, .75, 1.75)*PI;
static const float4 wsets = wset*.5;
static const float4 wp = sin(wset)*sin(wsets)/(wset*wsets);
static const float2 wc = (1.-dot(1., wp))*float2(.75, .25);// compensate truncated window factor by bilinear factoring on the two nearest samples
static const float4 w = wp+float4(0., wc, 0.);
inline float3 dubois(float4 l, float4 r) {
float red = l.r* 0.456 + l.g* 0.500 + l.b* 0.176 + r.r*-0.043 + r.g*-0.088 + r.b*-0.002;
float green = l.r*-0.040 + l.g*-0.038 + l.b*-0.016 + r.r* 0.378 + r.g* 0.734 + r.b*-0.018;
float blue = l.r*-0.015 + l.g*-0.021 + l.b*-0.005 + r.r*-0.072 + r.g*-0.113 + r.b* 1.226;
return float3(red, green, blue);
}
/*
inline float3 dubois(float4 l, float4 r) {
float red = l.r* 0.437 + l.g* 0.449 + l.b* 0.164 + r.r*-0.011 + r.g*-0.032 + r.b*-0.007;
float green = l.r*-0.062 + l.g*-0.062 + l.b*-0.024 + r.r* 0.377 + r.g* 0.761 + r.b* 0.009;
float blue = l.r*-0.048 + l.g*-0.050 + l.b*-0.017 + r.r*-0.026 + r.g*-0.093 + r.b* 1.234;
return float3(red, green, blue);
}
*/
float4 main(float2 tex : TEXCOORD0) : COLOR {
float ox = tex.x*c0.x-.5;// video-relative x position
// detect half of the even or odd coordinates
ox = ox*.5+.25;// the two images are half-width, compensate for that
float n = frac(ox);
float dx = (n > .5)? -c1.x : c1.x;// even x positive, odd x negative
float pixeloffset = n-ox;// round half-pixel offsets down
// adjust the x coordinate of the source position to the correct side of the video image using the pixel offset we just calculated
tex.x += pixeloffset*c1.x;
float center = .5;
float texr = tex.x+center-c1.x;// adjust the pixel offset to the video frame on the right side of the video data
// sample from the left and right images and only pass-trough the red channel for the left, and the green and blue channels for the right view
float3 Q0 = dubois(tex2D(s0, float2(tex.x-dx, tex.y)), tex2D(s0, float2(texr-dx, tex.y)));
float3 Q1 = dubois(tex2D(s0, tex), tex2D(s0, float2(texr, tex.y)));
float3 Q2 = dubois(tex2D(s0, float2(tex.x+dx, tex.y)), tex2D(s0, float2(texr+dx, tex.y)));
float3 Q3 = dubois(tex2D(s0, float2(tex.x+dx+dx, tex.y)), tex2D(s0, float2(texr+dx+dx, tex.y)));
return (w.x*Q0+w.y*Q1+w.z*Q2+w.w*Q3).rgbb;// interpolate and output
}
references:
http://www.site.uottawa.ca/~edubois/anaglyph/
http://stereo.jpn.org/eng/stphmkr/help/stereo_13.htm
http://www.site.uottawa.ca/~edubois/anaglyph/LeastSquaresHowToPhotoshop.pdf
JanWillem32
6th May 2014, 01:58
It's conceptually a good idea, but its execution needs refinement. That applies to both the shader and the implementation of the method underlying it.
The concept is to take the color intended for one of the eyes, desaturate it by half and then send it to one or two color channels on the display (depending on the eye you are targeting). The downside to this is that it will of course yield colors that are even more dull than the baseline with the general anaglyph 3D stereoscopic view method, but it will allow objects with strong colors to be actually seen with both eyes, as opposed to just one (objects will still somewhat vary in visibility with this method because of color, though).
The XYZ-RGB-grayscale-base matrix you are using is mentioned in the papers as an example for a particular display. In reality, the display primaries and white point vary per display. Also, trying to desaturate in the XYZ color space will not yield good results. Even the early CAM methods from the '60s have better models to handle desaturation.
I'll try to write a prototype with a lovely, complicated, and very long name again.
kazuya2k8
7th May 2014, 22:01
Yeah~ I feel like I'm missing some steps. And I really don't have any idea how to code for shader. Nice can't wait to see your work. :3
Meanwhile this looks interesting~ Implemented using javascript > 2D to 3D conversion using Dubois Anaglyph algorithm (http://stackoverflow.com/questions/16148742/2d-to-3d-conversion-using-dubois-anaglyph-algorithm)
JanWillem32
19th August 2014, 15:13
Sorry for digging up this older thread. As I've written accurate color control shaders (in the form of CAM shaders) with saturation and chroma/colorfulness controls, these can be used to pre-process the colors for the anaglyph 3D stereoscopic view shaders for those people that prefer that option. As it is not efficient to merge the color controls with the 3D stereoscopic view shaders, I will not write merged shaders. People can simply set multiple shaders in the rendering chain.
The CAM shaders are at the moment still a but experimental, but I'll keep updating them, and I will probably just add them to the regular pixel shader pack at some point in the future. http://forum.doom9.org/showthread.php?p=1690164
TOM_SK
20th August 2014, 21:17
3Dfier (http://www.radiantstar.com.tw/index.php?option=com_forme&Itemid=92) ?
http://i.imgur.com/7AusZcz.jpg
RenderGuy2
7th May 2015, 22:46
I am new to hlsl shaders and can't accomplish anything. I am trying to write a shader for mpc-hc/MPDN that will change the spacing between frames so as to avoid chroma resampling in avisynth. HDMI 3D framepacking requires a 45 pixel border between frames. So this odd border requires 4:2:2 chroma. I'd like to feed a script into the player with a 46 pixel space between frames and then use a shader to re-position the top or bottom frame by one pixel, resulting in a 45 pixel gap between left view and right view.
Anyway could someone point me in the right direction? Input frame would be 1920x2206 (left view on top, 46 pixel gap, then right view on bottom), then the shader shifts the top 1920x1080 pixels down by one pixel.
Hope that makes sense!
Thanks
JanWillem32
8th May 2015, 14:46
That one's easy. Good luck with getting things to work.// shift the top half of the image down by one pixel
sampler s0 : register(s0);
float4 c1 : register(c1);
float4 main(float2 tex : TEXCOORD0) : COLOR
{
if(tex.y < .5) tex.y += c1.y;
return tex2D(s0, tex);
}
RenderGuy2
8th May 2015, 16:57
Thank you JanWillem32.
I look forward to testing this surprisingly short shader.
I was thinking I would need to register the top and bottom separately in addition the original sampler.
JanWillem32
8th May 2015, 20:21
If you would have two actual input frames in the renderer, that would indeed be the case. If the image is merged before the renderer to 1920x2206 or 1920x2205 frames, then not.
RenderGuy2
10th May 2015, 03:14
JanWillem32, Thank you. With this 1 pixel shift I am able to get pixel-perfect registration between left and right views and am able to let the video renderer do all of the chroma interpolation.
Eventually I hope to purchase a passive 3d display. Can you tell me what I am doing wrong with this shader?
sampler s : register(s0);
float2 size : register(c0);
#define LV p.y -= size.y / 4.0; \
if (any(p < float2(0.0, 0.0)) || any(p > float2(size.x, size.y / 2.0)))\
return float4(0.0, 0.0, 0.0, 1.0);
#define RV p.y += size.y / 4.0; \
if (any(p < float2(0.0, size.y / 2.0)) || any(p > size)) \
return float4(0.0, 0.0, 0.0, 1.0);
float4 main(float2 tc : TEXCOORD0) : COLOR
{
float2 p = tc * size;
if ((p.y % 2) == 0)
{
LV
}
else
{
RV
}
return tex2D(s, p / size);
}
I am attempting to Interleave full over/under stereo video by selecting even rows of pixels from one view and odd rows from the other, but obviously I have made a mistake as this is only returning one view. Thanks again for your help.
JanWillem32
10th May 2015, 14:42
You're trying to use a floating-point modulo in a case where it will never work. Use the frac intrinsic instead.
-Use the result of p.y*.5.
This will give results of .25 on the first pixel, .75 on the next, 1.25 on the next, 1.75 on the next, etc..
-Use the result of frac(oldresult)
This will give results of .25 on the first pixel, .75 on the next, .25 on the next, .75 on the next, etc..
-Compare (oldresult < .5) in the if statement
I'm not sure what you are trying to do in the LV and RV blocks. If it's just to get interleaved lines (left, right, left, right alternating), it's way too complicated. Also, If you're trying to merge a 1920×2160 frame, you're discarding half of all pixels instead of blending each set of two pixels.
RenderGuy2
10th May 2015, 15:49
Okay, thanks. I'll give frac intrinsic a try. I was trying to get interleaved lines (left, right, left, right alternating). I wanted to avoid blending as I was imagining what would happen to a pixel-scale checkerboard pattern. Granted, this is a synthetic example, but perhaps for fine patterns it would be better to avoid blending.
If it isn't too much trouble, would you mind posting an example shader to interleave lines/rows of pixels (even lines from top half, odd lines from bottom half)?
Thanks again.
RenderGuy2
10th May 2015, 16:30
JanWillem32, Frac intrinsic worked perfectly. I was thinking row values would be integer, but p is not. Anyway, thanks again.
JanWillem32
10th May 2015, 16:33
So just to be sure, you are looking for blending a 1920×2160 frame to 1920×1080 top-bottom alternating lines? I can try that.
Also, note that it's better to blend pixels, else fine details will alias horribly and I also wouldn't know which of each two pixels to discard, too.
edit, a first try:// interleave a 1920×2160 frame to 1920×1080 top-bottom alternating lines (blended per line)
sampler s0;
float2 c0;
float2 c1;
float4 main(float2 tex : TEXCOORD0) : COLOR
{
if (tex.y < .25 || tex.y > .75) return float4(0.0, 0.0, 0.0, 1.0);// discard the upper and lower quarters
float direction = c1.y;
if (frac(tex.y*c0.y*.5) >= .5) {// lines for the right view
direction = -direction;// bottom-to-top
tex.y+= .5;
}
tex.y-= .25;
return (tex2D(s0, tex)+tex2D(s0, float2(tex.x, tex.y+direction)))*.5;// blend the two pixels of the frame and output
}
RenderGuy2
10th May 2015, 22:27
Your first try attempt appears to work perfectly. I suspect you are correct about blending rows being advantageous, especially for "real" content. Blending does bad things to synthetic patterns containing checkerboards or horizontal lines, reducing them to middle-gray, but it's unlikely that this sort of pattern would appear in films.
Now I just need to wait for passive 3d 4k oled televisions to cost less than used cars:)
Thanks again.
foxyshadis
11th May 2015, 02:37
Anything else?
Hi Jan, I tried to adapt the SBS Lanczos shader to just cutting off half the image and scaling the rest up, so one file can be used for 2D or 3D display. That's dirt simple if I want ugly NN resampling:
float4 main(float2 tex : TEXCOORD0) : COLOR
{
tex.x = tex.x / 2;
float4 l = tex2D(s0, tex);
return float4(l.r, l.g, l.b, 1);
}
But I'm having trouble getting your shader to work. First, if I leave the if(any(float4(tex, -tex) < float4(c2.xy, -c2.zw))) line in, it always returns the original, no matter what. Removing that, and editing the end to:
float3 Q0 = float3(tex2D(s0, float2(clampedouterpositions.x, tex.y)).rgb);
float3 Q1 = float3(tex2D(s0, tex).rgb);
float3 Q2 = float3(tex2D(s0, float2(tex.x+dx, tex.y)).rgb);
float3 Q3 = float3(tex2D(s0, float2(clampedouterpositions.y, tex.y)).rgb);
return (w.x*Q0+w.y*Q1+w.z*Q2+w.w*Q3).rgbb; // interpolate and output
It works and looks sharp, but I get horizontal streaks through the video (https://dl.dropboxusercontent.com/u/54412753/doom9/3d-streaks.png), mostly dark and sometimes reddish (sometimes they get bright red). Am I doing something wrong? This is with Optimus HD 4600/755M on Win8.1, btw.
JanWillem32
11th May 2015, 15:13
The c2 register contains the video outlines (normalized, and on pixel borders, so you can directly compare it to TEXCOORD0) in my version of the renderer. Other renderers don't implement it (yet). You should use the simple fullscreen version in other cases.
I called it the "extended register layout (usage of register c2)" in my first post of this thread. Here's a basic Lanczos2 resizer. It seems to work for me.// 2 times vertical Lanczos2 magnification
sampler s0;
float2 c0;
float2 c1;
static const float PI = acos(-1.);
static const float4 wset = float4(1.25, .25, .75, 1.75)*PI;
static const float4 wsets = wset*.5;
static const float4 wp = sin(wset)*sin(wsets)/(wset*wsets);
static const float2 wc = (1.-dot(1., wp))*float2(.75, .25);// compensate truncated window factor by bilinear factoring on the two nearest samples
static const float4 w = wp+float4(0., wc, 0.);
float4 main(float2 tex : TEXCOORD0) : COLOR
{
float oy = tex.y*c0.y-.5;// video-relative y position
// detect half of the even or odd coordinates
oy = oy*.5+.25;// the two images are half-width, compensate for that
float n = frac(oy);
float dy = (n > .5)? -c1.y : c1.y;// even y positive, odd y negative
float pixeloffset = n-oy;// round half-pixel offsets down
// adjust the y coordinate of the source position to the correct side of the video image using the pixel offset we just calculated
tex.y += pixeloffset*c1.y;
float center = .5;
float texr = tex.y+center-c1.y;// adjust the pixel offset to the video frame on the bottom side of the video data
// top view
float4 Q0 = tex2D(s0, float2(tex.x, tex.y-dy));
float4 Q1 = tex2D(s0, tex);
float4 Q2 = tex2D(s0, float2(tex.x, tex.y+dy));
float4 Q3 = tex2D(s0, float2(tex.x, tex.y+dy+dy));
/* bottom view
float4 Q0 = tex2D(s0, float2(tex.x, texr-dy));
float4 Q1 = tex2D(s0, float2(tex.x, texr));
float4 Q2 = tex2D(s0, float2(tex.x, texr+dy));
float4 Q3 = tex2D(s0, float2(tex.x, texr+dy+dy));
*/
return w.x*Q0+w.y*Q1+w.z*Q2+w.w*Q3;// interpolate and output
}Note that the regular pan and scan functions can do this, too.
foxyshadis
12th May 2015, 01:29
Thank you! I'm still pretty new to writing shaders, and didn't realize c2 wasn't available in BE. I'll remember to test your HC before reporting in the future.
JanWillem32
12th May 2015, 14:49
I actually added values to c1.zw as well (the integral left and top distance in pixels from the image relative to the display device). It's otherwise just impossible to get some 3D-vision related shaders to work correctly. The basic fullscreen shaders in this thread only work correctly in fullscreen with the top-left corner(s) on an even amount of pixel distance.
JanWillem32
14th October 2015, 19:09
I was asked to renew the set of shaders I wrote, but with reversed eyes as an option.
As I previously stated, I will not be writing direct Dubois Anaglyph Algorithm shaders. People can just use the XLRCAM shader to modify saturation, colorfulness or chroma before these conversion to red-cyan anaglyph shaders to achieve the proper effect.// (C) 2015 Jan-Willem Krans (janwillem32 <at> hotmail.com)
// This file is part of Video pixel shader pack.
// This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
// You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
// top-and-bottom to red-cyan anaglyph 3D stereoscopic view with compensated Lanczos2 resizing
// This shader should be run as a screen space pixel shader.
// This shader requires compiling with ps_2_0, but higher is better, see http://en.wikipedia.org/wiki/Pixel_shader to look up what PS version your video card supports.
// For this shader to work correctly, the video height in pixels on screen has to be an even amount.
// ReversedEyes, swap channels for each eye
#define ReversedEyes 0
sampler s0;
float2 c0;
float2 c1;
float4 c2;
static const float PI = acos(-1.);
static const float4 wset = float4(1.25, .25, .75, 1.75)*PI;
static const float4 wsets = wset*.5;
static const float4 wp = sin(wset)*sin(wsets)/(wset*wsets);
static const float2 wc = (1.-dot(1., wp))*float2(.75, .25);// compensate truncated window factor by linear factoring on the two nearest samples
static const float4 w = wp+float4(0., wc, 0.);
float4 main(float2 tex : TEXCOORD0) : COLOR
{
if(any(float4(tex, -tex) < float4(c2.xy, -c2.zw))) return tex2D(s0, tex);// discard if outside video rect
else {
float oy = (tex.y-c2.y)*c0.y-.5;// video-relative y position
// detect half of the even or odd coordinates
oy = oy*.5+.25;// the two images are half-width, compensate for that
float n = frac(oy);
float dy = (n > .5)? -c1.y : c1.y;// even y positive, odd y negative
float pixeloffset = n-oy;// round half-pixel offsets down
// adjust the y coordinate of the source position to the correct side of the video image using the pixel offset we just calculated
tex.y += pixeloffset*c1.y;
float center = .5*(c2.w-c2.y);
float texr = tex.y+center-c1.y;// adjust the pixel offset to the video frame on the bottom side of the video data
// resolve position data and limit the outer samples to the active video rectangle
float2 outeroffsets = float2(-dy, dy+dy);
float4 outerpositions = float2(tex.y, texr).xxyy+outeroffsets.xyxy;
float4 limits = float3(c2.yw, center).xzzy+(c1.yy*float2(.5, -.5)).xyxy;
float4 clampedouterpositions = clamp(outerpositions, limits.xxzz, limits.yyww);
// sample from the left and right images and only pass-trough the red channel for the left, and the green and blue channels for the right view
#if ReversedEyes
float3 Q0 = float3(tex2D(s0, float2(tex.x, clampedouterpositions.z)).r, tex2D(s0, float2(tex.x, clampedouterpositions.x)).gb);
float3 Q1 = float3(tex2D(s0, float2(tex.x, texr)).r, tex2D(s0, tex).gb);
float3 Q2 = float3(tex2D(s0, float2(tex.x, texr+dy)).r, tex2D(s0, float2(tex.x, tex.y+dy)).gb);
float3 Q3 = float3(tex2D(s0, float2(tex.x, clampedouterpositions.w)).r, tex2D(s0, float2(tex.x, clampedouterpositions.y)).gb);
#else
float3 Q0 = float3(tex2D(s0, float2(tex.x, clampedouterpositions.x)).r, tex2D(s0, float2(tex.x, clampedouterpositions.z)).gb);
float3 Q1 = float3(tex2D(s0, tex).r, tex2D(s0, float2(tex.x, texr)).gb);
float3 Q2 = float3(tex2D(s0, float2(tex.x, tex.y+dy)).r, tex2D(s0, float2(tex.x, texr+dy)).gb);
float3 Q3 = float3(tex2D(s0, float2(tex.x, clampedouterpositions.y)).r, tex2D(s0, float2(tex.x, clampedouterpositions.w)).gb);
#endif
return (w.x*Q0+w.y*Q1+w.z*Q2+w.w*Q3).rgbb;}// interpolate and output
}// (C) 2015 Jan-Willem Krans (janwillem32 <at> hotmail.com)
// This file is part of Video pixel shader pack.
// This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
// You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
// side-by-side to red-cyan anaglyph 3D stereoscopic view with compensated Lanczos2 resizing
// This shader should be run as a screen space pixel shader.
// This shader requires compiling with ps_2_0, but higher is better, see http://en.wikipedia.org/wiki/Pixel_shader to look up what PS version your video card supports.
// For this shader to work correctly, the video width in pixels on screen has to be an even amount.
// ReversedEyes, swap channels for each eye
#define ReversedEyes 0
sampler s0;
float2 c0;
float2 c1;
float4 c2;
static const float PI = acos(-1.);
static const float4 wset = float4(1.25, .25, .75, 1.75)*PI;
static const float4 wsets = wset*.5;
static const float4 wp = sin(wset)*sin(wsets)/(wset*wsets);
static const float2 wc = (1.-dot(1., wp))*float2(.75, .25);// compensate truncated window factor by bilinear factoring on the two nearest samples
static const float4 w = wp+float4(0., wc, 0.);
float4 main(float2 tex : TEXCOORD0) : COLOR
{
if(any(float4(tex, -tex) < float4(c2.xy, -c2.zw))) return tex2D(s0, tex);// discard if outside video rect
else {
float ox = (tex.x-c2.x)*c0.x-.5;// video-relative x position
// detect half of the even or odd coordinates
ox = ox*.5+.25;// the two images are half-width, compensate for that
float n = frac(ox);
float dx = (n > .5)? -c1.x : c1.x;// even y positive, odd y negative
float pixeloffset = n-ox;// round half-pixel offsets down
// adjust the x coordinate of the source position to the correct side of the video image using the pixel offset we just calculated
tex.x += pixeloffset*c1.x;
float center = .5*(c2.z-c2.x);
float texr = tex.x+center-c1.x;// adjust the pixel offset to the video frame on the right side of the video data
// resolve position data and limit the outer samples to the active video rectangle
float2 outeroffsets = float2(-dx, dx+dx);
float4 outerpositions = float2(tex.x, texr).xxyy+outeroffsets.xyxy;
float4 limits = float3(c2.xz, center).xzzy+(c1.xx*float2(.5, -.5)).xyxy;
float4 clampedouterpositions = clamp(outerpositions, limits.xxzz, limits.yyww);
// sample from the left and right images and only pass-trough the red channel for the left, and the green and blue channels for the right view
#if ReversedEyes
float3 Q0 = float3(tex2D(s0, float2(clampedouterpositions.z, tex.y)).r, tex2D(s0, float2(clampedouterpositions.x, tex.y)).gb);
float3 Q1 = float3(tex2D(s0, float2(texr, tex.y)).r, tex2D(s0, tex).gb);
float3 Q2 = float3(tex2D(s0, float2(texr+dx, tex.y)).r, tex2D(s0, float2(tex.x+dx, tex.y)).gb);
float3 Q3 = float3(tex2D(s0, float2(clampedouterpositions.w, tex.y)).r, tex2D(s0, float2(clampedouterpositions.y, tex.y)).gb);
#else
float3 Q0 = float3(tex2D(s0, float2(clampedouterpositions.x, tex.y)).r, tex2D(s0, float2(clampedouterpositions.z, tex.y)).gb);
float3 Q1 = float3(tex2D(s0, tex).r, tex2D(s0, float2(texr, tex.y)).gb);
float3 Q2 = float3(tex2D(s0, float2(tex.x+dx, tex.y)).r, tex2D(s0, float2(texr+dx, tex.y)).gb);
float3 Q3 = float3(tex2D(s0, float2(clampedouterpositions.y, tex.y)).r, tex2D(s0, float2(clampedouterpositions.w, tex.y)).gb);
#endif
return (w.x*Q0+w.y*Q1+w.z*Q2+w.w*Q3).rgbb;}// interpolate and output
}
JanWillem32
14th October 2015, 19:09
// (C) 2015 Jan-Willem Krans (janwillem32 <at> hotmail.com)
// This file is part of Video pixel shader pack.
// This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
// You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
// basic fullscreen side-by-side to red-cyan anaglyph 3D stereoscopic view with compensated Lanczos2 resizing
// This shader should be run as a screen space pixel shader.
// This shader requires compiling with ps_2_0, but higher is better, see http://en.wikipedia.org/wiki/Pixel_shader to look up what PS version your video card supports.
// For this shader to work correctly, the video width in pixels on screen has to be an even amount.
// ReversedEyes, swap channels for each eye
#define ReversedEyes 0
sampler s0;
float2 c0;
float2 c1;
static const float PI = acos(-1.);
static const float4 wset = float4(1.25, .25, .75, 1.75)*PI;
static const float4 wsets = wset*.5;
static const float4 wp = sin(wset)*sin(wsets)/(wset*wsets);
static const float2 wc = (1.-dot(1., wp))*float2(.75, .25);// compensate truncated window factor by linear factoring on the two nearest samples
static const float4 w = wp+float4(0., wc, 0.);
float4 main(float2 tex : TEXCOORD0) : COLOR
{
float ox = tex.x*c0.x-.5;// video-relative x position
// detect half of the even or odd coordinates
ox = ox*.5+.25;// the two images are half-width, compensate for that
float n = frac(ox);
float dx = (n > .5)? -c1.x : c1.x;// even x positive, odd x negative
float pixeloffset = n-ox;// round half-pixel offsets down
// adjust the x coordinate of the source position to the correct side of the video image using the pixel offset we just calculated
tex.x += pixeloffset*c1.x;
float center = .5;
float texr = tex.x+center-c1.x;// adjust the pixel offset to the video frame on the right side of the video data
// sample from the left and right images and only pass-trough the red channel for the left, and the green and blue channels for the right view
#if ReversedEyes
float3 Q0 = float3(tex2D(s0, float2(texr-dx, tex.y)).r, tex2D(s0, float2(tex.x-dx, tex.y)).gb);
float3 Q1 = float3(tex2D(s0, float2(texr, tex.y)).r, tex2D(s0, tex).gb);
float3 Q2 = float3(tex2D(s0, float2(texr+dx, tex.y)).r, tex2D(s0, float2(tex.x+dx, tex.y)).gb);
float3 Q3 = float3(tex2D(s0, float2(texr+dx+dx, tex.y)).r, tex2D(s0, float2(tex.x+dx+dx, tex.y)).gb);
#else
float3 Q0 = float3(tex2D(s0, float2(tex.x-dx, tex.y)).r, tex2D(s0, float2(texr-dx, tex.y)).gb);
float3 Q1 = float3(tex2D(s0, tex).r, tex2D(s0, float2(texr, tex.y)).gb);
float3 Q2 = float3(tex2D(s0, float2(tex.x+dx, tex.y)).r, tex2D(s0, float2(texr+dx, tex.y)).gb);
float3 Q3 = float3(tex2D(s0, float2(tex.x+dx+dx, tex.y)).r, tex2D(s0, float2(texr+dx+dx, tex.y)).gb);
#endif
return (w.x*Q0+w.y*Q1+w.z*Q2+w.w*Q3).rgbb;// interpolate and output
}// (C) 2015 Jan-Willem Krans (janwillem32 <at> hotmail.com)
// This file is part of Video pixel shader pack.
// This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
// You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
// basic fullscreen top-and-bottom to red-cyan anaglyph 3D stereoscopic view with compensated Lanczos2 resizing
// This shader should be run as a screen space pixel shader.
// This shader requires compiling with ps_2_0, but higher is better, see http://en.wikipedia.org/wiki/Pixel_shader to look up what PS version your video card supports.
// For this shader to work correctly, the video height in pixels on screen has to be an even amount.
// ReversedEyes, swap channels for each eye
#define ReversedEyes 0
sampler s0;
float2 c0;
float2 c1;
static const float PI = acos(-1.);
static const float4 wset = float4(1.25, .25, .75, 1.75)*PI;
static const float4 wsets = wset*.5;
static const float4 wp = sin(wset)*sin(wsets)/(wset*wsets);
static const float2 wc = (1.-dot(1., wp))*float2(.75, .25);// compensate truncated window factor by linear factoring on the two nearest samples
static const float4 w = wp+float4(0., wc, 0.);
float4 main(float2 tex : TEXCOORD0) : COLOR
{
float oy = tex.y*c0.y-.5;// video-relative y position
// detect half of the even or odd coordinates
oy = oy*.5+.25;// the two images are half-width, compensate for that
float n = frac(oy);
float dy = (n > .5)? -c1.y : c1.y;// even y positive, odd y negative
float pixeloffset = n-oy;// round half-pixel offsets down
// adjust the y coordinate of the source position to the correct side of the video image using the pixel offset we just calculated
tex.y += pixeloffset*c1.y;
float center = .5;
float texr = tex.y+center-c1.y;// adjust the pixel offset to the video frame on the bottom side of the video data
// sample from the left and right images and only pass-trough the red channel for the left, and the green and blue channels for the right view
#if ReversedEyes
float3 Q0 = float3(tex2D(s0, float2(tex.x, texr-dy)).r, tex2D(s0, float2(tex.x, tex.y-dy)).gb);
float3 Q1 = float3(tex2D(s0, float2(tex.x, texr)).r, tex2D(s0, tex).gb);
float3 Q2 = float3(tex2D(s0, float2(tex.x, texr+dy)).r, tex2D(s0, float2(tex.x, tex.y+dy)).gb);
float3 Q3 = float3(tex2D(s0, float2(tex.x, texr+dy+dy)).r, tex2D(s0, float2(tex.x, tex.y+dy+dy)).gb);
#else
float3 Q0 = float3(tex2D(s0, float2(tex.x, tex.y-dy)).r, tex2D(s0, float2(tex.x, texr-dy)).gb);
float3 Q1 = float3(tex2D(s0, tex).r, tex2D(s0, float2(tex.x, texr)).gb);
float3 Q2 = float3(tex2D(s0, float2(tex.x, tex.y+dy)).r, tex2D(s0, float2(tex.x, texr+dy)).gb);
float3 Q3 = float3(tex2D(s0, float2(tex.x, tex.y+dy+dy)).r, tex2D(s0, float2(tex.x, texr+dy+dy)).gb);
#endif
return (w.x*Q0+w.y*Q1+w.z*Q2+w.w*Q3).rgbb;// interpolate and output
}
ashlar42
21st October 2015, 12:06
Any chance of getting a version for ColorCode 3D anaglyph? https://en.wikipedia.org/wiki/Anaglyph_3D#Types
I bought a couple of sets of eyeglasses for this but I failed to find a working solution for them. I opted for those glasses based on better color reproduction that they're supposed to offer.
JanWillem32
21st October 2015, 18:19
I'd have to re-shuffle the color outputs to do amber-blue. I can do that. A few hours, please.
JanWillem32
21st October 2015, 20:49
// (C) 2015 Jan-Willem Krans (janwillem32 <at> hotmail.com)
// This file is part of Video pixel shader pack.
// This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
// You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
// basic fullscreen side-by-side to yellow-blue anaglyph 3D stereoscopic view with compensated Lanczos2 resizing
// This shader should be run as a screen space pixel shader.
// This shader requires compiling with ps_2_0, but higher is better, see http://en.wikipedia.org/wiki/Pixel_shader to look up what PS version your video card supports.
// For this shader to work correctly, the video width in pixels on screen has to be an even amount.
// ReversedEyes, swap channels for each eye
#define ReversedEyes 0
sampler s0;
float2 c0;
float2 c1;
static const float PI = acos(-1.);
static const float4 wset = float4(1.25, .25, .75, 1.75)*PI;
static const float4 wsets = wset*.5;
static const float4 wp = sin(wset)*sin(wsets)/(wset*wsets);
static const float2 wc = (1.-dot(1., wp))*float2(.75, .25);// compensate truncated window factor by linear factoring on the two nearest samples
static const float4 w = wp+float4(0., wc, 0.);
float4 main(float2 tex : TEXCOORD0) : COLOR
{
float ox = tex.x*c0.x-.5;// video-relative x position
// detect half of the even or odd coordinates
ox = ox*.5+.25;// the two images are half-width, compensate for that
float n = frac(ox);
float dx = (n > .5)? -c1.x : c1.x;// even x positive, odd x negative
float pixeloffset = n-ox;// round half-pixel offsets down
// adjust the x coordinate of the source position to the correct side of the video image using the pixel offset we just calculated
tex.x += pixeloffset*c1.x;
float center = .5;
float texr = tex.x+center-c1.x;// adjust the pixel offset to the video frame on the right side of the video data
// sample from the left and right images and only pass-trough the red channel for the left, and the green and blue channels for the right view
#if ReversedEyes
float3 Q0 = float3(tex2D(s0, float2(texr-dx, tex.y)).rg, tex2D(s0, float2(tex.x-dx, tex.y)).b);
float3 Q1 = float3(tex2D(s0, float2(texr, tex.y)).rg, tex2D(s0, tex).b);
float3 Q2 = float3(tex2D(s0, float2(texr+dx, tex.y)).rg, tex2D(s0, float2(tex.x+dx, tex.y)).b);
float3 Q3 = float3(tex2D(s0, float2(texr+dx+dx, tex.y)).rg, tex2D(s0, float2(tex.x+dx+dx, tex.y)).b);
#else
float3 Q0 = float3(tex2D(s0, float2(tex.x-dx, tex.y)).rg, tex2D(s0, float2(texr-dx, tex.y)).b);
float3 Q1 = float3(tex2D(s0, tex).rg, tex2D(s0, float2(texr, tex.y)).b);
float3 Q2 = float3(tex2D(s0, float2(tex.x+dx, tex.y)).rg, tex2D(s0, float2(texr+dx, tex.y)).b);
float3 Q3 = float3(tex2D(s0, float2(tex.x+dx+dx, tex.y)).rg, tex2D(s0, float2(texr+dx+dx, tex.y)).b);
#endif
return (w.x*Q0+w.y*Q1+w.z*Q2+w.w*Q3).rgbb;// interpolate and output
}// (C) 2015 Jan-Willem Krans (janwillem32 <at> hotmail.com)
// This file is part of Video pixel shader pack.
// This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
// You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
// basic fullscreen top-and-bottom to yellow-blue anaglyph 3D stereoscopic view with compensated Lanczos2 resizing
// This shader should be run as a screen space pixel shader.
// This shader requires compiling with ps_2_0, but higher is better, see http://en.wikipedia.org/wiki/Pixel_shader to look up what PS version your video card supports.
// For this shader to work correctly, the video height in pixels on screen has to be an even amount.
// ReversedEyes, swap channels for each eye
#define ReversedEyes 0
sampler s0;
float2 c0;
float2 c1;
static const float PI = acos(-1.);
static const float4 wset = float4(1.25, .25, .75, 1.75)*PI;
static const float4 wsets = wset*.5;
static const float4 wp = sin(wset)*sin(wsets)/(wset*wsets);
static const float2 wc = (1.-dot(1., wp))*float2(.75, .25);// compensate truncated window factor by linear factoring on the two nearest samples
static const float4 w = wp+float4(0., wc, 0.);
float4 main(float2 tex : TEXCOORD0) : COLOR
{
float oy = tex.y*c0.y-.5;// video-relative y position
// detect half of the even or odd coordinates
oy = oy*.5+.25;// the two images are half-width, compensate for that
float n = frac(oy);
float dy = (n > .5)? -c1.y : c1.y;// even y positive, odd y negative
float pixeloffset = n-oy;// round half-pixel offsets down
// adjust the y coordinate of the source position to the correct side of the video image using the pixel offset we just calculated
tex.y += pixeloffset*c1.y;
float center = .5;
float texr = tex.y+center-c1.y;// adjust the pixel offset to the video frame on the bottom side of the video data
// sample from the left and right images and only pass-trough the red channel for the left, and the green and blue channels for the right view
#if ReversedEyes
float3 Q0 = float3(tex2D(s0, float2(tex.x, texr-dy)).rg, tex2D(s0, float2(tex.x, tex.y-dy)).b);
float3 Q1 = float3(tex2D(s0, float2(tex.x, texr)).rg, tex2D(s0, tex).b);
float3 Q2 = float3(tex2D(s0, float2(tex.x, texr+dy)).rg, tex2D(s0, float2(tex.x, tex.y+dy)).b);
float3 Q3 = float3(tex2D(s0, float2(tex.x, texr+dy+dy)).rg, tex2D(s0, float2(tex.x, tex.y+dy+dy)).b);
#else
float3 Q0 = float3(tex2D(s0, float2(tex.x, tex.y-dy)).rg, tex2D(s0, float2(tex.x, texr-dy)).b);
float3 Q1 = float3(tex2D(s0, tex).rg, tex2D(s0, float2(tex.x, texr)).b);
float3 Q2 = float3(tex2D(s0, float2(tex.x, tex.y+dy)).rg, tex2D(s0, float2(tex.x, texr+dy)).b);
float3 Q3 = float3(tex2D(s0, float2(tex.x, tex.y+dy+dy)).rg, tex2D(s0, float2(tex.x, texr+dy+dy)).b);
#endif
return (w.x*Q0+w.y*Q1+w.z*Q2+w.w*Q3).rgbb;// interpolate and output
}
JanWillem32
21st October 2015, 20:50
// (C) 2015 Jan-Willem Krans (janwillem32 <at> hotmail.com)
// This file is part of Video pixel shader pack.
// This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
// You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
// side-by-side to yellow-blue anaglyph 3D stereoscopic view with compensated Lanczos2 resizing
// This shader should be run as a screen space pixel shader.
// This shader requires compiling with ps_2_0, but higher is better, see http://en.wikipedia.org/wiki/Pixel_shader to look up what PS version your video card supports.
// For this shader to work correctly, the video width in pixels on screen has to be an even amount.
// ReversedEyes, swap channels for each eye
#define ReversedEyes 0
sampler s0;
float2 c0;
float2 c1;
float4 c2;
static const float PI = acos(-1.);
static const float4 wset = float4(1.25, .25, .75, 1.75)*PI;
static const float4 wsets = wset*.5;
static const float4 wp = sin(wset)*sin(wsets)/(wset*wsets);
static const float2 wc = (1.-dot(1., wp))*float2(.75, .25);// compensate truncated window factor by bilinear factoring on the two nearest samples
static const float4 w = wp+float4(0., wc, 0.);
float4 main(float2 tex : TEXCOORD0) : COLOR
{
if(any(float4(tex, -tex) < float4(c2.xy, -c2.zw))) return tex2D(s0, tex);// discard if outside video rect
else {
float ox = (tex.x-c2.x)*c0.x-.5;// video-relative x position
// detect half of the even or odd coordinates
ox = ox*.5+.25;// the two images are half-width, compensate for that
float n = frac(ox);
float dx = (n > .5)? -c1.x : c1.x;// even y positive, odd y negative
float pixeloffset = n-ox;// round half-pixel offsets down
// adjust the x coordinate of the source position to the correct side of the video image using the pixel offset we just calculated
tex.x += pixeloffset*c1.x;
float center = .5*(c2.z-c2.x);
float texr = tex.x+center-c1.x;// adjust the pixel offset to the video frame on the right side of the video data
// resolve position data and limit the outer samples to the active video rectangle
float2 outeroffsets = float2(-dx, dx+dx);
float4 outerpositions = float2(tex.x, texr).xxyy+outeroffsets.xyxy;
float4 limits = float3(c2.xz, center).xzzy+(c1.xx*float2(.5, -.5)).xyxy;
float4 clampedouterpositions = clamp(outerpositions, limits.xxzz, limits.yyww);
// sample from the left and right images and only pass-trough the red channel for the left, and the green and blue channels for the right view
#if ReversedEyes
float3 Q0 = float3(tex2D(s0, float2(clampedouterpositions.z, tex.y)).rg, tex2D(s0, float2(clampedouterpositions.x, tex.y)).b);
float3 Q1 = float3(tex2D(s0, float2(texr, tex.y)).rg, tex2D(s0, tex).b);
float3 Q2 = float3(tex2D(s0, float2(texr+dx, tex.y)).rg, tex2D(s0, float2(tex.x+dx, tex.y)).b);
float3 Q3 = float3(tex2D(s0, float2(clampedouterpositions.w, tex.y)).rg, tex2D(s0, float2(clampedouterpositions.y, tex.y)).b);
#else
float3 Q0 = float3(tex2D(s0, float2(clampedouterpositions.x, tex.y)).rg, tex2D(s0, float2(clampedouterpositions.z, tex.y)).b);
float3 Q1 = float3(tex2D(s0, tex).rg, tex2D(s0, float2(texr, tex.y)).b);
float3 Q2 = float3(tex2D(s0, float2(tex.x+dx, tex.y)).rg, tex2D(s0, float2(texr+dx, tex.y)).b);
float3 Q3 = float3(tex2D(s0, float2(clampedouterpositions.y, tex.y)).rg, tex2D(s0, float2(clampedouterpositions.w, tex.y)).b);
#endif
return (w.x*Q0+w.y*Q1+w.z*Q2+w.w*Q3).rgbb;}// interpolate and output
}// (C) 2015 Jan-Willem Krans (janwillem32 <at> hotmail.com)
// This file is part of Video pixel shader pack.
// This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
// You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
// top-and-bottom to yellow-blue anaglyph 3D stereoscopic view with compensated Lanczos2 resizing
// This shader should be run as a screen space pixel shader.
// This shader requires compiling with ps_2_0, but higher is better, see http://en.wikipedia.org/wiki/Pixel_shader to look up what PS version your video card supports.
// For this shader to work correctly, the video height in pixels on screen has to be an even amount.
// ReversedEyes, swap channels for each eye
#define ReversedEyes 0
sampler s0;
float2 c0;
float2 c1;
float4 c2;
static const float PI = acos(-1.);
static const float4 wset = float4(1.25, .25, .75, 1.75)*PI;
static const float4 wsets = wset*.5;
static const float4 wp = sin(wset)*sin(wsets)/(wset*wsets);
static const float2 wc = (1.-dot(1., wp))*float2(.75, .25);// compensate truncated window factor by linear factoring on the two nearest samples
static const float4 w = wp+float4(0., wc, 0.);
float4 main(float2 tex : TEXCOORD0) : COLOR
{
if(any(float4(tex, -tex) < float4(c2.xy, -c2.zw))) return tex2D(s0, tex);// discard if outside video rect
else {
float oy = (tex.y-c2.y)*c0.y-.5;// video-relative y position
// detect half of the even or odd coordinates
oy = oy*.5+.25;// the two images are half-width, compensate for that
float n = frac(oy);
float dy = (n > .5)? -c1.y : c1.y;// even y positive, odd y negative
float pixeloffset = n-oy;// round half-pixel offsets down
// adjust the y coordinate of the source position to the correct side of the video image using the pixel offset we just calculated
tex.y += pixeloffset*c1.y;
float center = .5*(c2.w-c2.y);
float texr = tex.y+center-c1.y;// adjust the pixel offset to the video frame on the bottom side of the video data
// resolve position data and limit the outer samples to the active video rectangle
float2 outeroffsets = float2(-dy, dy+dy);
float4 outerpositions = float2(tex.y, texr).xxyy+outeroffsets.xyxy;
float4 limits = float3(c2.yw, center).xzzy+(c1.yy*float2(.5, -.5)).xyxy;
float4 clampedouterpositions = clamp(outerpositions, limits.xxzz, limits.yyww);
// sample from the left and right images and only pass-trough the red channel for the left, and the green and blue channels for the right view
#if ReversedEyes
float3 Q0 = float3(tex2D(s0, float2(tex.x, clampedouterpositions.z)).rg, tex2D(s0, float2(tex.x, clampedouterpositions.x)).b);
float3 Q1 = float3(tex2D(s0, float2(tex.x, texr)).rg, tex2D(s0, tex).b);
float3 Q2 = float3(tex2D(s0, float2(tex.x, texr+dy)).rg, tex2D(s0, float2(tex.x, tex.y+dy)).b);
float3 Q3 = float3(tex2D(s0, float2(tex.x, clampedouterpositions.w)).rg, tex2D(s0, float2(tex.x, clampedouterpositions.y)).b);
#else
float3 Q0 = float3(tex2D(s0, float2(tex.x, clampedouterpositions.x)).rg, tex2D(s0, float2(tex.x, clampedouterpositions.z)).b);
float3 Q1 = float3(tex2D(s0, tex).rg, tex2D(s0, float2(tex.x, texr)).b);
float3 Q2 = float3(tex2D(s0, float2(tex.x, tex.y+dy)).rg, tex2D(s0, float2(tex.x, texr+dy)).b);
float3 Q3 = float3(tex2D(s0, float2(tex.x, clampedouterpositions.y)).rg, tex2D(s0, float2(tex.x, clampedouterpositions.w)).b);
#endif
return (w.x*Q0+w.y*Q1+w.z*Q2+w.w*Q3).rgbb;}// interpolate and output
}
ashlar42
27th October 2015, 12:02
Hi! First of all an enormous thank you for doing this. It works! :)
Three questions:
1) You posted in rapid succession two shaders sets for side by side and top and bottom. Are they the same?
2) The reverse eyes is needed when? My glasses have, while wearing them, the amber lens on the left and blue lens on the right.
3) Where in the script are the color defined? I feel my blue lens is too... "bright" and I was wondering if I could somehow correct for that by manipulating the script. What I mean is that I have, I think that's the word, some crosstalk between the two lenses. I see that on subtitles, where the white color makes it easier to check. Maybe by manipulating the colors I could try and reduce it, I don't know...
Again, an heartfelt thank you. :)
JanWillem32
27th October 2015, 15:12
1) You posted in rapid succession two shaders sets for side by side and top and bottom. Are they the same?No, the basic fullscreen versions are renderer-independent, but the other two require the extended register layout I implemented in my version of the renderer.2) The reverse eyes is needed when? My glasses have, while wearing them, the amber lens on the left and blue lens on the right.The reverse eyes feature was requested some time ago for the red-cyan version of the script. I simply copied the code from there. In my opinion it's not useful.3) Where in the script are the color defined? I feel my blue lens is too... "bright" and I was wondering if I could somehow correct for that by manipulating the script. What I mean is that I have, I think that's the word, some crosstalk between the two lenses. I see that on subtitles, where the white color makes it easier to check. Maybe by manipulating the colors I could try and reduce it, I don't know...The color separation is absolute. Red, green and blue are separated from the beginning of the sampling (using the .rg and .b masks). There is no possible better degree of separation in software. If there's crosstalk, it's caused by the glasses and the monitor combination. My best guess is that the monitor emits a green tint that gets picked up by the blue lens. That is unless the renderer applies sometype of color correction after this pixel shader, of course.
ashlar42
27th October 2015, 16:10
And how does it compute the amber/yellow portion? Sorry if it's a stupid question.
JanWillem32
27th October 2015, 17:30
Yellow is simply the combination of the red and green channels (.rg mask in the shader).
ashlar42
27th October 2015, 18:14
I was tricked by them talking about complex spectral curves for the lenses... Sorry.
In any case, I managed to configure the shader to load only for 3D files in Kodi (DSPlayer) and it's fantastic. I have to say that Amber/Blue glasses really are a world apart from other anaglyph configurations, colors are very close to being natural.
Thanks you, thank you, thank you!
JanWillem32
28th October 2015, 14:27
I was tricked by them talking about complex spectral curves for the lenses... Sorry.There are more factors than the spectral curves of the lenses involved.
https://en.wikipedia.org/wiki/Color_vision
https://en.wikipedia.org/wiki/LMS_color_space
https://en.wikipedia.org/wiki/Trichromacy
The color purity of the red, green and blue emissions of your monitor varies. See the CIE 1931 xy chromaticity diagram for the sRGB triangle. As you can see there, sRGB is very unpure in terms of maximum red, green and blue. Monitors, especially cheaper consumer types have similar limitations in their red, green and blue sub-pixels. (Some are even more limited than sRGB). Cross-talk through anaglyph filters is inevitable when the purity of sub-pixels is low.
There is also the issue with monitors having an 'sRGB' mode. In such a mode a monitor will blend a bit of red, green and blue to limit the colors on display to sRGB. In such a mode bits of red, green and blue will "leak" when displaying images. This is an even bigger source of cross-talk. Check your monitor configuration and make sure it's not blending colors to sRGB or some other chromatic limit. The red, green and blue sub-pixels should be directly mapped to the red, green and blue channels in software from the source image.
ashlar42
28th October 2015, 15:07
I don't know if it matters or not. But the result seems far better on my Pioneer Kuro (calibrated to rather satisfactory results with a Chroma5) than on my monitor (a Philips 1440p).
JanWillem32
28th October 2015, 18:27
Monitor calibration causes the red green and blue subpixels to blend actually (which is bad for crosstalk in an anaglyph 3D system), unless you only calibrate on the gamma curves. What kind of calibration profiles are you using?
ashlar42
31st January 2016, 13:25
I was asked to renew the set of shaders I wrote, but with reversed eyes as an option.
As I previously stated, I will not be writing direct Dubois Anaglyph Algorithm shaders. People can just use the XLRCAM shader to modify saturation, colorfulness or chroma before these conversion to red-cyan anaglyph shaders to achieve the proper effect
I came here to ask whether your work was using Dubois algorithm and I searched before asking... seeing the above I ask: what if one doesn't have the slightest idea about how to proceed with the XLRCAM shader to get the right implementation? Are there guidelines of some kind?
Thank you. :)
Hale812
29th October 2017, 07:21
Since it is very hard to get good anaglyph glasses, and getting Anachromme or nVidia Discover became impossible these days, I tried to derive a solution for simple fake chinese anaglyph glasses. These plastic glasses are priced in the range from $1 for a pair to $30 at Amazon, or 5 times cheaper at AliExpress. With an exception of complete crap made from recycled PET bags, most of them are completely the same. The same quality, the same filters, only the price and the bundles are different. Believe me, I tried ordering a score of those. Most of them go to trashcan the moment you get them. Personally, I liked only clones of nVidia for ergonomic design of branded prototype, and fine covering of side-light.
One thing, common for the most of Chinese plastic glasses, is very dark narrow-band red, displaced a bit to purple, and almost completely BLUE cyan filter. Yes, it passes some green, but the proportion is very weird, and looks like there are few local maximums in green band. Anyway, you can recognize them by blue color, even if they actually pass some green.
I would say, this gives better channel separation, than film glasses normally offer. But will not say that, because one of "pseudo-CYAN" parasitic pass-bands fall on red. And there is a strong ghosting appear, just a bit weaker than in film glasses.
Why? I believe, because of some cheap standard pigment used in casting process, not designed for optical applications.
So, that is one problem, which you can fix by carefully altering per-channel RGB amplification. Why per-cahnnel RGB and not some other more natural transformation? Don't forget that the same RGB are primaries of your monitor, and 90% of TVs on the market. It is not a cimnema-film. Probably, by that reason, such glasses give worse effect when compared with viewing printed photos.
Another very strong effect I did not know about, is psychovisual color cancellation.
Don't forget, that your retina consists of 2 kinds of photoreceptors making 4 band-sensitive channels to brain: lightness, red, green, and blue. Where red and blue bands are strongly overlapped. (It was a bad idea making red-cyan. amber-blue glasses are more natural) . And, all the bands tend to have a parasitic passband 250nm above the center. It is weaker in human eye, but stronger in other mammals.
So, guess, both, red and green have a weak component in blue band! Moreover, nonlinearity creates a threshold of sensitivity to each color.
Now, remember that we have 2 eyes. Well balanced and focused by auto tuning system in the brain. In each eye red and green are somewhat linked to blue. And red sensitivity is linked to green.
Now think, what happens, if in the left eye, you have perfectly red.But in the right eye, you don't. So the brain would search for remnants of red in green and blue channels(RIGHT eye) for balancing your vision... But what happens if there is nothing in the right eye? A black spot in the right, and a red spot in the left?
Well - my brain simply cancels the red like there is total blackness! I could not believe first time when seeing the test-spectra (made of LCD RGB primaries!!!) on my IPS monitor!
So, it is very necessary to have some lightness in the right eye, to see any red through a red-filter by the left eye!
Concluding, we have 3 problems with Chinese plastic "anaglyph" glasses.
1)disbalance between RED and CYAN, where red is usually much(~40%) darker than it should be(~1/3 - it is the passing light, still not the eye-weighted sensitivity).
2)ghosting
3)red cancellation.
With 3 you can do almost nothing, but just try to light-up the black spots in the RIGHT channel, when there is a strong RED in the LEFT. You will not get the real red, but it will be somewhat golden-amber. Fire splashes are displayed quite fine, but it will not save LEGO-movie from weird blackening.
Then, after properly balancing, you can add a bit of LEFT-green to the LEFT-red channel. Just to make human faces more natural.
2 -you can fight ghosting by detecting difference between LEFT-red and RIGHT-blue, and then applying some RIGHT-red to the LEFT-red, and some LEFT-blue to the RIGHT-blue proportionally. On your taste.
3 - Making the right balance is not as easy as it seems. The vision is nonlinear, and after previous manipulations, the right effect can be hard to reach. Worse, numerical inversion visible as white or black dots can appear on the picture; highlights can get incorrect tint if the image is too bright.
So I recommend making the processed image just a bit darker than original, but increasing the brightness of your monitor.
And the last thing. Such per-eye/per-channel separation reduces overall color perception. So I recommend separately increasing RED-GREEN saturation. Basically, you can see green as green only when there is no blue, and vice versa. SO make colors hard.
Next, I will offer you two modified shader scripts. The TB script is a complete mess, I was just trying my Ideas there. The SbS script is the same, but easier to read and modify.
Both of those are very straightforward and not optimized as shaders and probably would not work on some low-end boards.
Thanks again to JanWillem32 for giving us a great basis for experiments, and an impact to studying hlsl.
One question, can someone rewrite this mess below and post here a good-looking code?
Hale812
29th October 2017, 07:22
RED-CYAN SbS(LR)
//The original part of this is made by:
// (C) 2015 Jan-Willem Krans (janwillem32 <at> hotmail.com)
// This file is part of Video pixel shader pack.
// This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
// You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//The crazy looking part of this is a hack for psychovisual perception in Chinese plastic glasses, by Hale812
// basic fullscreen side-by-side to red-cyan anaglyph 3D stereoscopic view with compensated Lanczos2 resizing
// This shader should be run as a screen space pixel shader.
// This shader requires compiling with ps_2_0, but higher is better, see http://en.wikipedia.org/wiki/Pixel_shader to look up what PS version your video card supports.
// For this shader to work correctly, the video width in pixels on screen has to be an even amount.
#define RXD 6 //R-B cross-talk compensation damping (weaken right stereo) 0 - full compensation proportional to RB-LR difference. 4 - good for chinese glasses. 255 - almost disabled.
#define LXD 10 //B-R cross-talk compensation damping (weaken left stereo)0 - full compensation proportional to LR-RB difference. 9 - good for chinese glasses. 255 - almost disabled.
#define RGW 0.02 // Dynamic LEFT green channel weight in weighed green mixing for RED-BLUE glasses. Default 0, don't upmix left green(completely blocked by red filter).
#define RAMP 0.7 //Red channel amplyfying
#define GAMP 0.39 //Red channel amplyfying
#define BAMP 0.42 //Red channel amplyfying
#define LAMP 1.5 //POSTprocessing lightness amplifier channel amplyfying
#define G2R .03 //.2 // Green(left) channel to Red(left) upmixing for compensating greenish. From 0 to 1
#define R2C 0.6 //.75// Red(right) channel to Cyanright) upmixing for compensating psychovisual red cancellation. Brain cancells left-red, if there are no other colors(perfect red spots in one eye) From 0 to 1
#define COLEN 1.35 // Color Enhance. Amplify Blue or Green selectively. 1 is normal. 0-decolorize right channel
sampler s0;
float2 c0;
float2 c1;
static const float PI = acos(-1.);
static const float4 wset = float4(1.25, .25, .75, 1.75)*PI;
static const float4 wsets = wset*.5;
static const float4 wp = sin(wset)*sin(wsets)/(wset*wsets);
static const float2 wc = (1.-dot(1., wp))*float2(.75, .25);// compensate truncated window factor by linear factoring on the two nearest samples
static const float4 w = wp+float4(0., wc, 0.);
float4 main(float2 tex : TEXCOORD0) : COLOR
{
float ox = tex.x*c0.x-.5;// video-relative x position
// detect half of the even or odd coordinates
ox = ox*.5+.25;// the two images are half-width, compensate for that
float n = frac(ox);
float dx = (n > .5)? -c1.x : c1.x;// even x positive, odd x negative
float pixeloffset = n-ox;// round half-pixel offsets down
// adjust the x coordinate of the source position to the correct side of the video image using the pixel offset we just calculated
tex.x += pixeloffset*c1.x;
float center = .5;
float texr = tex.x+center-c1.x;// adjust the pixel offset to the video frame on the right side of the video data
// sample from the left and right images and only pass-trough the red channel for the left, and the green and blue channels for the right view
float3 Q;
float3 L0 = tex2D(s0, float2(tex.x-dx, tex.y)).rgb; float3 R0 = tex2D(s0, float2(texr-dx, tex.y)).rgb;
float3 L1 = tex2D(s0, tex).rgb; float3 R1 = tex2D(s0, float2(texr, tex.y)).rgb;
float3 L2 = tex2D(s0, float2(tex.x+dx, tex.y)).rgb; float3 R2 = tex2D(s0, float2(texr+dx, tex.y)).rgb;
float3 L3 = tex2D(s0, float2(tex.x+dx+dx, tex.y)).rgb; float3 R3 = tex2D(s0, float2(texr+dx+dx, tex.y)).rgb;
float3 L; float3 R;
float RXcomp;
float LXcomp;
float greenmix;
float lumBG;
float difBG;
float redmix;
float3 Q0;
L=L0; R=R0;
RXcomp = saturate( pow(R.b - L.r ,.5)/(pow(R.b,.5)+pow(L.r,.5) )/RXD); //Reduce 3D separation in Right-CYAN glass for high contrast spot
LXcomp = saturate( pow(L.r-R.b ,.5)/(pow(R.b,.5)+pow(L.r,.5) )/LXD); //Reduce 3D separation in Left-RED glass for high contrast spot
greenmix = saturate(float( L.r/( L.r + R.b*RGW) ) ); //save some "discarded" green from LEFT eye for RIGHT eye
Q.r = lerp(L.r , R.r,LXcomp) *RAMP+L.g*G2R;
Q.g = lerp( L.g, R.g, greenmix)*GAMP;
Q.b = lerp(R.b,L.b, RXcomp )*BAMP;
lumBG=Q.g+Q.b;
difBG = Q.b-Q.g;
Q.r=(Q.r+L.g+L.b+( 2*Q.r-L.g-L.b )*(lerp(COLEN,1,R.r*1.5)/2+.5))/3; //Enhance Blue or Green, which is stronger. REDUCE RED adaptively.
Q.b=(lumBG+difBG*lerp(COLEN,1,R.r*R.r))/2; //Enhance Blue or Green, which is stronger. RED can not be enhanced due to Blind-EYE PsychoVisual Cancellation
Q.g=(lumBG-difBG*lerp(COLEN,1,R.r*R.r))/2; //Enhance Blue or Green, which is stronger. RED can not be enhanced due to Blind-EYE PsychoVisual Cancellation
redmix = clamp(float(.5- pow(Q.g+Q.b,0.5) ) , 0 ,0.5 )*R2C; //Lighten BLACK spots in Right eye for reducing Blind-EYE PsychoVisual Cancellation of REDs.
Q.g=Q.g+R.r*redmix;
Q.b=Q.b+R.r*redmix;
Q0 = Q;
float3 Q1;
L=L1; R=R1;
RXcomp = saturate( pow(R.b - L.r ,.5)/(pow(R.b,.5)+pow(L.r,.5) )/RXD); //Reduce 3D separation in Right-CYAN glass for high contrast spot
LXcomp = saturate( pow(L.r-R.b ,.5)/(pow(R.b,.5)+pow(L.r,.5) )/LXD); //Reduce 3D separation in Left-RED glass for high contrast spot
greenmix = saturate(float( L.r/( L.r + R.b*RGW) ) ); //save some "discarded" green from LEFT eye for RIGHT eye
Q.r = lerp(L.r , R.r,LXcomp) *RAMP+L.g*G2R;
Q.g = lerp( L.g, R.g, greenmix)*GAMP;
Q.b = lerp(R.b,L.b, RXcomp )*BAMP;
lumBG=Q.g+Q.b;
difBG = Q.b-Q.g;
Q.r=(Q.r+L.g+L.b+( 2*Q.r-L.g-L.b )*(lerp(COLEN,1,R.r*1.5)/2+.5))/3; //Enhance Blue or Green, which is stronger. REDUCE RED adaptively.
Q.b=(lumBG+difBG*lerp(COLEN,1,R.r*R.r))/2; //Enhance Blue or Green, which is stronger. RED can not be enhanced due to Blind-EYE PsychoVisual Cancellation
Q.g=(lumBG-difBG*lerp(COLEN,1,R.r*R.r))/2; //Enhance Blue or Green, which is stronger. RED can not be enhanced due to Blind-EYE PsychoVisual Cancellation
redmix = clamp(float(.5- pow(Q.g+Q.b,0.5) ) , 0 ,0.5 )*R2C; //Lighten BLACK spots in Right eye for reducing Blind-EYE PsychoVisual Cancellation of REDs.
Q.g=Q.g+R.r*redmix;
Q.b=Q.b+R.r*redmix;
Q1 = Q;
float3 Q2;
L=L2; R=R2;
RXcomp = saturate( pow(R.b - L.r ,.5)/(pow(R.b,.5)+pow(L.r,.5) )/RXD); //Reduce 3D separation in Right-CYAN glass for high contrast spot
LXcomp = saturate( pow(L.r-R.b ,.5)/(pow(R.b,.5)+pow(L.r,.5) )/LXD); //Reduce 3D separation in Left-RED glass for high contrast spot
greenmix = saturate(float( L.r/( L.r + R.b*RGW) ) ); //save some "discarded" green from LEFT eye for RIGHT eye
Q.r = lerp(L.r , R.r,LXcomp) *RAMP+L.g*G2R;
Q.g = lerp( L.g, R.g, greenmix)*GAMP;
Q.b = lerp(R.b,L.b, RXcomp )*BAMP;
lumBG=Q.g+Q.b;
difBG = Q.b-Q.g;
Q.r=(Q.r+L.g+L.b+( 2*Q.r-L.g-L.b )*(lerp(COLEN,1,R.r*1.5)/2+.5))/3; //Enhance Blue or Green, which is stronger. REDUCE RED adaptively.
Q.b=(lumBG+difBG*lerp(COLEN,1,R.r*R.r))/2; //Enhance Blue or Green, which is stronger. RED can not be enhanced due to Blind-EYE PsychoVisual Cancellation
Q.g=(lumBG-difBG*lerp(COLEN,1,R.r*R.r))/2; //Enhance Blue or Green, which is stronger. RED can not be enhanced due to Blind-EYE PsychoVisual Cancellation
redmix = clamp(float(.5- pow(Q.g+Q.b,0.5) ) , 0 ,0.5 )*R2C; //Lighten BLACK spots in Right eye for reducing Blind-EYE PsychoVisual Cancellation of REDs.
Q.g=Q.g+R.r*redmix;
Q.b=Q.b+R.r*redmix;
Q2 = Q;
float3 Q3;
L=L3; R=R3;
RXcomp = saturate( pow(R.b - L.r ,.5)/(pow(R.b,.5)+pow(L.r,.5) )/RXD); //Reduce 3D separation in Right-CYAN glass for high contrast spot
LXcomp = saturate( pow(L.r-R.b ,.5)/(pow(R.b,.5)+pow(L.r,.5) )/LXD); //Reduce 3D separation in Left-RED glass for high contrast spot
greenmix = saturate(float( L.r/( L.r + R.b*RGW) ) ); //save some "discarded" green from LEFT eye for RIGHT eye
Q.r = lerp(L.r , R.r,LXcomp) *RAMP+L.g*G2R;
Q.g = lerp( L.g, R.g, greenmix)*GAMP;
Q.b = lerp(R.b,L.b, RXcomp )*BAMP;
lumBG=Q.g+Q.b;
difBG = Q.b-Q.g;
Q.r=(Q.r+L.g+L.b+( 2*Q.r-L.g-L.b )*(lerp(COLEN,1,R.r*1.5)/2+.5))/3; //Enhance Blue or Green, which is stronger. REDUCE RED adaptively.
Q.b=(lumBG+difBG*lerp(COLEN,1,R.r*R.r))/2; //Enhance Blue or Green, which is stronger. RED can not be enhanced due to Blind-EYE PsychoVisual Cancellation
Q.g=(lumBG-difBG*lerp(COLEN,1,R.r*R.r))/2; //Enhance Blue or Green, which is stronger. RED can not be enhanced due to Blind-EYE PsychoVisual Cancellation
redmix = clamp(float(.5- pow(Q.g+Q.b,0.5) ) , 0 ,0.5 )*R2C; //Lighten BLACK spots in Right eye for reducing Blind-EYE PsychoVisual Cancellation of REDs.
Q.g=Q.g+R.r*redmix;
Q.b=Q.b+R.r*redmix;
Q3 = Q;
Q=(w.x*Q0+w.y*Q1+w.z*Q2+w.w*Q3);
float LUM=(Q.r+Q.g+Q.b)/3; //here L is Lightness, not LEFT... just reusing float
float DR=(Q.r*2-Q.g-Q.b)/3;//color dif. components
float DG=(Q.g*2-Q.r-Q.b)/3;
float DB=(Q.b*2-Q.r-Q.g)/3;
float DM=max(DR,max(DG,DB));//maximal colorating difference
float EXCESS=saturate((LUM+DM)*LAMP-1); //excess to be clamped
float DESAT=1;
[branch] if(DM>0)
{
DESAT=saturate(DM*LAMP-EXCESS)/(DM*LAMP); //trying fitting into dynamic range by reducing color difference. clamping makes pure white
}
Q=saturate( (float3(DR*DESAT,DG*DESAT,DB*DESAT)+LUM)*LAMP); //COLOR-correct clamping
return Q.rgbb;// interpolate and output
}
Hale812
29th October 2017, 07:23
RED-CYAN TD(HoU)
//The original part of this is made by:
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
// You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//The crazy looking part of this is a hack for psychovisual perception in Chinese plastic glasses, by Hale812
// basic fullscreen top-and-bottom to red-cyan anaglyph 3D stereoscopic view with compensated Lanczos2 resizing
// This shader should be run as a screen space pixel shader.
// This shader requires compiling with ps_2_0, but higher is better, see http://en.wikipedia.org/wiki/Pixel_shader to look up what PS version your video card supports.
// For this shader to work correctly, the video height in pixels on screen has to be an even amount.
#define RXD 6 //R-B cross-talk compensation damping (weaken right stereo) 0 - full compensation proportional to RB-LR difference. 4 - good for chinese glasses. 255 - almost disabled.
#define LXD 10 //B-R cross-talk compensation damping (weaken left stereo)0 - full compensation proportional to LR-RB difference. 9 - good for chinese glasses. 255 - almost disabled.
#define RGW 0.02 // Dynamic LEFT green channel weight in weighed green mixing for RED-BLUE glasses. Default 0, don't upmix left green(completely blocked by red filter).
#define RAMP 0.7 //Red channel amplyfying
#define GAMP 0.39 //Red channel amplyfying
#define BAMP 0.42 //Red channel amplyfying
#define LAMP 1.5 //POSTprocessing lightness amplifier channel amplyfying
#define G2R .03 //.2 // Green(left) channel to Red(left) upmixing for compensating greenish. From 0 to 1
#define R2C 0.6 //.75// Red(right) channel to Cyanright) upmixing for compensating psychovisual red cancellation. Brain cancells left-red, if there are no other colors(perfect red spots in one eye) From 0 to 1
#define COLEN 1.35 // Color Enhance. Amplify Blue or Green selectively. 1 is normal. 0-decolorize right channel
sampler s0;
float2 c0;
float2 c1;
static const float PI = acos(-1.);
static const float4 wset = float4(1.25, .25, .75, 1.75)*PI;
static const float4 wsets = wset*0.5;
static const float4 wp = sin(wset)*sin(wsets)/(wset*wsets);
static const float2 wc = (1.-dot(1., wp))*float2(.75, .25);// compensate truncated window factor by linear factoring on the two nearest samples
static const float4 w = wp+float4(0., wc, 0.);
float4 main(float2 tex : TEXCOORD0) : COLOR
{
float oy = tex.y*c0.y-.5;// video-relative y position
// detect half of the even or odd coordinates
oy = oy*.5+.25;// the two images are half-width, compensate for that
float n = frac(oy);
float dy = (n > .5)? -c1.y : c1.y;// even y positive, odd y negative
float pixeloffset = n-oy;// round half-pixel offsets down
// adjust the y coordinate of the source position to the correct side of the video image using the pixel offset we just calculated
tex.y += pixeloffset*c1.y;
float center = .5;
float texr = tex.y+center-c1.y;// adjust the pixel offset to the video frame on the bottom side of the video data
// sample from the left and right images and only pass-trough the red channel for the left, and the green and blue channels for the right view
float3 Q;
float3 L0 = tex2D(s0, float2(tex.x, tex.y-dy)).rgb; float3 R0 = tex2D(s0, float2(tex.x, texr-dy)).rgb;
float3 L1 =tex2D(s0, tex).rgb; float3 R1 = tex2D(s0, float2(tex.x, texr)).rgb;
float3 L2 = tex2D(s0, float2(tex.x, tex.y+dy)).rgb; float3 R2 = tex2D(s0, float2(tex.x, texr+dy)).rgb;
float3 L3 = tex2D(s0, float2(tex.x, tex.y+dy+dy)).rgb; float3 R3 = tex2D(s0, float2(tex.x, texr+dy+dy)).rgb;
float3 L; float3 R;
float RXcomp;
float LXcomp;
float greenmix;
float lumBG;
float difBG;
float redmix;
float3 Q0;
L=L0; R=R0;
RXcomp = saturate( pow(R.b - L.r ,.5)/(pow(R.b,.5)+pow(L.r,.5) )/RXD); //Reduce 3D separation in Right-CYAN glass for high contrast spot
LXcomp = saturate( pow(L.r-R.b ,.5)/(pow(R.b,.5)+pow(L.r,.5) )/LXD); //Reduce 3D separation in Left-RED glass for high contrast spot
greenmix = saturate(float( L.r/( L.r + R.b*RGW) ) ); //save some "discarded" green from LEFT eye for RIGHT eye
Q.r = lerp(L.r , R.r,LXcomp) *RAMP+L.g*G2R;
Q.g = lerp( L.g, R.g, greenmix)*GAMP;
Q.b = lerp(R.b,L.b, RXcomp )*BAMP;
lumBG=Q.g+Q.b;
difBG = Q.b-Q.g;
Q.r=(Q.r+L.g+L.b+( 2*Q.r-L.g-L.b )*(lerp(COLEN,1,R.r*1.5)/2+.5))/3; //Enhance Blue or Green, which is stronger. REDUCE RED adaptively.
Q.b=(lumBG+difBG*lerp(COLEN,1,R.r*R.r))/2; //Enhance Blue or Green, which is stronger. RED can not be enhanced due to Blind-EYE PsychoVisual Cancellation
Q.g=(lumBG-difBG*lerp(COLEN,1,R.r*R.r))/2; //Enhance Blue or Green, which is stronger. RED can not be enhanced due to Blind-EYE PsychoVisual Cancellation
redmix = clamp(float(.5- pow(Q.g+Q.b,0.5) ) , 0 ,0.5 )*R2C; //Lighten BLACK spots in Right eye for reducing Blind-EYE PsychoVisual Cancellation of REDs.
Q.g=Q.g+R.r*redmix;
Q.b=Q.b+R.r*redmix;
Q0 = Q;
float3 Q1;
L=L1; R=R1;
RXcomp = saturate( pow(R.b - L.r ,.5)/(pow(R.b,.5)+pow(L.r,.5) )/RXD); //Reduce 3D separation in Right-CYAN glass for high contrast spot
LXcomp = saturate( pow(L.r-R.b ,.5)/(pow(R.b,.5)+pow(L.r,.5) )/LXD); //Reduce 3D separation in Left-RED glass for high contrast spot
greenmix = saturate(float( L.r/( L.r + R.b*RGW) ) ); //save some "discarded" green from LEFT eye for RIGHT eye
Q.r = lerp(L.r , R.r,LXcomp) *RAMP+L.g*G2R;
Q.g = lerp( L.g, R.g, greenmix)*GAMP;
Q.b = lerp(R.b,L.b, RXcomp )*BAMP;
lumBG=Q.g+Q.b;
difBG = Q.b-Q.g;
Q.r=(Q.r+L.g+L.b+( 2*Q.r-L.g-L.b )*(lerp(COLEN,1,R.r*1.5)/2+.5))/3; //Enhance Blue or Green, which is stronger. REDUCE RED adaptively.
Q.b=(lumBG+difBG*lerp(COLEN,1,R.r*R.r))/2; //Enhance Blue or Green, which is stronger. RED can not be enhanced due to Blind-EYE PsychoVisual Cancellation
Q.g=(lumBG-difBG*lerp(COLEN,1,R.r*R.r))/2; //Enhance Blue or Green, which is stronger. RED can not be enhanced due to Blind-EYE PsychoVisual Cancellation
redmix = clamp(float(.5- pow(Q.g+Q.b,0.5) ) , 0 ,0.5 )*R2C; //Lighten BLACK spots in Right eye for reducing Blind-EYE PsychoVisual Cancellation of REDs.
Q.g=Q.g+R.r*redmix;
Q.b=Q.b+R.r*redmix;
Q1 = Q;
float3 Q2;
L=L2; R=R2;
RXcomp = saturate( pow(R.b - L.r ,.5)/(pow(R.b,.5)+pow(L.r,.5) )/RXD); //Reduce 3D separation in Right-CYAN glass for high contrast spot
LXcomp = saturate( pow(L.r-R.b ,.5)/(pow(R.b,.5)+pow(L.r,.5) )/LXD); //Reduce 3D separation in Left-RED glass for high contrast spot
greenmix = saturate(float( L.r/( L.r + R.b*RGW) ) ); //save some "discarded" green from LEFT eye for RIGHT eye
Q.r = lerp(L.r , R.r,LXcomp) *RAMP+L.g*G2R;
Q.g = lerp( L.g, R.g, greenmix)*GAMP;
Q.b = lerp(R.b,L.b, RXcomp )*BAMP;
lumBG=Q.g+Q.b;
difBG = Q.b-Q.g;
Q.r=(Q.r+L.g+L.b+( 2*Q.r-L.g-L.b )*(lerp(COLEN,1,R.r*1.5)/2+.5))/3; //Enhance Blue or Green, which is stronger. REDUCE RED adaptively.
Q.b=(lumBG+difBG*lerp(COLEN,1,R.r*R.r))/2; //Enhance Blue or Green, which is stronger. RED can not be enhanced due to Blind-EYE PsychoVisual Cancellation
Q.g=(lumBG-difBG*lerp(COLEN,1,R.r*R.r))/2; //Enhance Blue or Green, which is stronger. RED can not be enhanced due to Blind-EYE PsychoVisual Cancellation
redmix = clamp(float(.5- pow(Q.g+Q.b,0.5) ) , 0 ,0.5 )*R2C; //Lighten BLACK spots in Right eye for reducing Blind-EYE PsychoVisual Cancellation of REDs.
Q.g=Q.g+R.r*redmix;
Q.b=Q.b+R.r*redmix;
Q2 = Q;
float3 Q3;
L=L3; R=R3;
RXcomp = saturate( pow(R.b - L.r ,.5)/(pow(R.b,.5)+pow(L.r,.5) )/RXD); //Reduce 3D separation in Right-CYAN glass for high contrast spot
LXcomp = saturate( pow(L.r-R.b ,.5)/(pow(R.b,.5)+pow(L.r,.5) )/LXD); //Reduce 3D separation in Left-RED glass for high contrast spot
greenmix = saturate(float( L.r/( L.r + R.b*RGW) ) ); //save some "discarded" green from LEFT eye for RIGHT eye
Q.r = lerp(L.r , R.r,LXcomp) *RAMP+L.g*G2R;
Q.g = lerp( L.g, R.g, greenmix)*GAMP;
Q.b = lerp(R.b,L.b, RXcomp )*BAMP;
lumBG=Q.g+Q.b;
difBG = Q.b-Q.g;
Q.r=(Q.r+L.g+L.b+( 2*Q.r-L.g-L.b )*(lerp(COLEN,1,R.r*1.5)/2+.5))/3; //Enhance Blue or Green, which is stronger. REDUCE RED adaptively.
Q.b=(lumBG+difBG*lerp(COLEN,1,R.r*R.r))/2; //Enhance Blue or Green, which is stronger. RED can not be enhanced due to Blind-EYE PsychoVisual Cancellation
Q.g=(lumBG-difBG*lerp(COLEN,1,R.r*R.r))/2; //Enhance Blue or Green, which is stronger. RED can not be enhanced due to Blind-EYE PsychoVisual Cancellation
redmix = clamp(float(.5- pow(Q.g+Q.b,0.5) ) , 0 ,0.5 )*R2C; //Lighten BLACK spots in Right eye for reducing Blind-EYE PsychoVisual Cancellation of REDs.
Q.g=Q.g+R.r*redmix;
Q.b=Q.b+R.r*redmix;
Q3 = Q;
Q=(w.x*Q0+w.y*Q1+w.z*Q2+w.w*Q3);
float LUM=(Q.r+Q.g+Q.b)/3; //here L is Lightness, not LEFT... just reusing float
float DR=(Q.r*2-Q.g-Q.b)/3;//color dif. components
float DG=(Q.g*2-Q.r-Q.b)/3;
float DB=(Q.b*2-Q.r-Q.g)/3;
float DM=max(DR,max(DG,DB));//maximal colorating difference
float EXCESS=saturate((LUM+DM)*LAMP-1); //excess to be clamped
float DESAT=1;
[branch] if(DM>0)
{
DESAT=saturate(DM*LAMP-EXCESS)/(DM*LAMP); //trying fitting into dynamic range by reducing color difference. clamping makes pure white
}
Q=saturate( (float3(DR*DESAT,DG*DESAT,DB*DESAT)+LUM)*LAMP); //COLOR-correct clamping
return Q.rgbb;// interpolate and output
}
Hale812
3rd November 2017, 15:42
While these scripts compensate yellow and dark-blue ghosting, they still produce lightness ghosting around bright areas (windows etc) which is caused by corrected R-GB balance. Red is amplified, so half tones are lost, and BG is reduced, so original clipping gets obvious. This can be overridden by more sophisticated Anaglyph pixel mixing, where two full-range Left-Right RGB pixels with compensations in each are prepared first, then mixed to one Anaglyph pixel. But I am not prepared yet rewriting it that way. By the same reason, increasing coloration can produce some ghosting on pure blue, for example (in animated movies)
Hale812
8th November 2017, 20:02
Here is heavier per-eye color processing with gamma correction
Still, the code is something you would make up in matlab, not for performance, or compatibility.
TOP-DOWN:
//The original part of this is made by :
//The original part of this is made by:
// (C) 2015 Jan-Willem Krans (janwillem32 <at> hotmail.com)
// This file is part of Video pixel shader pack.
// This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
// You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//The crazy looking part of this is a hack for psychovisual perception in Chinese plastic glasses, by Hale812
// basic fullscreen top-and-bottom to red-cyan anaglyph 3D stereoscopic view with compensated Lanczos2 resizing
// This shader should be run as a screen space pixel shader.
// This shader requires compiling with ps_2_0, but higher is better, see http://en.wikipedia.org/wiki/Pixel_shader to look up what PS version your video card supports.
// For this shader to work correctly, the video height in pixels on screen has to be an even amount.
#define RXD 6 //R-B cross-talk compensation damping (weaken right stereo) 0 - full compensation proportional to RB-LR difference. 4 - good for chinese glasses. 255 - almost disabled.
#define LXD 10 //B-R cross-talk compensation damping (weaken left stereo)0 - full compensation proportional to LR-RB difference. 9 - good for chinese glasses. 255 - almost disabled.
#define RGW 0.0 // Dynamic LEFT green channel weight in weighed green mixing for RED-BLUE glasses. Default 0, don't upmix left green(completely blocked by red filter).
#define RAMP 0.71 //Red channel amplyfying
#define GAMP 0.4 //Red channel amplyfying
#define BAMP 0.44 //Red channel amplyfying
#define LAMP 1.4 //POSTprocessing lightness amplifier channel amplyfying
#define G2R .06 //.2 // Green(left) channel to Red(left) upmixing for compensating greenish. From 0 to 1
#define R2C 0.3 //.75// Red(right) channel to Cyanright) upmixing for compensating psychovisual red cancellation. Brain cancells left-red, if there are no other colors(perfect red spots in one eye) From 0 to 1
#define COLEN 1.35 // Color Enhance. Amplify Blue or Green selectively. 1 is normal. 0-decolorize right channel
#define GAMMA .86 // make darks brighter
sampler s0;
float2 c0;
float2 c1;
static const float PI = acos(-1.);
static const float4 wset = float4(1.25, .25, .75, 1.75)*PI;
static const float4 wsets = wset*0.5;
static const float4 wp = sin(wset)*sin(wsets)/(wset*wsets);
static const float2 wc = (1.-dot(1., wp))*float2(.75, .25);// compensate truncated window factor by linear factoring on the two nearest samples
static const float4 w = wp+float4(0., wc, 0.);
float4 main(float2 tex : TEXCOORD0) : COLOR
{
float oy = tex.y*c0.y-.5;// video-relative y position
// detect half of the even or odd coordinates
oy = oy*.5+.25;// the two images are half-width, compensate for that
float n = frac(oy);
float dy = (n > .5)? -c1.y : c1.y;// even y positive, odd y negative
float pixeloffset = n-oy;// round half-pixel offsets down
// adjust the y coordinate of the source position to the correct side of the video image using the pixel offset we just calculated
tex.y += pixeloffset*c1.y;
float center = .5;
float texr = tex.y+center-c1.y;// adjust the pixel offset to the video frame on the bottom side of the video data
// sample from the left and right images and only pass-trough the red channel for the left, and the green and blue channels for the right view
float3 Q; float3 P;
float3 L0 = tex2D(s0, float2(tex.x, tex.y-dy)).rgb; float3 R0 = tex2D(s0, float2(tex.x, texr-dy)).rgb;
float3 L1 =tex2D(s0, tex).rgb; float3 R1 = tex2D(s0, float2(tex.x, texr)).rgb;
float3 L2 = tex2D(s0, float2(tex.x, tex.y+dy)).rgb; float3 R2 = tex2D(s0, float2(tex.x, texr+dy)).rgb;
float3 L3 = tex2D(s0, float2(tex.x, tex.y+dy+dy)).rgb; float3 R3 = tex2D(s0, float2(tex.x, texr+dy+dy)).rgb;
float3 L; float3 R;;
float RXcomp;
float LXcomp;
float greenmix;
float lumBG;
float difBG;
float redmix;
float3 Q0; float3 P0;
L=L0; R=R0;
RXcomp = saturate( pow(R.b - L.r ,.5)/(pow(R.b,.5)+pow(L.r,.5) )/RXD); //Reduce 3D separation in Right-CYAN glass for high contrast spot
LXcomp = saturate( pow(L.r-R.b ,.5)/(pow(R.b,.5)+pow(L.r,.5) )/LXD); //Reduce 3D separation in Left-RED glass for high contrast spot
greenmix = saturate(float( L.r/( L.r + R.b*RGW) ) ); //save some "discarded" green from LEFT eye for RIGHT eye
Q= float3(lerp(L.r , R.r,LXcomp) *RAMP+L.g*G2R , L.g*GAMP, L.b*BAMP );
P = float3(R.r*RAMP, lerp( L.g, R.g, greenmix)*GAMP, lerp(R.b,L.b, RXcomp )*BAMP);
lumBG=P.g+P.b;
difBG = P.b-P.g;
Q.r=(Q.r+Q.g+Q.b+( 2*Q.r-Q.g-Q.b )*(lerp(COLEN,1,saturate(P.r*1.5))/2+.5))/3; //When RIGHT(P) is Bluish, or Greenish. push down LEFT(Q) Red.
P.b=(lumBG+difBG*lerp(COLEN,1,P.r*P.r))/2; //Enhance pure Blue or Green, which is stronger. RED can not be enhanced due to Blind-EYE PsychoVisual Cancellation
P.g=(lumBG-difBG*lerp(COLEN,1,P.r*P.r))/2; //Enhance pure Blue or Green, which is stronger. RED can not be enhanced due to Blind-EYE PsychoVisual Cancellation
redmix = clamp(float(.5- pow(P.g+P.b,0.5) ) , 0 ,0.5 )*R2C; //Lighten BLACK spots in Right(P) eye for reducing Blind-EYE PsychoVisual Cancellation of REDs.
P.g=P.g+P.r*redmix; P.b=P.b+R.r*redmix;
Q0 = Q; P0=P;
float3 Q1; float3 P1;
L=L1; R=R1;
RXcomp = saturate( pow(R.b - L.r ,.5)/(pow(R.b,.5)+pow(L.r,.5) )/RXD); //Reduce 3D separation in Right-CYAN glass for high contrast spot
LXcomp = saturate( pow(L.r-R.b ,.5)/(pow(R.b,.5)+pow(L.r,.5) )/LXD); //Reduce 3D separation in Left-RED glass for high contrast spot
greenmix = saturate(float( L.r/( L.r + R.b*RGW) ) ); //save some "discarded" green from LEFT eye for RIGHT eye
Q= float3(lerp(L.r , R.r,LXcomp) *RAMP+L.g*G2R , L.g*GAMP, L.b*BAMP );
P = float3(R.r*RAMP, lerp( L.g, R.g, greenmix)*GAMP, lerp(R.b,L.b, RXcomp )*BAMP);
lumBG=P.g+P.b;
difBG = P.b-P.g;
Q.r=(Q.r+Q.g+Q.b+( 2*Q.r-Q.g-Q.b )*(lerp(COLEN,1,saturate(P.r*1.5))/2+.5))/3; //When RIGHT(P) is Bluish, or Greenish. push down LEFT(Q) Red.
P.b=(lumBG+difBG*lerp(COLEN,1,P.r*P.r))/2; //Enhance pure Blue or Green, which is stronger. RED can not be enhanced due to Blind-EYE PsychoVisual Cancellation
P.g=(lumBG-difBG*lerp(COLEN,1,P.r*P.r))/2; //Enhance pure Blue or Green, which is stronger. RED can not be enhanced due to Blind-EYE PsychoVisual Cancellation
redmix = clamp(float(.5- pow(P.g+P.b,0.5) ) , 0 ,0.5 )*R2C; //Lighten BLACK spots in Right(P) eye for reducing Blind-EYE PsychoVisual Cancellation of REDs.
P.g=P.g+P.r*redmix; P.b=P.b+R.r*redmix;
Q1 = Q; P1=P;
float3 Q2; float3 P2;
L=L2; R=R2;
RXcomp = saturate( pow(R.b - L.r ,.5)/(pow(R.b,.5)+pow(L.r,.5) )/RXD); //Reduce 3D separation in Right-CYAN glass for high contrast spot
LXcomp = saturate( pow(L.r-R.b ,.5)/(pow(R.b,.5)+pow(L.r,.5) )/LXD); //Reduce 3D separation in Left-RED glass for high contrast spot
greenmix = saturate(float( L.r/( L.r + R.b*RGW) ) ); //save some "discarded" green from LEFT eye for RIGHT eye
Q= float3(lerp(L.r , R.r,LXcomp) *RAMP+L.g*G2R , L.g*GAMP, L.b*BAMP );
P = float3(R.r*RAMP, lerp( L.g, R.g, greenmix)*GAMP, lerp(R.b,L.b, RXcomp )*BAMP);
lumBG=P.g+P.b;
difBG = P.b-P.g;
Q.r=(Q.r+Q.g+Q.b+( 2*Q.r-Q.g-Q.b )*(lerp(COLEN,1,saturate(P.r*1.5))/2+.5))/3; //When RIGHT(P) is Bluish, or Greenish. push down LEFT(Q) Red.
P.b=(lumBG+difBG*lerp(COLEN,1,P.r*P.r))/2; //Enhance pure Blue or Green, which is stronger. RED can not be enhanced due to Blind-EYE PsychoVisual Cancellation
P.g=(lumBG-difBG*lerp(COLEN,1,P.r*P.r))/2; //Enhance pure Blue or Green, which is stronger. RED can not be enhanced due to Blind-EYE PsychoVisual Cancellation
redmix = clamp(float(.5- pow(P.g+P.b,0.5) ) , 0 ,0.5 )*R2C; //Lighten BLACK spots in Right(P) eye for reducing Blind-EYE PsychoVisual Cancellation of REDs.
P.g=P.g+P.r*redmix; P.b=P.b+R.r*redmix;
Q2 = Q; P2=P;
float3 Q3; float3 P3;
L=L3; R=R3;
RXcomp = saturate( pow(R.b - L.r ,.5)/(pow(R.b,.5)+pow(L.r,.5) )/RXD); //Reduce 3D separation in Right-CYAN glass for high contrast spot
LXcomp = saturate( pow(L.r-R.b ,.5)/(pow(R.b,.5)+pow(L.r,.5) )/LXD); //Reduce 3D separation in Left-RED glass for high contrast spot
greenmix = saturate(float( L.r/( L.r + R.b*RGW) ) ); //save some "discarded" green from LEFT eye for RIGHT eye
Q= float3(lerp(L.r , R.r,LXcomp) *RAMP+L.g*G2R , L.g*GAMP, L.b*BAMP );
P = float3(R.r*RAMP, lerp( L.g, R.g, greenmix)*GAMP, lerp(R.b,L.b, RXcomp )*BAMP);
lumBG=P.g+P.b;
difBG = P.b-P.g;
Q.r=(Q.r+Q.g+Q.b+( 2*Q.r-Q.g-Q.b )*(lerp(COLEN,1,saturate(P.r*1.5))/2+.5))/3; //When RIGHT(P) is Bluish, or Greenish. push down LEFT(Q) Red.
P.b=(lumBG+difBG*lerp(COLEN,1,P.r*P.r))/2; //Enhance pure Blue or Green, which is stronger. RED can not be enhanced due to Blind-EYE PsychoVisual Cancellation
P.g=(lumBG-difBG*lerp(COLEN,1,P.r*P.r))/2; //Enhance pure Blue or Green, which is stronger. RED can not be enhanced due to Blind-EYE PsychoVisual Cancellation
redmix = clamp(float(.5- pow(P.g+P.b,0.5) ) , 0 ,0.5 )*R2C; //Lighten BLACK spots in Right(P) eye for reducing Blind-EYE PsychoVisual Cancellation of REDs.
P.g=P.g+P.r*redmix; P.b=P.b+R.r*redmix;
Q3 = Q; P3=P;
Q=(w.x*Q0+w.y*Q1+w.z*Q2+w.w*Q3);
float LUM=(Q.r+Q.g+Q.b)/3; //here L is Lightness, not LEFT... just reusing float
float DR=(Q.r*2-Q.g-Q.b)/3;//color dif. components
float DG=(Q.g*2-Q.r-Q.b)/3;
float DB=(Q.b*2-Q.r-Q.g)/3;
float gamamp=LAMP*pow(LUM,GAMMA)/LUM; //GAMMA-corrected AMP. Implemented after calculating other corrections, so do not use power in formulas below
float DM=max(DR,max(DG,DB));//maximal colorating difference
float EXCESS=saturate((LUM+DM)*gamamp-1); //excess to be clamped
float DESAT=1;
[branch] if(DM>0)
{
DESAT=saturate((DM*gamamp-EXCESS)/(DM*gamamp)); //trying fitting into dynamic range by reducing color difference. clamping makes pure white
}
Q=saturate( (float3(DR,DG,DB)*DESAT+LUM)*gamamp); //COLOR-correct clamping
P=(w.x*P0+w.y*P1+w.z*P2+w.w*P3);
LUM=(P.r+P.g+P.b)/3; //here L is Lightness, not LEFT... just reusing float
DR=(P.r*2-P.g-P.b)/3;//color dif. components
DG=(P.g*2-P.r-P.b)/3;
DB=(P.b*2-P.r-P.g)/3;
gamamp=LAMP*pow(LUM,GAMMA)/LUM; //GAMMA-corrected AMP. Implemented after calculating other corrections, so do not use power in formulas below
DM=max(DR,max(DG,DB));//maximal colorating difference
EXCESS=saturate((LUM+DM)*gamamp-1); //excess to be clamped
DESAT=1;
[branch] if(DM>0)
{
DESAT=saturate((DM*gamamp-EXCESS)/(DM*gamamp)); //trying fitting into dynamic range by reducing color difference. clamping makes pure white
}
P=saturate( (float3(DR,DG,DB)*DESAT+LUM)*gamamp); //COLOR-correct clamping
return float4(Q.r, P.g, P.b, P.b);// interpolate and output
}
Hale812
8th November 2017, 20:03
And the LEFT-RIGH pair
//The original part of this is made by:
// (C) 2015 Jan-Willem Krans (janwillem32 <at> hotmail.com)
// This file is part of Video pixel shader pack.
// This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
// You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//The crazy looking part of this is a hack for psychovisual perception in Chinese plastic glasses, by Hale812
// basic fullscreen side-by-side to red-cyan anaglyph 3D stereoscopic view with compensated Lanczos2 resizing
// This shader should be run as a screen space pixel shader.
// This shader requires compiling with ps_2_0, but higher is better, see http://en.wikipedia.org/wiki/Pixel_shader to look up what PS version your video card supports.
// For this shader to work correctly, the video width in pixels on screen has to be an even amount.
#define RXD 996 //R-B cross-talk compensation damping (weaken right stereo) 0 - full compensation proportional to RB-LR difference. 4 - good for chinese glasses. 255 - almost disabled.
#define LXD 9910 //B-R cross-talk compensation damping (weaken left stereo)0 - full compensation proportional to LR-RB difference. 9 - good for chinese glasses. 255 - almost disabled.
#define RGW 0.0 // Dynamic LEFT green channel weight in weighed green mixing for RED-BLUE glasses. Default 0, don't upmix left green(completely blocked by red filter).
#define RAMP 0.71 //Red channel amplyfying
#define GAMP 0.4 //Red channel amplyfying
#define BAMP 0.44 //Red channel amplyfying
#define LAMP 1.4 //POSTprocessing lightness amplifier channel amplyfying
#define G2R .06 //.2 // Green(left) channel to Red(left) upmixing for compensating greenish. From 0 to 1
#define R2C 0.3 //.75// Red(right) channel to Cyanright) upmixing for compensating psychovisual red cancellation. Brain cancells left-red, if there are no other colors(perfect red spots in one eye) From 0 to 1
#define COLEN 1.35 // Color Enhance. Amplify Blue or Green selectively. 1 is normal. 0-decolorize right channel
#define GAMMA .86 // make darks brighter
sampler s0;
float2 c0;
float2 c1;
static const float PI = acos(-1.);
static const float4 wset = float4(1.25, .25, .75, 1.75)*PI;
static const float4 wsets = wset*.5;
static const float4 wp = sin(wset)*sin(wsets)/(wset*wsets);
static const float2 wc = (1.-dot(1., wp))*float2(.75, .25);// compensate truncated window factor by linear factoring on the two nearest samples
static const float4 w = wp+float4(0., wc, 0.);
float4 main(float2 tex : TEXCOORD0) : COLOR
{
float ox = tex.x*c0.x-.5;// video-relative x position
// detect half of the even or odd coordinates
ox = ox*.5+.25;// the two images are half-width, compensate for that
float n = frac(ox);
float dx = (n > .5)? -c1.x : c1.x;// even x positive, odd x negative
float pixeloffset = n-ox;// round half-pixel offsets down
// adjust the x coordinate of the source position to the correct side of the video image using the pixel offset we just calculated
tex.x += pixeloffset*c1.x;
float center = .5;
float texr = tex.x+center-c1.x;// adjust the pixel offset to the video frame on the right side of the video data
// sample from the left and right images and only pass-trough the red channel for the left, and the green and blue channels for the right view
float3 Q; float3 P;
float3 L0 = tex2D(s0, float2(tex.x-dx, tex.y)).rgb; float3 R0 = tex2D(s0, float2(texr-dx, tex.y)).rgb;
float3 L1 = tex2D(s0, tex).rgb; float3 R1 = tex2D(s0, float2(texr, tex.y)).rgb;
float3 L2 = tex2D(s0, float2(tex.x+dx, tex.y)).rgb; float3 R2 = tex2D(s0, float2(texr+dx, tex.y)).rgb;
float3 L3 = tex2D(s0, float2(tex.x+dx+dx, tex.y)).rgb; float3 R3 = tex2D(s0, float2(texr+dx+dx, tex.y)).rgb;
float3 L; float3 R;;
float RXcomp;
float LXcomp;
float greenmix;
float lumBG;
float difBG;
float redmix;
float3 Q0; float3 P0;
L=L0; R=R0;
RXcomp = saturate( pow(R.b - L.r ,.5)/(pow(R.b,.5)+pow(L.r,.5) )/RXD); //Reduce 3D separation in Right-CYAN glass for high contrast spot
LXcomp = saturate( pow(L.r-R.b ,.5)/(pow(R.b,.5)+pow(L.r,.5) )/LXD); //Reduce 3D separation in Left-RED glass for high contrast spot
greenmix = saturate(float( L.r/( L.r + R.b*RGW) ) ); //save some "discarded" green from LEFT eye for RIGHT eye
Q= float3(lerp(L.r , R.r,LXcomp) *RAMP+L.g*G2R , L.g*GAMP, L.b*BAMP );
P = float3(R.r*RAMP, lerp( L.g, R.g, greenmix)*GAMP, lerp(R.b,L.b, RXcomp )*BAMP);
lumBG=P.g+P.b;
difBG = P.b-P.g;
Q.r=(Q.r+Q.g+Q.b+( 2*Q.r-Q.g-Q.b )*(lerp(COLEN,1,saturate(P.r*1.5))/2+.5))/3; //When RIGHT(P) is Bluish, or Greenish. push down LEFT(Q) Red.
P.b=(lumBG+difBG*lerp(COLEN,1,P.r*P.r))/2; //Enhance pure Blue or Green, which is stronger. RED can not be enhanced due to Blind-EYE PsychoVisual Cancellation
P.g=(lumBG-difBG*lerp(COLEN,1,P.r*P.r))/2; //Enhance pure Blue or Green, which is stronger. RED can not be enhanced due to Blind-EYE PsychoVisual Cancellation
redmix = clamp(float(.5- pow(P.g+P.b,0.5) ) , 0 ,0.5 )*R2C; //Lighten BLACK spots in Right(P) eye for reducing Blind-EYE PsychoVisual Cancellation of REDs.
P.g=P.g+P.r*redmix; P.b=P.b+R.r*redmix;
Q0 = Q; P0=P;
float3 Q1; float3 P1;
L=L1; R=R1;
RXcomp = saturate( pow(R.b - L.r ,.5)/(pow(R.b,.5)+pow(L.r,.5) )/RXD); //Reduce 3D separation in Right-CYAN glass for high contrast spot
LXcomp = saturate( pow(L.r-R.b ,.5)/(pow(R.b,.5)+pow(L.r,.5) )/LXD); //Reduce 3D separation in Left-RED glass for high contrast spot
greenmix = saturate(float( L.r/( L.r + R.b*RGW) ) ); //save some "discarded" green from LEFT eye for RIGHT eye
Q= float3(lerp(L.r , R.r,LXcomp) *RAMP+L.g*G2R , L.g*GAMP, L.b*BAMP );
P = float3(R.r*RAMP, lerp( L.g, R.g, greenmix)*GAMP, lerp(R.b,L.b, RXcomp )*BAMP);
lumBG=P.g+P.b;
difBG = P.b-P.g;
Q.r=(Q.r+Q.g+Q.b+( 2*Q.r-Q.g-Q.b )*(lerp(COLEN,1,saturate(P.r*1.5))/2+.5))/3; //When RIGHT(P) is Bluish, or Greenish. push down LEFT(Q) Red.
P.b=(lumBG+difBG*lerp(COLEN,1,P.r*P.r))/2; //Enhance pure Blue or Green, which is stronger. RED can not be enhanced due to Blind-EYE PsychoVisual Cancellation
P.g=(lumBG-difBG*lerp(COLEN,1,P.r*P.r))/2; //Enhance pure Blue or Green, which is stronger. RED can not be enhanced due to Blind-EYE PsychoVisual Cancellation
redmix = clamp(float(.5- pow(P.g+P.b,0.5) ) , 0 ,0.5 )*R2C; //Lighten BLACK spots in Right(P) eye for reducing Blind-EYE PsychoVisual Cancellation of REDs.
P.g=P.g+P.r*redmix; P.b=P.b+R.r*redmix;
Q1 = Q; P1=P;
float3 Q2; float3 P2;
L=L2; R=R2;
RXcomp = saturate( pow(R.b - L.r ,.5)/(pow(R.b,.5)+pow(L.r,.5) )/RXD); //Reduce 3D separation in Right-CYAN glass for high contrast spot
LXcomp = saturate( pow(L.r-R.b ,.5)/(pow(R.b,.5)+pow(L.r,.5) )/LXD); //Reduce 3D separation in Left-RED glass for high contrast spot
greenmix = saturate(float( L.r/( L.r + R.b*RGW) ) ); //save some "discarded" green from LEFT eye for RIGHT eye
Q= float3(lerp(L.r , R.r,LXcomp) *RAMP+L.g*G2R , L.g*GAMP, L.b*BAMP );
P = float3(R.r*RAMP, lerp( L.g, R.g, greenmix)*GAMP, lerp(R.b,L.b, RXcomp )*BAMP);
lumBG=P.g+P.b;
difBG = P.b-P.g;
Q.r=(Q.r+Q.g+Q.b+( 2*Q.r-Q.g-Q.b )*(lerp(COLEN,1,saturate(P.r*1.5))/2+.5))/3; //When RIGHT(P) is Bluish, or Greenish. push down LEFT(Q) Red.
P.b=(lumBG+difBG*lerp(COLEN,1,P.r*P.r))/2; //Enhance pure Blue or Green, which is stronger. RED can not be enhanced due to Blind-EYE PsychoVisual Cancellation
P.g=(lumBG-difBG*lerp(COLEN,1,P.r*P.r))/2; //Enhance pure Blue or Green, which is stronger. RED can not be enhanced due to Blind-EYE PsychoVisual Cancellation
redmix = clamp(float(.5- pow(P.g+P.b,0.5) ) , 0 ,0.5 )*R2C; //Lighten BLACK spots in Right(P) eye for reducing Blind-EYE PsychoVisual Cancellation of REDs.
P.g=P.g+P.r*redmix; P.b=P.b+R.r*redmix;
Q2 = Q; P2=P;
float3 Q3; float3 P3;
L=L3; R=R3;
RXcomp = saturate( pow(R.b - L.r ,.5)/(pow(R.b,.5)+pow(L.r,.5) )/RXD); //Reduce 3D separation in Right-CYAN glass for high contrast spot
LXcomp = saturate( pow(L.r-R.b ,.5)/(pow(R.b,.5)+pow(L.r,.5) )/LXD); //Reduce 3D separation in Left-RED glass for high contrast spot
greenmix = saturate(float( L.r/( L.r + R.b*RGW) ) ); //save some "discarded" green from LEFT eye for RIGHT eye
Q= float3(lerp(L.r , R.r,LXcomp) *RAMP+L.g*G2R , L.g*GAMP, L.b*BAMP );
P = float3(R.r*RAMP, lerp( L.g, R.g, greenmix)*GAMP, lerp(R.b,L.b, RXcomp )*BAMP);
lumBG=P.g+P.b;
difBG = P.b-P.g;
Q.r=(Q.r+Q.g+Q.b+( 2*Q.r-Q.g-Q.b )*(lerp(COLEN,1,saturate(P.r*1.5))/2+.5))/3; //When RIGHT(P) is Bluish, or Greenish. push down LEFT(Q) Red.
P.b=(lumBG+difBG*lerp(COLEN,1,P.r*P.r))/2; //Enhance pure Blue or Green, which is stronger. RED can not be enhanced due to Blind-EYE PsychoVisual Cancellation
P.g=(lumBG-difBG*lerp(COLEN,1,P.r*P.r))/2; //Enhance pure Blue or Green, which is stronger. RED can not be enhanced due to Blind-EYE PsychoVisual Cancellation
redmix = clamp(float(.5- pow(P.g+P.b,0.5) ) , 0 ,0.5 )*R2C; //Lighten BLACK spots in Right(P) eye for reducing Blind-EYE PsychoVisual Cancellation of REDs.
P.g=P.g+P.r*redmix; P.b=P.b+R.r*redmix;
Q3 = Q; P3=P;
Q=(w.x*Q0+w.y*Q1+w.z*Q2+w.w*Q3);
float LUM=(Q.r+Q.g+Q.b)/3; //here L is Lightness, not LEFT... just reusing float
float DR=(Q.r*2-Q.g-Q.b)/3;//color dif. components
float DG=(Q.g*2-Q.r-Q.b)/3;
float DB=(Q.b*2-Q.r-Q.g)/3;
float gamamp=LAMP*pow(LUM,GAMMA)/LUM; //GAMMA-corrected AMP. Implemented after calculating other corrections, so do not use power in formulas below
float DM=max(DR,max(DG,DB));//maximal colorating difference
float EXCESS=saturate((LUM+DM)*gamamp-1); //excess to be clamped
float DESAT=1;
[branch] if(DM>0)
{
DESAT=saturate((DM*gamamp-EXCESS)/(DM*gamamp)); //trying fitting into dynamic range by reducing color difference. clamping makes pure white
}
Q=saturate( (float3(DR,DG,DB)*DESAT+LUM)*gamamp); //COLOR-correct clamping
P=(w.x*P0+w.y*P1+w.z*P2+w.w*P3);
LUM=(P.r+P.g+P.b)/3; //here L is Lightness, not LEFT... just reusing float
DR=(P.r*2-P.g-P.b)/3;//color dif. components
DG=(P.g*2-P.r-P.b)/3;
DB=(P.b*2-P.r-P.g)/3;
gamamp=LAMP*pow(LUM,GAMMA)/LUM; //GAMMA-corrected AMP. Implemented after calculating other corrections, so do not use power in formulas below
DM=max(DR,max(DG,DB));//maximal colorating difference
EXCESS=saturate((LUM+DM)*gamamp-1); //excess to be clamped
DESAT=1;
[branch] if(DM>0)
{
DESAT=saturate((DM*gamamp-EXCESS)/(DM*gamamp)); //trying fitting into dynamic range by reducing color difference. clamping makes pure white
}
P=saturate( (float3(DR,DG,DB)*DESAT+LUM)*gamamp); //COLOR-correct clamping
return float4(Q.r, P.g, P.b, P.b);// interpolate and output
}
Hale812
8th November 2017, 20:07
I have some Ideas about ghosting. Seems like rough upmixing of interfering channel is not right.
Probably, I should rewrite that part with a routine, smoothly reducing LUMA difference in channels in contrast-overlapping(only) areas.
Since I made separate left-right (P-Q vars) processing, it becomes simpler.
jeanlee20xx
18th December 2017, 07:38
I want play 2d as SBS,how to write the code?thank you!!!!:thanks::thanks::thanks:
vBulletin® v3.8.11, Copyright ©2000-2026, vBulletin Solutions Inc.