Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ebiten: rendering distortion with the screen (crisp-edge) shader #3182

Closed
1 of 11 tasks
hajimehoshi opened this issue Jan 17, 2025 · 2 comments
Closed
1 of 11 tasks

ebiten: rendering distortion with the screen (crisp-edge) shader #3182

hajimehoshi opened this issue Jan 17, 2025 · 2 comments

Comments

@hajimehoshi
Copy link
Owner

Ebitengine Version

b297611

Operating System

  • Windows
  • macOS
  • Linux
  • FreeBSD
  • OpenBSD
  • Android
  • iOS
  • Nintendo Switch
  • PlayStation 5
  • Xbox
  • Web Browsers

Go Version (go version)

go version go1.23.2 windows/amd64

What steps will reproduce the problem?

Run this prgoram on a certain machine

package main

import (
	"image/color"

	"github.com/hajimehoshi/ebiten/v2"
)

var crispScalingShaderSrc = []byte(`//kage:unit pixels

package main

func Fragment(dstPos vec4, srcPos vec2, color vec4) vec4 {
	// Blend source colors in a rectangle region.
	srcRegionSize := vec2(abs(dfdx(srcPos.x)), abs(dfdy(srcPos.y)))
	p0 := srcPos - srcRegionSize/2.0
	p1 := srcPos + srcRegionSize/2.0

	c0 := imageSrc0UnsafeAt(p0)
	c1 := imageSrc0UnsafeAt(vec2(p1.x, p0.y))
	c2 := imageSrc0UnsafeAt(vec2(p0.x, p1.y))
	c3 := imageSrc0UnsafeAt(p1)

	rate := clamp(fract(p1)/srcRegionSize, 0, 1)
	return mix(mix(c0, c1, rate.x), mix(c2, c3, rate.x), rate.y) * color
}
`)

type Game struct {
	shader *ebiten.Shader
	img    *ebiten.Image
}

func (g *Game) Update() error {
	if g.shader == nil {
		shader, err := ebiten.NewShader(crispScalingShaderSrc)
		if err != nil {
			return err
		}
		g.shader = shader
	}
	if g.img == nil {
		img := ebiten.NewImage(72, 72)
		for j := 0; j < 72; j++ {
			for i := 0; i < 72; i++ {
				if (i+j)%2 == 0 {
					img.Set(i, j, color.RGBA{0xff, 0xff, 0xff, 0xff})
				} else {
					img.Set(i, j, color.RGBA{0x00, 0x00, 0x00, 0xff})
				}
			}
		}
		g.img = img
	}
	return nil
}

func (g *Game) Draw(screen *ebiten.Image) {
	op := &ebiten.DrawRectShaderOptions{}
	op.Images[0] = g.img
	screen.DrawRectShader(72, 72, g.shader, op)
}

func (g *Game) Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeight int) {
	return 72, 72
}

func main() {
	ebiten.SetWindowTitle("Test")
	if err := ebiten.RunGame(&Game{}); err != nil {
		panic(err)
	}
}

My machine spec is:

/////////////////  /////////////////    hajimehoshi@LAPTOP-31PU6LDL
/////////////////  /////////////////    ---------------------------
/////////////////  /////////////////    OS: Windows 11 (Home) x86_64
/////////////////  /////////////////    Host: VJS143C11N (Reserved)
/////////////////  /////////////////    Kernel: WIN32_NT 10.0.22631.4602 (23H2)
/////////////////  /////////////////    Uptime: 4 days, 1 hour, 29 mins
/////////////////  /////////////////    Packages: 3 (scoop)
/////////////////  /////////////////    Shell: Windows PowerShell 5.1.22621.4391
                                        Display (BOE079F): 3840x2160 @ 60 Hz (as 1920x1080) in 14" [Built-in]
/////////////////  /////////////////    DE: Fluent
/////////////////  /////////////////    WM: Desktop Window Manager
/////////////////  /////////////////    WM Theme: Custom - Blue (System: Light, Apps: Light)
/////////////////  /////////////////    Icons: Recycle Bin
/////////////////  /////////////////    Font: Yu Gothic UI (12pt) [Caption / Menu / Message / Status]
/////////////////  /////////////////    Cursor: Windows 標準 (32px)
/////////////////  /////////////////    Terminal: Windows Terminal 1.21.3231.0
/////////////////  /////////////////    Terminal Font: Cascadia Mono (12pt)
                                        CPU: Intel(R) Core(TM) i7-1065G7 (8) @ 1.50 GHz
                                        GPU: Intel(R) Iris(R) Plus Graphics (128.00 MiB) [Integrated]
                                        Memory: 14.32 GiB / 15.73 GiB (91%)
                                        Swap: 3.44 GiB / 25.00 GiB (14%)
                                        Disk (C:\): 333.44 GiB / 930.52 GiB (36%) - NTFS
                                        Disk (G:\): 363.30 GiB / 930.52 GiB (39%) - FAT32
                                        Disk (P:\): 8.00 KiB / 8.00 KiB (100%) - PS5_FS [External]
                                        Local IP (Wi-Fi): 192.168.0.16/24
                                        Battery: 89% [Discharging]
                                        Locale: en-US

What is the expected result?

Dot patterns are rendered correctly

Image

What happens instead?

Dot patterns are distorted

Image

Anything else you feel useful to add?

This doesn't happen on MacBook M3 Pro.

@hajimehoshi
Copy link
Owner Author

Simply, dfdx/dfdy was not accurate as I expected on some machines.

@hajimehoshi hajimehoshi closed this as not planned Won't fix, can't repro, duplicate, stale Jan 17, 2025
@hajimehoshi
Copy link
Owner Author

In the specific case of the crisp shader, capping the src region size worked.

//kage:unit pixels

package main

func Fragment(dstPos vec4, srcPos vec2, color vec4) vec4 {
	// Blend source colors in a rectangle region.
	srcRegionSize := vec2(abs(dfdx(srcPos.x)), abs(dfdy(srcPos.y)))
	srcRegionSize = min(srcRegionSize, vec2(1)) // Add this
	p0 := srcPos - srcRegionSize/2.0
	p1 := srcPos + srcRegionSize/2.0

	c0 := imageSrc0UnsafeAt(p0)
	c1 := imageSrc0UnsafeAt(vec2(p1.x, p0.y))
	c2 := imageSrc0UnsafeAt(vec2(p0.x, p1.y))
	c3 := imageSrc0UnsafeAt(p1)

	rate := clamp(fract(p1)/srcRegionSize, 0, 1)
	return mix(mix(c0, c1, rate.x), mix(c2, c3, rate.x), rate.y) * color
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant