Skip to content

Commit

Permalink
Merge pull request #10 from quadproduction/enhancement/280-fixbrushed…
Browse files Browse the repository at this point in the history
…metal-shader

Add fixstudio custom arnold shaders for maya
  • Loading branch information
BenSouchet authored Jan 11, 2024
2 parents 6e07f1f + bf3cf91 commit 27a4fce
Show file tree
Hide file tree
Showing 6 changed files with 1,651 additions and 0 deletions.
59 changes: 59 additions & 0 deletions host/maya/custom_arnold_shaders/fixBrushedMetalNormal.mtd
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
[node fixBrushedMetalNormal]
maya.name STRING "fixBrushedMetalNormal"
maya.id INT 0x00010001
maya.classification STRING "utility/color"
maya.output_name STRING "outNormal"
maya.output_shortname STRING "out"

[attr map_type]
min INT 0
max INT 2

[attr pattern_type]
min INT 0
max INT 2

[attr scale]
min FLOAT 0
softmax FLOAT 1000

[attr slit_length_min]
min FLOAT 0
softmax FLOAT 100

[attr slit_length_max]
min FLOAT 0
softmax FLOAT 100

[attr slit_depth_min]
min FLOAT 0
softmax FLOAT 2

[attr slit_depth_max]
min FLOAT 0
softmax FLOAT 1

[attr slit_width]
min FLOAT 0
max FLOAT 1

[attr noise_frequency]
min FLOAT 0
softmax FLOAT 10

[attr noise_amplitude]
min FLOAT 0
softmax FLOAT 1

[attr angle_randomness]
min FLOAT 0
max FLOAT 1

[attr layers]
min INT 1
softmax INT 100

[attr slit_profile]
min INT 0
max INT 2

230 changes: 230 additions & 0 deletions host/maya/custom_arnold_shaders/fixBrushedMetalNormal.osl
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
/*
* Brushed metal normal map
*
* This creates an Arnold node that outputs a customizable, procedural brushed
* metal normal map
*/

// UTILITY FUNCTIONS

/*
* normal_to_rgb - convert a unit vector to a valid RGB value
*/
color normal_to_rgb(normal n) {
return .5 + .5 * color(n);
}

/*
* depth_to_rgb - transforms range [-depth_max, 0] to [0, 1] for use as an RGB map
*/
color depth_to_rgb(float d, float depth_max) {
return color(1 + d / depth_max);
}

// SLIT PROFILE FUNCTIONS
/*
* v_slit_normal - takes in UV values inside a given slit,
* and returns the normal corresponding to a V profile
* with d0 as its maximum depth
* and l0 as its slit length.
*/
normal v_slit_normal(float u, float v, float l0, float d0) {
normal n = normal(0, v > 0 ? -d0 : d0, 1);
n /= length(n);
return n;
}

/*
* v_slit_depth - takes in UV values inside a given slit,
* and returns the depth corresponding to a V profile
* with d0 as its maximum depth
* and l0 as its slit length.
*/
float v_slit_depth(float u, float v, float l0, float d0) {
return (abs(v / l0) - 1) * d0;
}

/*
* x2_slit_normal - takes in UV values inside a given slit,
* and returns the normal corresponding to a parabola profile
* with d0 as its maximum depth
* and l0 as its slit length.
*/
normal x2_slit_normal(float u, float v, float l0, float d0) {
float frac = v / l0;
float dfdv = 2 * d0 * frac;

normal n = normal(0, -dfdv, 1);
n /= length(n);
return n;
}

/*
* x2_slit_depth - takes in UV values inside a given slit,
* and returns the depth corresponding to a parabola profile
* with d0 as its maximum depth
* and l0 as its slit length.
*/
float x2_slit_depth(float u, float v, float l0, float d0) {
float frac = v / l0;
return (frac * frac - 1) * d0;
}

/*
* random_slit_normal - UNUSED; returns a random normal inside a slit
*/
normal random_slit_normal(float u, float v, float l0, float d0) {
float rnd_angle = noise(v) * M_PI;
return normal(0, cos(rnd_angle), sin(rnd_angle));
}

// UV TRANSFORM FUNCTIONS
/*
* use UV as cartesian coordinates, with du and dv representing the local derivatives of UVs
*/
void transform_uvs_linear(output float u, output float v,
output vector du, output vector dv) {
du = vector(1, 0, 0);
dv = vector(0, 1, 0);
}

/*
* use UV as polar coordinates, with du and dv representing the local derivatives of UVs
*/
void transform_uvs_circular(output float u, output float v,
output vector du, output vector dv) {
u = 2 * u - 1;
v = 2 * v - 1;
float r = sqrt(u * u + v * v);
float theta = atan2(v, u);
float inv_r = 1 / r;
float cosTheta = u * inv_r;
float sinTheta = v * inv_r;

du = vector(-sinTheta, cosTheta, 0);
dv = vector(cosTheta, sinTheta, 0);

u = theta / M_2PI;
v = r;
}

// MAIN FUNCTION
shader fixBrushedMetalNormal(
int map_type = 0 [[ string widget = "popup", string options = "Normal|Depth" ]],
int pattern_type = 0 [[ string widget = "popup", string options = "Linear|Circular" ]],
float scale = 500, // scales u and v
float slit_length_min = 5, // scales u only (min)
float slit_length_max = 5, // scales u only (max)
float slit_depth_min = .5, // depth of each slit (min),
float slit_depth_max = .5, // depth of each slit (max),
float slit_width = .5, // width of each slit, 0 < slit_width < 1
int slit_profile = 0 [[ string widget = "popup", string options = "Parabola|V-shape|Random" ]], // 0: x^2, 1: |x|, 3: random
float noise_frequency = 1, // frequency of the UV perturbation
float noise_amplitude = .1, // amplitude of the UV perturbation
float angle_randomness = 0,
int seed = 0, // random seed
int layers = 1, // scratch passes
string version = "1.1",
// version is not an actual parameter, should be left unchanged by the user (found no cleaner way to do this)
output color Cout = color(.5, .5, 1))
{
vector du, dv; // unit vectors that describe the local orientation of u and v
if (pattern_type == 0) {
transform_uvs_linear(u, v, du, dv);
} else if (pattern_type == 1) {
transform_uvs_circular(u, v, du, dv);
} else {
return;
}

// map u and v to range [-1, 1]
float global_u = scale * (2 * u - 1);
float global_v = scale * (2 * v - 1);

// add noise to the texture coordinates so as to make the slits less perfect
vector perturbation = noise_amplitude *
noise("perlin", vector(seed,
global_u * noise_frequency,
global_v * noise_frequency));

global_u += perturbation[0];
global_v += perturbation[1];

if (map_type == 0)
Cout = normal_to_rgb(normal(0, 0, 1));
else if (map_type == 1)
Cout = depth_to_rgb(0, 1);


// create slit layers
for (int layer_seed = seed * layers; layer_seed < (seed + 1) * layers; layer_seed++) {
/* -- rotate each layer randomly -- */

float random_angle = (2 * cellnoise(layer_seed, 0) - 1) * M_PI_2 * angle_randomness;
float random_cos = cos(random_angle);
float random_sin = sin(random_angle);

float layer_v = global_u * (-random_sin) + global_v * random_cos;
float layer_u = global_u * random_cos + global_v * random_sin;

vector layer_du = du * random_cos + dv * random_sin;
vector layer_dv = du * (-random_sin) + dv * random_cos;

/* -- randomly shift columns based on the current layer -- */
layer_v += scale * cellnoise(layer_seed, 1);

/* -- randomize slit length -- */

// random number based on layer and current row
float rnd = cellnoise(point(
layer_seed,
layer_v,
0)
);
float length_here = slit_length_min + rnd * (slit_length_max - slit_length_min);
layer_u /= length_here;

/* -- randomly shift rows -- */

// shift each row by a random amount in order to avoid having a visible normal discontinuity
float period = 2 * scale / length_here; // the maximum value of layer_u, assuming u is in range[0, 1]
layer_u = mod(layer_u + period * cellnoise(layer_seed, layer_v),
period);


float rnd_cells = cellnoise(point(layer_u, layer_v, layer_seed), 1);
float depth_here = slit_depth_min + cellnoise(point(layer_u, layer_v, layer_seed), 2) * (slit_depth_max - slit_depth_min);

float density = .2;

if (rnd_cells < density) { // keep "density" % of cells
float slit_u = 2 * mod(layer_u, 1) - 1;
float slit_v = 2 * mod(layer_v, 1) - 1;

// In radial mode, scale slits to have a uniform width
if (pattern_type == 2) {
slit_v *= u;
}
if (abs(slit_v) < slit_width) {
normal n;
float d = 0;
if (slit_profile == 0) {
n = x2_slit_normal(slit_u, slit_v, slit_width, depth_here);
d = x2_slit_depth(slit_u, slit_v, slit_width, depth_here);
} else if (slit_profile == 1) {
n = v_slit_normal(slit_u, slit_v, slit_width, depth_here);
d = v_slit_depth(slit_u, slit_v, slit_width, depth_here);
} else if (slit_profile == 2) {
n = random_slit_normal(slit_u, slit_v, slit_width, depth_here);
d = x2_slit_depth(slit_u, slit_v, slit_width, depth_here);
}
if (map_type == 0)
Cout = normal_to_rgb(
n[0] * layer_du + n[1] * layer_dv + n[2] * vector(0, 0, 1)
);
else if (map_type == 1)
Cout = depth_to_rgb(d, slit_depth_max);
}
}
}
}
Loading

0 comments on commit 27a4fce

Please sign in to comment.