Multi display. Extend the central screen virtually onto the lateral.
- ExtasZ
- Topic Author
Less
More
10 months 2 weeks ago - 10 months 2 weeks ago #1
by ExtasZ
Multi display. Extend the central screen virtually onto the lateral. was created by ExtasZ
/* --== Multi_2_Virtu V 1.2 ==--
Helps align the image across monitors with different resolutions and PPI
set bezel boundary and compensation
Extends the central display virtually onto side monitors
for a portrait-landscape-portrait
in windowed mode: set the resolution to the sum of monitor widths on the X-axis with a slight reduction,
and add 80-200 pixels to the height of the central monitor
*/
//[Shader Options]
uniform float imageWidth
< ui_type = "drag"; ui_label = "Image Width in Pixels";
ui_min = 1; ui_max = 8000; ui_step = 1; > = 6000;
uniform float BezelBoundary
< ui_type = "drag"; ui_label = "Screen Bezel Boundary";
ui_min = 0.0; ui_max = 1.0; ui_step = 0.0001; > = 0.1747;
uniform float BezelCompensationPixels
< ui_type = "drag"; ui_label = "Bezel Compensation (Pixels)";
ui_min = 0.0; ui_max = 100.0; ui_step = 1.0; > = 45;
uniform float LateralZoom
< ui_type = "drag"; ui_label = "Zoom Level";
ui_min = 0.5; ui_max = 2.0; ui_step = 0.0001; > = 0.7767;
uniform float CentralZoomY
< ui_type = "drag"; ui_label = "Center Y Zoom Level";
ui_min = 0.5; ui_max = 2.0; ui_step = 0.001; > = 1.211;
uniform float OffsetYCenter
< ui_type = "drag"; ui_label = "Center Y Offset";
ui_min = -1.0; ui_max = 1.0; ui_step = 0.001; > = 0.5; // offset and adding the recalibraion for the uv.y (no offset is 0.5)
uniform float2 ZoomYLat
< ui_type = "drag"; ui_label = "Y Zoom for Lateral Zones (Outer | Inner)";
ui_tooltip = "First value is for the outer edge, second for the inner edge of lateral zones.";
ui_min = 0.5; ui_max = 3.0; ui_step = 0.001; > = float2(1.688, 1.663);
uniform float2 OffsetYLat
< ui_type = "drag"; ui_label = "Y Offset for Lateral Zones (Outer | Inner)";
ui_tooltip = "First value is for the outer edge, second for the inner edge of lateral zones.";
ui_min = -1.0; ui_max = 1.0; ui_step = 0.001; > = float2(-0.261, -0.263);
//[Pixel Shader]
#include "ReShade.fxh"
// Function to calculate the pixel source of the inner edge of the lateral zone
float CalculatePixelSourceAtBorder(float leftZoneEnd, float imageWidth, float lateralZoom)
{
float uv_border = leftZoneEnd;
float uv_source_x = (uv_border - 0.5) / lateralZoom + 0.5;
float pixelX = uv_source_x * imageWidth;
return pixelX;
}
// Function to calculate central zoom with bezel compensation
float CalculateCentralZoomWithBezel(float pixelSourceX, float imageWidth, float bezelCompensation, float centralStartUV)
{
float nextPixelSourceX = pixelSourceX - bezelCompensation; // Compensation approx in pixels
float uv_source_central = nextPixelSourceX / imageWidth;
float centralZoom = (centralStartUV - 0.5) / (uv_source_central - 0.5);
return centralZoom;
}
float4 PS_Multi_2_Virtu(float4 position : SV_Position, float2 uv : TEXCOORD0) : SV_Target
{
// static const float only 1 time by frame precalculation
// Define boundaries for each display zone
static const float leftZoneEnd = BezelBoundary;
static const float centerZoneEnd = 1.0 - BezelBoundary;
// Automatic calculation of central zoom
static const float pixelXAtBorder = CalculatePixelSourceAtBorder(leftZoneEnd, imageWidth, LateralZoom);
static const float CentralZoom = CalculateCentralZoomWithBezel(pixelXAtBorder, imageWidth, BezelCompensationPixels, leftZoneEnd);
float zoomFactor; // global zoom
if (uv.x < leftZoneEnd) // Left side
{
float t = uv.x / leftZoneEnd;
zoomFactor = lerp(1.0, LateralZoom, t); // global zoom
float zoomY = lerp(ZoomYLat.x, ZoomYLat.y, t); // Interpolate Y zoom
float offsetY = lerp(OffsetYLat.x, OffsetYLat.y, t); // Interpolate Y offset
uv.y = uv.y * zoomY + offsetY;
}
else if (uv.x < centerZoneEnd) // Center
{
zoomFactor = CentralZoom; // global zoom
uv.y = (-0.5 + uv.y) * CentralZoomY + OffsetYCenter ; // Y zoom + Y Offset
}
else // Right side
{
float t = (uv.x - centerZoneEnd) / (1.0 - centerZoneEnd);
zoomFactor = lerp(LateralZoom, 1.0, t); // global zoom
float zoomY = lerp(ZoomYLat.y, ZoomYLat.x, t); // Interpolate Y zoom
float offsetY = lerp(OffsetYLat.y, OffsetYLat.x, t); // Interpolate Y offset
uv.y = uv.y * zoomY + offsetY;
}
// Apply the global zoom factor to the UV coordinates
float2 center = float2(0.5, 0.5);
uv = lerp(center, uv, zoomFactor);
float4 Color = tex2D(ReShade::BackBuffer, uv);
return Color;
}
//[Technique]
technique Multi_to_Virtu <
ui_tooltip = "Applies a customized backbuffer copy with individual zoom levels for central and lateral zones, including separate Y-axis zoom and vertical offsets for each zone."; >
{
pass
{
BlendEnable = FALSE; // Disable blending to fully replace the image
VertexShader = PostProcessVS;
PixelShader = PS_Multi_2_Virtu;
}
}
Last edit: 10 months 2 weeks ago by ExtasZ.
Please Log in or Create an account to join the conversation.
- ExtasZ
- Topic Author
Less
More
10 months 2 weeks ago - 10 months 2 weeks ago #2
by ExtasZ
Replied by ExtasZ on topic Multi display. Extend the central screen virtually onto the lateral.
Of course, you need to play in windowed mode with a window size that covers the total width of your monitors, and you can position it wherever you want. Your setup should ideally be symmetrical, meaning you use two matching monitors for the side sections. For example, my setup is portrait (1080x1920) - landscape (3860x1600) - portrait (1080x1920). I play in windowed mode at a resolution of 5992x1720.Almost all games allow you to set custom resolutions in their configuration files.Another option is to use the Magnifier tool windows key + + but it doesn’t look as nice as playing in native windowed mode.
Last edit: 10 months 2 weeks ago by ExtasZ.
Please Log in or Create an account to join the conversation.
- ExtasZ
- Topic Author
Less
More
10 months 1 week ago - 10 months 1 week ago #3
by ExtasZ
Replied by ExtasZ on topic Multi display. Extend the central screen virtually onto the lateral.
For those who want to move a window outside the screen a little, like hiding the border and all, I made a PowerShell script for moving a window outside the monitor limits. Pixelaligns with the coordinates of the OS.Save the script asin a directory of your choice.
Script:
Instructions:
This will move the window to the specified position and can hide the borders (e.g., -3 -26 may remove the borders depending on your Windows theme).
of curse you dont see anymore border of windows so you will need to close your game like in full screen or in the task manager
you willl have the comman set-window aveable as long as you don't close your powersell session
(0,0)
place.ps1
Script:
Add-Type -AssemblyName System.Windows.Forms
function Set-Window {
param (
[Parameter(Position = 0)]
[string]$ProcessName, # Process name
[Parameter(Position = 1)]
[int]$MonitorIndex, # Monitor index
[Parameter(Position = 2)]
[int]$X,
[Parameter(Position = 3)]
[int]$Y
)
Add-Type @"
using System;
using System.Runtime.InteropServices;
public class Window {
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
[DllImport("user32.dll")]
public static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);
}
public struct RECT {
public int Left;
public int Top;
public int Right;
public int Bottom;
}
"@
# Retrieve the process by name
$process = Get-Process -Name $ProcessName -ErrorAction SilentlyContinue
if (-not $process) {
Write-Host "The process with the name '$ProcessName' is not running."
return
}
$handle = $process.MainWindowHandle
# Get the current window dimensions
$rect = New-Object RECT
[Window]::GetWindowRect($handle, [ref]$rect)
# Preserve the current width and height while moving the window
$currentWidth = $rect.Right - $rect.Left
$currentHeight = $rect.Bottom - $rect.Top
# Retrieve monitors
$screens = [System.Windows.Forms.Screen]::AllScreens
if ($MonitorIndex -ge $screens.Length -or $MonitorIndex -lt 0) {
Write-Host "Invalid monitor index."
return
}
# Get the position of the chosen monitor
$monitor = $screens[$MonitorIndex]
$monitorBounds = $monitor.Bounds
# Move the window to the specified monitor
[Window]::MoveWindow($handle, $monitorBounds.Left + $X, $monitorBounds.Top + $Y, $currentWidth, $currentHeight, $true)
}
- Find the process name of your application or game in the Task Manager (Ctrl + Alt + Delete). For example, for War Thunder, the process name is aces.exe
- Launch PowerShell in administrator mode, navigate to the directory where you saved the script, and execute it:
- Use Set-Window command made by this script with the following arguments:
- The name of your process.( without .exe)
- The monitor ID (starting from 0 for the first monitor).
- The X coordinate.
- The Y coordinate.
cd c:\moving
. .\place.ps1
Set-Window aces 0 -3 -111
This will move the window to the specified position and can hide the borders (e.g., -3 -26 may remove the borders depending on your Windows theme).
of curse you dont see anymore border of windows so you will need to close your game like in full screen or in the task manager
you willl have the comman set-window aveable as long as you don't close your powersell session
Last edit: 10 months 1 week ago by ExtasZ. Reason: more clear
Please Log in or Create an account to join the conversation.
- ExtasZ
- Topic Author
Less
More
9 months 2 days ago #4
by ExtasZ
Replied by ExtasZ on topic Multi display. Extend the central screen virtually onto the lateral.
Update
triple_display_alignement.fx
TripleDisplayAlignementHelper.fx
triple_display_alignement.fx
#include "ReShade.fxh"
/* --== Screen Alignment v 1.3 ==--
Helps align the image across monitors with different resolutions and PPI.
Set the bezel boundary and compensation to align the lateral monitors with the central one.
Play in windowed mode, ensuring the total resolution matches the combined size of all your monitors.
For a portrait-landscape-portrait setup, try to force the same vertical resolution (in pixels) for the portrait and central monitors.
While this does not align the portrait monitors with the central one, it minimizes the UV range required to cover all monitors,
resulting in a smaller UV space to process and better optimization.
In the NVIDIA Control Panel [Display > Adjust Desktop Size and Position], resize the lateral monitors.
Note: For monitors in portrait mode, the resizing is bugged in nvidia) and must be applied horizontally.
*/
// [Shader Options]
uniform float Bezels
< ui_type = "drag"; ui_label = " ***[2] Screen Bezel Boundary";
ui_tooltip = "Informs the shader of the location of your screen borders.";
ui_min = 0.125; ui_max = 0.33333; ui_step = 0.0001; > = 0.25;
uniform float BezelXOffset
< ui_type = "drag"; ui_label = " ***[3] Bezel Compensation";
ui_tooltip = "Offsets pixels under bezel frames for better alignment.\n"
"Necessary to refine diagonal pixel alignment.";
ui_min = 0; ui_max = 0.03; ui_step = 0.0005; > = 0.01;
uniform float3 OffsetY
< ui_type = "drag"; ui_label = "Offsets y outer // ***[1] inner // Center";
ui_tooltip = "Align central pixels for the corresponding sections.";
ui_min = -1.5; ui_max = 1; ui_step = 0.0005; > = float3(0.5, 0.5, 0.5);
uniform float2 ZoomYLat
< ui_type = "drag"; ui_label = "Zooms Outer // ***[4] Inner (diagonal)";
ui_tooltip = "Oouter = for reduce de fisheyes effect of *Curve Y*.\n"
"Inner = diagonal alignment with central monitors. Reminder don't forget to set *Bezel Compensation* ";
ui_min = 0.5; ui_max = 3; ui_step = 0.01; > = float2(1.4, 1.4);
uniform float Zoom
< ui_type = "drag"; ui_label = "Zoom Level";
ui_tooltip = "YOU NEED TO make few adjustement of previous major *** [1][3][4] if you change ";
ui_min = 0.5; ui_max = 1.2; ui_step = 0.005; > = 0.79;
uniform float DistribZoom
< ui_label = "Redistribution X )"; ui_type = "drag";
ui_tooltip = "Zoom effect from the center to the borders along the X axis 0 = no effect, enlarge center";
ui_min = -2; ui_max = 2; ui_step = 0.002; > = 0;
uniform float Strength
< ui_label = "Convexity Strength"; ui_type = "drag";
ui_tooltip = "Adjusts the strength of the convexity effect";
ui_min = -1.0; ui_max = 1.0;ui_step = 0.01; > = 0.0;
uniform float Center
< ui_label = "Angle of convexity"; ui_type = "drag";
ui_min = 0; ui_max = 1.0; ui_step = 0.01;> = 0.5;
uniform float RadialOffset
< ui_label = "Fov X (uv texture)"; ui_type = "drag";
ui_min = -0.2; ui_max = 1.8; ui_step = 0.01;> = 0.1;
uniform float yZoom
< ui_label = "Zoom Y (planar view xy)"; ui_type = "drag";
ui_min = 0.5; ui_max = 1.75; ui_step = 0.01; > = 1.5;
uniform float yCurve
< ui_label = "Curve Y (planar view xy)"; ui_type = "drag";
ui_min = 0; ui_max = 4; ui_step = 0.01; > = 0;
//[Pixel Shader]
float4 PS_MultiMonitorAlign(float4 position : SV_Position, float2 uv : TEXCOORD0) : SV_Target
{
float absuvx = abs(uv.x - 0.5);
float isCentralZone = step(absuvx, 0.5 - Bezels); // Determine if the area is central or lateral --- 1 (inside) or 0 (outside)
float uvLateralRatio = (0.5 - absuvx) / Bezels; // Ratio relative to the center, outside the bezel
float sqrabsuvx = absuvx * absuvx;
float scaled_X_Redistribution = lerp(0.5, uv.x, 1 + DistribZoom * (1 - sqrabsuvx * 4)); // Zoom effect multiplier
float scaled_Y_Compression = 1 - sqrabsuvx * yCurve; // Parabolic compression calculation. Each Y column will undergo a different zoom factor
// X-axis adjustment
uv.x = isCentralZone * (0.5 + (scaled_X_Redistribution - 0.5) * (Zoom - BezelXOffset))
+ (1 - isCentralZone) * lerp(0.5, scaled_X_Redistribution, lerp(1, Zoom + BezelXOffset, uvLateralRatio) );
// Y-axis adjustment
uv.y = (uv.y - 0.5) * Zoom * yZoom * scaled_Y_Compression; // At the center of uv.y. Global zoom, Y zoom, and parabolic compression.
uv.y = isCentralZone * (uv.y + OffsetY.z) // Central offset
+ (1 - isCentralZone) * (uv.y * lerp(ZoomYLat.x, ZoomYLat.y, uvLateralRatio) + lerp(OffsetY.x, OffsetY.y, uvLateralRatio)); // Lateral zoom and offset
// Last 3 uniform convergence: Reapply a deformation
float2 toCenter = uv - float2(0.5,Center);
float distance = length(toCenter);
uv.x += toCenter.x * (distance * distance * Strength + RadialOffset);
return (uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0) ? float4(0, 0, 0, 1) : tex2D(ReShade::BackBuffer, uv);
}
technique Screen_Alignment
{
pass
{
BlendEnable = FALSE;
VertexShader = PostProcessVS;
PixelShader = PS_MultiMonitorAlign;
}
}
TripleDisplayAlignementHelper.fx
#include "ReShade.fxh"
// [Shader Options]
uniform float2 Points // Points.x and Points.y are limited to [0, 0.5] because of symmetry optimization.
< ui_type = "drag"; ui_label = "Control point positions (symmetrical)";
ui_min = 0; ui_max = 0.5; ui_step = 0.001; > = 0.5;
// Function for checking if point is on the line segment
bool sdLineSegment(float2 p, float2 a, float2 b) {
// Compute the two vectors
float2 ba = b - a;
float2 pa = p - a;
float2 closestPoint = a + dot(pa, ba) / dot(ba, ba) * ba; // Projection onto the segment
return length(p - closestPoint) < 0.001; // Thickness check
}
// [Pixel Shader]
void PS_Lignes(float4 vpos : SV_Position, float2 uv : TEXCOORD, out float4 fragment : SV_Target)
{
float v = Points.x, w = Points.y;
float2 sym_uv = float2(min(uv.x, 1 - uv.x), min(uv.y, 1 - uv.y)); // Reduce the check to segments in a single quadrant by symmetry. Both *axes* limit is reducted to 0.5
float Max = 0.5; // The symmetry will automatically handle the other quadrants mirror
bool isOnInQuarter = sdLineSegment(sym_uv, float2(0, 0), float2(w, Max)) || // 1st diagonal from corner
sdLineSegment(sym_uv, float2(v - 0.0125, 0), float2(w, Max)) || // 2nd diagonal from (a - an offset)
sdLineSegment(sym_uv, float2(v, 0), float2(v, Max)) || // vertical left
sdLineSegment(sym_uv, float2(0, 0.15), float2(Max, 0.15)) || // 1st horizontal highest
sdLineSegment(sym_uv, float2(0, 0.5), float2(Max, 0.5)); // 2nd center
fragment = isOnInQuarter
? float4(1.0, 0.0, 0.0, 1.0) // red
: tex2D(ReShade::BackBuffer, uv); // original uv
}
// [Technique]
technique Screen_Alignment_Helper <
ui_tooltip = "Draws calibration lines on screen for alignment purposes."; >
{
pass {
VertexShader = PostProcessVS;
PixelShader = PS_Lignes;
}
}
Please Log in or Create an account to join the conversation.