ebiten/internal/builtinshader/shader.go
Hajime Hoshi 7ed2d73406 internal/builtinshader: bug fix: wrong blending rate
The blending rate of colors in a square vertices should be calculated
by the lower-right point, not the upper-left point.

mix(a, b, rate) function calculates (1-rate)*a + rate*b, so a should
be weighted if rate is close to 0, and b should be weighted if rate
is close to 1. The current implementation was opposite.

Rendering results don't seem to be changed so much actually, but the
current implementation doesn't make sense.
2023-08-31 15:11:37 +09:00

168 lines
3.8 KiB
Go

// Copyright 2022 The Ebitengine Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package builtinshader
import (
"bytes"
"fmt"
"sync"
"text/template"
)
type Filter int
const (
FilterNearest Filter = iota
FilterLinear
)
const FilterCount = 2
type Address int
const (
AddressUnsafe Address = iota
AddressClampToZero
AddressRepeat
)
const AddressCount = 3
const (
UniformColorMBody = "ColorMBody"
UniformColorMTranslation = "ColorMTranslation"
)
var (
shaders [FilterCount][AddressCount][2][]byte
shadersM sync.Mutex
)
var tmpl = template.Must(template.New("tmpl").Parse(`//kage:unit pixels
package main
{{if .UseColorM}}
var ColorMBody mat4
var ColorMTranslation vec4
{{end}}
{{if eq .Address .AddressRepeat}}
func adjustTexelForAddressRepeat(p vec2) vec2 {
origin := imageSrc0Origin()
size := imageSrc0Size()
return mod(p - origin, size) + origin
}
{{end}}
func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
{{if eq .Filter .FilterNearest}}
{{if eq .Address .AddressUnsafe}}
clr := imageSrc0UnsafeAt(texCoord)
{{else if eq .Address .AddressClampToZero}}
clr := imageSrc0At(texCoord)
{{else if eq .Address .AddressRepeat}}
clr := imageSrc0At(adjustTexelForAddressRepeat(texCoord))
{{end}}
{{else if eq .Filter .FilterLinear}}
p0 := texCoord - 1/2.0
p1 := texCoord + 1/2.0
{{if eq .Address .AddressRepeat}}
p0 = adjustTexelForAddressRepeat(p0)
p1 = adjustTexelForAddressRepeat(p1)
{{end}}
{{if eq .Address .AddressUnsafe}}
c0 := imageSrc0UnsafeAt(p0)
c1 := imageSrc0UnsafeAt(vec2(p1.x, p0.y))
c2 := imageSrc0UnsafeAt(vec2(p0.x, p1.y))
c3 := imageSrc0UnsafeAt(p1)
{{else}}
c0 := imageSrc0At(p0)
c1 := imageSrc0At(vec2(p1.x, p0.y))
c2 := imageSrc0At(vec2(p0.x, p1.y))
c3 := imageSrc0At(p1)
{{end}}
rate := fract(p1)
clr := mix(mix(c0, c1, rate.x), mix(c2, c3, rate.x), rate.y)
{{end}}
{{if .UseColorM}}
// Un-premultiply alpha.
// When the alpha is 0, 1-sign(alpha) is 1.0, which means division does nothing.
clr.rgb /= clr.a + (1-sign(clr.a))
// Apply the clr matrix.
clr = (ColorMBody * clr) + ColorMTranslation
// Premultiply alpha
clr.rgb *= clr.a
// Apply the color scale.
clr *= color
// Clamp the output.
clr.rgb = min(clr.rgb, clr.a)
{{else}}
// Apply the color scale.
clr *= color
{{end}}
return clr
}
`))
// Shader returns the built-in shader based on the given parameters.
//
// The returned shader always uses a color matrix so far.
func Shader(filter Filter, address Address, useColorM bool) []byte {
shadersM.Lock()
defer shadersM.Unlock()
var c int
if useColorM {
c = 1
}
if s := shaders[filter][address][c]; s != nil {
return s
}
var buf bytes.Buffer
if err := tmpl.Execute(&buf, struct {
Filter Filter
FilterNearest Filter
FilterLinear Filter
Address Address
AddressUnsafe Address
AddressClampToZero Address
AddressRepeat Address
UseColorM bool
}{
Filter: filter,
FilterNearest: FilterNearest,
FilterLinear: FilterLinear,
Address: address,
AddressUnsafe: AddressUnsafe,
AddressClampToZero: AddressClampToZero,
AddressRepeat: AddressRepeat,
UseColorM: useColorM,
}); err != nil {
panic(fmt.Sprintf("builtinshader: tmpl.Execute failed: %v", err))
}
b := buf.Bytes()
shaders[filter][address][c] = b
return b
}