After some testing with my usual code for this type of thing it looks like 3D is treated differently than 2D when rendering to an image. 3D seems to use the aspect ratio of the window whereas 2D uses the virtual resolution (but only if you set the virtual resolution AFTER SetRenderToImage() for some reason). Check out this snippet. The Sprite's aspect ratio stays the same in windowed vs fullscreen, but not the cube's. I'm guessing it'll require some messing around with the 3D projection to compensate for the difference in the window's aspect ratio vs the render image's.
#constant GAME_WIDTH 64
#constant GAME_HEIGHT 64
#constant WINDOW_WIDTH 720
#constant WINDOW_HEIGHT 720
SetDefaultMinFilter(0)
SetDefaultMagFilter(0)
Renderer as tRenderer
InitRenderer(Renderer, GAME_WIDTH, GAME_HEIGHT, WINDOW_WIDTH, WINDOW_HEIGHT, 0, 0)
CreateSprite(1, CreateImageColor(255,0,0,255))
SetSpriteSize(1, 16, 16)
SetSpritePositionByOffset(1, 32, 32)
CreateObjectBox(1, 10, 10, 10)
fullscreen = 0
do
dt# = GetFrameTime()
RotateObjectGlobalY(1, 90 * dt#)
// F to toggle fullscreen
if GetRawKeyPressed(70)
fullscreen = 1 - fullscreen
InitRenderer(Renderer, GAME_WIDTH, GAME_HEIGHT, WINDOW_WIDTH, WINDOW_HEIGHT, fullscreen, 0)
endif
SetSpriteAngle(1, GetSpriteAngle(1) + 90 * dt#)
UpdateRenderer(Renderer)
loop
type tRenderer
width as float
height as float
wWidth as float
wHeight as float
fullScreen as integer
pixelPerfect as integer
img as integer
spr as integer
endtype
function InitRenderer(inst ref as tRenderer, width, height, wWidth, wHeight, fullScreen, pixelPerfect)
inst.width = width
inst.height = height
inst.wWidth = wWidth
inst.wHeight = wHeight
inst.fullScreen = fullScreen
inst.pixelPerfect = pixelPerfect
if inst.fullScreen
inst.wWidth = GetMaxDeviceWidth()
inst.wHeight = GetMaxDeviceHeight()
endif
scale# = _Min(inst.wWidth / inst.width, inst.wHeight / inst.height)
if inst.pixelPerfect then scale# = floor(scale#)
SetWindowSize(inst.wWidth, inst.wHeight, inst.fullScreen)
// some platforms don't set the window size instantly
timeout = 0
while ((GetWindowWidth() <> inst.wWidth or GetWindowHeight() <> inst.wHeight) and timeout < 100)
sync()
timeout = timeout + 1
endwhile
SetVirtualResolution(inst.wWidth, inst.wHeight)
if GetImageExists(inst.img) then DeleteImage(inst.img)
if GetSpriteExists(inst.spr) then DeleteSprite(inst.spr)
inst.img = CreateRenderImage(inst.width, inst.height, 0, 0)
inst.spr = CreateSprite(inst.img)
SetSpriteScale(inst.spr, scale#, scale#)
SetSpritePositionByOffset(inst.spr, inst.wWidth / 2, inst.wHeight / 2)
FixSpriteToScreen(inst.spr, 1)
SetSpriteVisible(inst.spr, 0)
SetVirtualResolution(inst.width, inst.height)
endfunction
function UpdateRenderer(inst ref as tRenderer)
// re-init if window size changes externally
if (GetWindowWidth() <> inst.wWidth or GetWindowHeight() <> inst.wHeight)
InitRenderer(inst, inst.width, inst.height, GetWindowWidth(), GetWindowHeight(), inst.fullScreen, inst.pixelPerfect)
endif
Update(GetFrameTime())
ClearScreen()
Render()
SetVirtualResolution(inst.wWidth, inst.wHeight)
SetRenderToScreen()
SetSpriteVisible(inst.spr, 1)
DrawSprite(inst.spr)
SetSpriteVisible(inst.spr, 0)
Swap()
SetRenderToImage(inst.img, -1)
SetVirtualResolution(inst.width, inst.height)
SetCameraAspect(1, inst.width / inst.height)
endfunction
function _Min(a as float, b as float)
if a < b then exitfunction a
endfunction b
EDIT: Ah, looks like the answer is SetCameraAspect() ... just setting it to the render image width / height each frame seems to fix it. I've edited the code snippet.