examples/airship/main.go
func drawGroundImage(screen *ebiten.Image, ground *ebiten.Image) {
	perspectiveGroundImage.Clear()
	gw, _ := ground.Size()
	pw, ph := perspectiveGroundImage.Size()
	for j := 0; j < ph; j++ {
		// z is in [2, -1]
		rate := float64(j) / float64(ph)
		z := (1-rate)*2 + rate*-1
		if z <= 0 {
			break
		}
		op := &ebiten.DrawImageOptions{}
		op.GeoM.Translate(-float64(pw)/2, 0)
		op.GeoM.Scale(1/z, 8) // 8 is an arbitrary number not to make empty lines.
		op.GeoM.Translate(float64(pw)/2, float64(j)/z)

		perspectiveGroundImage.DrawImage(ground.SubImage(image.Rect(0, j, gw, j+1)).(*ebiten.Image), op)
	}

	perspectiveGroundImage.DrawImage(fogImage, nil)

	op := &ebiten.DrawImageOptions{}
	op.GeoM.Translate(-float64(pw)/2, 0)
	op.GeoM.Rotate(-1 * float64(thePlayer.lean) / maxLean * math.Pi / 8)
	op.GeoM.Translate(float64(screenWidth)/2, screenHeight/3)
	screen.DrawImage(perspectiveGroundImage, op)
}
func drawGroundImage(screen *ebiten.Image, ground *ebiten.Image) {
	perspectiveGroundImage.Clear()
	gw, _ := ground.Size()
	pw, ph := perspectiveGroundImage.Size()
	for j := 0; j < ph; j++ {
		// z is in [2, -1]
		rate := float64(j) / float64(ph)
		z := (1-rate)*2 + rate*-1
		if z <= 0 {
			break
		}
		op := &ebiten.DrawImageOptions{}
		op.GeoM.Translate(-float64(pw)/2, 0)
		op.GeoM.Scale(1/z, 8) // 8 is an arbitrary number not to make empty lines.
		op.GeoM.Translate(float64(pw)/2, float64(j)/z)

		perspectiveGroundImage.DrawImage(ground.SubImage(image.Rect(0, j, gw, j+1)).(*ebiten.Image), op)
	}

	perspectiveGroundImage.DrawImage(fogImage, nil)

	op := &ebiten.DrawImageOptions{}
	op.GeoM.Translate(-float64(pw)/2, 0)
	op.GeoM.Rotate(-1 * float64(thePlayer.lean) / maxLean * math.Pi / 8)
	op.GeoM.Translate(float64(screenWidth)/2, screenHeight/3)
	screen.DrawImage(perspectiveGroundImage, op)
}
// drawGroundImage draws the ground image to the given screen image.