From b2466322c017aa7cc1e31a658dcd968b3559d8e3 Mon Sep 17 00:00:00 2001 From: Lubos Lenco Date: Thu, 19 May 2016 22:22:41 +0200 Subject: [PATCH] Color ramp, decals, shader compiler work. --- {raw => blender/lib}/compile.py | 0 {raw => blender/lib}/make_resources.py | 49 ++++---- {raw => blender/lib}/make_variants.py | 47 +++++--- blender/nodes_material.py | 78 +++++++++---- blender/project.py | 35 ++++-- raw/__init__.py | 0 raw/deferred/decals.frag.glsl | 110 ++++++++++++------ raw/deferred/decals.vert.glsl | 24 ++++ raw/deferred/deferred.frag.glsl | 2 +- raw/deferred/deferred.shader.json | 5 + raw/deferred_light/deferred_light.frag.glsl | 2 +- .../motion_blur_pass.frag.glsl | 18 +-- raw/sss_pass/sss_pass.frag.glsl | 2 +- 13 files changed, 261 insertions(+), 111 deletions(-) rename {raw => blender/lib}/compile.py (100%) rename {raw => blender/lib}/make_resources.py (89%) rename {raw => blender/lib}/make_variants.py (61%) delete mode 100755 raw/__init__.py diff --git a/raw/compile.py b/blender/lib/compile.py similarity index 100% rename from raw/compile.py rename to blender/lib/compile.py diff --git a/raw/make_resources.py b/blender/lib/make_resources.py similarity index 89% rename from raw/make_resources.py rename to blender/lib/make_resources.py index 1664bc37..cceb3d52 100644 --- a/raw/make_resources.py +++ b/blender/lib/make_resources.py @@ -203,30 +203,43 @@ def parse_shader(sres, c, con, defs, lines, parse_attributes): if 'ifdef' in l: valid_link = False for d in defs: - if d == l['ifdef']: - valid_link = True + for lifdef in l['ifdef']: + if d == lifdef: + valid_link = True + break + if valid_link: break if valid_link: const.link = l['link'] break con.constants.append(const) -def make(json_name): - #base_name = sys.argv[1].split('.', 1)[0] +def saveResource(path, base_name, subset, res): + res_name = base_name + for s in subset: + res_name += s + with open(path + '/' + res_name + '.json', 'w') as f: + r = Object() + r.shader_resources = [res.shader_resources[-1]] + f.write(r.to_JSON()) + +def make(json_name, defs=None): base_name = json_name.split('.', 1)[0] # Make out dir - #if not os.path.exists('out'): - # os.makedirs('out') path = '../../../../compiled/ShaderResources/' + base_name if not os.path.exists(path): os.makedirs(path) - # Open json file - # json_file = open(sys.argv[1]).read() - json_file = open(json_name).read() - json_data = json.loads(json_file) + # Open json file + # json_file = open(sys.argv[1]).read() + json_file = open(json_name).read() + json_data = json.loads(json_file) + res = Object() + res.shader_resources = [] + + if defs == None: # Go through every context shaders and gather ifdefs defs = [] for c in json_data['contexts']: @@ -243,21 +256,17 @@ def make(json_name): defs = sorted(list(set(defs))) # Process #defines - res = Object() - res.shader_resources = [] for L in range(0, len(defs)+1): for subset in itertools.combinations(defs, L): writeResource(res, subset, json_data, base_name) # Save separately - res_name = base_name - for s in subset: - res_name += s - #with open('out/' + res_name + '.json', 'w') as f: - with open(path + '/' + res_name + '.json', 'w') as f: - r = Object() - r.shader_resources = [res.shader_resources[-1]] - f.write(r.to_JSON()) + saveResource(path, base_name, subset, res) # Save combined #with open('out/' + base_name + '_resource.json', 'w') as f: # f.write(res.to_JSON()) + + # Specified defs + else: + writeResource(res, defs, json_data, base_name) + saveResource(path, base_name, defs, res) diff --git a/raw/make_variants.py b/blender/lib/make_variants.py similarity index 61% rename from raw/make_variants.py rename to blender/lib/make_variants.py index 20fa0cda..763016fa 100644 --- a/raw/make_variants.py +++ b/blender/lib/make_variants.py @@ -22,34 +22,38 @@ def writeFile(path, name, defs, lines): f.write('#define ' + d + '\n') defs_written = True -def make(json_name): +def make(json_name, defs=None): vert_shaders = [] frag_shaders = [] shader_names = [] - defs = [] + + if defs != None: + parse_defs = False + else: + parse_defs = True + defs = [] base_name = json_name.split('.', 1)[0] # Make out dir - #if not os.path.exists('out'): - # os.makedirs('out') path = '../../../../compiled/Shaders/' + base_name if not os.path.exists(path): os.makedirs(path) - # Open json file - #json_file = open(sys.argv[1]).read() - json_file = open(json_name).read() - json_data = json.loads(json_file) + # Open json file + #json_file = open(sys.argv[1]).read() + json_file = open(json_name).read() + json_data = json.loads(json_file) - # Go through every context shaders and gather ifdefs - for c in json_data['contexts']: - vs = open(c['vertex_shader']).read().splitlines() - fs = open(c['fragment_shader']).read().splitlines() - shader_names.append(c['vertex_shader'].split('.', 1)[0]) - vert_shaders.append(vs) - frag_shaders.append(fs) + # Go through every context shaders and gather ifdefs + for c in json_data['contexts']: + vs = open(c['vertex_shader']).read().splitlines() + fs = open(c['fragment_shader']).read().splitlines() + shader_names.append(c['vertex_shader'].split('.', 1)[0]) + vert_shaders.append(vs) + frag_shaders.append(fs) + if parse_defs == True: lines = vs + fs for line in lines: if line.startswith('#ifdef'): @@ -57,6 +61,8 @@ def make(json_name): if d != 'GL_ES': defs.append(d) + + if parse_defs == True: # Merge duplicates and sort defs = sorted(list(set(defs))) @@ -72,3 +78,14 @@ def make(json_name): shader_name += s writeFile(path, shader_name + '.vert.glsl', subset, vert_lines) writeFile(path, shader_name + '.frag.glsl', subset, frag_lines) + + # Defs specified + else: + for i in range(0, len(vert_shaders)): + vert_lines = vert_shaders[i] + frag_lines = frag_shaders[i] + shader_name = shader_names[i] + for s in defs: + shader_name += s + writeFile(path, shader_name + '.vert.glsl', defs, vert_lines) + writeFile(path, shader_name + '.frag.glsl', defs, frag_lines) diff --git a/blender/nodes_material.py b/blender/nodes_material.py index 7141d999..7fd5740f 100644 --- a/blender/nodes_material.py +++ b/blender/nodes_material.py @@ -26,6 +26,8 @@ def parse(self, material, c, defs): # Manualy set starting material point def parse_from(self, material, c, defs, surface_node): parse.const_color = None + parse.const_roughness = None + parse.const_metalness = None tree = material.node_tree parse_material_surface(self, material, c, defs, tree, surface_node) @@ -125,6 +127,10 @@ def parse_bsdf_glossy(self, material, c, defs, tree, node): parse_metalness_socket(self, metalness_input, material, c, defs, tree, node, reverse_float_value=True) def mix_float(f1, f2): + if f1 == None: + return f2 + if f2 == None: + return f1 return (f1 + f2) / 2.0 def mix_color_vec4(col1, col2): @@ -134,6 +140,31 @@ def mix_color_vec4(col1, col2): return col1 return [mix_float(col1[0], col2[0]), mix_float(col1[1], col2[1]), mix_float(col1[2], col2[2]), mix_float(col1[3], col2[3])] +def parse_val_to_rgb(node, c, defs): + factor = node.inputs[0].default_value + if not node.inputs[0].is_linked: # Take ramp color + return node.color_ramp.evaluate(factor) + else: # Assume 2 colors interpolated by id for now + defs.append('_RampID') + # Link albedo_color2 as color 2 + const = Object() + c.bind_constants.append(const) + const.id = 'albedo_color2' + res = node.color_ramp.elements[1].color + const.vec4 = [res[0], res[1], res[2], res[3]] + # Return color 1 + return node.color_ramp.elements[0].color + +def add_albedo_color(c, col): + const = parse.const_color + if const == None: + const = Object() + parse.const_color = const + c.bind_constants.append(const) + const.id = 'albedo_color' + res = mix_color_vec4(col, const.vec4 if hasattr(const, 'vec4') else None) + const.vec4 = [res[0], res[1], res[2], res[3]] + def parse_base_color_socket(self, base_color_input, material, c, defs, tree, node): if base_color_input.is_linked: color_node = find_node_by_link(tree, node, base_color_input) @@ -141,19 +172,15 @@ def parse_base_color_socket(self, base_color_input, material, c, defs, tree, nod defs.append('_AMTex') tex = make_texture(self, 'salbedo', color_node, material) c.bind_textures.append(tex) - # elif color_node.type == 'TEX_CHECKER': + elif color_node.type == 'TEX_CHECKER': + pass elif color_node.type == 'ATTRIBUTE': # Assume vcols for now defs.append('_VCols') + elif color_node.type == 'VALTORGB': + col = parse_val_to_rgb(color_node, c, defs) + add_albedo_color(c, col) else: # Take node color - const = parse.const_color - if const == None: - const = Object() - parse.const_color = const - const.id = "albedo_color" - col = base_color_input.default_value - res = mix_color_vec4(col, const.vec4 if hasattr(const, 'vec4') else None) - const.vec4 = [res[0], res[1], res[2], res[3]] - c.bind_constants.append(const) + add_albedo_color(c, base_color_input.default_value) def parse_metalness_socket(self, metalness_input, material, c, defs, tree, node, reverse_float_value=False): if metalness_input.is_linked: @@ -161,12 +188,18 @@ def parse_metalness_socket(self, metalness_input, material, c, defs, tree, node, metalness_node = find_node_by_link(tree, node, metalness_input) tex = make_texture(self, 'smm', metalness_node, material) c.bind_textures.append(tex) - else: - col = metalness_input.default_value - const = Object() + if parse.const_metalness != None: # If texture is used, remove constant + c.bind_constants.remove(parse.const_metalness) + elif '_MMTex' not in defs: + const = parse.const_metalness + if const == None: + const = Object() + parse.const_metalness = const + c.bind_constants.append(const) const.id = "metalness" - const.float = 1.0 - col if reverse_float_value else col - c.bind_constants.append(const) + col = metalness_input.default_value + res = 1.0 - col if reverse_float_value else col + const.float = mix_float(res, const.float if hasattr(const, 'float') else None) def parse_roughness_socket(self, roughness_input, material, c, defs, tree, node): if roughness_input.is_linked: @@ -174,12 +207,17 @@ def parse_roughness_socket(self, roughness_input, material, c, defs, tree, node) roughness_node = find_node_by_link(tree, node, roughness_input) tex = make_texture(self, 'srm', roughness_node, material) c.bind_textures.append(tex) - else: - col = roughness_input.default_value - const = Object() + if parse.const_roughness != None: + c.bind_constants.remove(parse.const_roughness) + elif '_RMTex' not in defs: + const = parse.const_roughness + if const == None: + const = Object() + parse.const_roughness = const + c.bind_constants.append(const) const.id = "roughness" - const.float = col - c.bind_constants.append(const) + col = roughness_input.default_value + const.float = mix_float(col, const.float if hasattr(const, 'float') else None) def parse_normal_map_socket(self, normal_input, material, c, defs, tree, node): if normal_input.is_linked: diff --git a/blender/project.py b/blender/project.py index 36d9059e..b725dd45 100755 --- a/blender/project.py +++ b/blender/project.py @@ -15,6 +15,8 @@ import nodes_pipeline import nodes_world import path_tracer from armory import ArmoryExporter +import lib.make_resources +import lib.make_variants def defaultSettings(): wrd = bpy.data.worlds[0] @@ -126,8 +128,13 @@ def get_export_scene_override(scene): 'scene': scene} return override +def compile_shader(raw_path, shader_name, defs): + os.chdir(raw_path + './' + shader_name) + lib.make_resources.make(shader_name + '.shader.json', defs) + lib.make_variants.make(shader_name + '.shader.json', defs) + # Transform Blender data into game data -def exportGameData(): +def exportGameData(fp, raw_path): shader_references = [] asset_references = [] @@ -172,6 +179,23 @@ def exportGameData(): # Write Main.hx write_data.write_main() + + # Write referenced shader variants + for ref in asset_references: + # Resource does not exist yet + os.chdir(fp) + if not os.path.exists(ref): + shader_name = ref.split('/')[2] + defs = ref[:-5] # Remove .json extnsion + defs = defs.split(shader_name) # 'name/name_def_def' + if len(defs) > 2: + defs = defs[2] # Apended defs + defs = defs.split('_') + defs = defs[1:] + defs = ['_' + d for d in defs] # Restore _ + else: + defs = [] + compile_shader(raw_path, shader_name, defs) def buildProject(self, build_type=0): # Save blend @@ -210,16 +234,9 @@ def buildProject(self, build_type=0): # Compile path tracer shaders if len(bpy.data.cameras) > 0 and bpy.data.cameras[0].pipeline_path == 'pathtrace_pipeline': path_tracer.compile(raw_path + 'pt_trace_pass/pt_trace_pass.frag.glsl') - - # Compile shaders if needed - # TODO: create only referenced variants - # if os.path.isdir("compiled") == False: - os.chdir(raw_path) - call(["python", "compile.py"]) - os.chdir(fp) # Export - exportGameData() + exportGameData(fp, raw_path) # Set build command if (bpy.data.worlds[0]['CGProjectTarget'] == 0): diff --git a/raw/__init__.py b/raw/__init__.py deleted file mode 100755 index e69de29b..00000000 diff --git a/raw/deferred/decals.frag.glsl b/raw/deferred/decals.frag.glsl index c7b3757c..dd2cd259 100644 --- a/raw/deferred/decals.frag.glsl +++ b/raw/deferred/decals.frag.glsl @@ -11,6 +11,16 @@ uniform sampler2D salbedo; #ifdef _NMTex uniform sampler2D snormal; #endif +#ifdef _RMTex +uniform sampler2D srm; +#else +uniform float roughness; +#endif +#ifdef _MMTex +uniform sampler2D smm; +#else +uniform float metalness; +#endif uniform mat4 invVP; uniform mat4 invM; @@ -19,6 +29,7 @@ uniform mat4 V; in vec4 mvpposition; in vec4 mposition; in vec4 matColor; +// in vec3 orientation; mat3 cotangentFrame(vec3 nor, vec3 pos, vec2 uv) { // Get edge vectors of the pixel triangle @@ -58,57 +69,86 @@ vec4 reconstructPos(float z, vec2 uv_f) { return vec4((sPos.xyz / sPos.w), sPos.w); } +float packFloat(float f1, float f2) { + int index = int(f1 * 1000); + float alpha = f2 == 0.0 ? f2 : (f2 - 0.0001); + float result = index + alpha; + return result; +} + void main() { vec2 screenPosition = mvpposition.xy / mvpposition.w; vec2 depthUV = screenPosition * 0.5 + 0.5; - const vec2 resoluion = vec2(800.0, 600.0); + const vec2 resoluion = vec2(1920.0, 1080.0); depthUV += vec2(0.5 / resoluion); // Half pixel offset float depth = texture(gbufferD, depthUV).r * 2.0 - 1.0; vec4 worldPos = reconstructPos(depth, depthUV); worldPos.w = 1.0; - vec4 localPos = invM * worldPos; + + // Angle reject + // Reconstruct normal + // vec3 dnor = normalize(cross(dFdx(worldPos.xyz), dFdy(worldPos.xyz))); + // Get decal box orientation + // vec3 orientation = vec3(1.0, 0.0, 0.0); + // if (dot(dnor, orientation) < cos(3.1415)) discard; + + vec4 localPos = invM * worldPos; localPos.y *= -1.0; if (abs(localPos.x) > 1.0) discard; if (abs(localPos.y) > 1.0) discard; if (abs(localPos.z) > 1.0) discard; - vec2 uv = (localPos.xy / 2.0) - 0.5; // / 2.0 - adjust decal box size - vec4 baseColor = texture(salbedo, uv) * matColor; + vec2 texCoord = (localPos.xy / 2.0) - 0.5; // / 2.0 - adjust decal box size + +#ifdef _AMTex + vec4 baseColor = texture(salbedo, texCoord) * matColor; +#else + vec4 baseColor = matColor; +#endif + // Alpha write is disabled in shader res, we acces all channels for blending - gl_FragData[1] = baseColor; + // Use separate texture for base color in the future + gl_FragData[1].rgb = baseColor.rgb; + gl_FragData[1].a = baseColor.a; + // gl_FragData[1].a = packFloat(roughness, metalness) * baseColor.a; - - - - // n /= (abs(n.x) + abs(n.y) + abs(n.z)); - // n.xy = n.z >= 0.0 ? n.xy : octahedronWrap(n.xy); - - - /* - vec3 ddxWp = dFdx(worldPos); - vec3 ddyWp = dFdy(worldPos); - vec3 normal = normalize(cross(ddyWp, ddxWp)); - - // Get values across and along the surface - vec3 ddxWp = dFdx(worldPos); - vec3 ddyWp = dFdy(worldPos); +#ifdef _MMTex + float metalness = texture(smm, texCoord).r; +#endif - // Determine the normal - vec3 normal = normalize(cross(ddyWp, ddxWp)); +#ifdef _RMTex + float roughness = texture(srm, texCoord).r; +#endif + +#ifdef _NMTex + vec3 normal = texture(snormal, texCoord).rgb * 2.0 - 1.0; + vec3 nn = normalize(normal); + vec3 dp1 = dFdx(worldPos.xyz); + vec3 dp2 = dFdy(worldPos.xyz); + vec2 duv1 = dFdx(texCoord); + vec2 duv2 = dFdy(texCoord); + vec3 dp2perp = cross(dp2, nn); + vec3 dp1perp = cross(nn, dp1); + vec3 T = dp2perp * duv1.x + dp1perp * duv2.x; + vec3 B = dp2perp * duv1.y + dp1perp * duv2.y; + float invmax = inversesqrt(max(dot(T,T), dot(B,B))); + mat3 TBN = mat3(T * invmax, B * invmax, nn); + vec3 n = normalize(TBN * nn); + + n /= (abs(n.x) + abs(n.y) + abs(n.z)); + n.xy = n.z >= 0.0 ? n.xy : octahedronWrap(n.xy); + + gl_FragData[0].rg = n.xy; +#else + gl_FragData[0].rg = vec2(1.0); +#endif - // Normalizing things is cool - binormal = normalize(ddxWp); - tangent = normalize(ddyWp); - - // Create a matrix transforming from tangent space to view space - mat3 tangentToView; - tangentToView[0] = V * pixelTangent; - tangentToView[1] = V * pixelBinormal; - tangentToView[2] = V * pixelNormal; - - // Transform normal from tangent space into view space - normal = tangentToView * normal; - */ + // gl_FragData[0].b unused for now so we can rewrite it + gl_FragData[0].b = 0.0; + // use separete RG texture for normal storage in the future + // Color mask does not disable write for all buffers so mask is overwritten + // Half of color alpha to soft normals blend + gl_FragData[0].a = baseColor.a / 2.0; } diff --git a/raw/deferred/decals.vert.glsl b/raw/deferred/decals.vert.glsl index 80ebad84..f730825f 100644 --- a/raw/deferred/decals.vert.glsl +++ b/raw/deferred/decals.vert.glsl @@ -29,11 +29,18 @@ in vec3 pos; uniform mat4 VP; uniform mat4 M; +// uniform mat4 MV; uniform vec4 albedo_color; +#ifdef _RampID +uniform vec4 albedo_color2; +uniform int uid; +#endif + out vec4 mvpposition; out vec4 mposition; out vec4 matColor; +// out vec3 orientation; // #ifdef _AMTex // out vec2 texCoord; // #endif @@ -45,10 +52,27 @@ out vec4 matColor; // out vec3 normal; // #endif +#ifdef _RampID +float hash(vec2 p) { + float h = dot(p, vec2(127.1, 311.7)); + return fract(sin(h) * 43758.5453123); +} +#endif + void main() { vec4 sPos = (vec4(pos, 1.0)); mposition = M * sPos; mvpposition = VP * mposition; + + // orientation = normalize(MV[1].xyz); + +#ifdef _RampID + vec2 p = vec2(float(uid), float(uid)); + float factor = hash(p); + matColor = mix(albedo_color, albedo_color2, factor); +#else matColor = albedo_color; +#endif + gl_Position = mvpposition; } diff --git a/raw/deferred/deferred.frag.glsl b/raw/deferred/deferred.frag.glsl index 62b47fa5..1121fcdd 100644 --- a/raw/deferred/deferred.frag.glsl +++ b/raw/deferred/deferred.frag.glsl @@ -91,6 +91,6 @@ void main() { n /= (abs(n.x) + abs(n.y) + abs(n.z)); n.xy = n.z >= 0.0 ? n.xy : octahedronWrap(n.xy); - gl_FragData[0] = vec4(n.xy, mask, 1.0 - (mvpposition.z / mvpposition.w)); + gl_FragData[0] = vec4(n.xy, 0.0, mask); gl_FragData[1] = vec4(baseColor.rgb, packFloat(roughness, metalness)); } diff --git a/raw/deferred/deferred.shader.json b/raw/deferred/deferred.shader.json index dbddd8e8..3bd63802 100755 --- a/raw/deferred/deferred.shader.json +++ b/raw/deferred/deferred.shader.json @@ -124,6 +124,11 @@ { "id": "V", "link": "_viewMatrix" + }, + { + "id": "uid", + "link": "_uid", + "ifdef": ["_RampID"] } ], "texture_params": [], diff --git a/raw/deferred_light/deferred_light.frag.glsl b/raw/deferred_light/deferred_light.frag.glsl index 11afa8dd..c60d092f 100644 --- a/raw/deferred_light/deferred_light.frag.glsl +++ b/raw/deferred_light/deferred_light.frag.glsl @@ -441,7 +441,7 @@ void main() { direct = direct * lightColor * lightStrength; // SSS only masked objects - if (texture(gbuffer0, texCoord).b == 2.0) { + if (texture(gbuffer0, texCoord).a == 2.0) { direct.rgb = direct.rgb * SSSSTransmittance(1.0, 0.005, p, n, lightDir); } diff --git a/raw/motion_blur_pass/motion_blur_pass.frag.glsl b/raw/motion_blur_pass/motion_blur_pass.frag.glsl index f7bfcbe5..75e7b6cb 100644 --- a/raw/motion_blur_pass/motion_blur_pass.frag.glsl +++ b/raw/motion_blur_pass/motion_blur_pass.frag.glsl @@ -46,7 +46,7 @@ void main() { vec4 color = texture(tex, texCoord); // Do not blur masked objects - if (texture(gbuffer0, texCoord).b == 1.0) { + if (texture(gbuffer0, texCoord).a == 1.0) { gl_FragColor = color; return; } @@ -66,49 +66,49 @@ void main() { int processed = 1; // for(int i = 1; i < samples; ++i) { offset += velocity; - if (texture(gbuffer0, offset).b != 1.0) { + if (texture(gbuffer0, offset).a != 1.0) { color += texture(tex, offset); processed++; } offset += velocity; - if (texture(gbuffer0, offset).b != 1.0) { + if (texture(gbuffer0, offset).a != 1.0) { color += texture(tex, offset); processed++; } offset += velocity; - if (texture(gbuffer0, offset).b != 1.0) { + if (texture(gbuffer0, offset).a != 1.0) { color += texture(tex, offset); processed++; } offset += velocity; - if (texture(gbuffer0, offset).b != 1.0) { + if (texture(gbuffer0, offset).a != 1.0) { color += texture(tex, offset); processed++; } offset += velocity; - if (texture(gbuffer0, offset).b != 1.0) { + if (texture(gbuffer0, offset).a != 1.0) { color += texture(tex, offset); processed++; } offset += velocity; - if (texture(gbuffer0, offset).b != 1.0) { + if (texture(gbuffer0, offset).a != 1.0) { color += texture(tex, offset); processed++; } offset += velocity; - if (texture(gbuffer0, offset).b != 1.0) { + if (texture(gbuffer0, offset).a != 1.0) { color += texture(tex, offset); processed++; } offset += velocity; - if (texture(gbuffer0, offset).b != 1.0) { + if (texture(gbuffer0, offset).a != 1.0) { color += texture(tex, offset); processed++; } diff --git a/raw/sss_pass/sss_pass.frag.glsl b/raw/sss_pass/sss_pass.frag.glsl index 4348d29c..865a9366 100644 --- a/raw/sss_pass/sss_pass.frag.glsl +++ b/raw/sss_pass/sss_pass.frag.glsl @@ -155,7 +155,7 @@ vec4 SSSSBlur(float sssWidth) { void main() { // SSS only masked objects - if (texture(gbuffer0, texCoord).b == 2.0) { + if (texture(gbuffer0, texCoord).a == 2.0) { gl_FragColor = SSSSBlur(0.005); } else {