Hair Info Length Attribute

Goal is to add the length attribute to the Hair Info node, for better control over color gradients or similar along the hair.

Reviewed By: #eevee_viewport, brecht

Differential Revision: https://developer.blender.org/D10481
This commit is contained in:
Jeroen Bakker 2021-09-24 07:42:36 +02:00 committed by Jeroen Bakker
parent 0f764ade1a
commit 6a88f83d67
Notes: blender-bot 2023-02-14 09:48:23 +01:00
Referenced by issue #91734, Crash snapping mesh if a beveled curve is present
Referenced by issue #91697, EEVEE regression: Generated texture coordinates completly missing
26 changed files with 228 additions and 68 deletions

View File

@ -283,10 +283,13 @@ static void ExportCurveSegments(Scene *scene, Hair *hair, ParticleCurveData *CDa
return;
Attribute *attr_intercept = NULL;
Attribute *attr_length = NULL;
Attribute *attr_random = NULL;
if (hair->need_attribute(scene, ATTR_STD_CURVE_INTERCEPT))
attr_intercept = hair->attributes.add(ATTR_STD_CURVE_INTERCEPT);
if (hair->need_attribute(scene, ATTR_STD_CURVE_LENGTH))
attr_length = hair->attributes.add(ATTR_STD_CURVE_LENGTH);
if (hair->need_attribute(scene, ATTR_STD_CURVE_RANDOM))
attr_random = hair->attributes.add(ATTR_STD_CURVE_RANDOM);
@ -336,6 +339,11 @@ static void ExportCurveSegments(Scene *scene, Hair *hair, ParticleCurveData *CDa
num_curve_keys++;
}
if (attr_length != NULL)
{
attr_length->add(CData->curve_length[curve]);
}
if (attr_random != NULL) {
attr_random->add(hash_uint2_to_float(num_curves, 0));
}
@ -657,11 +665,16 @@ static void export_hair_curves(Scene *scene, Hair *hair, BL::Hair b_hair)
/* Add requested attributes. */
Attribute *attr_intercept = NULL;
Attribute *attr_length = NULL;
Attribute *attr_random = NULL;
if (hair->need_attribute(scene, ATTR_STD_CURVE_INTERCEPT)) {
attr_intercept = hair->attributes.add(ATTR_STD_CURVE_INTERCEPT);
}
if (hair->need_attribute(scene, ATTR_STD_CURVE_LENGTH))
{
attr_length = hair->attributes.add(ATTR_STD_CURVE_LENGTH);
}
if (hair->need_attribute(scene, ATTR_STD_CURVE_RANDOM)) {
attr_random = hair->attributes.add(ATTR_STD_CURVE_RANDOM);
}
@ -714,6 +727,11 @@ static void export_hair_curves(Scene *scene, Hair *hair, BL::Hair b_hair)
}
}
if (attr_length)
{
attr_length->add(length);
}
/* Random number per curve. */
if (attr_random != NULL) {
attr_random->add(hash_uint2_to_float(b_curve.index(), 0));

View File

@ -572,6 +572,7 @@ typedef enum AttributeStandard {
ATTR_STD_MOTION_VERTEX_NORMAL,
ATTR_STD_PARTICLE,
ATTR_STD_CURVE_INTERCEPT,
ATTR_STD_CURVE_LENGTH,
ATTR_STD_CURVE_RANDOM,
ATTR_STD_PTEX_FACE_ID,
ATTR_STD_PTEX_UV,

View File

@ -107,6 +107,7 @@ ustring OSLRenderServices::u_geom_undisplaced("geom:undisplaced");
ustring OSLRenderServices::u_is_smooth("geom:is_smooth");
ustring OSLRenderServices::u_is_curve("geom:is_curve");
ustring OSLRenderServices::u_curve_thickness("geom:curve_thickness");
ustring OSLRenderServices::u_curve_length("geom:curve_length");
ustring OSLRenderServices::u_curve_tangent_normal("geom:curve_tangent_normal");
ustring OSLRenderServices::u_curve_random("geom:curve_random");
ustring OSLRenderServices::u_path_ray_length("path:ray_length");

View File

@ -294,6 +294,7 @@ class OSLRenderServices : public OSL::RendererServices {
static ustring u_is_smooth;
static ustring u_is_curve;
static ustring u_curve_thickness;
static ustring u_curve_length;
static ustring u_curve_tangent_normal;
static ustring u_curve_random;
static ustring u_path_ray_length;

View File

@ -18,12 +18,14 @@
shader node_hair_info(output float IsStrand = 0.0,
output float Intercept = 0.0,
output float Length = 0.0,
output float Thickness = 0.0,
output normal TangentNormal = N,
output float Random = 0)
{
getattribute("geom:is_curve", IsStrand);
getattribute("geom:curve_intercept", Intercept);
getattribute("geom:curve_length", Length);
getattribute("geom:curve_thickness", Thickness);
getattribute("geom:curve_tangent_normal", TangentNormal);
getattribute("geom:curve_random", Random);

View File

@ -213,6 +213,8 @@ ccl_device_noinline void svm_node_hair_info(
}
case NODE_INFO_CURVE_INTERCEPT:
break; /* handled as attribute */
case NODE_INFO_CURVE_LENGTH:
break; /* handled as attribute */
case NODE_INFO_CURVE_RANDOM:
break; /* handled as attribute */
case NODE_INFO_CURVE_THICKNESS: {

View File

@ -173,6 +173,7 @@ typedef enum NodeParticleInfo {
typedef enum NodeHairInfo {
NODE_INFO_CURVE_IS_STRAND,
NODE_INFO_CURVE_INTERCEPT,
NODE_INFO_CURVE_LENGTH,
NODE_INFO_CURVE_THICKNESS,
/* Fade for minimum hair width transiency. */
// NODE_INFO_CURVE_FADE,

View File

@ -342,6 +342,8 @@ const char *Attribute::standard_name(AttributeStandard std)
return "particle";
case ATTR_STD_CURVE_INTERCEPT:
return "curve_intercept";
case ATTR_STD_CURVE_LENGTH:
return "curve_length";
case ATTR_STD_CURVE_RANDOM:
return "curve_random";
case ATTR_STD_PTEX_FACE_ID:
@ -586,6 +588,9 @@ Attribute *AttributeSet::add(AttributeStandard std, ustring name)
case ATTR_STD_CURVE_INTERCEPT:
attr = add(name, TypeDesc::TypeFloat, ATTR_ELEMENT_CURVE_KEY);
break;
case ATTR_STD_CURVE_LENGTH:
attr = add(name, TypeDesc::TypeFloat, ATTR_ELEMENT_CURVE);
break;
case ATTR_STD_CURVE_RANDOM:
attr = add(name, TypeDesc::TypeFloat, ATTR_ELEMENT_CURVE);
break;

View File

@ -4368,6 +4368,7 @@ NODE_DEFINE(HairInfoNode)
SOCKET_OUT_FLOAT(is_strand, "Is Strand");
SOCKET_OUT_FLOAT(intercept, "Intercept");
SOCKET_OUT_FLOAT(size, "Length");
SOCKET_OUT_FLOAT(thickness, "Thickness");
SOCKET_OUT_NORMAL(tangent_normal, "Tangent Normal");
#if 0 /* Output for minimum hair width transparency - deactivated. */
@ -4390,6 +4391,9 @@ void HairInfoNode::attributes(Shader *shader, AttributeRequestSet *attributes)
if (!intercept_out->links.empty())
attributes->add(ATTR_STD_CURVE_INTERCEPT);
if (!output("Length")->links.empty())
attributes->add(ATTR_STD_CURVE_LENGTH);
if (!output("Random")->links.empty())
attributes->add(ATTR_STD_CURVE_RANDOM);
}
@ -4412,6 +4416,12 @@ void HairInfoNode::compile(SVMCompiler &compiler)
compiler.add_node(NODE_ATTR, attr, compiler.stack_assign(out), NODE_ATTR_OUTPUT_FLOAT);
}
out = output("Length");
if (!out->links.empty()) {
int attr = compiler.attribute(ATTR_STD_CURVE_LENGTH);
compiler.add_node(NODE_ATTR, attr, compiler.stack_assign(out), NODE_ATTR_OUTPUT_FLOAT);
}
out = output("Thickness");
if (!out->links.empty()) {
compiler.add_node(NODE_HAIR_INFO, NODE_INFO_CURVE_THICKNESS, compiler.stack_assign(out));

View File

@ -1856,6 +1856,8 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
NULL,
NULL,
NULL},
/* 51: CD_HAIRLENGTH */
{sizeof(float), "float", 1, NULL, NULL, NULL, NULL, NULL, NULL},
};
static const char *LAYERTYPENAMES[CD_NUMTYPES] = {
@ -1912,6 +1914,7 @@ static const char *LAYERTYPENAMES[CD_NUMTYPES] = {
"CDPropFloat3",
"CDPropFloat2",
"CDPropBoolean",
"CDHairLength",
};
const CustomData_MeshMasks CD_MASK_BAREMESH = {

View File

@ -255,7 +255,7 @@ static void eevee_cryptomatte_hair_cache_populate(EEVEE_Data *vedata,
{
DRWShadingGroup *grp = eevee_cryptomatte_shading_group_create(
vedata, sldata, ob, material, true);
DRW_shgroup_hair_create_sub(ob, psys, md, grp);
DRW_shgroup_hair_create_sub(ob, psys, md, grp, NULL);
}
void EEVEE_cryptomatte_object_hair_cache_populate(EEVEE_Data *vedata,

View File

@ -769,15 +769,15 @@ static void eevee_hair_cache_populate(EEVEE_Data *vedata,
EeveeMaterialCache matcache = eevee_material_cache_get(vedata, sldata, ob, matnr - 1, true);
if (matcache.depth_grp) {
*matcache.depth_grp_p = DRW_shgroup_hair_create_sub(ob, psys, md, matcache.depth_grp);
*matcache.depth_grp_p = DRW_shgroup_hair_create_sub(ob, psys, md, matcache.depth_grp, NULL);
DRW_shgroup_add_material_resources(*matcache.depth_grp_p, matcache.shading_gpumat);
}
if (matcache.shading_grp) {
*matcache.shading_grp_p = DRW_shgroup_hair_create_sub(ob, psys, md, matcache.shading_grp);
*matcache.shading_grp_p = DRW_shgroup_hair_create_sub(ob, psys, md, matcache.shading_grp, matcache.shading_gpumat);
DRW_shgroup_add_material_resources(*matcache.shading_grp_p, matcache.shading_gpumat);
}
if (matcache.shadow_grp) {
*matcache.shadow_grp_p = DRW_shgroup_hair_create_sub(ob, psys, md, matcache.shadow_grp);
*matcache.shadow_grp_p = DRW_shgroup_hair_create_sub(ob, psys, md, matcache.shadow_grp, NULL);
DRW_shgroup_add_material_resources(*matcache.shadow_grp_p, matcache.shading_gpumat);
*cast_shadow = true;
}

View File

@ -269,7 +269,7 @@ void EEVEE_motion_blur_hair_cache_populate(EEVEE_ViewLayerData *UNUSED(sldata),
GPUTexture *tex_prev = mb_hair->psys[psys_id].hair_pos_tx[MB_PREV];
GPUTexture *tex_next = mb_hair->psys[psys_id].hair_pos_tx[MB_NEXT];
grp = DRW_shgroup_hair_create_sub(ob, psys, md, effects->motion_blur.hair_grp);
grp = DRW_shgroup_hair_create_sub(ob, psys, md, effects->motion_blur.hair_grp, NULL);
DRW_shgroup_uniform_mat4(grp, "prevModelMatrix", mb_data->obmat[MB_PREV]);
DRW_shgroup_uniform_mat4(grp, "currModelMatrix", mb_data->obmat[MB_CURR]);
DRW_shgroup_uniform_mat4(grp, "nextModelMatrix", mb_data->obmat[MB_NEXT]);

View File

@ -238,7 +238,7 @@ static void workbench_cache_hair_populate(WORKBENCH_PrivateData *wpd,
workbench_image_hair_setup(wpd, ob, matnr, ima, NULL, state) :
workbench_material_hair_setup(wpd, ob, matnr, color_type);
DRW_shgroup_hair_create_sub(ob, psys, md, grp);
DRW_shgroup_hair_create_sub(ob, psys, md, grp, NULL);
}
/**

View File

@ -342,6 +342,9 @@ static void curve_cd_calc_used_gpu_layers(CustomDataMask *cd_layers,
case CD_ORCO:
*cd_layers |= CD_MASK_ORCO;
break;
case CD_HAIRLENGTH:
*cd_layers |= CD_MASK_HAIRLENGTH;
break;
}
}
}

View File

@ -30,6 +30,7 @@
#include "BLI_math_base.h"
#include "BLI_math_vector.h"
#include "BLI_utildefines.h"
#include "BLI_listbase.h"
#include "DNA_hair_types.h"
#include "DNA_object_types.h"
@ -38,6 +39,7 @@
#include "GPU_batch.h"
#include "GPU_texture.h"
#include "GPU_material.h"
#include "draw_cache_impl.h" /* own include */
#include "draw_hair_private.h" /* own include */
@ -141,7 +143,7 @@ static void ensure_seg_pt_count(Hair *hair, ParticleHairCache *hair_cache)
}
}
static void hair_batch_cache_fill_segments_proc_pos(Hair *hair, GPUVertBufRaw *attr_step)
static void hair_batch_cache_fill_segments_proc_pos(Hair *hair, GPUVertBufRaw *attr_step, GPUVertBufRaw *length_step)
{
/* TODO: use hair radius layer if available. */
HairCurve *curve = hair->curves;
@ -162,6 +164,8 @@ static void hair_batch_cache_fill_segments_proc_pos(Hair *hair, GPUVertBufRaw *a
seg_data[3] = total_len;
co_prev = curve_co[j];
}
/* Assign length value*/
*(float *)GPU_vertbuf_raw_step(length_step) = total_len;
if (total_len > 0.0f) {
/* Divide by total length to have a [0-1] number. */
for (int j = 0; j < curve->numpoints; j++, seg_data_first += 4) {
@ -171,28 +175,48 @@ static void hair_batch_cache_fill_segments_proc_pos(Hair *hair, GPUVertBufRaw *a
}
}
static void hair_batch_cache_ensure_procedural_pos(Hair *hair, ParticleHairCache *cache)
static void hair_batch_cache_ensure_procedural_pos(Hair *hair,
ParticleHairCache *cache,
GPUMaterial *gpu_material)
{
if (cache->proc_point_buf != NULL) {
return;
if (cache->proc_point_buf == NULL) {
/* initialize vertex format */
GPUVertFormat format = {0};
uint pos_id = GPU_vertformat_attr_add(&format, "posTime", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
cache->proc_point_buf = GPU_vertbuf_create_with_format(&format);
GPU_vertbuf_data_alloc(cache->proc_point_buf, cache->point_len);
GPUVertBufRaw point_step;
GPU_vertbuf_attr_get_raw_data(cache->proc_point_buf, pos_id, &point_step);
GPUVertFormat length_format = {0};
uint length_id = GPU_vertformat_attr_add(
&length_format, "hairLength", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
cache->proc_length_buf = GPU_vertbuf_create_with_format(&length_format);
GPU_vertbuf_data_alloc(cache->proc_length_buf, cache->strands_len);
GPUVertBufRaw length_step;
GPU_vertbuf_attr_get_raw_data(cache->proc_length_buf, length_id, &length_step);
hair_batch_cache_fill_segments_proc_pos(hair, &point_step, &length_step);
/* Create vbo immediately to bind to texture buffer. */
GPU_vertbuf_use(cache->proc_point_buf);
cache->point_tex = GPU_texture_create_from_vertbuf("hair_point", cache->proc_point_buf);
}
/* initialize vertex format */
GPUVertFormat format = {0};
uint pos_id = GPU_vertformat_attr_add(&format, "posTime", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
cache->proc_point_buf = GPU_vertbuf_create_with_format(&format);
GPU_vertbuf_data_alloc(cache->proc_point_buf, cache->point_len);
GPUVertBufRaw pos_step;
GPU_vertbuf_attr_get_raw_data(cache->proc_point_buf, pos_id, &pos_step);
hair_batch_cache_fill_segments_proc_pos(hair, &pos_step);
/* Create vbo immediately to bind to texture buffer. */
GPU_vertbuf_use(cache->proc_point_buf);
cache->point_tex = GPU_texture_create_from_vertbuf("hair_point", cache->proc_point_buf);
if (gpu_material && cache->proc_length_buf != NULL && cache->length_tex) {
ListBase gpu_attrs = GPU_material_attributes(gpu_material);
LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &gpu_attrs) {
if (attr->type == CD_HAIRLENGTH) {
GPU_vertbuf_use(cache->proc_length_buf);
cache->length_tex = GPU_texture_create_from_vertbuf("hair_length", cache->proc_length_buf);
break;
}
}
}
}
static void hair_batch_cache_fill_strands_data(Hair *hair,
@ -310,6 +334,7 @@ static void hair_batch_cache_ensure_procedural_indices(Hair *hair,
/* Ensure all textures and buffers needed for GPU accelerated drawing. */
bool hair_ensure_procedural_data(Object *object,
ParticleHairCache **r_hair_cache,
GPUMaterial *gpu_material,
int subdiv,
int thickness_res)
{
@ -325,7 +350,7 @@ bool hair_ensure_procedural_data(Object *object,
/* Refreshed on combing and simulation. */
if ((*r_hair_cache)->proc_point_buf == NULL) {
ensure_seg_pt_count(hair, &cache->hair);
hair_batch_cache_ensure_procedural_pos(hair, &cache->hair);
hair_batch_cache_ensure_procedural_pos(hair, &cache->hair, gpu_material);
need_ft_update = true;
}

View File

@ -46,6 +46,7 @@
#include "ED_particle.h"
#include "GPU_batch.h"
#include "GPU_material.h"
#include "DEG_depsgraph_query.h"
@ -183,7 +184,9 @@ void particle_batch_cache_clear_hair(ParticleHairCache *hair_cache)
{
/* TODO: more granular update tagging. */
GPU_VERTBUF_DISCARD_SAFE(hair_cache->proc_point_buf);
GPU_VERTBUF_DISCARD_SAFE(hair_cache->proc_length_buf);
DRW_TEXTURE_FREE_SAFE(hair_cache->point_tex);
DRW_TEXTURE_FREE_SAFE(hair_cache->length_tex);
GPU_VERTBUF_DISCARD_SAFE(hair_cache->proc_strand_buf);
GPU_VERTBUF_DISCARD_SAFE(hair_cache->proc_strand_seg_buf);
@ -609,7 +612,8 @@ static int particle_batch_cache_fill_segments(ParticleSystem *psys,
static void particle_batch_cache_fill_segments_proc_pos(ParticleCacheKey **path_cache,
const int num_path_keys,
GPUVertBufRaw *attr_step)
GPUVertBufRaw *attr_step,
GPUVertBufRaw *length_step)
{
for (int i = 0; i < num_path_keys; i++) {
ParticleCacheKey *path = path_cache[i];
@ -630,6 +634,8 @@ static void particle_batch_cache_fill_segments_proc_pos(ParticleCacheKey **path_
seg_data[3] = total_len;
co_prev = path[j].co;
}
/* Assign length value*/
*(float *)GPU_vertbuf_raw_step(length_step) = total_len;
if (total_len > 0.0f) {
/* Divide by total length to have a [0-1] number. */
for (int j = 0; j <= path->segments; j++, seg_data_first += 4) {
@ -1079,40 +1085,64 @@ static void particle_batch_cache_ensure_procedural_indices(PTCacheEdit *edit,
static void particle_batch_cache_ensure_procedural_pos(PTCacheEdit *edit,
ParticleSystem *psys,
ParticleHairCache *cache)
ParticleHairCache *cache,
GPUMaterial *gpu_material)
{
if (cache->proc_point_buf != NULL) {
return;
}
if (cache->proc_point_buf == NULL) {
/* initialize vertex format */
GPUVertFormat pos_format = {0};
uint pos_id = GPU_vertformat_attr_add(
&pos_format, "posTime", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
/* initialize vertex format */
GPUVertFormat format = {0};
uint pos_id = GPU_vertformat_attr_add(&format, "posTime", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
cache->proc_point_buf = GPU_vertbuf_create_with_format(&pos_format);
GPU_vertbuf_data_alloc(cache->proc_point_buf, cache->point_len);
cache->proc_point_buf = GPU_vertbuf_create_with_format(&format);
GPU_vertbuf_data_alloc(cache->proc_point_buf, cache->point_len);
GPUVertBufRaw pos_step;
GPU_vertbuf_attr_get_raw_data(cache->proc_point_buf, pos_id, &pos_step);
GPUVertBufRaw pos_step;
GPU_vertbuf_attr_get_raw_data(cache->proc_point_buf, pos_id, &pos_step);
GPUVertFormat length_format = {0};
uint length_id = GPU_vertformat_attr_add(
&length_format, "hairLength", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
if (edit != NULL && edit->pathcache != NULL) {
particle_batch_cache_fill_segments_proc_pos(edit->pathcache, edit->totcached, &pos_step);
}
else {
if ((psys->pathcache != NULL) &&
(!psys->childcache || (psys->part->draw & PART_DRAW_PARENT))) {
particle_batch_cache_fill_segments_proc_pos(psys->pathcache, psys->totpart, &pos_step);
cache->proc_length_buf = GPU_vertbuf_create_with_format(&length_format);
GPU_vertbuf_data_alloc(cache->proc_length_buf, cache->strands_len);
GPUVertBufRaw length_step;
GPU_vertbuf_attr_get_raw_data(cache->proc_length_buf, length_id, &length_step);
if (edit != NULL && edit->pathcache != NULL) {
particle_batch_cache_fill_segments_proc_pos(
edit->pathcache, edit->totcached, &pos_step, &length_step);
}
if (psys->childcache) {
const int child_count = psys->totchild * psys->part->disp / 100;
particle_batch_cache_fill_segments_proc_pos(psys->childcache, child_count, &pos_step);
else {
if ((psys->pathcache != NULL) &&
(!psys->childcache || (psys->part->draw & PART_DRAW_PARENT))) {
particle_batch_cache_fill_segments_proc_pos(
psys->pathcache, psys->totpart, &pos_step, &length_step);
}
if (psys->childcache) {
const int child_count = psys->totchild * psys->part->disp / 100;
particle_batch_cache_fill_segments_proc_pos(
psys->childcache, child_count, &pos_step, &length_step);
}
}
/* Create vbo immediately to bind to texture buffer. */
GPU_vertbuf_use(cache->proc_point_buf);
cache->point_tex = GPU_texture_create_from_vertbuf("part_point", cache->proc_point_buf);
}
/* Checking hair length seperatly, only allocating gpu memory when needed */
if (gpu_material && cache->proc_length_buf != NULL && cache->length_tex == NULL) {
ListBase gpu_attrs = GPU_material_attributes(gpu_material);
LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &gpu_attrs) {
if (attr->type == CD_HAIRLENGTH) {
GPU_vertbuf_use(cache->proc_length_buf);
cache->length_tex = GPU_texture_create_from_vertbuf("hair_length", cache->proc_length_buf);
break;
}
}
}
/* Create vbo immediately to bind to texture buffer. */
GPU_vertbuf_use(cache->proc_point_buf);
cache->point_tex = GPU_texture_create_from_vertbuf("part_point", cache->proc_point_buf);
}
static void particle_batch_cache_ensure_pos_and_seg(PTCacheEdit *edit,
@ -1649,6 +1679,7 @@ bool particles_ensure_procedural_data(Object *object,
ParticleSystem *psys,
ModifierData *md,
ParticleHairCache **r_hair_cache,
GPUMaterial *gpu_material,
int subdiv,
int thickness_res)
{
@ -1666,9 +1697,9 @@ bool particles_ensure_procedural_data(Object *object,
(*r_hair_cache)->final[subdiv].strands_res = 1 << (part->draw_step + subdiv);
/* Refreshed on combing and simulation. */
if ((*r_hair_cache)->proc_point_buf == NULL) {
if ((*r_hair_cache)->proc_point_buf == NULL || (gpu_material && (*r_hair_cache)->length_tex == NULL)) {
ensure_seg_pt_count(source.edit, source.psys, &cache->hair);
particle_batch_cache_ensure_procedural_pos(source.edit, source.psys, &cache->hair);
particle_batch_cache_ensure_procedural_pos(source.edit, source.psys, &cache->hair, gpu_material);
need_ft_update = true;
}

View File

@ -29,6 +29,7 @@ struct Object;
struct ParticleSystem;
struct RegionView3D;
struct ViewLayer;
struct GPUMaterial;
#define UBO_FIRST_COLOR colorWire
#define UBO_LAST_COLOR colorUVShadow
@ -175,7 +176,8 @@ bool DRW_object_axis_orthogonal_to_view(struct Object *ob, int axis);
struct DRWShadingGroup *DRW_shgroup_hair_create_sub(struct Object *object,
struct ParticleSystem *psys,
struct ModifierData *md,
struct DRWShadingGroup *shgrp);
struct DRWShadingGroup *shgrp,
struct GPUMaterial *gpu_material);
struct GPUVertBuf *DRW_hair_pos_buffer_get(struct Object *object,
struct ParticleSystem *psys,
struct ModifierData *md);

View File

@ -41,6 +41,7 @@
#include "GPU_shader.h"
#include "GPU_texture.h"
#include "GPU_vertex_buffer.h"
#include "GPU_material.h"
#include "draw_hair_private.h"
#include "draw_shader.h"
@ -173,17 +174,17 @@ static void drw_hair_particle_cache_update_transform_feedback(ParticleHairCache
}
static ParticleHairCache *drw_hair_particle_cache_get(
Object *object, ParticleSystem *psys, ModifierData *md, int subdiv, int thickness_res)
Object *object, ParticleSystem *psys, ModifierData *md, GPUMaterial* gpu_material, int subdiv, int thickness_res)
{
bool update;
ParticleHairCache *cache;
if (psys) {
/* Old particle hair. */
update = particles_ensure_procedural_data(object, psys, md, &cache, subdiv, thickness_res);
update = particles_ensure_procedural_data(object, psys, md, &cache, gpu_material, subdiv, thickness_res);
}
else {
/* New hair object. */
update = hair_ensure_procedural_data(object, &cache, subdiv, thickness_res);
update = hair_ensure_procedural_data(object, &cache, gpu_material, subdiv, thickness_res);
}
if (update) {
@ -206,7 +207,7 @@ GPUVertBuf *DRW_hair_pos_buffer_get(Object *object, ParticleSystem *psys, Modifi
int subdiv = scene->r.hair_subdiv;
int thickness_res = (scene->r.hair_type == SCE_HAIR_SHAPE_STRAND) ? 1 : 2;
ParticleHairCache *cache = drw_hair_particle_cache_get(object, psys, md, subdiv, thickness_res);
ParticleHairCache *cache = drw_hair_particle_cache_get(object, psys, md, NULL, subdiv, thickness_res);
return cache->final[subdiv].proc_buf;
}
@ -248,7 +249,8 @@ void DRW_hair_duplimat_get(Object *object,
DRWShadingGroup *DRW_shgroup_hair_create_sub(Object *object,
ParticleSystem *psys,
ModifierData *md,
DRWShadingGroup *shgrp_parent)
DRWShadingGroup *shgrp_parent,
GPUMaterial* gpu_material)
{
const DRWContextState *draw_ctx = DRW_context_state_get();
Scene *scene = draw_ctx->scene;
@ -258,7 +260,7 @@ DRWShadingGroup *DRW_shgroup_hair_create_sub(Object *object,
int thickness_res = (scene->r.hair_type == SCE_HAIR_SHAPE_STRAND) ? 1 : 2;
ParticleHairCache *hair_cache = drw_hair_particle_cache_get(
object, psys, md, subdiv, thickness_res);
object, psys, md, gpu_material, subdiv, thickness_res);
DRWShadingGroup *shgrp = DRW_shgroup_create_sub(shgrp_parent);
@ -308,6 +310,8 @@ DRWShadingGroup *DRW_shgroup_hair_create_sub(Object *object,
}
DRW_shgroup_uniform_texture(shgrp, "hairPointBuffer", hair_cache->final[subdiv].proc_tex);
if (hair_cache->length_tex)
DRW_shgroup_uniform_texture(shgrp, "hairLen", hair_cache->length_tex);
DRW_shgroup_uniform_int(shgrp, "hairStrandsRes", &hair_cache->final[subdiv].strands_res, 1);
DRW_shgroup_uniform_int_copy(shgrp, "hairThicknessRes", thickness_res);
DRW_shgroup_uniform_float_copy(shgrp, "hairRadShape", hair_rad_shape);

View File

@ -66,6 +66,10 @@ typedef struct ParticleHairCache {
GPUVertBuf *proc_strand_buf;
GPUTexture *strand_tex;
/* Hair Length */
GPUVertBuf *proc_length_buf;
GPUTexture *length_tex;
GPUVertBuf *proc_strand_seg_buf;
GPUTexture *strand_seg_tex;
@ -93,11 +97,13 @@ bool particles_ensure_procedural_data(struct Object *object,
struct ParticleSystem *psys,
struct ModifierData *md,
struct ParticleHairCache **r_hair_cache,
struct GPUMaterial *gpu_material,
int subdiv,
int thickness_res);
bool hair_ensure_procedural_data(struct Object *object,
struct ParticleHairCache **r_hair_cache,
struct GPUMaterial *gpu_material,
int subdiv,
int thickness_res);

View File

@ -210,6 +210,12 @@ void hair_get_pos_tan_binor_time(bool is_persp,
}
}
float hair_get_customdata_float(const samplerBuffer cd_buf)
{
int id = hair_get_strand_id();
return texelFetch(cd_buf, id).r;
}
vec2 hair_get_customdata_vec2(const samplerBuffer cd_buf)
{
int id = hair_get_strand_id();

View File

@ -656,6 +656,8 @@ static const char *attr_prefix_get(CustomDataType type)
return "c";
case CD_AUTO_FROM_NAME:
return "a";
case CD_HAIRLENGTH:
return "hl";
default:
BLI_assert_msg(0, "GPUVertAttr Prefix type not found : This should not happen!");
return "";
@ -675,7 +677,12 @@ static char *code_generate_interface(GPUNodeGraph *graph, int builtins)
BLI_dynstr_append(ds, "\n");
LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph->attributes) {
BLI_dynstr_appendf(ds, "%s var%d;\n", gpu_data_type_to_string(attr->gputype), attr->id);
if (attr->type == CD_HAIRLENGTH) {
BLI_dynstr_appendf(ds, "float var%d;\n", attr->id);
}
else {
BLI_dynstr_appendf(ds, "%s var%d;\n", gpu_data_type_to_string(attr->gputype), attr->id);
}
}
if (builtins & GPU_BARYCENTRIC_TEXCO) {
BLI_dynstr_append(ds, "vec2 barycentricTexCo;\n");
@ -711,6 +718,11 @@ static char *code_generate_vertex(GPUNodeGraph *graph,
BLI_dynstr_append(ds, datatoc_gpu_shader_common_obinfos_lib_glsl);
BLI_dynstr_append(ds, "DEFINE_ATTR(vec4, orco);\n");
}
if (attr->type == CD_HAIRLENGTH)
{
BLI_dynstr_append(ds, datatoc_gpu_shader_common_obinfos_lib_glsl);
BLI_dynstr_append(ds, "DEFINE_ATTR(float, hairLen);\n");
}
else if (attr->name[0] == '\0') {
BLI_dynstr_appendf(ds, "DEFINE_ATTR(%s, %s);\n", type_str, prefix);
BLI_dynstr_appendf(ds, "#define att%d %s\n", attr->id, prefix);
@ -755,6 +767,10 @@ static char *code_generate_vertex(GPUNodeGraph *graph,
BLI_dynstr_appendf(
ds, " var%d = orco_get(position, modelmatinv, OrcoTexCoFactors, orco);\n", attr->id);
}
else if (attr->type == CD_HAIRLENGTH) {
BLI_dynstr_appendf(
ds, " var%d = hair_len_get(hair_get_strand_id(), hairLen);\n", attr->id);
}
else {
const char *type_str = gpu_data_type_to_string(attr->gputype);
BLI_dynstr_appendf(ds, " var%d = GET_ATTR(%s, att%d);\n", attr->id, type_str, attr->id);

View File

@ -42,6 +42,11 @@ vec3 orco_get(vec3 local_pos, mat4 modelmatinv, vec4 orco_madd[2], const sampler
return orco_madd[0].xyz + orco * orco_madd[1].xyz;
}
float hair_len_get(int id, const samplerBuffer len)
{
return texelFetch(len, id).x;
}
vec4 tangent_get(const samplerBuffer attr, mat3 normalmat)
{
/* Unsupported */
@ -71,6 +76,11 @@ vec3 orco_get(vec3 local_pos, mat4 modelmatinv, vec4 orco_madd[2], vec4 orco)
}
}
float hair_len_get(int id, const float len)
{
return len;
}
vec4 tangent_get(vec4 attr, mat3 normalmat)
{
vec4 tangent;

View File

@ -10,12 +10,15 @@ float wang_hash_noise(uint s)
return fract(float(s) / 4294967296.0);
}
void node_hair_info(out float is_strand,
void node_hair_info(float hair_length,
out float is_strand,
out float intercept,
out float length,
out float thickness,
out vec3 tangent,
out float random)
{
length = hair_length;
#ifdef HAIR_SHADER
is_strand = 1.0;
intercept = hairTime;

View File

@ -84,7 +84,8 @@ typedef struct CustomData {
* MUST be >= CD_NUMTYPES, but we can't use a define here.
* Correct size is ensured in CustomData_update_typemap assert().
*/
int typemap[51];
int typemap[52];
char _pad[4];
/** Number of layers, size of layers array. */
int totlayer, maxlayer;
/** In editmode, total size of all data layers. */
@ -166,7 +167,9 @@ typedef enum CustomDataType {
CD_PROP_BOOL = 50,
CD_NUMTYPES = 51,
CD_HAIRLENGTH = 51,
CD_NUMTYPES = 52,
} CustomDataType;
/* Bits for CustomDataMask */
@ -220,6 +223,8 @@ typedef enum CustomDataType {
#define CD_MASK_PROP_FLOAT2 (1ULL << CD_PROP_FLOAT2)
#define CD_MASK_PROP_BOOL (1ULL << CD_PROP_BOOL)
#define CD_MASK_HAIRLENGTH (1ULL << CD_HAIRLENGTH)
/** Multires loop data. */
#define CD_MASK_MULTIRES_GRIDS (CD_MASK_MDISPS | CD_GRID_PAINT_MASK)

View File

@ -22,6 +22,7 @@
static bNodeSocketTemplate outputs[] = {
{SOCK_FLOAT, N_("Is Strand"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
{SOCK_FLOAT, N_("Intercept"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
{SOCK_FLOAT, N_("Length"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
{SOCK_FLOAT, N_("Thickness"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
{SOCK_VECTOR, N_("Tangent Normal"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
// { SOCK_FLOAT, 0, N_("Fade"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
@ -35,7 +36,11 @@ static int node_shader_gpu_hair_info(GPUMaterial *mat,
GPUNodeStack *in,
GPUNodeStack *out)
{
return GPU_stack_link(mat, node, "node_hair_info", in, out);
/* Length: don't request length if not needed. */
const static float zero = 0;
GPUNodeLink *length_link = (!out[2].hasoutput) ? GPU_constant(&zero) :
GPU_attribute(mat, CD_HAIRLENGTH, "");
return GPU_stack_link(mat, node, "node_hair_info", in, out, length_link);
}
/* node type definition */