Posted: 20th Aug 2021 21:01
Hi all,
I am trying to obtain in 3d the effect that simulates low resolution, as if it were a playstation one, with large pixels and saw teeth. For this I have tried to do SetVirtualResolution (320, 240) and SetWindowSize (960, 720, 1) to achieve full screen and large pixels ... but I can't get it... I get small pixels
As I said, I am looking for an effect as if it were a PS1. Do you know if it is possible to achieve it with AppGameKit classic or studio?
Thank you!
Posted: 20th Aug 2021 21:50
You could use SetVirtualResolution (320, 240) and then instead of drawing to the screen use SetRenderToImage method ( https://www.appgamekit.com/documentation/Reference/Core/SetRenderToImage.htm )

I've used that method for old-skool graphics in 2D and it keeps the blocky effect when scaled up, even for movement and rotation.
Posted: 21st Aug 2021 4:57
Modern Graphics Architectures do not support Resolutions lower than 640x480., and the PlayStation uses a Native Resolution slightly lower than this (360p is the closest _modern_ equivalent)
Even still., almost every Display today is Widescreen (16:9 or 16:10).

Now while you could tinker and tweak with the Physics Resolution Functionality until you get a close approx. of "5th Gen" Graphics... I'd actually make a different suggestion.
Create a Full Scene (Post-Process) Shader.

It should handle 3 Factors.
1) Resolution Scaling, this is actually trivial to create as there are many Pixilation / Tile Shaders; and AMD even has an SDK (FidelityFX Downsampler) that specifically does this.
2) Colour Reduction, remember most old-school Consoles use 16bit Colour; this reduces the colour depth and helps to create the "illusion" of an old school output
3) Affine / Triangle Texture Filtering., these are the most complex elements to pull off; but are essentially to mimicking 5th Gen Hardware.

These three elements can and should be done via a Shader (essentially as a "Screen Filter") to produce the output look you desire., this also means you don't have to cater the Engine specifically to use "Old School" techniques for everything.
Posted: 21st Aug 2021 11:25
Thank for yor answers!
I'm going to use the SetRenderToImage approach as I don't know ho to write shaders.
I've tryed and it works really well, adding setimageminfilter and setimagemagfilter I can get the chunky pixel aspect in the image with the rendered scene.
Thank four you help.
Best regards!
Posted: 21st Aug 2021 22:27
If you want sharp pixels use;
SetDefaultMinFilter(0)
SetDefaultMagFilter(0)
Posted: 25th Aug 2021 15:45
Hi all,
yes blink0k, I'm already using SetDefaultMinFilter and SetDefaultMagFilter, the result is really good to get pixelated images
Thank you!
Posted: 25th Aug 2021 17:11
how do i fix this to work right when window is resized/maximized?

+ Code Snippet
SetErrorMode(2)

SetWindowSize( 640,640, 0 )
SetWindowAllowResize( 1 )
SetScissor(0,0,0,0)
SetSyncRate( 30, 0 ) 
UseNewDefaultFonts( 1 ) 

This = CreateObjectCapsule( 100, 200, 0 )
SetCameraPosition(1,0,0,0)
SetCameraRotation(1,0,0,0)

MoveCameraLocalZ(1,-200)

SetDefaultMinFilter(0)
SetDefaultMagFilter(0)


Img = CreateRenderImage(64,64,0,0)
Spr = CreateSprite(Img)				:	SetSpriteSize(Spr,640,640)

Point = CreateSprite(0)	:	SetSpriteSize(Point,1,1)	:	SetSpriteColor(Point,255,0,0,255)

`SetGlobal3DDepth( 1000 )

turret = CreateSprite(0)	:	SetSpriteSize(turret,2,8)	:	SetSpriteColor(Turret,0,0,255,255)
SetSpritePositionByOffset(turret,32,32)	:

Do
    If GetRawKeyState(27) then Exit

    SetVirtualResolution(64,64)
		SetRenderToImage(Img,0)
		SetSpriteVisible(Spr,0)
		RotateObjectLocalZ(This,0.5)
		SetSpriteAngle(Turret,GetSpriteAngle(turret) + 1)
		SetSpriteVisible(Point,1)
		SetSpriteVisible(turret,1)
		SetSpritePosition(Point, GetPointerX()/10.0, GetPointerY()/10.0)
	ClearScreen()

	Render()

	SetVirtualResolution(640,640)
		SetRenderToScreen()
		SetSpriteVisible(Spr,1)
		SetSpriteVisible(Point,0)
		SetSpriteVisible(turret,0)
	ClearScreen()
	
    Sync()
loop

i know i can remove SetScissor() but the aspect ratio is jacked?
Posted: 26th Aug 2021 16:10
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.

+ Code Snippet
#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.