Merge pull request #2139 from Naxela/master

Add map range node, vector rotate node, missing math operations and blackbody node
This commit is contained in:
Lubos Lenco 2021-03-26 08:24:41 +01:00 committed by GitHub
commit 8118733474
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 287 additions and 64 deletions

View file

@ -331,6 +331,7 @@ def parse_vector(node: bpy.types.Node, socket: bpy.types.NodeSocket) -> str:
'COMBXYZ': nodes_converter.parse_combxyz,
'VECT_MATH': nodes_converter.parse_vectormath,
'DISPLACEMENT': nodes_vector.parse_displacement,
'VECTOR_ROTATE': nodes_vector.parse_vectorrotate,
}
if node.type in node_parser_funcs:
@ -433,6 +434,7 @@ def parse_value(node, socket):
'SEPRGB': nodes_converter.parse_seprgb,
'SEPXYZ': nodes_converter.parse_sepxyz,
'VECT_MATH': nodes_converter.parse_vectormath,
'MAP_RANGE': nodes_converter.parse_maprange,
}
if node.type in node_parser_funcs:

View file

@ -332,3 +332,186 @@ vec3 wrap(const vec3 value, const vec3 max, const vec3 min) {
\t wrap(value.z, max.z, min.z));
}
"""
str_blackbody = """
vec3 blackbody(const float temperature){
vec3 rgb = vec3(0.0, 0.0, 0.0);
vec3 r = vec3(0.0, 0.0, 0.0);
vec3 g = vec3(0.0, 0.0, 0.0);
vec3 b = vec3(0.0, 0.0, 0.0);
float t_inv = float(1.0 / temperature);
if (temperature >= 12000.0) {
rgb = vec3(0.826270103, 0.994478524, 1.56626022);
} else if(temperature < 965.0) {
rgb = vec3(4.70366907, 0.0, 0.0);
} else {
if (temperature >= 6365.0) {
vec3 r = vec3(3.78765709e+03, 9.36026367e-06, 3.98995841e-01);
vec3 g = vec3(-5.00279505e+02, -4.59745390e-06, 1.09090465e+00);
vec4 b = vec4(6.72595954e-13, -2.73059993e-08, 4.24068546e-04, -7.52204323e-01);
rgb = vec3(r.r * t_inv + r.g * temperature + r.b, g.r * t_inv + g.g * temperature + g.b, ((b.r * temperature + b.g) * temperature + b.b) * temperature + b.a );
} else if (temperature >= 3315.0) {
vec3 r = vec3(4.60124770e+03, 2.89727618e-05, 1.48001316e-01);
vec3 g = vec3(-1.18134453e+03, -2.18913373e-05, 1.30656109e+00);
vec4 b = vec4(-2.22463426e-13, -1.55078698e-08, 3.81675160e-04, -7.30646033e-01);
rgb = vec3(r.r * t_inv + r.g * temperature + r.b, g.r * t_inv + g.g * temperature + g.b, ((b.r * temperature + b.g) * temperature + b.b) * temperature + b.a );
} else if (temperature >= 1902.0) {
vec3 r = vec3(4.66849800e+03, 2.85655028e-05, 1.29075375e-01);
vec3 g = vec3(-1.42546105e+03, -4.01730887e-05, 1.44002695e+00);
vec4 b = vec4(-2.02524603e-11, 1.79435860e-07, -2.60561875e-04, -1.41761141e-02);
rgb = vec3(r.r * t_inv + r.g * temperature + r.b, g.r * t_inv + g.g * temperature + g.b, ((b.r * temperature + b.g) * temperature + b.b) * temperature + b.a );
} else if (temperature >= 1449.0) {
vec3 r = vec3(4.10671449e+03, -8.61949938e-05, 6.41423749e-01);
vec3 g = vec3(-1.22075471e+03, 2.56245413e-05, 1.20753416e+00);
vec4 b = vec4(0.0, 0.0, 0.0, 0.0);
rgb = vec3(r.r * t_inv + r.g * temperature + r.b, g.r * t_inv + g.g * temperature + g.b, ((b.r * temperature + b.g) * temperature + b.b) * temperature + b.a );
} else if (temperature >= 1167.0) {
vec3 r = vec3(3.37763626e+03, -4.34581697e-04, 1.64843306e+00);
vec3 g = vec3(-1.00402363e+03, 1.29189794e-04, 9.08181524e-01);
vec4 b = vec4(0.0, 0.0, 0.0, 0.0);
rgb = vec3(r.r * t_inv + r.g * temperature + r.b, g.r * t_inv + g.g * temperature + g.b, ((b.r * temperature + b.g) * temperature + b.b) * temperature + b.a );
} else {
vec3 r = vec3(2.52432244e+03, -1.06185848e-03, 3.11067539e+00);
vec3 g = vec3(-7.50343014e+02, 3.15679613e-04, 4.73464526e-01);
vec4 b = vec4(0.0, 0.0, 0.0, 0.0);
rgb = vec3(r.r * t_inv + r.g * temperature + r.b, g.r * t_inv + g.g * temperature + g.b, ((b.r * temperature + b.g) * temperature + b.b) * temperature + b.a );
}
}
return rgb;
}
"""
# Adapted from https://github.com/blender/blender/blob/594f47ecd2d5367ca936cf6fc6ec8168c2b360d0/source/blender/gpu/shaders/material/gpu_shader_material_map_range.glsl
str_map_range_linear = """
float map_range_linear(const float value, const float fromMin, const float fromMax, const float toMin, const float toMax) {
if (fromMax != fromMin) {
return float(toMin + ((value - fromMin) / (fromMax - fromMin)) * (toMax - toMin));
}
else {
return float(0.0);
}
}
"""
str_map_range_stepped = """
float map_range_stepped(const float value, const float fromMin, const float fromMax, const float toMin, const float toMax, const float steps) {
if (fromMax != fromMin) {
float factor = (value - fromMin) / (fromMax - fromMin);
factor = (steps > 0.0) ? floor(factor * (steps + 1.0)) / steps : 0.0;
return float(toMin + factor * (toMax - toMin));
}
else {
return float(0.0);
}
}
"""
str_map_range_smoothstep = """
float map_range_smoothstep(const float value, const float fromMin, const float fromMax, const float toMin, const float toMax)
{
if (fromMax != fromMin) {
float factor = (fromMin > fromMax) ? 1.0 - smoothstep(fromMax, fromMin, value) :
smoothstep(fromMin, fromMax, value);
return float(toMin + factor * (toMax - toMin));
}
else {
return float(0.0);
}
}
"""
str_map_range_smootherstep = """
float safe_divide(float a, float b)
{
return (b != 0.0) ? a / b : 0.0;
}
float smootherstep(float edge0, float edge1, float x)
{
x = clamp(safe_divide((x - edge0), (edge1 - edge0)), 0.0, 1.0);
return x * x * x * (x * (x * 6.0 - 15.0) + 10.0);
}
float map_range_smootherstep(const float value, const float fromMin, const float fromMax, const float toMin, const float toMax) {
if (fromMax != fromMin) {
float factor = (fromMin > fromMax) ? 1.0 - smootherstep(fromMax, fromMin, value) :
smootherstep(fromMin, fromMax, value);
return float(toMin + factor * (toMax - toMin));
}
else {
return float(0.0);
}
}
"""
str_rotate_around_axis = """
vec3 rotate_around_axis(const vec3 p, const vec3 axis, const float angle)
{
float costheta = cos(angle);
float sintheta = sin(angle);
vec3 r;
r.x = ((costheta + (1.0 - costheta) * axis.x * axis.x) * p.x) +
(((1.0 - costheta) * axis.x * axis.y - axis.z * sintheta) * p.y) +
(((1.0 - costheta) * axis.x * axis.z + axis.y * sintheta) * p.z);
r.y = (((1.0 - costheta) * axis.x * axis.y + axis.z * sintheta) * p.x) +
((costheta + (1.0 - costheta) * axis.y * axis.y) * p.y) +
(((1.0 - costheta) * axis.y * axis.z - axis.x * sintheta) * p.z);
r.z = (((1.0 - costheta) * axis.x * axis.z - axis.y * sintheta) * p.x) +
(((1.0 - costheta) * axis.y * axis.z + axis.x * sintheta) * p.y) +
((costheta + (1.0 - costheta) * axis.z * axis.z) * p.z);
return r;
}
"""
str_euler_to_mat3 = """
mat3 euler_to_mat3(vec3 euler)
{
float cx = cos(euler.x);
float cy = cos(euler.y);
float cz = cos(euler.z);
float sx = sin(euler.x);
float sy = sin(euler.y);
float sz = sin(euler.z);
mat3 mat;
mat[0][0] = cy * cz;
mat[0][1] = cy * sz;
mat[0][2] = -sy;
mat[1][0] = sy * sx * cz - cx * sz;
mat[1][1] = sy * sx * sz + cx * cz;
mat[1][2] = cy * sx;
mat[2][0] = sy * cx * cz + sx * sz;
mat[2][1] = sy * cx * sz - sx * cz;
mat[2][2] = cy * cx;
return mat;
}
"""

View file

@ -8,73 +8,43 @@ import arm.material.cycles_functions as c_functions
from arm.material.parser_state import ParserState
from arm.material.shader import floatstr, vec3str
def parse_maprange(node: bpy.types.ShaderNodeMapRange, out_socket: bpy.types.NodeSocket, state: ParserState) -> floatstr:
interp = node.interpolation_type
value: str = c.parse_value_input(node.inputs[0]) if node.inputs[0].is_linked else c.to_vec1(node.inputs[0].default_value)
fromMin = c.parse_value_input(node.inputs[1])
fromMax = c.parse_value_input(node.inputs[2])
toMin = c.parse_value_input(node.inputs[3])
toMax = c.parse_value_input(node.inputs[4])
if interp == "LINEAR":
state.curshader.add_function(c_functions.str_map_range_linear)
return f'map_range_linear({value}, {fromMin}, {fromMax}, {toMin}, {toMax})'
elif interp == "STEPPED":
steps = float(c.parse_value_input(node.inputs[5]))
state.curshader.add_function(c_functions.str_map_range_stepped)
return f'map_range_stepped({value}, {fromMin}, {fromMax}, {toMin}, {toMax}, {steps})'
elif interp == "SMOOTHSTEP":
state.curshader.add_function(c_functions.str_map_range_smoothstep)
return f'map_range_smoothstep({value}, {fromMin}, {fromMax}, {toMin}, {toMax})'
elif interp == "SMOOTHERSTEP":
state.curshader.add_function(c_functions.str_map_range_smootherstep)
return f'map_range_smootherstep({value}, {fromMin}, {fromMax}, {toMin}, {toMax})'
def parse_blackbody(node: bpy.types.ShaderNodeBlackbody, out_socket: bpy.types.NodeSocket, state: ParserState) -> vec3str:
t = float(c.parse_value_input(node.inputs[0]))
rgb = [0, 0, 0]
blackbody_table_r = [
[2.52432244e+03, -1.06185848e-03, 3.11067539e+00],
[3.37763626e+03, -4.34581697e-04, 1.64843306e+00],
[4.10671449e+03, -8.61949938e-05, 6.41423749e-01],
[4.66849800e+03, 2.85655028e-05, 1.29075375e-01],
[4.60124770e+03, 2.89727618e-05, 1.48001316e-01],
[3.78765709e+03, 9.36026367e-06, 3.98995841e-01]
]
blackbody_table_g = [
[-7.50343014e+02, 3.15679613e-04, 4.73464526e-01],
[-1.00402363e+03, 1.29189794e-04, 9.08181524e-01],
[-1.22075471e+03, 2.56245413e-05, 1.20753416e+00],
[-1.42546105e+03, -4.01730887e-05, 1.44002695e+00],
[-1.18134453e+03, -2.18913373e-05, 1.30656109e+00],
[-5.00279505e+02, -4.59745390e-06, 1.09090465e+00]
]
blackbody_table_b = [
[0.0, 0.0, 0.0, 0.0],
[0.0, 0.0, 0.0, 0.0],
[0.0, 0.0, 0.0, 0.0],
[-2.02524603e-11, 1.79435860e-07, -2.60561875e-04, -1.41761141e-02],
[-2.22463426e-13, -1.55078698e-08, 3.81675160e-04, -7.30646033e-01],
[6.72595954e-13, -2.73059993e-08, 4.24068546e-04, -7.52204323e-01]
]
if t >= 12000:
rgb[0] = 0.826270103
rgb[1] = 0.994478524
rgb[2] = 1.56626022
elif t < 965.0:
rgb[0] = 4.70366907
rgb[1] = 0.0
rgb[2] = 0.0
else:
if t >= 6365.0:
i = 5
elif t >= 3315.0:
i = 4
elif t >= 1902.0:
i = 3
elif t >= 1449.0:
i = 2
elif t >= 1167.0:
i = 1
else:
i = 0
r = blackbody_table_r[i]
g = blackbody_table_g[i]
b = blackbody_table_b[i]
t_inv = 1.0 / t
rgb[0] = r[0] * t_inv + r[1] * t + r[2]
rgb[1] = g[0] * t_inv + g[1] * t + g[2]
rgb[2] = ((b[0] * t + b[1]) * t + b[2]) * t + b[3]
# Pass constant
return c.to_vec3([rgb[0], rgb[1], rgb[2]])
t = c.parse_value_input(node.inputs[0])
state.curshader.add_function(c_functions.str_blackbody)
return f'blackbody({t})'
def parse_clamp(node: bpy.types.ShaderNodeClamp, out_socket: bpy.types.NodeSocket, state: ParserState) -> floatstr:
value = c.parse_value_input(node.inputs['Value'])
@ -262,14 +232,21 @@ def parse_math(node: bpy.types.ShaderNodeMath, out_socket: bpy.types.NodeSocket,
out_val = '({0} * {1})'.format(val1, val2)
elif op == 'DIVIDE':
out_val = '({0} / {1})'.format(val1, val2)
elif op == 'MULTIPLY_ADD':
val3 = c.parse_value_input(node.inputs[2])
out_val = '({0} * {1} + {2})'.format(val1, val2, val3)
elif op == 'POWER':
out_val = 'pow({0}, {1})'.format(val1, val2)
elif op == 'LOGARITHM':
out_val = 'log({0})'.format(val1)
elif op == 'SQRT':
out_val = 'sqrt({0})'.format(val1)
elif op == 'INVERSE_SQRT':
out_val = 'inversesqrt({0})'.format(val1)
elif op == 'ABSOLUTE':
out_val = 'abs({0})'.format(val1)
elif op == 'EXPONENT':
out_val = 'exp({0})'.format(val1)
elif op == 'MINIMUM':
out_val = 'min({0}, {1})'.format(val1, val2)
elif op == 'MAXIMUM':
@ -278,6 +255,17 @@ def parse_math(node: bpy.types.ShaderNodeMath, out_socket: bpy.types.NodeSocket,
out_val = 'float({0} < {1})'.format(val1, val2)
elif op == 'GREATER_THAN':
out_val = 'float({0} > {1})'.format(val1, val2)
elif op == 'SIGN':
out_val = 'sign({0})'.format(val1)
elif op == 'COMPARE':
val3 = c.parse_value_input(node.inputs[2])
out_val = 'float((abs({0} - {1}) <= max({2}, 1e-5)) ? 1.0 : 0.0)'.format(val1, val2, val3)
elif op == 'SMOOTH_MIN':
val3 = c.parse_value_input(node.inputs[2])
out_val = 'float(float({2} != 0.0 ? min({0},{1}) - (max({2} - abs({0} - {1}), 0.0) / {2}) * (max({2} - abs({0} - {1}), 0.0) / {2}) * (max({2} - abs({0} - {1}), 0.0) / {2}) * {2} * (1.0 / 6.0) : min({0}, {1})))'.format(val1, val2, val3)
elif op == 'SMOOTH_MAX':
val3 = c.parse_value_input(node.inputs[2])
out_val = 'float(0-(float({2} != 0.0 ? min(-{0},-{1}) - (max({2} - abs(-{0} - (-{1})), 0.0) / {2}) * (max({2} - abs(-{0} - (-{1})), 0.0) / {2}) * (max({2} - abs(-{0} - (-{1})), 0.0) / {2}) * {2} * (1.0 / 6.0) : min(-{0}, (-{1})))))'.format(val1, val2, val3)
elif op == 'ROUND':
# out_val = 'round({0})'.format(val1)
out_val = 'floor({0} + 0.5)'.format(val1)
@ -285,11 +273,20 @@ def parse_math(node: bpy.types.ShaderNodeMath, out_socket: bpy.types.NodeSocket,
out_val = 'floor({0})'.format(val1)
elif op == 'CEIL':
out_val = 'ceil({0})'.format(val1)
elif op == 'TRUNC':
out_val = 'trunc({0})'.format(val1)
elif op == 'FRACT':
out_val = 'fract({0})'.format(val1)
elif op == 'MODULO':
# out_val = 'float({0} % {1})'.format(val1, val2)
out_val = 'mod({0}, {1})'.format(val1, val2)
elif op == 'WRAP':
val3 = c.parse_value_input(node.inputs[2])
out_val = 'float((({1}-{2}) != 0.0) ? {0} - (({1}-{2}) * floor(({0} - {2}) / ({1}-{2}))) : {2})'.format(val1, val2, val3)
elif op == 'SNAP':
out_val = 'floor(({1} != 0.0) ? {0} / {1} : 0.0) * {1}'.format(val1, val2)
elif op == 'PINGPONG':
out_val = 'float(({1} != 0.0) ? abs(fract(({0} - {1}) / ({1} * 2.0)) * {1} * 2.0 - {1}) : 0.0)'.format(val1, val2)
elif op == 'SINE':
out_val = 'sin({0})'.format(val1)
elif op == 'COSINE':
@ -304,6 +301,16 @@ def parse_math(node: bpy.types.ShaderNodeMath, out_socket: bpy.types.NodeSocket,
out_val = 'atan({0})'.format(val1)
elif op == 'ARCTAN2':
out_val = 'atan({0}, {1})'.format(val1, val2)
elif op == 'SINH':
out_val = 'sinh({0})'.format(val1)
elif op == 'COSH':
out_val = 'cosh({0})'.format(val1)
elif op == 'TANH':
out_val = 'tanh({0})'.format(val1)
elif op == 'RADIANS':
out_val = 'radians({0})'.format(val1)
elif op == 'DEGREES':
out_val = 'degrees({0})'.format(val1)
if node.use_clamp:
return 'clamp({0}, 0.0, 1.0)'.format(out_val)

View file

@ -4,6 +4,7 @@ import bpy
from mathutils import Euler, Vector
import arm.material.cycles as c
import arm.material.cycles_functions as c_functions
from arm.material.parser_state import ParserState
from arm.material.shader import floatstr, vec3str
@ -141,3 +142,33 @@ def parse_displacement(node: bpy.types.ShaderNodeDisplacement, out_socket: bpy.t
scale = c.parse_value_input(node.inputs[2])
nor = c.parse_vector_input(node.inputs[3])
return f'(vec3({height}) * {scale})'
def parse_vectorrotate(node: bpy.types.ShaderNodeVectorRotate, out_socket: bpy.types.NodeSocket, state: ParserState) -> vec3str:
type = node.rotation_type
input_vector: bpy.types.NodeSocket = c.parse_vector_input(node.inputs[0])
input_center: bpy.types.NodeSocket = c.parse_vector_input(node.inputs[1])
input_axis: bpy.types.NodeSocket = c.parse_vector_input(node.inputs[2])
input_angle: bpy.types.NodeSocket = c.parse_value_input(node.inputs[3])
input_rotation: bpy.types.NodeSocket = c.parse_vector_input(node.inputs[4])
if node.invert:
input_invert = "0"
else:
input_invert = "1"
state.curshader.add_function(c_functions.str_rotate_around_axis)
if type == 'AXIS_ANGLE':
return f'vec3( (length({input_axis}) != 0.0) ? rotate_around_axis({input_vector} - {input_center}, normalize({input_axis}), {input_angle} * {input_invert}) + {input_center} : {input_vector} )'
elif type == 'X_AXIS':
return f'vec3( rotate_around_axis({input_vector} - {input_center}, vec3(1.0, 0.0, 0.0), {input_angle} * {input_invert}) + {input_center} )'
elif type == 'Y_AXIS':
return f'vec3( rotate_around_axis({input_vector} - {input_center}, vec3(0.0, 1.0, 0.0), {input_angle} * {input_invert}) + {input_center} )'
elif type == 'Z_AXIS':
return f'vec3( rotate_around_axis({input_vector} - {input_center}, vec3(0.0, 0.0, 1.0), {input_angle} * {input_invert}) + {input_center} )'
elif type == 'EULER_XYZ':
state.curshader.add_function(c_functions.str_euler_to_mat3)
return f'vec3( mat3(({input_invert} < 0.0) ? transpose(euler_to_mat3({input_rotation})) : euler_to_mat3({input_rotation})) * ({input_vector} - {input_center}) + {input_center})'
return f'(vec3(1.0, 0.0, 0.0))'