mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-25 03:08:54 +01:00
graphicsdriver/opengl: Refactoring: Filter as a uniform value
This commit is contained in:
parent
529cd3376b
commit
7e50ae39c9
@ -119,13 +119,8 @@ type openGLState struct {
|
|||||||
// elementArrayBuffer is OpenGL's element array buffer (indices data).
|
// elementArrayBuffer is OpenGL's element array buffer (indices data).
|
||||||
elementArrayBuffer buffer
|
elementArrayBuffer buffer
|
||||||
|
|
||||||
// programNearest is OpenGL's program for rendering a texture with nearest filter.
|
// program is OpenGL's program for rendering a texture.
|
||||||
programNearest program
|
program program
|
||||||
|
|
||||||
// programLinear is OpenGL's program for rendering a texture with linear filter.
|
|
||||||
programLinear program
|
|
||||||
|
|
||||||
programScreen program
|
|
||||||
|
|
||||||
lastProgram program
|
lastProgram program
|
||||||
lastViewportWidth int
|
lastViewportWidth int
|
||||||
@ -134,6 +129,7 @@ type openGLState struct {
|
|||||||
lastColorMatrixTranslation []float32
|
lastColorMatrixTranslation []float32
|
||||||
lastSourceWidth int
|
lastSourceWidth int
|
||||||
lastSourceHeight int
|
lastSourceHeight int
|
||||||
|
lastFilter *graphics.Filter
|
||||||
|
|
||||||
source *Image
|
source *Image
|
||||||
destination *Image
|
destination *Image
|
||||||
@ -162,18 +158,13 @@ func (s *openGLState) reset(context *context) error {
|
|||||||
s.lastColorMatrixTranslation = nil
|
s.lastColorMatrixTranslation = nil
|
||||||
s.lastSourceWidth = 0
|
s.lastSourceWidth = 0
|
||||||
s.lastSourceHeight = 0
|
s.lastSourceHeight = 0
|
||||||
|
s.lastFilter = nil
|
||||||
|
|
||||||
// When context lost happens, deleting programs or buffers is not necessary.
|
// When context lost happens, deleting programs or buffers is not necessary.
|
||||||
// However, it is not assumed that reset is called only when context lost happens.
|
// However, it is not assumed that reset is called only when context lost happens.
|
||||||
// Let's delete them explicitly.
|
// Let's delete them explicitly.
|
||||||
if s.programNearest != zeroProgram {
|
if s.program != zeroProgram {
|
||||||
context.deleteProgram(s.programNearest)
|
context.deleteProgram(s.program)
|
||||||
}
|
|
||||||
if s.programLinear != zeroProgram {
|
|
||||||
context.deleteProgram(s.programLinear)
|
|
||||||
}
|
|
||||||
if s.programScreen != zeroProgram {
|
|
||||||
context.deleteProgram(s.programScreen)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// On browsers (at least Chrome), buffers are already detached from the context
|
// On browsers (at least Chrome), buffers are already detached from the context
|
||||||
@ -193,43 +184,15 @@ func (s *openGLState) reset(context *context) error {
|
|||||||
}
|
}
|
||||||
defer context.deleteShader(shaderVertexModelviewNative)
|
defer context.deleteShader(shaderVertexModelviewNative)
|
||||||
|
|
||||||
shaderFragmentNearestNative, err := context.newShader(fragmentShader, shaderStr(shaderFragmentNearest))
|
shaderFragmentColorMatrixNative, err := context.newShader(fragmentShader, shaderStr(shaderFragmentColorMatrix))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Sprintf("graphics: shader compiling error:\n%s", err))
|
panic(fmt.Sprintf("graphics: shader compiling error:\n%s", err))
|
||||||
}
|
}
|
||||||
defer context.deleteShader(shaderFragmentNearestNative)
|
defer context.deleteShader(shaderFragmentColorMatrixNative)
|
||||||
|
|
||||||
shaderFragmentLinearNative, err := context.newShader(fragmentShader, shaderStr(shaderFragmentLinear))
|
s.program, err = context.newProgram([]shader{
|
||||||
if err != nil {
|
|
||||||
panic(fmt.Sprintf("graphics: shader compiling error:\n%s", err))
|
|
||||||
}
|
|
||||||
defer context.deleteShader(shaderFragmentLinearNative)
|
|
||||||
|
|
||||||
shaderFragmentScreenNative, err := context.newShader(fragmentShader, shaderStr(shaderFragmentScreen))
|
|
||||||
if err != nil {
|
|
||||||
panic(fmt.Sprintf("graphics: shader compiling error:\n%s", err))
|
|
||||||
}
|
|
||||||
defer context.deleteShader(shaderFragmentScreenNative)
|
|
||||||
|
|
||||||
s.programNearest, err = context.newProgram([]shader{
|
|
||||||
shaderVertexModelviewNative,
|
shaderVertexModelviewNative,
|
||||||
shaderFragmentNearestNative,
|
shaderFragmentColorMatrixNative,
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
s.programLinear, err = context.newProgram([]shader{
|
|
||||||
shaderVertexModelviewNative,
|
|
||||||
shaderFragmentLinearNative,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
s.programScreen, err = context.newProgram([]shader{
|
|
||||||
shaderVertexModelviewNative,
|
|
||||||
shaderFragmentScreenNative,
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -277,18 +240,7 @@ func (d *Driver) useProgram(mode graphics.CompositeMode, colorM *affine.ColorM,
|
|||||||
|
|
||||||
d.context.blendFunc(mode)
|
d.context.blendFunc(mode)
|
||||||
|
|
||||||
var program program
|
program := d.state.program
|
||||||
switch filter {
|
|
||||||
case graphics.FilterNearest:
|
|
||||||
program = d.state.programNearest
|
|
||||||
case graphics.FilterLinear:
|
|
||||||
program = d.state.programLinear
|
|
||||||
case graphics.FilterScreen:
|
|
||||||
program = d.state.programScreen
|
|
||||||
default:
|
|
||||||
panic("not reached")
|
|
||||||
}
|
|
||||||
|
|
||||||
if d.state.lastProgram != program {
|
if d.state.lastProgram != program {
|
||||||
d.context.useProgram(program)
|
d.context.useProgram(program)
|
||||||
if d.state.lastProgram != zeroProgram {
|
if d.state.lastProgram != zeroProgram {
|
||||||
@ -341,7 +293,12 @@ func (d *Driver) useProgram(mode graphics.CompositeMode, colorM *affine.ColorM,
|
|||||||
d.state.lastSourceHeight = sh
|
d.state.lastSourceHeight = sh
|
||||||
}
|
}
|
||||||
|
|
||||||
if program == d.state.programScreen {
|
if d.state.lastFilter == nil || *d.state.lastFilter != filter {
|
||||||
|
d.context.uniformInt(program, "filter", int(filter))
|
||||||
|
d.state.lastFilter = &filter
|
||||||
|
}
|
||||||
|
|
||||||
|
if filter == graphics.FilterScreen {
|
||||||
scale := float32(dstW) / float32(srcW)
|
scale := float32(dstW) / float32(srcW)
|
||||||
d.context.uniformFloat(program, "scale", scale)
|
d.context.uniformFloat(program, "scale", scale)
|
||||||
}
|
}
|
||||||
|
@ -15,34 +15,37 @@
|
|||||||
package opengl
|
package opengl
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/hajimehoshi/ebiten/internal/graphics"
|
||||||
)
|
)
|
||||||
|
|
||||||
type shaderID int
|
type shaderID int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
shaderVertexModelview shaderID = iota
|
shaderVertexModelview shaderID = iota
|
||||||
shaderFragmentNearest
|
shaderFragmentColorMatrix
|
||||||
shaderFragmentLinear
|
|
||||||
shaderFragmentScreen
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func shaderStr(id shaderID) string {
|
func shaderStr(id shaderID) string {
|
||||||
if id == shaderVertexModelview {
|
|
||||||
return shaderStrVertex
|
|
||||||
}
|
|
||||||
defs := []string{}
|
|
||||||
switch id {
|
switch id {
|
||||||
case shaderFragmentNearest:
|
case shaderVertexModelview:
|
||||||
defs = append(defs, "#define FILTER_NEAREST")
|
return shaderStrVertex
|
||||||
case shaderFragmentLinear:
|
case shaderFragmentColorMatrix:
|
||||||
defs = append(defs, "#define FILTER_LINEAR")
|
replaces := map[string]string{
|
||||||
case shaderFragmentScreen:
|
"{{.FilterNearest}}": fmt.Sprintf("%d", graphics.FilterNearest),
|
||||||
defs = append(defs, "#define FILTER_SCREEN")
|
"{{.FilterLinear}}": fmt.Sprintf("%d", graphics.FilterLinear),
|
||||||
|
"{{.FilterScreen}}": fmt.Sprintf("%d", graphics.FilterScreen),
|
||||||
|
}
|
||||||
|
src := shaderStrFragment
|
||||||
|
for k, v := range replaces {
|
||||||
|
src = strings.Replace(src, k, v, -1)
|
||||||
|
}
|
||||||
|
return src
|
||||||
default:
|
default:
|
||||||
panic("not reached")
|
panic("not reached")
|
||||||
}
|
}
|
||||||
return strings.Replace(shaderStrFragment, "{{Definitions}}", strings.Join(defs, "\n"), -1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -79,12 +82,15 @@ precision mediump float;
|
|||||||
#define highp
|
#define highp
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
{{Definitions}}
|
#define FILTER_NEAREST ({{.FilterNearest}})
|
||||||
|
#define FILTER_LINEAR ({{.FilterLinear}})
|
||||||
|
#define FILTER_SCREEN ({{.FilterScreen}})
|
||||||
|
|
||||||
uniform sampler2D texture;
|
uniform sampler2D texture;
|
||||||
uniform mat4 color_matrix_body;
|
uniform mat4 color_matrix_body;
|
||||||
uniform vec4 color_matrix_translation;
|
uniform vec4 color_matrix_translation;
|
||||||
|
|
||||||
|
uniform int filter;
|
||||||
uniform highp vec2 source_size;
|
uniform highp vec2 source_size;
|
||||||
|
|
||||||
#if defined(FILTER_SCREEN)
|
#if defined(FILTER_SCREEN)
|
||||||
@ -113,65 +119,64 @@ void main(void) {
|
|||||||
highp vec2 pos = varying_tex;
|
highp vec2 pos = varying_tex;
|
||||||
highp vec2 texel_size = 1.0 / source_size;
|
highp vec2 texel_size = 1.0 / source_size;
|
||||||
|
|
||||||
#if defined(FILTER_NEAREST)
|
vec4 color;
|
||||||
vec4 color = texture2D(texture, pos);
|
|
||||||
if (pos.x < varying_tex_region[0] ||
|
if (filter == FILTER_NEAREST) {
|
||||||
pos.y < varying_tex_region[1] ||
|
color = texture2D(texture, pos);
|
||||||
(varying_tex_region[2] - texel_size.x / 512.0) <= pos.x ||
|
if (pos.x < varying_tex_region[0] ||
|
||||||
(varying_tex_region[3] - texel_size.y / 512.0) <= pos.y) {
|
pos.y < varying_tex_region[1] ||
|
||||||
color = vec4(0, 0, 0, 0);
|
(varying_tex_region[2] - texel_size.x / 512.0) <= pos.x ||
|
||||||
|
(varying_tex_region[3] - texel_size.y / 512.0) <= pos.y) {
|
||||||
|
color = vec4(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
} else if (filter == FILTER_LINEAR) {
|
||||||
|
highp vec2 p0 = pos - texel_size / 2.0;
|
||||||
|
highp vec2 p1 = pos + texel_size / 2.0;
|
||||||
|
|
||||||
|
p1 = adjustTexel(p0, p1);
|
||||||
|
|
||||||
|
vec4 c0 = texture2D(texture, p0);
|
||||||
|
vec4 c1 = texture2D(texture, vec2(p1.x, p0.y));
|
||||||
|
vec4 c2 = texture2D(texture, vec2(p0.x, p1.y));
|
||||||
|
vec4 c3 = texture2D(texture, p1);
|
||||||
|
if (p0.x < varying_tex_region[0]) {
|
||||||
|
c0 = vec4(0, 0, 0, 0);
|
||||||
|
c2 = vec4(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
if (p0.y < varying_tex_region[1]) {
|
||||||
|
c0 = vec4(0, 0, 0, 0);
|
||||||
|
c1 = vec4(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
if ((varying_tex_region[2] - texel_size.x / 512.0) <= p1.x) {
|
||||||
|
c1 = vec4(0, 0, 0, 0);
|
||||||
|
c3 = vec4(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
if ((varying_tex_region[3] - texel_size.y / 512.0) <= p1.y) {
|
||||||
|
c2 = vec4(0, 0, 0, 0);
|
||||||
|
c3 = vec4(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec2 rate = fract(p0 * source_size);
|
||||||
|
color = mix(mix(c0, c1, rate.x), mix(c2, c3, rate.x), rate.y);
|
||||||
|
} else if (filter == FILTER_SCREEN) {
|
||||||
|
highp vec2 p0 = pos - texel_size / 2.0 / scale;
|
||||||
|
highp vec2 p1 = pos + texel_size / 2.0 / scale;
|
||||||
|
|
||||||
|
p1 = adjustTexel(p0, p1);
|
||||||
|
|
||||||
|
vec4 c0 = texture2D(texture, p0);
|
||||||
|
vec4 c1 = texture2D(texture, vec2(p1.x, p0.y));
|
||||||
|
vec4 c2 = texture2D(texture, vec2(p0.x, p1.y));
|
||||||
|
vec4 c3 = texture2D(texture, p1);
|
||||||
|
// Texels must be in the source rect, so it is not necessary to check that like linear filter.
|
||||||
|
|
||||||
|
vec2 rateCenter = vec2(1.0, 1.0) - texel_size / 2.0 / scale;
|
||||||
|
vec2 rate = clamp(((fract(p0 * source_size) - rateCenter) * scale) + rateCenter, 0.0, 1.0);
|
||||||
|
color = mix(mix(c0, c1, rate.x), mix(c2, c3, rate.x), rate.y);
|
||||||
|
} else {
|
||||||
|
// Not reached.
|
||||||
|
discard;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(FILTER_LINEAR)
|
|
||||||
highp vec2 p0 = pos - texel_size / 2.0;
|
|
||||||
highp vec2 p1 = pos + texel_size / 2.0;
|
|
||||||
|
|
||||||
p1 = adjustTexel(p0, p1);
|
|
||||||
|
|
||||||
vec4 c0 = texture2D(texture, p0);
|
|
||||||
vec4 c1 = texture2D(texture, vec2(p1.x, p0.y));
|
|
||||||
vec4 c2 = texture2D(texture, vec2(p0.x, p1.y));
|
|
||||||
vec4 c3 = texture2D(texture, p1);
|
|
||||||
if (p0.x < varying_tex_region[0]) {
|
|
||||||
c0 = vec4(0, 0, 0, 0);
|
|
||||||
c2 = vec4(0, 0, 0, 0);
|
|
||||||
}
|
|
||||||
if (p0.y < varying_tex_region[1]) {
|
|
||||||
c0 = vec4(0, 0, 0, 0);
|
|
||||||
c1 = vec4(0, 0, 0, 0);
|
|
||||||
}
|
|
||||||
if ((varying_tex_region[2] - texel_size.x / 512.0) <= p1.x) {
|
|
||||||
c1 = vec4(0, 0, 0, 0);
|
|
||||||
c3 = vec4(0, 0, 0, 0);
|
|
||||||
}
|
|
||||||
if ((varying_tex_region[3] - texel_size.y / 512.0) <= p1.y) {
|
|
||||||
c2 = vec4(0, 0, 0, 0);
|
|
||||||
c3 = vec4(0, 0, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
vec2 rate = fract(p0 * source_size);
|
|
||||||
vec4 color = mix(mix(c0, c1, rate.x), mix(c2, c3, rate.x), rate.y);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(FILTER_SCREEN)
|
|
||||||
highp vec2 p0 = pos - texel_size / 2.0 / scale;
|
|
||||||
highp vec2 p1 = pos + texel_size / 2.0 / scale;
|
|
||||||
// Prevent this variable from being optimized out.
|
|
||||||
p0 += varying_tex_region.xy - varying_tex_region.xy;
|
|
||||||
|
|
||||||
p1 = adjustTexel(p0, p1);
|
|
||||||
|
|
||||||
vec4 c0 = texture2D(texture, p0);
|
|
||||||
vec4 c1 = texture2D(texture, vec2(p1.x, p0.y));
|
|
||||||
vec4 c2 = texture2D(texture, vec2(p0.x, p1.y));
|
|
||||||
vec4 c3 = texture2D(texture, p1);
|
|
||||||
// Texels must be in the source rect, so it is not necessary to check that like linear filter.
|
|
||||||
|
|
||||||
vec2 rateCenter = vec2(1.0, 1.0) - texel_size / 2.0 / scale;
|
|
||||||
vec2 rate = clamp(((fract(p0 * source_size) - rateCenter) * scale) + rateCenter, 0.0, 1.0);
|
|
||||||
vec4 color = mix(mix(c0, c1, rate.x), mix(c2, c3, rate.x), rate.y);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Un-premultiply alpha
|
// Un-premultiply alpha
|
||||||
if (0.0 < color.a) {
|
if (0.0 < color.a) {
|
||||||
|
Loading…
Reference in New Issue
Block a user