Geometry Nodes: Add Brick Texture node

Port brick shader node to GN

Reviewed By: JacquesLucke

Differential Revision: https://developer.blender.org/D12783
This commit is contained in:
Charlie Jolly 2021-10-26 15:10:47 +01:00 committed by Charlie Jolly
parent a90cb41cb9
commit 52ccb44501
2 changed files with 173 additions and 2 deletions

View File

@ -727,6 +727,7 @@ geometry_node_categories = [
NodeItem("FunctionNodeReplaceString"),
]),
GeometryNodeCategory("GEO_TEXTURE", "Texture", items=[
NodeItem("ShaderNodeTexBrick"),
NodeItem("ShaderNodeTexChecker"),
NodeItem("ShaderNodeTexGradient"),
NodeItem("ShaderNodeTexMagic"),

View File

@ -19,6 +19,9 @@
#include "../node_shader_util.h"
#include "BLI_float2.hh"
#include "BLI_float4.hh"
namespace blender::nodes {
static void sh_node_tex_brick_declare(NodeDeclarationBuilder &b)
@ -98,18 +101,185 @@ static int node_shader_gpu_tex_brick(GPUMaterial *mat,
GPU_constant(&squash_freq));
}
/* node type definition */
namespace blender::nodes {
class BrickFunction : public fn::MultiFunction {
private:
const float offset_;
const int offset_freq_;
const float squash_;
const int squash_freq_;
public:
BrickFunction(const float offset,
const int offset_freq,
const float squash,
const int squash_freq)
: offset_(offset), offset_freq_(offset_freq), squash_(squash), squash_freq_(squash_freq)
{
static fn::MFSignature signature = create_signature();
this->set_signature(&signature);
}
static fn::MFSignature create_signature()
{
fn::MFSignatureBuilder signature{"BrickTexture"};
signature.single_input<float3>("Vector");
signature.single_input<ColorGeometry4f>("Color1");
signature.single_input<ColorGeometry4f>("Color2");
signature.single_input<ColorGeometry4f>("Mortar");
signature.single_input<float>("Scale");
signature.single_input<float>("Mortar Size");
signature.single_input<float>("Mortar Smooth");
signature.single_input<float>("Bias");
signature.single_input<float>("Brick Width");
signature.single_input<float>("Row Height");
signature.single_output<ColorGeometry4f>("Color");
signature.single_output<float>("Fac");
return signature.build();
}
/* Fast integer noise. */
static float brick_noise(uint n)
{
n = (n + 1013) & 0x7fffffff;
n = (n >> 13) ^ n;
const uint nn = (n * (n * n * 60493 + 19990303) + 1376312589) & 0x7fffffff;
return 0.5f * ((float)nn / 1073741824.0f);
}
static float smoothstepf(const float f)
{
const float ff = f * f;
return (3.0f * ff - 2.0f * ff * f);
}
static float2 brick(float3 p,
float mortar_size,
float mortar_smooth,
float bias,
float brick_width,
float row_height,
float offset_amount,
int offset_frequency,
float squash_amount,
int squash_frequency)
{
float offset = 0.0f;
const int rownum = (int)floorf(p.y / row_height);
if (offset_frequency && squash_frequency) {
brick_width *= (rownum % squash_frequency) ? 1.0f : squash_amount;
offset = (rownum % offset_frequency) ? 0.0f : (brick_width * offset_amount);
}
const int bricknum = (int)floorf((p.x + offset) / brick_width);
const float x = (p.x + offset) - brick_width * bricknum;
const float y = p.y - row_height * rownum;
const float tint = clamp_f(
brick_noise((rownum << 16) + (bricknum & 0xFFFF)) + bias, 0.0f, 1.0f);
float min_dist = std::min(std::min(x, y), std::min(brick_width - x, row_height - y));
float mortar;
if (min_dist >= mortar_size) {
mortar = 0.0f;
}
else if (mortar_smooth == 0.0f) {
mortar = 1.0f;
}
else {
min_dist = 1.0f - min_dist / mortar_size;
mortar = (min_dist < mortar_smooth) ? smoothstepf(min_dist / mortar_smooth) : 1.0f;
}
return float2(tint, mortar);
}
void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override
{
const VArray<float3> &vector = params.readonly_single_input<float3>(0, "Vector");
const VArray<ColorGeometry4f> &color1_values = params.readonly_single_input<ColorGeometry4f>(
1, "Color1");
const VArray<ColorGeometry4f> &color2_values = params.readonly_single_input<ColorGeometry4f>(
2, "Color2");
const VArray<ColorGeometry4f> &mortar_values = params.readonly_single_input<ColorGeometry4f>(
3, "Mortar");
const VArray<float> &scale = params.readonly_single_input<float>(4, "Scale");
const VArray<float> &mortar_size = params.readonly_single_input<float>(5, "Mortar Size");
const VArray<float> &mortar_smooth = params.readonly_single_input<float>(6, "Mortar Smooth");
const VArray<float> &bias = params.readonly_single_input<float>(7, "Bias");
const VArray<float> &brick_width = params.readonly_single_input<float>(8, "Brick Width");
const VArray<float> &row_height = params.readonly_single_input<float>(9, "Row Height");
MutableSpan<ColorGeometry4f> r_color =
params.uninitialized_single_output_if_required<ColorGeometry4f>(10, "Color");
MutableSpan<float> r_fac = params.uninitialized_single_output_if_required<float>(11, "Fac");
const bool store_fac = !r_fac.is_empty();
const bool store_color = !r_color.is_empty();
for (int64_t i : mask) {
const float2 f2 = brick(vector[i] * scale[i],
mortar_size[i],
mortar_smooth[i],
bias[i],
brick_width[i],
row_height[i],
offset_,
offset_freq_,
squash_,
squash_freq_);
float4 color_data, color1, color2, mortar;
copy_v4_v4(color_data, color1_values[i]);
copy_v4_v4(color1, color1_values[i]);
copy_v4_v4(color2, color2_values[i]);
copy_v4_v4(mortar, mortar_values[i]);
const float tint = f2.x;
const float f = f2.y;
if (f != 1.0f) {
const float facm = 1.0f - tint;
color_data = color1 * facm + color2 * tint;
}
if (store_color) {
color_data = color_data * (1.0f - f) + mortar * f;
copy_v4_v4(r_color[i], color_data);
}
if (store_fac) {
r_fac[i] = f;
}
}
}
};
static void sh_node_brick_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder)
{
bNode &node = builder.node();
NodeTexBrick *tex = (NodeTexBrick *)node.storage;
builder.construct_and_set_matching_fn<BrickFunction>(
tex->offset, tex->offset_freq, tex->squash, tex->squash_freq);
}
} // namespace blender::nodes
void register_node_type_sh_tex_brick(void)
{
static bNodeType ntype;
sh_node_type_base(&ntype, SH_NODE_TEX_BRICK, "Brick Texture", NODE_CLASS_TEXTURE, 0);
sh_fn_node_type_base(&ntype, SH_NODE_TEX_BRICK, "Brick Texture", NODE_CLASS_TEXTURE, 0);
ntype.declare = blender::nodes::sh_node_tex_brick_declare;
node_type_size_preset(&ntype, NODE_SIZE_MIDDLE);
node_type_init(&ntype, node_shader_init_tex_brick);
node_type_storage(
&ntype, "NodeTexBrick", node_free_standard_storage, node_copy_standard_storage);
node_type_gpu(&ntype, node_shader_gpu_tex_brick);
ntype.build_multi_function = blender::nodes::sh_node_brick_build_multi_function;
nodeRegisterType(&ntype);
}