mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-11-10 04:57:26 +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 opengl.Buffer
|
||||
|
||||
// programTexture is OpenGL's program for rendering a texture.
|
||||
programTexture opengl.Program
|
||||
// programNearest is OpenGL's program for rendering a texture with nearest filter.
|
||||
programNearest opengl.Program
|
||||
|
||||
// programLinear is OpenGL's program for rendering a texture with linear filter.
|
||||
programLinear opengl.Program
|
||||
|
||||
lastProgram opengl.Program
|
||||
lastProjectionMatrix []float32
|
||||
lastColorMatrix []float32
|
||||
lastColorMatrixTranslation []float32
|
||||
lastFilterType Filter
|
||||
lastSourceWidth int
|
||||
lastSourceHeight int
|
||||
}
|
||||
@ -153,15 +155,17 @@ func (s *openGLState) reset() error {
|
||||
s.lastProjectionMatrix = nil
|
||||
s.lastColorMatrix = nil
|
||||
s.lastColorMatrixTranslation = nil
|
||||
s.lastFilterType = FilterNone
|
||||
s.lastSourceWidth = 0
|
||||
s.lastSourceHeight = 0
|
||||
|
||||
// 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.
|
||||
// Let's delete them explicitly.
|
||||
if s.programTexture != zeroProgram {
|
||||
opengl.GetContext().DeleteProgram(s.programTexture)
|
||||
if s.programNearest != zeroProgram {
|
||||
opengl.GetContext().DeleteProgram(s.programNearest)
|
||||
}
|
||||
if s.programLinear != zeroProgram {
|
||||
opengl.GetContext().DeleteProgram(s.programLinear)
|
||||
}
|
||||
if s.arrayBuffer != zeroBuffer {
|
||||
opengl.GetContext().DeleteBuffer(s.arrayBuffer)
|
||||
@ -176,15 +180,29 @@ func (s *openGLState) reset() error {
|
||||
}
|
||||
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 {
|
||||
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,
|
||||
shaderFragmentTextureNative,
|
||||
shaderFragmentNearestNative,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.programLinear, err = opengl.GetContext().NewProgram([]opengl.Shader{
|
||||
shaderVertexModelviewNative,
|
||||
shaderFragmentLinearNative,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
@ -222,7 +240,16 @@ func areSameFloat32Array(a, b []float32) bool {
|
||||
// useProgram uses the program (programTexture).
|
||||
func (s *openGLState) useProgram(proj []float32, texture opengl.Texture, sourceWidth, sourceHeight int, colorM affine.ColorM, filter Filter) {
|
||||
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 {
|
||||
c.UseProgram(program)
|
||||
@ -231,7 +258,7 @@ func (s *openGLState) useProgram(proj []float32, texture opengl.Texture, sourceW
|
||||
}
|
||||
theArrayBufferLayout.enable(program)
|
||||
|
||||
s.lastProgram = s.programTexture
|
||||
s.lastProgram = program
|
||||
s.lastProjectionMatrix = nil
|
||||
s.lastColorMatrix = nil
|
||||
s.lastColorMatrixTranslation = nil
|
||||
@ -279,16 +306,13 @@ func (s *openGLState) useProgram(proj []float32, texture opengl.Texture, sourceW
|
||||
copy(s.lastColorMatrixTranslation, colorMatrixTranslation)
|
||||
}
|
||||
|
||||
if s.lastFilterType != filter {
|
||||
c.UniformInt(program, "filter_type", int(filter))
|
||||
s.lastFilterType = filter
|
||||
}
|
||||
|
||||
if s.lastSourceWidth != sourceWidth || s.lastSourceHeight != sourceHeight {
|
||||
c.UniformFloats(program, "source_size",
|
||||
[]float32{float32(sourceWidth), float32(sourceHeight)})
|
||||
s.lastSourceWidth = sourceWidth
|
||||
s.lastSourceHeight = sourceHeight
|
||||
if program == s.programLinear {
|
||||
if s.lastSourceWidth != sourceWidth || 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
|
||||
|
@ -14,19 +14,34 @@
|
||||
|
||||
package graphics
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
type shaderID int
|
||||
|
||||
const (
|
||||
shaderVertexModelview shaderID = iota
|
||||
shaderFragmentTexture
|
||||
shaderFragmentNearest
|
||||
shaderFragmentLinear
|
||||
)
|
||||
|
||||
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{
|
||||
shaderVertexModelview: `
|
||||
const (
|
||||
shaderStrVertex = `
|
||||
uniform mat4 projection_matrix;
|
||||
attribute vec2 vertex;
|
||||
attribute vec4 tex_coord;
|
||||
@ -50,8 +65,8 @@ void main(void) {
|
||||
);
|
||||
gl_Position = projection_matrix * geo_matrix * vec4(vertex, 0, 1);
|
||||
}
|
||||
`,
|
||||
shaderFragmentTexture: `
|
||||
`
|
||||
shaderStrFragment = `
|
||||
#if defined(GL_ES)
|
||||
precision mediump float;
|
||||
#else
|
||||
@ -60,11 +75,15 @@ precision mediump float;
|
||||
#define highp
|
||||
#endif
|
||||
|
||||
{{Definitions}}
|
||||
|
||||
uniform sampler2D texture;
|
||||
uniform mat4 color_matrix;
|
||||
uniform vec4 color_matrix_translation;
|
||||
|
||||
#if defined(FILTER_LINEAR)
|
||||
uniform highp vec2 source_size;
|
||||
uniform int filter_type;
|
||||
#endif
|
||||
|
||||
varying highp vec2 varying_tex_coord;
|
||||
varying highp vec2 varying_tex_coord_min;
|
||||
@ -80,51 +99,48 @@ highp vec2 roundTexel(highp vec2 p) {
|
||||
}
|
||||
|
||||
void main(void) {
|
||||
vec4 color = vec4(0, 0, 0, 0);
|
||||
|
||||
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;
|
||||
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);
|
||||
color = mix(mix(c0, c1, rate.x), mix(c2, c3, rate.x), rate.y);
|
||||
} else {
|
||||
// Error
|
||||
color = vec4(1, 0, 0, 1);
|
||||
#if defined(FILTER_NEAREST)
|
||||
vec4 color = texture2D(texture, pos);
|
||||
if (pos.x < varying_tex_coord_min.x ||
|
||||
pos.y < varying_tex_coord_min.y ||
|
||||
varying_tex_coord_max.x <= pos.x ||
|
||||
varying_tex_coord_max.y <= pos.y) {
|
||||
color = vec4(0, 0, 0, 0);
|
||||
}
|
||||
#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
|
||||
if (0.0 < color.a) {
|
||||
@ -138,5 +154,5 @@ void main(void) {
|
||||
|
||||
gl_FragColor = color;
|
||||
}
|
||||
`,
|
||||
}
|
||||
`
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user