zorldo

Goofing around with Ebiten
git clone git://bsandro.tech/zorldo
Log | Files | Refs | README

defaultshader.go (8052B)


      1 // Copyright 2014 Hajime Hoshi
      2 //
      3 // Licensed under the Apache License, Version 2.0 (the "License");
      4 // you may not use this file except in compliance with the License.
      5 // You may obtain a copy of the License at
      6 //
      7 //     http://www.apache.org/licenses/LICENSE-2.0
      8 //
      9 // Unless required by applicable law or agreed to in writing, software
     10 // distributed under the License is distributed on an "AS IS" BASIS,
     11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 // See the License for the specific language governing permissions and
     13 // limitations under the License.
     14 
     15 package opengl
     16 
     17 import (
     18 	"fmt"
     19 	"regexp"
     20 	"strings"
     21 
     22 	"github.com/hajimehoshi/ebiten/v2/internal/driver"
     23 )
     24 
     25 // glslReservedKeywords is a set of reserved keywords that cannot be used as an indentifier on some environments.
     26 // See https://www.khronos.org/registry/OpenGL/specs/gl/GLSLangSpec.4.60.pdf.
     27 var glslReservedKeywords = map[string]struct{}{
     28 	"common": {}, "partition": {}, "active": {},
     29 	"asm":   {},
     30 	"class": {}, "union": {}, "enum": {}, "typedef": {}, "template": {}, "this": {},
     31 	"resource": {},
     32 	"goto":     {},
     33 	"inline":   {}, "noinline": {}, "public": {}, "static": {}, "extern": {}, "external": {}, "interface": {},
     34 	"long": {}, "short": {}, "half": {}, "fixed": {}, "unsigned": {}, "superp": {},
     35 	"input": {}, "output": {},
     36 	"hvec2": {}, "hvec3": {}, "hvec4": {}, "fvec2": {}, "fvec3": {}, "fvec4": {},
     37 	"filter": {},
     38 	"sizeof": {}, "cast": {},
     39 	"namespace": {}, "using": {},
     40 	"sampler3DRect": {},
     41 }
     42 
     43 var glslIdentifier = regexp.MustCompile(`[_a-zA-Z][_a-zA-Z0-9]*`)
     44 
     45 func checkGLSL(src string) {
     46 	for _, l := range strings.Split(src, "\n") {
     47 		if strings.Contains(l, "//") {
     48 			l = l[:strings.Index(l, "//")]
     49 		}
     50 		for _, token := range glslIdentifier.FindAllString(l, -1) {
     51 			if _, ok := glslReservedKeywords[token]; ok {
     52 				panic(fmt.Sprintf("opengl: %q is a reserved keyword", token))
     53 			}
     54 		}
     55 	}
     56 }
     57 
     58 func vertexShaderStr() string {
     59 	src := shaderStrVertex
     60 	checkGLSL(src)
     61 	return src
     62 }
     63 
     64 func fragmentShaderStr(useColorM bool, filter driver.Filter, address driver.Address) string {
     65 	replaces := map[string]string{
     66 		"{{.AddressClampToZero}}": fmt.Sprintf("%d", driver.AddressClampToZero),
     67 		"{{.AddressRepeat}}":      fmt.Sprintf("%d", driver.AddressRepeat),
     68 		"{{.AddressUnsafe}}":      fmt.Sprintf("%d", driver.AddressUnsafe),
     69 	}
     70 	src := shaderStrFragment
     71 	for k, v := range replaces {
     72 		src = strings.Replace(src, k, v, -1)
     73 	}
     74 
     75 	var defs []string
     76 
     77 	if useColorM {
     78 		defs = append(defs, "#define USE_COLOR_MATRIX")
     79 	}
     80 
     81 	switch filter {
     82 	case driver.FilterNearest:
     83 		defs = append(defs, "#define FILTER_NEAREST")
     84 	case driver.FilterLinear:
     85 		defs = append(defs, "#define FILTER_LINEAR")
     86 	case driver.FilterScreen:
     87 		defs = append(defs, "#define FILTER_SCREEN")
     88 	default:
     89 		panic(fmt.Sprintf("opengl: invalid filter: %d", filter))
     90 	}
     91 
     92 	switch address {
     93 	case driver.AddressClampToZero:
     94 		defs = append(defs, "#define ADDRESS_CLAMP_TO_ZERO")
     95 	case driver.AddressRepeat:
     96 		defs = append(defs, "#define ADDRESS_REPEAT")
     97 	case driver.AddressUnsafe:
     98 		defs = append(defs, "#define ADDRESS_UNSAFE")
     99 	default:
    100 		panic(fmt.Sprintf("opengl: invalid address: %d", address))
    101 	}
    102 
    103 	src = strings.Replace(src, "{{.Definitions}}", strings.Join(defs, "\n"), -1)
    104 
    105 	checkGLSL(src)
    106 	return src
    107 }
    108 
    109 const (
    110 	shaderStrVertex = `
    111 uniform vec2 viewport_size;
    112 attribute vec2 A0;
    113 attribute vec2 A1;
    114 attribute vec4 A2;
    115 varying vec2 varying_tex;
    116 varying vec4 varying_color_scale;
    117 
    118 void main(void) {
    119   varying_tex = A1;
    120   varying_color_scale = A2;
    121 
    122   mat4 projection_matrix = mat4(
    123     vec4(2.0 / viewport_size.x, 0, 0, 0),
    124     vec4(0, 2.0 / viewport_size.y, 0, 0),
    125     vec4(0, 0, 1, 0),
    126     vec4(-1, -1, 0, 1)
    127   );
    128   gl_Position = projection_matrix * vec4(A0, 0, 1);
    129 }
    130 `
    131 	shaderStrFragment = `
    132 #if defined(GL_ES)
    133 precision mediump float;
    134 #else
    135 #define lowp
    136 #define mediump
    137 #define highp
    138 #endif
    139 
    140 {{.Definitions}}
    141 
    142 uniform sampler2D T0;
    143 uniform vec4 source_region;
    144 
    145 #if defined(USE_COLOR_MATRIX)
    146 uniform mat4 color_matrix_body;
    147 uniform vec4 color_matrix_translation;
    148 #endif
    149 
    150 uniform highp vec2 source_size;
    151 
    152 #if defined(FILTER_SCREEN)
    153 uniform highp float scale;
    154 #endif
    155 
    156 varying highp vec2 varying_tex;
    157 varying highp vec4 varying_color_scale;
    158 
    159 highp float floorMod(highp float x, highp float y) {
    160   if (x < 0.0) {
    161     return y - (-x - y * floor(-x/y));
    162   }
    163   return x - y * floor(x/y);
    164 }
    165 
    166 highp vec2 adjustTexelByAddress(highp vec2 p, highp vec4 source_region) {
    167 #if defined(ADDRESS_CLAMP_TO_ZERO)
    168   return p;
    169 #endif
    170 
    171 #if defined(ADDRESS_REPEAT)
    172   highp vec2 o = vec2(source_region[0], source_region[1]);
    173   highp vec2 size = vec2(source_region[2] - source_region[0], source_region[3] - source_region[1]);
    174   return vec2(floorMod((p.x - o.x), size.x) + o.x, floorMod((p.y - o.y), size.y) + o.y);
    175 #endif
    176 
    177 #if defined(ADDRESS_UNSAFE)
    178   return p;
    179 #endif
    180 }
    181 
    182 void main(void) {
    183   highp vec2 pos = varying_tex;
    184 
    185 #if defined(FILTER_NEAREST)
    186   vec4 color;
    187 # if defined(ADDRESS_UNSAFE)
    188   color = texture2D(T0, pos);
    189 # else
    190   pos = adjustTexelByAddress(pos, source_region);
    191   if (source_region[0] <= pos.x &&
    192       source_region[1] <= pos.y &&
    193       pos.x < source_region[2] &&
    194       pos.y < source_region[3]) {
    195     color = texture2D(T0, pos);
    196   } else {
    197     color = vec4(0, 0, 0, 0);
    198   }
    199 # endif  // defined(ADDRESS_UNSAFE)
    200 #endif  // defined(FILTER_NEAREST)
    201 
    202 #if defined(FILTER_LINEAR)
    203   vec4 color;
    204   highp vec2 texel_size = 1.0 / source_size;
    205 
    206   // Shift 1/512 [texel] to avoid the tie-breaking issue.
    207   // As all the vertex positions are aligned to 1/16 [pixel], this shiting should work in most cases.
    208   highp vec2 p0 = pos - (texel_size) / 2.0 + (texel_size / 512.0);
    209   highp vec2 p1 = pos + (texel_size) / 2.0 + (texel_size / 512.0);
    210 
    211 # if !defined(ADDRESS_UNSAFE)
    212   p0 = adjustTexelByAddress(p0, source_region);
    213   p1 = adjustTexelByAddress(p1, source_region);
    214 # endif  // defined(ADDRESS_UNSAFE)
    215 
    216   vec4 c0 = texture2D(T0, p0);
    217   vec4 c1 = texture2D(T0, vec2(p1.x, p0.y));
    218   vec4 c2 = texture2D(T0, vec2(p0.x, p1.y));
    219   vec4 c3 = texture2D(T0, p1);
    220 # if !defined(ADDRESS_UNSAFE)
    221   if (p0.x < source_region[0]) {
    222     c0 = vec4(0, 0, 0, 0);
    223     c2 = vec4(0, 0, 0, 0);
    224   }
    225   if (p0.y < source_region[1]) {
    226     c0 = vec4(0, 0, 0, 0);
    227     c1 = vec4(0, 0, 0, 0);
    228   }
    229   if (source_region[2] <= p1.x) {
    230     c1 = vec4(0, 0, 0, 0);
    231     c3 = vec4(0, 0, 0, 0);
    232   }
    233   if (source_region[3] <= p1.y) {
    234     c2 = vec4(0, 0, 0, 0);
    235     c3 = vec4(0, 0, 0, 0);
    236   }
    237 # endif  // defined(ADDRESS_UNSAFE)
    238 
    239   vec2 rate = fract(p0 * source_size);
    240   color = mix(mix(c0, c1, rate.x), mix(c2, c3, rate.x), rate.y);
    241 #endif  // defined(FILTER_LINEAR)
    242 
    243 #if defined(FILTER_SCREEN)
    244   highp vec2 texel_size = 1.0 / source_size;
    245   highp vec2 half_scaled_texel_size = texel_size / 2.0 / scale;
    246 
    247   highp vec2 p0 = pos - half_scaled_texel_size + (texel_size / 512.0);
    248   highp vec2 p1 = pos + half_scaled_texel_size + (texel_size / 512.0);
    249 
    250   vec4 c0 = texture2D(T0, p0);
    251   vec4 c1 = texture2D(T0, vec2(p1.x, p0.y));
    252   vec4 c2 = texture2D(T0, vec2(p0.x, p1.y));
    253   vec4 c3 = texture2D(T0, p1);
    254   // Texels must be in the source rect, so it is not necessary to check that like linear filter.
    255 
    256   vec2 rate_center = vec2(1.0, 1.0) - half_scaled_texel_size;
    257   vec2 rate = clamp(((fract(p0 * source_size) - rate_center) * scale) + rate_center, 0.0, 1.0);
    258   gl_FragColor = mix(mix(c0, c1, rate.x), mix(c2, c3, rate.x), rate.y);
    259 
    260   // Assume that a color matrix and color vector values are not used with FILTER_SCREEN.
    261 
    262 #else
    263 
    264 # if defined(USE_COLOR_MATRIX)
    265   // Un-premultiply alpha.
    266   // When the alpha is 0, 1.0 - sign(alpha) is 1.0, which means division does nothing.
    267   color.rgb /= color.a + (1.0 - sign(color.a));
    268   // Apply the color matrix or scale.
    269   color = (color_matrix_body * color) + color_matrix_translation;
    270   color *= varying_color_scale;
    271   // Premultiply alpha
    272   color.rgb *= color.a;
    273 # else
    274   vec4 s = varying_color_scale;
    275   color *= vec4(s.r, s.g, s.b, 1.0) * s.a;
    276 # endif  // defined(USE_COLOR_MATRIX)
    277 
    278   color = min(color, color.a);
    279 
    280   gl_FragColor = color;
    281 
    282 #endif  // defined(FILTER_SCREEN)
    283 
    284 }
    285 `
    286 )