Jump to content

Archived

This topic is now archived and is closed to further replies.

denali

Multi-monitor (dual, triple, etc) Stretch/Fisheye Distortion Fix

Recommended Posts

I've decided to get this out for you all to enjoy.  I have created a few tools to do install and configuration, but they are too messy to include, might cause more confusion than they're worth at this time.  I plan to clean them up later.  So this is just the most elegant smallest pieces.

 

This will also help reposition a view for use with two monitors as well, or work for a single ultra display to zap distortion.

 

This code is not optimized, nor elegant.  I am a bad codemonkey.  Works now.  Polish later.  For your enjoyment:

 

Open notepad, but with administrator rights (right click / run as administrator).  

 

From here on "context menu" will mean "right click".

 

Paste this into notepad:

<?xml version="1.0" encoding="UTF-8"?><SimBase.Document Type="AceXML" version="1,0"> <PostProcessEffects.PostProcessDefinition> <EffectName>AntiStretch</EffectName> <ShaderFileName>AS01.psh</ShaderFileName> <ShaderEntryPoint>PsAntiStretchMain</ShaderEntryPoint> <ColorRequest>TRUE</ColorRequest> <DepthRequest>FALSE</DepthRequest> </PostProcessEffects.PostProcessDefinition> </SimBase.Document>

 

 
Save in C:\Program Files (x86)\Lockheed Martin\Prepar3D v2\ShadersHLSL\PostProcess as
AS01.xml  (adjust for your installation location) 
 
Be sure to save as type "all files" at the bottom of the save dialog, or it may have a txt extension.
 

Clear the contents of notepad and paste this: 

// Copyright John Lacquey 2014

#include "Quad.sh"

Texture2D<float4> srcTex;

//static const float PI = 3.14159265f; too much PI is a bad thing

static const float horizontalFOV = 150;
static const float verticalFOV = 70;
 
// this is not needed at this time
//static const float horizontalRes = 5760;
//static const float verticalRes = 1080;
// // B // /| // / | // / | // / | // / | // h / | // hypotenuse / |a opposite // / | // /\ | // / Theta | // /____\_____| // A b C // adjacent // float4 PsAntiStretchMain(PsQuad vert) : SV_Target { // Texture coordinates (0.0 - 1.0) float X = vert.texcoord.x; float Y = vert.texcoord.y; // Texture coordinates normalized to -1.0, 1.0 float normFactor = 0.5f; float normX = 2 * (X - normFactor); float normY = 2 * (Y - normFactor); // Get the source texture dimemsions. uint2 uTDim; srcTex.GetDimensions(uTDim.x,uTDim.y); // Get the radians of the new image. This corresponds to the screen dimensions, // the spherical rectangle which is the image that we want. float horizontalFOVRadians_newImageXWidth = radians(horizontalFOV / 2); float verticalFOVRadians_newImageYHeight = radians(verticalFOV / 2); float rightAngleRadians = radians(asfloat(90)); // The dimensions of the current distorted image, tangent of FOV Radians, // the plane tangent to the sphere. float distortedImageWidth = tan(horizontalFOVRadians_newImageXWidth); float distortedImageHeight = tan(verticalFOVRadians_newImageYHeight); ////// // The ratio of the desired image to the distorted image // possibly use the inverse of this to clip float correctXRatio = 1; //(horizontalFOVRadians_newImageXWidth / distortedImageWidth); float correctYRatio = 1; //(verticalFOVRadians_newImageYHeight / distortedImageHeight); // Increase the dimensions of the distorted image radian measurement by correct_Ratio // in order to have more to crop the new image from. float expandedDistortionWidth = correctXRatio * distortedImageWidth; float expandedDistortionHeight = correctYRatio * distortedImageHeight; // normXY will find the radians of the desired pixel. float horizontalRadiansOfTarget = normX * horizontalFOVRadians_newImageXWidth; float verticalRadiansOfTarget = normY * verticalFOVRadians_newImageYHeight; // find the x coordinate for the desired pixel float targetXPixelRadians_xOfDistortion = tan(horizontalRadiansOfTarget); // x coordinate of pixel on distortion float colorU1 = targetXPixelRadians_xOfDistortion / expandedDistortionWidth; // the ratio of the target pixel over the full distorted dimension radians - to be multiplied by the current image in the final step. float colorUfinal = colorU1;// * correctXRatio; // distortion correction // maintaining all of the color values from the original but distorted image, // some the y values will reach outside of the rectangular boundings of the original image, // and render no color //;float targetYPixelRadians_yOfDistortion = tan(verticalRadiansOfTarget); // find the bottom of the y triangle, the secant of x, reciprocal of cos(A), float secantX_bottomOfYTriangle = 1 / cos(horizontalRadiansOfTarget); //float secantX = 1 / (cos(verticalRadiansOfTarget) * sign(normY)) ; //float secantX = cos(verticalRadiansOfTarget) * sign(normY) ; // radians for the top angle of the new image at the target pixel float oppositeTargetAngleRads = radians(90 - degrees(verticalRadiansOfTarget)); // the y dimension, the maximum top angle radians to the original image plane, some being outside of the rectangle, // needs to be found for every y, for the final ratio, to be multiplied by the current image in the final step. //;float oppositeMaxYAngleRads = radians(90 - degrees(verticalFOVRadians_newImageYHeight)); // the y dimension needs to be ratioed against the original image radians, not the maximum extrapolated from the new image // the dimension in radians of the original image rectangle will be constant as x changes, // so we can use the radians from the maximum degrees of the y FOV, where x = 0, distortedImageHeight // angles will vary because the secant will change. //float oppositeMaxYAngleRads = radians(90 - degrees(verticalFOVRadians_newImageYHeight)); // Now we can use the Law of Sines for the maximum and target in y. // Law of Sines: b/sin( B) = c/sin© // b = (c * sin( B)) / sin© float targetYRadians = (secantX_bottomOfYTriangle * sin(verticalRadiansOfTarget)) / sin(oppositeTargetAngleRads); // float maxYRadians = (secantX_bottomOfYTriangle * sin(verticalFOVRadians_newImageYHeight)) / sin (oppositeMaxYAngleRads); // the final ratio, to be multiplied by the current image in the final step float colorV1 = targetYRadians / distortedImageHeight; float colorVfinal = colorV1; ////// // Denormalize float colorU = (colorUfinal / 2) + normFactor; float colorV = (colorVfinal / 2) + normFactor; // u, v coordinates for the new color int3 iTexCoord = int3(uTDim.x * colorU, uTDim.y * colorV, 0); // load them into the buffer float4 color = srcTex.Load( iTexCoord ); return color; }

 

"Save as" in the same location as above (e.g. C:\Program Files (x86)\Lockheed Martin\Prepar3D v2\ShadersHLSL\PostProcess) as
AS01.psh .  Again, Be sure to save as type "all files" at the bottom of the save dialog, or it may have a txt extension, and P3D2 will not be forgiving.
 
So that is a "High Level Shader Language" shader.  It's c programming language with some DirectX additions, only scripted, not compiled.  You have to re-start P3D2 for every change, AFAIK so far.  This is one of what the Oculus Rift will need to work, but there is probably plenty of code to source from for the math already, and there is already one included in P3D2.2.  
 
Now to load it into P3D2:
 

Quick summary, right click (on the view you wish to augment) / custom camera / save location.  While you're experimenting, put a short name, like "as" (one handed) in the Name box.  Then override FOV and SET THE FOV EXACTLY AS IT IS IN THE SHADER, 150 by 70.  You can set this whatever you like in both locations, but they must be the same.    But at this time you should use this setting as the image buffer faze is tuned for that, and it might give you a mess if  you don't accept my defaults to begin with.  There is more math I can put into this, and have, but it's just gonna hurt you if I give it to you now.     

 

The math is precise, and precise FOV is important.  (There are FOV calculators on the web when you're ready to break into it)

 

Now attach the shader.  Under the camera effects / effects dropdown, look for AS01 (did you save that notepad file as "all"?).  Leave the excludes out.
 

You can preview the view, and even grab the edges of the preview to fill your whole monitor/s.  Save and desire to exit the dialog.  Then  right click  / custom camera / [the name you gave] to use the view.  To edit the view: Views / Edit Custom Cameras / [your view name]

 

There are some issues when you change from view to view.  You have to reload the view (right click / custom camera / [view]) again to bring it back.  And have figured out how to save the shader view from session to session, but it requires a tool I'll put out later.

 
Now for the next part.  P3D needs to be tricked into giving us a image buffer.  
 
You need to download and install AutoHotKey:  http://ahkscript.org/v2/
(I don't think there is an installer, for version 2.0.  There is a 1.0 with installer here:  http://ahkscript.org/download/
I am not sure if that works with this code.  But give it a try if you want and if it doesn't just install 1.0 with it's installer, then copy the 2.0 exe into it's folder)
 
This is the script you'll want around to run with AutoHotKey:  I'm sorry it's messy, but it works.
#NoEnv  ; Recommended for performance and compatibility with future AutoHotkey releases.
; #Warn  ; Enable warnings to assist with detecting common errors.
SendMode Input  ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir%  ; Ensures a consistent starting directory.

Gui, New  ; Creates a new unnamed GUI.
;Gui, Name:New  ; Creates a new GUI, destroying any existing GUI with that name
;Gui, MakeScene:New

Gui, Add, Text,, Height:
Gui, Add, ComboBox, vHeight, 1080|1500|2000|2500|2900||3500|4000|4500|5000|5500|6000|6500|7000|7500|8000|8500

Gui, Add, Text,, Width:
Gui, Add, ComboBox, vWidth, 1000|2000|3000|4000|5000|5760|6500||7000|8000|9000|10000|11000|12000|13000|14000|15000|16000|17000

Gui, Add, Button, gDefaultSize , 5760x1080  ;
Gui, Add, Button, default, OK  ;

Gui, Show,, Prepar3 Position


return  ; End of auto-execute section. The script is idle until the user does something.

GuiClose:
ExitApp
DefaultSize:
Gui, Submit, NoHide ; Save the input from the user to each control's associated variable.
CropBlankMargin(5760 ,1080)
return

ButtonOK:
Gui, Submit, NoHide ; Save the input from the user to each control's associated variable.
CropBlankMargin(Width ,Height)
return

CropBlankMargin(Width = 0,Height = 0, Posx = 0, Posy = 0)
{
  WinGetPos,X,Y,W,H, Lockheed Martin
  If %Width% = 0
    Width := W

  If %Height% = 0
    Height := H

  If %Posx% = 0
    Posx := X

  If %Posy% = 0
    Posy := Y
WinGet, Prepar3D, , Lockheed Martin
 finalXPos := Round(((5760 - Width) / 2) - 1920)
 finalYPos := Round(((1080 - Height) / 2))
hwnd := Prepar3D
DllCall("SetWindowPos"
    , "UInt", hwnd
    , "UInt", 0        ; hWndInsertAfter
    , "Int" , finalXPos    ; 
    , "Int" , finalYPos   ; 
    , "Int" , Width     ; 
    , "Int" , Height     ; 
    , "UInt", 4|0x400) ; uFlags
}

ViewFR(Width = 0,Height = 0, Posx = 0, Posy = 0)
{
  WinGetPos,X,Y,W,H,A
  If %Width% = 0
    Width := W

  If %Height% = 0
    Height := H


  If %Posx% = 0
    Posx := X


  If %Posy% = 0
    Posy := Y

hwnd := WinActive("A")
DllCall("SetWindowPos"
    , "UInt", hwnd
    , "UInt", 0        ; hWndInsertAfter
    , "Int" , -1910    ; cx (width)
    , "Int" , 0     ; cy (height)
    , "Int" , Width    ; X
    , "Int" , Height   ; Y
    , "UInt", 4|0x400) ; uFlags
}

ResizeWin(Width = 0,Height = 0, Posx = 0, Posy = 0)
{
  WinGetPos,X,Y,W,H,A
  If %Width% = 0
    Width := W

  If %Height% = 0
    Height := H


  If %Posx% = 0
    Posx := X


  If %Posy% = 0
    Posy := Y

  WinMove,A,,-1920,0,%Width%,%Height%
}


#!space::CropBlankMargin(Width ,Height)
#space::ResizeWin(5760,1080, 100, 1)
#!f::ViewFR(Width ,Height ,1960, 1)

There are three hotkeys in there.

 

[control][windows key][space]  does the same as OK, but with the gui hidden.

[control][space]  restores to 5760 window so you can get to menus.

[control][windows key][f]  lets you see the framerate display (may need adjusting, and I might be able to move the display in P3D later)

 

What is going on here is the view is being stretched to put just what you want on screen.  Change those numbers at your peril, or to move the view for dual monitor configs, whatever.

 

Gotta go, hope it's enough to get things up for you.  Hope you enjoy.

 

For reference, open the Prepar3d Learning Center, and follow the Contents Tree to Prepar3D Product/Getting Started/ View System/ Custom Camera UI and Post Process right below that.

 

Grey Skies

 

 


And I don't have a second to fix that for you

 

BBL, sorry


Disclaimer:  9900k@5.3ghz on Asus Maximus X Formula, G.Skill TridentZ RGB 4x8GB 4266/17 XMP, EVGA 2080 ti Kingpin (8400/2160Mhz), Samsung 960 EVO 250GB PCIe M.2 NVMe SSD , 28TB HDD total - 4TB+ photoscenery, Romex Software PrimoCache RAM and SSD cache (must have!), 3x1080p 30" monitors, Samsung Odyssey VR HMD, Pimax 4k & BE HMDs, Samsung Gear VR '17, Homdio v1, Cardboard, custom loop 2x 360x64ML Rads, Thermaltake View 71, VRM watercool, Thermal Grizzly Conductonaut CPU (naked die), Fujipoly / ModRight Ultra Extreme System Builder Thermal Pad on MB VRM. 8x Corsair ML120 (slight positive pressure). 🙂

Share this post


Link to post

I apologize to the admins for the condition of the this first post of this topic.  I realize I didn't specify code type in the code box.  That was the problem.  But I tried to fix it and it said I wasn't authorized.  Please delete this post. 


Disclaimer:  9900k@5.3ghz on Asus Maximus X Formula, G.Skill TridentZ RGB 4x8GB 4266/17 XMP, EVGA 2080 ti Kingpin (8400/2160Mhz), Samsung 960 EVO 250GB PCIe M.2 NVMe SSD , 28TB HDD total - 4TB+ photoscenery, Romex Software PrimoCache RAM and SSD cache (must have!), 3x1080p 30" monitors, Samsung Odyssey VR HMD, Pimax 4k & BE HMDs, Samsung Gear VR '17, Homdio v1, Cardboard, custom loop 2x 360x64ML Rads, Thermaltake View 71, VRM watercool, Thermal Grizzly Conductonaut CPU (naked die), Fujipoly / ModRight Ultra Extreme System Builder Thermal Pad on MB VRM. 8x Corsair ML120 (slight positive pressure). 🙂

Share this post


Link to post
  • Tom Allensworth,
    Founder of AVSIM Online


  • Flight Simulation's Premier Resource!

    AVSIM is a free service to the flight simulation community. AVSIM is staffed completely by volunteers and all funds donated to AVSIM go directly back to supporting the community. Your donation here helps to pay our bandwidth costs, emergency funding, and other general costs that crop up from time to time. Thank you for your support!

    Click here for more information and to see all donations year to date.
  • Donation Goals

    AVSIM's 2020 Fundraising Goal

    Donate to our annual general fundraising goal. This donation keeps our doors open and providing you service 24 x 7 x 365. Your donation here helps to pay our bandwidth costs, emergency funding, and other general costs that crop up from time to time. We reset this goal every new year for the following year's goal.


    53%
    $13,405.00 of $25,000.00 Donate Now
×
×
  • Create New...