mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2025-02-04 23:14:28 +01:00
graphics: Separate GLSL programs for filters (#461)
This commit is contained in:
parent
a8914ff245
commit
8e72906f3d
@ -114,14 +114,16 @@ type openGLState struct {
|
|||||||
// elementArrayBuffer is OpenGL's element array buffer (indices data).
|
// elementArrayBuffer is OpenGL's element array buffer (indices data).
|
||||||
elementArrayBuffer opengl.Buffer
|
elementArrayBuffer opengl.Buffer
|
||||||
|
|
||||||
// programTexture is OpenGL's program for rendering a texture.
|
// programNearest is OpenGL's program for rendering a texture with nearest filter.
|
||||||
programTexture opengl.Program
|
programNearest opengl.Program
|
||||||
|
|
||||||
|
// programLinear is OpenGL's program for rendering a texture with linear filter.
|
||||||
|
programLinear opengl.Program
|
||||||
|
|
||||||
lastProgram opengl.Program
|
lastProgram opengl.Program
|
||||||
lastProjectionMatrix []float32
|
lastProjectionMatrix []float32
|
||||||
lastColorMatrix []float32
|
lastColorMatrix []float32
|
||||||
lastColorMatrixTranslation []float32
|
lastColorMatrixTranslation []float32
|
||||||
lastFilterType Filter
|
|
||||||
lastSourceWidth int
|
lastSourceWidth int
|
||||||
lastSourceHeight int
|
lastSourceHeight int
|
||||||
}
|
}
|
||||||
@ -153,15 +155,17 @@ func (s *openGLState) reset() error {
|
|||||||
s.lastProjectionMatrix = nil
|
s.lastProjectionMatrix = nil
|
||||||
s.lastColorMatrix = nil
|
s.lastColorMatrix = nil
|
||||||
s.lastColorMatrixTranslation = nil
|
s.lastColorMatrixTranslation = nil
|
||||||
s.lastFilterType = FilterNone
|
|
||||||
s.lastSourceWidth = 0
|
s.lastSourceWidth = 0
|
||||||
s.lastSourceHeight = 0
|
s.lastSourceHeight = 0
|
||||||
|
|
||||||
// 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.programTexture != zeroProgram {
|
if s.programNearest != zeroProgram {
|
||||||
opengl.GetContext().DeleteProgram(s.programTexture)
|
opengl.GetContext().DeleteProgram(s.programNearest)
|
||||||
|
}
|
||||||
|
if s.programLinear != zeroProgram {
|
||||||
|
opengl.GetContext().DeleteProgram(s.programLinear)
|
||||||
}
|
}
|
||||||
if s.arrayBuffer != zeroBuffer {
|
if s.arrayBuffer != zeroBuffer {
|
||||||
opengl.GetContext().DeleteBuffer(s.arrayBuffer)
|
opengl.GetContext().DeleteBuffer(s.arrayBuffer)
|
||||||
@ -176,15 +180,29 @@ func (s *openGLState) reset() error {
|
|||||||
}
|
}
|
||||||
defer opengl.GetContext().DeleteShader(shaderVertexModelviewNative)
|
defer opengl.GetContext().DeleteShader(shaderVertexModelviewNative)
|
||||||
|
|
||||||
shaderFragmentTextureNative, err := opengl.GetContext().NewShader(opengl.FragmentShader, shader(shaderFragmentTexture))
|
shaderFragmentNearestNative, err := opengl.GetContext().NewShader(opengl.FragmentShader, shader(shaderFragmentNearest))
|
||||||
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 opengl.GetContext().DeleteShader(shaderFragmentTextureNative)
|
defer opengl.GetContext().DeleteShader(shaderFragmentNearestNative)
|
||||||
|
|
||||||
s.programTexture, err = opengl.GetContext().NewProgram([]opengl.Shader{
|
shaderFragmentLinearNative, err := opengl.GetContext().NewShader(opengl.FragmentShader, shader(shaderFragmentLinear))
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("graphics: shader compiling error:\n%s", err))
|
||||||
|
}
|
||||||
|
defer opengl.GetContext().DeleteShader(shaderFragmentLinearNative)
|
||||||
|
|
||||||
|
s.programNearest, err = opengl.GetContext().NewProgram([]opengl.Shader{
|
||||||
shaderVertexModelviewNative,
|
shaderVertexModelviewNative,
|
||||||
shaderFragmentTextureNative,
|
shaderFragmentNearestNative,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
s.programLinear, err = opengl.GetContext().NewProgram([]opengl.Shader{
|
||||||
|
shaderVertexModelviewNative,
|
||||||
|
shaderFragmentLinearNative,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -222,7 +240,16 @@ func areSameFloat32Array(a, b []float32) bool {
|
|||||||
// useProgram uses the program (programTexture).
|
// useProgram uses the program (programTexture).
|
||||||
func (s *openGLState) useProgram(proj []float32, texture opengl.Texture, sourceWidth, sourceHeight int, colorM affine.ColorM, filter Filter) {
|
func (s *openGLState) useProgram(proj []float32, texture opengl.Texture, sourceWidth, sourceHeight int, colorM affine.ColorM, filter Filter) {
|
||||||
c := opengl.GetContext()
|
c := opengl.GetContext()
|
||||||
program := s.programTexture
|
|
||||||
|
var program opengl.Program
|
||||||
|
switch filter {
|
||||||
|
case FilterNearest:
|
||||||
|
program = s.programNearest
|
||||||
|
case FilterLinear:
|
||||||
|
program = s.programLinear
|
||||||
|
default:
|
||||||
|
panic("not reached")
|
||||||
|
}
|
||||||
|
|
||||||
if s.lastProgram != program {
|
if s.lastProgram != program {
|
||||||
c.UseProgram(program)
|
c.UseProgram(program)
|
||||||
@ -231,7 +258,7 @@ func (s *openGLState) useProgram(proj []float32, texture opengl.Texture, sourceW
|
|||||||
}
|
}
|
||||||
theArrayBufferLayout.enable(program)
|
theArrayBufferLayout.enable(program)
|
||||||
|
|
||||||
s.lastProgram = s.programTexture
|
s.lastProgram = program
|
||||||
s.lastProjectionMatrix = nil
|
s.lastProjectionMatrix = nil
|
||||||
s.lastColorMatrix = nil
|
s.lastColorMatrix = nil
|
||||||
s.lastColorMatrixTranslation = nil
|
s.lastColorMatrixTranslation = nil
|
||||||
@ -279,16 +306,13 @@ func (s *openGLState) useProgram(proj []float32, texture opengl.Texture, sourceW
|
|||||||
copy(s.lastColorMatrixTranslation, colorMatrixTranslation)
|
copy(s.lastColorMatrixTranslation, colorMatrixTranslation)
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.lastFilterType != filter {
|
if program == s.programLinear {
|
||||||
c.UniformInt(program, "filter_type", int(filter))
|
if s.lastSourceWidth != sourceWidth || s.lastSourceHeight != sourceHeight {
|
||||||
s.lastFilterType = filter
|
c.UniformFloats(program, "source_size",
|
||||||
}
|
[]float32{float32(sourceWidth), float32(sourceHeight)})
|
||||||
|
s.lastSourceWidth = sourceWidth
|
||||||
if s.lastSourceWidth != sourceWidth || s.lastSourceHeight != sourceHeight {
|
s.lastSourceHeight = sourceHeight
|
||||||
c.UniformFloats(program, "source_size",
|
}
|
||||||
[]float32{float32(sourceWidth), float32(sourceHeight)})
|
|
||||||
s.lastSourceWidth = sourceWidth
|
|
||||||
s.lastSourceHeight = sourceHeight
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// We don't have to call gl.ActiveTexture here: GL_TEXTURE0 is the default active texture
|
// We don't have to call gl.ActiveTexture here: GL_TEXTURE0 is the default active texture
|
||||||
|
@ -14,19 +14,34 @@
|
|||||||
|
|
||||||
package graphics
|
package graphics
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
type shaderID int
|
type shaderID int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
shaderVertexModelview shaderID = iota
|
shaderVertexModelview shaderID = iota
|
||||||
shaderFragmentTexture
|
shaderFragmentNearest
|
||||||
|
shaderFragmentLinear
|
||||||
)
|
)
|
||||||
|
|
||||||
func shader(id shaderID) string {
|
func shader(id shaderID) string {
|
||||||
return shaders[id]
|
if id == shaderVertexModelview {
|
||||||
|
return shaderStrVertex
|
||||||
|
}
|
||||||
|
defs := []string{}
|
||||||
|
switch id {
|
||||||
|
case shaderFragmentNearest:
|
||||||
|
defs = append(defs, "#define FILTER_NEAREST")
|
||||||
|
case shaderFragmentLinear:
|
||||||
|
defs = append(defs, "#define FILTER_LINEAR")
|
||||||
|
}
|
||||||
|
return strings.Replace(shaderStrFragment, "{{Definitions}}", strings.Join(defs, "\n"), -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
var shaders = map[shaderID]string{
|
const (
|
||||||
shaderVertexModelview: `
|
shaderStrVertex = `
|
||||||
uniform mat4 projection_matrix;
|
uniform mat4 projection_matrix;
|
||||||
attribute vec2 vertex;
|
attribute vec2 vertex;
|
||||||
attribute vec4 tex_coord;
|
attribute vec4 tex_coord;
|
||||||
@ -50,8 +65,8 @@ void main(void) {
|
|||||||
);
|
);
|
||||||
gl_Position = projection_matrix * geo_matrix * vec4(vertex, 0, 1);
|
gl_Position = projection_matrix * geo_matrix * vec4(vertex, 0, 1);
|
||||||
}
|
}
|
||||||
`,
|
`
|
||||||
shaderFragmentTexture: `
|
shaderStrFragment = `
|
||||||
#if defined(GL_ES)
|
#if defined(GL_ES)
|
||||||
precision mediump float;
|
precision mediump float;
|
||||||
#else
|
#else
|
||||||
@ -60,11 +75,15 @@ precision mediump float;
|
|||||||
#define highp
|
#define highp
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
{{Definitions}}
|
||||||
|
|
||||||
uniform sampler2D texture;
|
uniform sampler2D texture;
|
||||||
uniform mat4 color_matrix;
|
uniform mat4 color_matrix;
|
||||||
uniform vec4 color_matrix_translation;
|
uniform vec4 color_matrix_translation;
|
||||||
|
|
||||||
|
#if defined(FILTER_LINEAR)
|
||||||
uniform highp vec2 source_size;
|
uniform highp vec2 source_size;
|
||||||
uniform int filter_type;
|
#endif
|
||||||
|
|
||||||
varying highp vec2 varying_tex_coord;
|
varying highp vec2 varying_tex_coord;
|
||||||
varying highp vec2 varying_tex_coord_min;
|
varying highp vec2 varying_tex_coord_min;
|
||||||
@ -80,51 +99,48 @@ highp vec2 roundTexel(highp vec2 p) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void main(void) {
|
void main(void) {
|
||||||
vec4 color = vec4(0, 0, 0, 0);
|
|
||||||
|
|
||||||
highp vec2 pos = roundTexel(varying_tex_coord);
|
highp vec2 pos = roundTexel(varying_tex_coord);
|
||||||
if (filter_type == 1) {
|
|
||||||
// Nearest neighbor
|
|
||||||
if (varying_tex_coord_min.x <= pos.x &&
|
|
||||||
varying_tex_coord_min.y <= pos.y &&
|
|
||||||
pos.x < varying_tex_coord_max.x &&
|
|
||||||
pos.y < varying_tex_coord_max.y) {
|
|
||||||
color = texture2D(texture, pos);
|
|
||||||
}
|
|
||||||
} else if (filter_type == 2) {
|
|
||||||
// Bi-linear
|
|
||||||
highp vec2 texel_size = 1.0 / source_size;
|
|
||||||
pos -= texel_size * 0.5;
|
|
||||||
|
|
||||||
highp vec2 p0 = pos;
|
#if defined(FILTER_NEAREST)
|
||||||
highp vec2 p1 = pos + texel_size;
|
vec4 color = texture2D(texture, pos);
|
||||||
vec4 c0 = texture2D(texture, p0);
|
if (pos.x < varying_tex_coord_min.x ||
|
||||||
vec4 c1 = texture2D(texture, vec2(p1.x, p0.y));
|
pos.y < varying_tex_coord_min.y ||
|
||||||
vec4 c2 = texture2D(texture, vec2(p0.x, p1.y));
|
varying_tex_coord_max.x <= pos.x ||
|
||||||
vec4 c3 = texture2D(texture, p1);
|
varying_tex_coord_max.y <= pos.y) {
|
||||||
if (p0.x < varying_tex_coord_min.x) {
|
color = vec4(0, 0, 0, 0);
|
||||||
c0 = vec4(0, 0, 0, 0);
|
|
||||||
c2 = vec4(0, 0, 0, 0);
|
|
||||||
}
|
|
||||||
if (p0.y < varying_tex_coord_min.y) {
|
|
||||||
c0 = vec4(0, 0, 0, 0);
|
|
||||||
c1 = vec4(0, 0, 0, 0);
|
|
||||||
}
|
|
||||||
if (varying_tex_coord_max.x <= p1.x) {
|
|
||||||
c1 = vec4(0, 0, 0, 0);
|
|
||||||
c3 = vec4(0, 0, 0, 0);
|
|
||||||
}
|
|
||||||
if (varying_tex_coord_max.y <= p1.y) {
|
|
||||||
c2 = vec4(0, 0, 0, 0);
|
|
||||||
c3 = vec4(0, 0, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
vec2 rate = fract(pos * source_size);
|
|
||||||
color = mix(mix(c0, c1, rate.x), mix(c2, c3, rate.x), rate.y);
|
|
||||||
} else {
|
|
||||||
// Error
|
|
||||||
color = vec4(1, 0, 0, 1);
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(FILTER_LINEAR)
|
||||||
|
highp vec2 texel_size = 1.0 / source_size;
|
||||||
|
pos -= texel_size * 0.5;
|
||||||
|
|
||||||
|
highp vec2 p0 = pos;
|
||||||
|
highp vec2 p1 = pos + texel_size;
|
||||||
|
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_coord_min.x) {
|
||||||
|
c0 = vec4(0, 0, 0, 0);
|
||||||
|
c2 = vec4(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
if (p0.y < varying_tex_coord_min.y) {
|
||||||
|
c0 = vec4(0, 0, 0, 0);
|
||||||
|
c1 = vec4(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
if (varying_tex_coord_max.x <= p1.x) {
|
||||||
|
c1 = vec4(0, 0, 0, 0);
|
||||||
|
c3 = vec4(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
if (varying_tex_coord_max.y <= p1.y) {
|
||||||
|
c2 = vec4(0, 0, 0, 0);
|
||||||
|
c3 = vec4(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec2 rate = fract(pos * source_size);
|
||||||
|
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) {
|
||||||
@ -138,5 +154,5 @@ void main(void) {
|
|||||||
|
|
||||||
gl_FragColor = color;
|
gl_FragColor = color;
|
||||||
}
|
}
|
||||||
`,
|
`
|
||||||
}
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user