Color ramp, decals, shader compiler work.

This commit is contained in:
Lubos Lenco 2016-05-19 22:22:41 +02:00
parent 27683773fa
commit b2466322c0
13 changed files with 261 additions and 111 deletions

View file

@ -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)

View file

@ -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)

View file

@ -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:

View file

@ -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):

View file

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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));
}

View file

@ -124,6 +124,11 @@
{
"id": "V",
"link": "_viewMatrix"
},
{
"id": "uid",
"link": "_uid",
"ifdef": ["_RampID"]
}
],
"texture_params": [],

View file

@ -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);
}

View file

@ -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++;
}

View file

@ -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 {