Mesh: Rewrite split edges algorithm #110661

Merged
Hans Goudey merged 78 commits from HooglyBoogly/blender:split-edges-rewrite-2 into main 2023-08-30 14:23:49 +02:00
22 changed files with 465 additions and 130 deletions
Showing only changes of commit 6f82af0458 - Show all commits

View File

@ -18,6 +18,7 @@
#include "DNA_customdata_types.h"
#include "DNA_meshdata_types.h"
#include "BLI_bit_vector.hh"
#include "BLI_bitmap.h"
#include "BLI_color.hh"
#include "BLI_endian_switch.h"
@ -61,6 +62,7 @@
/* only for customdata_data_transfer_interp_normal_normals */
#include "data_transfer_intern.h"
using blender::BitVector;
using blender::float2;
using blender::ImplicitSharingInfo;
using blender::IndexRange;
@ -3909,16 +3911,17 @@ void CustomData_bmesh_set_default(CustomData *data, void **block)
}
}
static bool customdata_layer_copy_check(const CustomDataLayer &source, const CustomDataLayer &dest)
{
return source.type == dest.type && STREQ(source.name, dest.name);
}
void CustomData_bmesh_copy_data_exclude_by_type(const CustomData *source,
CustomData *dest,
void *src_block,
void **dest_block,
const eCustomDataMask mask_exclude)
{
/* Note that having a version of this function without a 'mask_exclude'
* would cause too much duplicate code, so add a check instead. */
const bool no_mask = (mask_exclude == 0);
if (*dest_block == nullptr) {
CustomData_bmesh_alloc_block(dest, dest_block);
if (*dest_block) {
@ -3926,51 +3929,41 @@ void CustomData_bmesh_copy_data_exclude_by_type(const CustomData *source,
}
}
/* copies a layer at a time */
int dest_i = 0;
for (int src_i = 0; src_i < source->totlayer; src_i++) {
BitVector<> copied_layers(dest->totlayer);
/* find the first dest layer with type >= the source type
* (this should work because layers are ordered by type)
*/
while (dest_i < dest->totlayer && dest->layers[dest_i].type < source->layers[src_i].type) {
CustomData_bmesh_set_default_n(dest, dest_block, dest_i);
dest_i++;
for (int layer_src_i : IndexRange(source->totlayer)) {
const CustomDataLayer &layer_src = source->layers[layer_src_i];
if (CD_TYPE_AS_MASK(layer_src.type) & mask_exclude) {
continue;
}
/* if there are no more dest layers, we're done */
if (dest_i >= dest->totlayer) {
return;
}
for (int layer_dst_i : IndexRange(dest->totlayer)) {
CustomDataLayer &layer_dst = dest->layers[layer_dst_i];
/* if we found a matching layer, copy the data */
if (dest->layers[dest_i].type == source->layers[src_i].type &&
STREQ(dest->layers[dest_i].name, source->layers[src_i].name))
{
if (no_mask || ((CD_TYPE_AS_MASK(dest->layers[dest_i].type) & mask_exclude) == 0)) {
const void *src_data = POINTER_OFFSET(src_block, source->layers[src_i].offset);
void *dest_data = POINTER_OFFSET(*dest_block, dest->layers[dest_i].offset);
const LayerTypeInfo *typeInfo = layerType_getInfo(
eCustomDataType(source->layers[src_i].type));
if (typeInfo->copy) {
typeInfo->copy(src_data, dest_data, 1);
}
else {
memcpy(dest_data, src_data, typeInfo->size);
}
if (!customdata_layer_copy_check(layer_src, layer_dst)) {
continue;
}
/* if there are multiple source & dest layers of the same type,
* we don't want to copy all source layers to the same dest, so
* increment dest_i
*/
dest_i++;
copied_layers[layer_dst_i].set(true);
const void *src_data = POINTER_OFFSET(src_block, layer_src.offset);
void *dest_data = POINTER_OFFSET(*dest_block, layer_dst.offset);
const LayerTypeInfo *typeInfo = layerType_getInfo(eCustomDataType(layer_src.type));
if (typeInfo->copy) {
typeInfo->copy(src_data, dest_data, 1);
}
else {
memcpy(dest_data, src_data, typeInfo->size);
}
}
}
while (dest_i < dest->totlayer) {
CustomData_bmesh_set_default_n(dest, dest_block, dest_i);
dest_i++;
/* Initialize dest layers that weren't in source. */
for (int layer_dst_i : IndexRange(dest->totlayer)) {
if (!copied_layers[layer_dst_i]) {
CustomData_bmesh_set_default_n(dest, dest_block, layer_dst_i);
}
}
}

View File

@ -495,6 +495,7 @@ set(GLSL_SRC
engines/eevee_next/shaders/eevee_geom_curves_vert.glsl
engines/eevee_next/shaders/eevee_geom_gpencil_vert.glsl
engines/eevee_next/shaders/eevee_geom_mesh_vert.glsl
engines/eevee_next/shaders/eevee_geom_point_cloud_vert.glsl
engines/eevee_next/shaders/eevee_geom_world_vert.glsl
engines/eevee_next/shaders/eevee_hiz_debug_frag.glsl
engines/eevee_next/shaders/eevee_hiz_update_comp.glsl

View File

@ -23,6 +23,8 @@
#include "eevee_engine.h"
#include "eevee_instance.hh"
#include "DNA_particle_types.h"
namespace blender::eevee {
/* -------------------------------------------------------------------- */
@ -175,7 +177,7 @@ void Instance::scene_sync()
void Instance::object_sync(Object *ob)
{
const bool is_renderable_type = ELEM(
ob->type, OB_CURVES, OB_GPENCIL_LEGACY, OB_MESH, OB_LAMP, OB_LIGHTPROBE);
ob->type, OB_CURVES, OB_GPENCIL_LEGACY, OB_MESH, OB_POINTCLOUD, OB_LAMP, OB_LIGHTPROBE);
const int ob_visibility = DRW_object_visibility_in_active_context(ob);
const bool partsys_is_visible = (ob_visibility & OB_VISIBLE_PARTICLES) != 0 &&
(ob->type == OB_MESH);
@ -193,9 +195,24 @@ void Instance::object_sync(Object *ob)
ObjectHandle &ob_handle = sync.sync_object(ob);
if (partsys_is_visible && ob != DRW_context_state_get()->object_edit) {
int sub_key = 1;
LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) {
if (md->type == eModifierType_ParticleSystem) {
sync.sync_curves(ob, ob_handle, res_handle, md);
ParticleSystem *particle_sys = reinterpret_cast<ParticleSystemModifierData *>(md)->psys;
ParticleSettings *part_settings = particle_sys->part;
const int draw_as = (part_settings->draw_as == PART_DRAW_REND) ? part_settings->ren_as :
part_settings->draw_as;
if (draw_as != PART_DRAW_PATH ||
!DRW_object_is_visible_psys_in_active_context(ob, particle_sys)) {
continue;
}
ObjectHandle _ob_handle = {0};
_ob_handle.object_key = ObjectKey(ob_handle.object_key.ob, sub_key++);
_ob_handle.recalc = particle_sys->recalc;
ResourceHandle _res_handle = manager->resource_handle(float4x4(ob->object_to_world));
sync.sync_curves(ob, _ob_handle, _res_handle, md, particle_sys);
}
}
}
@ -208,6 +225,8 @@ void Instance::object_sync(Object *ob)
case OB_MESH:
sync.sync_mesh(ob, ob_handle, res_handle, ob_ref);
break;
case OB_POINTCLOUD:
sync.sync_point_cloud(ob, ob_handle, res_handle, ob_ref);
case OB_VOLUME:
break;
case OB_CURVES:
@ -268,9 +287,7 @@ void Instance::render_sync()
/* TODO: Remove old draw manager calls. */
DRW_render_instance_buffer_finish();
/* Also we weed to have a correct FBO bound for #DRW_hair_update */
// GPU_framebuffer_bind();
// DRW_hair_update();
DRW_curves_update();
}
bool Instance::do_probe_sync() const

View File

@ -39,6 +39,7 @@ enum eMaterialPipeline {
enum eMaterialGeometry {
MAT_GEOM_MESH = 0,
MAT_GEOM_POINT_CLOUD,
MAT_GEOM_CURVES,
MAT_GEOM_GPENCIL,
MAT_GEOM_VOLUME,
@ -102,6 +103,8 @@ static inline eMaterialGeometry to_material_geometry(const Object *ob)
return MAT_GEOM_VOLUME;
case OB_GPENCIL_LEGACY:
return MAT_GEOM_GPENCIL;
case OB_POINTCLOUD:
return MAT_GEOM_POINT_CLOUD;
default:
return MAT_GEOM_MESH;
}

View File

@ -127,6 +127,8 @@ void Sampling::step()
/* TODO de-correlate. */
data_.dimensions[SAMPLING_AO_U] = r[0];
data_.dimensions[SAMPLING_AO_V] = r[1];
/* TODO de-correlate. */
data_.dimensions[SAMPLING_CURVES_U] = r[0];
}
{
/* Using leaped Halton sequence so we can reused the same primes as lens. */

View File

@ -314,6 +314,7 @@ void ShaderModule::material_create_info_ammend(GPUMaterial *gpumat, GPUCodegenOu
case MAT_GEOM_MESH:
/** Noop. */
break;
case MAT_GEOM_POINT_CLOUD:
case MAT_GEOM_CURVES:
/** Hair attributes come from sampler buffer. Transfer attributes to sampler. */
for (auto &input : info.vertex_inputs_) {
@ -382,8 +383,7 @@ void ShaderModule::material_create_info_ammend(GPUMaterial *gpumat, GPUCodegenOu
}
{
/* Only mesh and curves support vertex displacement for now. */
if (ELEM(geometry_type, MAT_GEOM_MESH, MAT_GEOM_CURVES, MAT_GEOM_GPENCIL)) {
if (!ELEM(geometry_type, MAT_GEOM_WORLD, MAT_GEOM_VOLUME)) {
vert_gen << "vec3 nodetree_displacement()\n";
vert_gen << "{\n";
vert_gen << ((codegen.displacement) ? codegen.displacement : "return vec3(0);\n");
@ -444,6 +444,9 @@ void ShaderModule::material_create_info_ammend(GPUMaterial *gpumat, GPUCodegenOu
case MAT_GEOM_MESH:
info.additional_info("eevee_geom_mesh");
break;
case MAT_GEOM_POINT_CLOUD:
info.additional_info("eevee_geom_point_cloud");
break;
}
/* Pipeline Info. */

View File

@ -101,6 +101,7 @@ enum eSamplingDimension : uint32_t {
SAMPLING_RAYTRACE_X = 18u,
SAMPLING_AO_U = 19u,
SAMPLING_AO_V = 20u,
SAMPLING_CURVES_U = 21u,
};
/**
@ -1094,6 +1095,14 @@ float4 utility_tx_sample(sampler2DArray util_tx, float2 uv, float layer)
{
return textureLod(util_tx, float3(uv, layer), 0.0);
}
/* Sample at uv position but with scale and bias so that uv space bounds lie on texel centers. */
float4 utility_tx_sample_lut(sampler2DArray util_tx, float2 uv, float layer)
{
/* Scale and bias coordinates, for correct filtered lookup. */
uv = uv * ((UTIL_TEX_SIZE - 1.0) / UTIL_TEX_SIZE) + (0.5 / UTIL_TEX_SIZE);
return textureLod(util_tx, float3(uv, layer), 0.0);
}
#endif
/** \} */

View File

@ -18,6 +18,8 @@
#include "DNA_modifier_types.h"
#include "DNA_particle_types.h"
#include "draw_common.hh"
#include "eevee_instance.hh"
namespace blender::eevee {
@ -154,6 +156,49 @@ void SyncModule::sync_mesh(Object *ob,
/** \} */
/* -------------------------------------------------------------------- */
/** \name Point Cloud
* \{ */
void SyncModule::sync_point_cloud(Object *ob,
ObjectHandle &ob_handle,
ResourceHandle res_handle,
const ObjectRef &ob_ref)
{
int material_slot = 1;
bool has_motion = inst_.velocity.step_object_sync(
ob, ob_handle.object_key, res_handle, ob_handle.recalc);
Material &material = inst_.materials.material_get(
ob, has_motion, material_slot - 1, MAT_GEOM_POINT_CLOUD);
auto drawcall_add = [&](MaterialPass &matpass) {
if (matpass.sub_pass == nullptr) {
return;
}
PassMain::Sub &object_pass = matpass.sub_pass->sub("Point Cloud Sub Pass");
GPUBatch *geometry = point_cloud_sub_pass_setup(object_pass, ob, matpass.gpumat);
object_pass.draw(geometry, res_handle);
};
drawcall_add(material.shading);
drawcall_add(material.prepass);
drawcall_add(material.shadow);
inst_.cryptomatte.sync_object(ob, res_handle);
GPUMaterial *gpu_material =
inst_.materials.material_array_get(ob, has_motion).gpu_materials[material_slot - 1];
::Material *mat = GPU_material_get_material(gpu_material);
inst_.cryptomatte.sync_material(mat);
bool is_caster = material.shadow.sub_pass != nullptr;
bool is_alpha_blend = material.is_alpha_blend_transparent;
inst_.shadows.sync_object(ob_handle, res_handle, is_caster, is_alpha_blend);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name GPencil
* \{ */
@ -298,52 +343,41 @@ void SyncModule::sync_gpencil(Object *ob, ObjectHandle &ob_handle, ResourceHandl
/** \name Hair
* \{ */
static void shgroup_curves_call(MaterialPass &matpass,
Object *ob,
ParticleSystem *part_sys = nullptr,
ModifierData *modifier_data = nullptr)
{
UNUSED_VARS(ob, modifier_data);
if (matpass.sub_pass == nullptr) {
return;
}
if (part_sys != nullptr) {
// DRW_shgroup_hair_create_sub(ob, part_sys, modifier_data, matpass.sub_pass, matpass.gpumat);
}
else {
// DRW_shgroup_curves_create_sub(ob, matpass.sub_pass, matpass.gpumat);
}
}
void SyncModule::sync_curves(Object *ob,
ObjectHandle &ob_handle,
ResourceHandle res_handle,
ModifierData *modifier_data)
ModifierData *modifier_data,
ParticleSystem *particle_sys)
{
UNUSED_VARS(res_handle);
int mat_nr = CURVES_MATERIAL_NR;
ParticleSystem *part_sys = nullptr;
if (modifier_data != nullptr) {
part_sys = reinterpret_cast<ParticleSystemModifierData *>(modifier_data)->psys;
if (!DRW_object_is_visible_psys_in_active_context(ob, part_sys)) {
return;
}
ParticleSettings *part_settings = part_sys->part;
const int draw_as = (part_settings->draw_as == PART_DRAW_REND) ? part_settings->ren_as :
part_settings->draw_as;
if (draw_as != PART_DRAW_PATH) {
return;
}
mat_nr = part_settings->omat;
if (particle_sys != nullptr) {
mat_nr = particle_sys->part->omat;
}
bool has_motion = inst_.velocity.step_object_sync(ob, ob_handle.object_key, ob_handle.recalc);
bool has_motion = inst_.velocity.step_object_sync(
ob, ob_handle.object_key, res_handle, ob_handle.recalc, modifier_data, particle_sys);
Material &material = inst_.materials.material_get(ob, has_motion, mat_nr - 1, MAT_GEOM_CURVES);
shgroup_curves_call(material.shading, ob, part_sys, modifier_data);
shgroup_curves_call(material.prepass, ob, part_sys, modifier_data);
shgroup_curves_call(material.shadow, ob, part_sys, modifier_data);
auto drawcall_add = [&](MaterialPass &matpass) {
if (matpass.sub_pass == nullptr) {
return;
}
if (particle_sys != nullptr) {
PassMain::Sub &sub_pass = matpass.sub_pass->sub("Hair SubPass");
GPUBatch *geometry = hair_sub_pass_setup(
sub_pass, inst_.scene, ob, particle_sys, modifier_data, matpass.gpumat);
sub_pass.draw(geometry, res_handle);
}
else {
PassMain::Sub &sub_pass = matpass.sub_pass->sub("Curves SubPass");
GPUBatch *geometry = curves_sub_pass_setup(sub_pass, inst_.scene, ob, matpass.gpumat);
sub_pass.draw(geometry, res_handle);
}
};
drawcall_add(material.shading);
drawcall_add(material.prepass);
drawcall_add(material.shadow);
inst_.cryptomatte.sync_object(ob, res_handle);
GPUMaterial *gpu_material =
@ -351,9 +385,6 @@ void SyncModule::sync_curves(Object *ob,
::Material *mat = GPU_material_get_material(gpu_material);
inst_.cryptomatte.sync_material(mat);
/* TODO(fclem) Hair velocity. */
// shading_passes.velocity.gpencil_add(ob, ob_handle);
bool is_caster = material.shadow.sub_pass != nullptr;
bool is_alpha_blend = material.is_alpha_blend_transparent;
inst_.shadows.sync_object(ob_handle, res_handle, is_caster, is_alpha_blend);

View File

@ -40,15 +40,15 @@ struct ObjectKey {
Object *parent;
/** Dupli objects recursive unique identifier */
int id[MAX_DUPLI_RECUR];
/** If object uses particle system hair. */
bool use_particle_hair;
/** Used for particle system hair. */
int sub_key_;
#ifdef DEBUG
char name[64];
#endif
ObjectKey() : ob(nullptr), parent(nullptr){};
ObjectKey(Object *ob_, Object *parent_, int id_[MAX_DUPLI_RECUR], bool use_particle_hair_)
: ob(ob_), parent(parent_), use_particle_hair(use_particle_hair_)
ObjectKey(Object *ob_, Object *parent_, int id_[MAX_DUPLI_RECUR], int sub_key_ = 0)
: ob(ob_), parent(parent_), sub_key_(sub_key_)
{
if (id_) {
memcpy(id, id_, sizeof(id));
@ -67,16 +67,19 @@ struct ObjectKey {
break;
}
}
if (sub_key_ != 0) {
hash_value = BLI_ghashutil_combine_hash(hash_value, sub_key_);
}
#ifdef DEBUG
STRNCPY(name, ob->id.name);
#endif
}
ObjectKey(Object *ob, DupliObject *dupli, Object *parent)
: ObjectKey(ob, parent, dupli ? dupli->persistent_id : nullptr, false){};
ObjectKey(Object *ob, DupliObject *dupli, Object *parent, int sub_key_ = 0)
: ObjectKey(ob, parent, dupli ? dupli->persistent_id : nullptr, sub_key_){};
ObjectKey(Object *ob)
: ObjectKey(ob, DRW_object_get_dupli(ob), DRW_object_get_dupli_parent(ob)){};
ObjectKey(Object *ob, int sub_key_ = 0)
: ObjectKey(ob, DRW_object_get_dupli(ob), DRW_object_get_dupli_parent(ob), sub_key_){};
uint64_t hash() const
{
@ -91,8 +94,8 @@ struct ObjectKey {
if (parent != k.parent) {
return (parent < k.parent);
}
if (use_particle_hair != k.use_particle_hair) {
return (use_particle_hair < k.use_particle_hair);
if (sub_key_ != k.sub_key_) {
return (sub_key_ < k.sub_key_);
}
return memcmp(id, k.id, sizeof(id)) < 0;
}
@ -105,7 +108,7 @@ struct ObjectKey {
if (parent != k.parent) {
return false;
}
if (use_particle_hair != k.use_particle_hair) {
if (sub_key_ != k.sub_key_) {
return false;
}
return memcmp(id, k.id, sizeof(id)) == 0;
@ -164,11 +167,16 @@ class SyncModule {
ObjectHandle &ob_handle,
ResourceHandle res_handle,
const ObjectRef &ob_ref);
void sync_point_cloud(Object *ob,
ObjectHandle &ob_handle,
ResourceHandle res_handle,
const ObjectRef &ob_ref);
void sync_gpencil(Object *ob, ObjectHandle &ob_handle, ResourceHandle res_handle);
void sync_curves(Object *ob,
ObjectHandle &ob_handle,
ResourceHandle res_handle,
ModifierData *modifier_data = nullptr);
ModifierData *modifier_data = nullptr,
ParticleSystem *particle_sys = nullptr);
void sync_light_probe(Object *ob, ObjectHandle &ob_handle);
};

View File

@ -15,8 +15,11 @@
#include "BKE_object.h"
#include "BLI_map.hh"
#include "DEG_depsgraph_query.h"
#include "DNA_particle_types.h"
#include "DNA_rigidbody_types.h"
#include "draw_cache_impl.h"
#include "eevee_instance.hh"
// #include "eevee_renderpasses.hh"
#include "eevee_shader.hh"
@ -85,7 +88,9 @@ void VelocityModule::step_camera_sync()
bool VelocityModule::step_object_sync(Object *ob,
ObjectKey &object_key,
ResourceHandle resource_handle,
int /*IDRecalcFlag*/ recalc)
int /*IDRecalcFlag*/ recalc,
ModifierData *modifier_data /*= nullptr*/,
ParticleSystem *particle_sys /*= nullptr*/)
{
bool has_motion = object_has_velocity(ob) || (recalc & ID_RECALC_TRANSFORM);
/* NOTE: Fragile. This will only work with 1 frame of lag since we can't record every geometry
@ -105,7 +110,7 @@ bool VelocityModule::step_object_sync(Object *ob,
VelocityObjectData &vel = velocity_map.lookup_or_add_default(object_key);
vel.obj.ofs[step_] = object_steps_usage[step_]++;
vel.obj.resource_id = resource_handle.resource_index();
vel.id = (ID *)ob->data;
vel.id = particle_sys ? &particle_sys->part->id : &ob->id;
object_steps[step_]->get_or_resize(vel.obj.ofs[step_]) = float4x4_view(ob->object_to_world);
if (step_ == STEP_CURRENT) {
/* Replace invalid steps. Can happen if object was hidden in one of those steps. */
@ -125,10 +130,17 @@ bool VelocityModule::step_object_sync(Object *ob,
if (has_deform) {
auto add_cb = [&]() {
VelocityGeometryData data;
if (particle_sys) {
data.pos_buf = DRW_hair_pos_buffer_get(ob, particle_sys, modifier_data);
return data;
}
switch (ob->type) {
case OB_CURVES:
data.pos_buf = DRW_curves_pos_buffer_get(ob);
break;
case OB_POINTCLOUD:
data.pos_buf = DRW_pointcloud_position_and_radius_buffer_get(ob);
break;
default:
data.pos_buf = DRW_cache_object_pos_vertbuf_get(ob);
break;
@ -173,7 +185,9 @@ bool VelocityModule::step_object_sync(Object *ob,
}
/* TODO(@fclem): Reset sampling here? Should ultimately be covered by depsgraph update tags. */
inst_.sampling.reset();
/* NOTE(Miguel Pozo): Disable, since is_deform is always true for objects with particle
* modifiers, and this causes the renderer to get stuck at sample 1. */
// inst_.sampling.reset();
return true;
}

View File

@ -108,7 +108,9 @@ class VelocityModule {
bool step_object_sync(Object *ob,
ObjectKey &object_key,
ResourceHandle resource_handle,
int recalc = 0);
int recalc = 0,
ModifierData *modifier_data = nullptr,
ParticleSystem *particle_sys = nullptr);
/* Moves next frame data to previous frame data. Nullify next frame data. */
void step_swap();

View File

@ -60,6 +60,56 @@ vec3 attr_load_uv(vec3 attr)
/** \} */
#elif defined(MAT_GEOM_POINT_CLOUD)
/* -------------------------------------------------------------------- */
/** \name Point Cloud
*
* Point Cloud objects loads attributes from buffers through sampler buffers.
* \{ */
# pragma BLENDER_REQUIRE(common_pointcloud_lib.glsl)
# ifdef OBINFO_LIB
vec3 attr_load_orco(vec4 orco)
{
vec3 P = pointcloud_get_pos();
vec3 lP = transform_point(ModelMatrixInverse, P);
return OrcoTexCoFactors[0].xyz + lP * OrcoTexCoFactors[1].xyz;
}
# endif
vec4 attr_load_tangent(samplerBuffer cd_buf)
{
return pointcloud_get_customdata_vec4(cd_buf);
}
vec3 attr_load_uv(samplerBuffer cd_buf)
{
return pointcloud_get_customdata_vec3(cd_buf);
}
vec4 attr_load_color(samplerBuffer cd_buf)
{
return pointcloud_get_customdata_vec4(cd_buf);
}
vec4 attr_load_vec4(samplerBuffer cd_buf)
{
return pointcloud_get_customdata_vec4(cd_buf);
}
vec3 attr_load_vec3(samplerBuffer cd_buf)
{
return pointcloud_get_customdata_vec3(cd_buf);
}
vec2 attr_load_vec2(samplerBuffer cd_buf)
{
return pointcloud_get_customdata_vec2(cd_buf);
}
float attr_load_float(samplerBuffer cd_buf)
{
return pointcloud_get_customdata_float(cd_buf);
}
/** \} */
#elif defined(MAT_GEOM_GPENCIL)
/* -------------------------------------------------------------------- */

View File

@ -16,8 +16,6 @@ void main()
init_interface();
vec3 T;
bool is_persp = (ProjectionMatrix[3][3] == 0.0);
hair_get_pos_tan_binor_time(is_persp,
ModelMatrixInverse,
@ -30,14 +28,15 @@ void main()
interp.curves_thickness,
interp.curves_time_width);
interp.N = cross(T, interp.curves_binormal);
interp.N = cross(interp.curves_tangent, interp.curves_binormal);
interp.curves_strand_id = hair_get_strand_id();
interp.barycentric_coords = hair_get_barycentric();
#ifdef MAT_VELOCITY
/* Due to the screen space nature of the vertex positioning, we compute only the motion of curve
* strand, not its cylinder. Otherwise we would add the rotation velocity. */
int vert_idx = hair_get_base_id();
vec3 prv, nxt, pos = texelFetch(hairPointBuffer, vert_idx).point_position;
vec3 prv, nxt;
vec3 pos = texelFetch(hairPointBuffer, vert_idx).point_position;
velocity_local_pos_get(pos, vert_idx, prv, nxt);
/* FIXME(fclem): Evaluating before displacement avoid displacement being treated as motion but
* ignores motion from animated displacement. Supporting animated displacement motion vectors

View File

@ -0,0 +1,48 @@
#pragma BLENDER_REQUIRE(gpu_shader_math_rotation_lib.glsl)
#pragma BLENDER_REQUIRE(common_pointcloud_lib.glsl)
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_attributes_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_nodetree_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_surf_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_velocity_lib.glsl)
void main()
{
DRW_VIEW_FROM_RESOURCE_ID;
#ifdef MAT_SHADOW
shadow_interp.view_id = drw_view_id;
#endif
init_interface();
point_cloud_interp.id = pointcloud_get_point_id();
pointcloud_get_pos_and_radius(point_cloud_interp.position, point_cloud_interp.radius);
pointcloud_get_pos_and_nor(interp.P, interp.N);
#ifdef MAT_SHADOW
/* Since point clouds always face the view, camera and shadow orientation don't match.
* Apply a bias to avoid self-shadow issues. */
/* TODO(fclem): remove multiplication here. Here only for keeping the size correct for now. */
float actual_radius = point_cloud_interp.radius * 0.01;
interp.P -= cameraVec(interp.P) * actual_radius;
#endif
#ifdef MAT_VELOCITY
vec3 lP = point_world_to_object(point_cloud_interp.position);
vec3 prv, nxt;
velocity_local_pos_get(lP, point_cloud_interp.id, prv, nxt);
/* FIXME(fclem): Evaluating before displacement avoid displacement being treated as motion but
* ignores motion from animated displacement. Supporting animated displacement motion vectors
* would require evaluating the nodetree multiple time with different nodetree UBOs evaluated at
* different times, but also with different attributes (maybe we could assume static attribute at
* least). */
velocity_vertex(prv, lP, nxt, motion.prev, motion.next);
#endif
init_globals();
attrib_load();
interp.P += nodetree_displacement();
gl_Position = point_world_to_ndc(interp.P);
}

View File

@ -235,26 +235,111 @@ float nodetree_thickness();
vec4 closure_to_rgba(Closure cl);
#endif
/* Stubs. */
vec2 btdf_lut(float a, float b, float c)
/* Fresnel monochromatic, perfect mirror */
float F_eta(float eta, float cos_theta)
{
return vec2(1, 0);
/* Compute fresnel reflectance without explicitly computing
* the refracted direction. */
float c = abs(cos_theta);
float g = eta * eta - 1.0 + c * c;
if (g > 0.0) {
g = sqrt(g);
float A = (g - c) / (g + c);
float B = (c * (g + c) - 1.0) / (c * (g - c) + 1.0);
return 0.5 * A * A * (1.0 + B * B);
}
/* Total internal reflections. */
return 1.0;
}
vec2 brdf_lut(float a, float b)
/* Simplified form of F_eta(eta, 1.0). */
float F0_from_ior(float eta)
{
return vec2(1, 0);
float A = (eta - 1.0) / (eta + 1.0);
return A * A;
}
vec3 F_brdf_multi_scatter(vec3 a, vec3 b, vec2 c)
/* Return the fresnel color from a precomputed LUT value (from brdf_lutb). */
vec3 F_brdf_single_scatter(vec3 f0, vec3 f90, vec2 lut)
{
return a;
return lut.y * f90 + lut.x * f0;
}
vec3 F_brdf_single_scatter(vec3 a, vec3 b, vec2 c)
/* Return the fresnel color from a precomputed LUT value (from brdf_lutb). */
vec3 F_brdf_multi_scatter(vec3 f0, vec3 f90, vec2 lut)
{
return a;
/**
* From "A Multiple-Scattering Microfacet Model for Real-Time Image-based Lighting"
* by Carmelo J. Fdez-Aguera
* https://jcgt.org/published/0008/01/03/paper.pdf
*/
vec3 FssEss = lut.y * f90 + lut.x * f0;
float Ess = lut.x + lut.y;
float Ems = 1.0 - Ess;
vec3 Favg = f0 + (1.0 - f0) / 21.0;
vec3 Fms = FssEss * Favg / (1.0 - (1.0 - Ess) * Favg);
/* We don't do anything special for diffuse surfaces because the principle bsdf
* does not care about energy conservation of the specular layer for dielectrics. */
return FssEss + Fms * Ems;
}
float F_eta(float a, float b)
vec2 brdf_lut(float cos_theta, float roughness)
{
return a;
#ifdef EEVEE_UTILITY_TX
return utility_tx_sample_lut(utility_tx, vec2(cos_theta, roughness), UTIL_BSDF_LAYER).rg;
#else
return vec2(1.0, 0.0);
#endif
}
vec2 btdf_lut(float cos_theta, float roughness, float ior)
{
if (ior <= 1e-5) {
return vec2(0.0);
}
if (ior >= 1.0) {
vec2 split_sum = brdf_lut(cos_theta, roughness);
float f0 = F0_from_ior(ior);
/* Baked IOR for GGX BRDF. */
const float specular = 1.0;
const float eta_brdf = (2.0 / (1.0 - sqrt(0.08 * specular))) - 1.0;
/* Avoid harsh transition coming from ior == 1. */
float f90 = fast_sqrt(saturate(f0 / (F0_from_ior(eta_brdf) * 0.25)));
float fresnel = F_brdf_single_scatter(vec3(f0), vec3(f90), split_sum).r;
/* Setting the BTDF to one is not really important since it is only used for multiscatter
* and it's already quite close to ground truth. */
float btdf = 1.0;
return vec2(btdf, fresnel);
}
/* IOR is sin of critical angle. */
float critical_cos = sqrt(1.0 - ior * ior);
vec3 coords;
coords.x = sqr(ior);
coords.y = cos_theta;
coords.y -= critical_cos;
coords.y /= (coords.y > 0.0) ? (1.0 - critical_cos) : critical_cos;
coords.y = coords.y * 0.5 + 0.5;
coords.z = roughness;
coords = saturate(coords);
float layer = coords.z * UTIL_BTDF_LAYER_COUNT;
float layer_floored = floor(layer);
#ifdef EEVEE_UTILITY_TX
coords.z = UTIL_BTDF_LAYER + layer_floored;
vec2 btdf_low = utility_tx_sample_lut(utility_tx, coords.xy, coords.z).rg;
vec2 btdf_high = utility_tx_sample_lut(utility_tx, coords.xy, coords.z + 1.0).rg;
/* Manual trilinear interpolation. */
vec2 btdf = mix(btdf_low, btdf_high, layer - layer_floored);
return btdf;
#else
return vec2(0.0);
#endif
}
void output_renderpass_color(int id, vec4 color)

View File

@ -2,6 +2,7 @@
#pragma BLENDER_REQUIRE(common_math_lib.glsl)
#pragma BLENDER_REQUIRE(gpu_shader_codegen_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_nodetree_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl)
#if defined(USE_BARYCENTRICS) && defined(GPU_FRAGMENT_SHADER) && defined(MAT_GEOM_MESH)
vec3 barycentric_distances_get()
@ -39,6 +40,18 @@ void init_globals_curves()
{
/* Shade as a cylinder. */
float cos_theta = interp.curves_time_width / interp.curves_thickness;
#if defined(GPU_FRAGMENT_SHADER) && defined(MAT_GEOM_CURVES)
if (hairThicknessRes == 1) {
/* Random cosine normal distribution on the hair surface. */
float noise = utility_tx_fetch(utility_tx, gl_FragCoord.xy, UTIL_BLUE_NOISE_LAYER).x;
# ifdef EEVEE_SAMPLING_DATA
/* Needs to check for SAMPLING_DATA,
* otherwise Surfel and World (?!?!) shader validation fails. */
noise = fract(noise + sampling_rng_1D_get(SAMPLING_CURVES_U));
# endif
cos_theta = noise * 2.0 - 1.0;
}
#endif
float sin_theta = sqrt(max(0.0, 1.0 - cos_theta * cos_theta));
g_data.N = g_data.Ni = normalize(interp.N * sin_theta + interp.curves_binormal * cos_theta);

View File

@ -20,6 +20,7 @@ GPU_SHADER_CREATE_INFO(eevee_sampling_data)
.storage_buf(SAMPLING_BUF_SLOT, Qualifier::READ, "SamplingData", "sampling_buf");
GPU_SHADER_CREATE_INFO(eevee_utility_texture)
.define("EEVEE_UTILITY_TX")
.sampler(RBUFS_UTILITY_TEX_SLOT, ImageType::FLOAT_2D_ARRAY, "utility_tx");
GPU_SHADER_CREATE_INFO(eevee_camera).uniform_buf(CAMERA_BUF_SLOT, "CameraData", "camera_buf");
@ -38,6 +39,25 @@ GPU_SHADER_CREATE_INFO(eevee_geom_mesh)
.vertex_source("eevee_geom_mesh_vert.glsl")
.additional_info("draw_modelmat_new", "draw_resource_id_varying", "draw_view");
GPU_SHADER_INTERFACE_INFO(eevee_surf_point_cloud_iface, "point_cloud_interp")
.smooth(Type::FLOAT, "radius")
.smooth(Type::VEC3, "position")
.flat(Type::INT, "id");
GPU_SHADER_CREATE_INFO(eevee_geom_point_cloud)
.additional_info("eevee_shared")
.define("MAT_GEOM_POINT_CLOUD")
.vertex_source("eevee_geom_point_cloud_vert.glsl")
.vertex_out(eevee_surf_point_cloud_iface)
/* TODO(Miguel Pozo): Remove once we get rid of old EEVEE. */
.define("pointRadius", "point_cloud_interp.radius")
.define("pointPosition", "point_cloud_interp.position")
.define("pointID", "point_cloud_interp.id")
.additional_info("draw_pointcloud_new",
"draw_modelmat_new",
"draw_resource_id_varying",
"draw_view");
GPU_SHADER_CREATE_INFO(eevee_geom_gpencil)
.additional_info("eevee_shared")
.define("MAT_GEOM_GPENCIL")
@ -48,10 +68,11 @@ GPU_SHADER_CREATE_INFO(eevee_geom_curves)
.additional_info("eevee_shared")
.define("MAT_GEOM_CURVES")
.vertex_source("eevee_geom_curves_vert.glsl")
.additional_info("draw_hair",
"draw_curves_infos",
.additional_info("draw_modelmat_new",
"draw_resource_id_varying",
"draw_resource_id_new");
"draw_view",
"draw_hair_new",
"draw_curves_infos");
GPU_SHADER_CREATE_INFO(eevee_geom_world)
.additional_info("eevee_shared")
@ -223,7 +244,8 @@ GPU_SHADER_CREATE_INFO(eevee_material_stub)
EEVEE_MAT_FINAL_VARIATION(prefix##_world, "eevee_geom_world", __VA_ARGS__) \
EEVEE_MAT_FINAL_VARIATION(prefix##_gpencil, "eevee_geom_gpencil", __VA_ARGS__) \
EEVEE_MAT_FINAL_VARIATION(prefix##_curves, "eevee_geom_curves", __VA_ARGS__) \
EEVEE_MAT_FINAL_VARIATION(prefix##_mesh, "eevee_geom_mesh", __VA_ARGS__)
EEVEE_MAT_FINAL_VARIATION(prefix##_mesh, "eevee_geom_mesh", __VA_ARGS__) \
EEVEE_MAT_FINAL_VARIATION(prefix##_point_cloud, "eevee_geom_point_cloud", __VA_ARGS__)
# define EEVEE_MAT_PIPE_VARIATIONS(name, ...) \
EEVEE_MAT_GEOM_VARIATIONS(name##_world, "eevee_surf_world", __VA_ARGS__) \

View File

@ -152,6 +152,8 @@ void DRW_curves_batch_cache_create_requested(struct Object *ob);
int DRW_pointcloud_material_count_get(struct PointCloud *pointcloud);
struct GPUVertBuf *DRW_pointcloud_position_and_radius_buffer_get(struct Object *ob);
struct GPUVertBuf **DRW_pointcloud_evaluated_attribute(struct PointCloud *pointcloud,
const char *name);
struct GPUBatch *DRW_pointcloud_batch_cache_get_dots(struct Object *ob);

View File

@ -387,6 +387,12 @@ GPUBatch *DRW_pointcloud_batch_cache_get_dots(Object *ob)
return DRW_batch_request(&cache->eval_cache.dots);
}
GPUVertBuf *DRW_pointcloud_position_and_radius_buffer_get(Object *ob)
{
PointCloud &pointcloud = *static_cast<PointCloud *>(ob->data);
return pointcloud_position_and_radius_get(&pointcloud);
}
GPUVertBuf **DRW_pointcloud_evaluated_attribute(PointCloud *pointcloud, const char *name)
{
PointCloudBatchCache &cache = *pointcloud_batch_cache_get(*pointcloud);

View File

@ -410,6 +410,18 @@ DRWShadingGroup *DRW_shgroup_curves_create_sub(Object *object,
void DRW_curves_update()
{
/* Ensure there's a valid active view.
* "Next" engines use this function, but this still uses the old Draw Manager. */
if (DRW_view_default_get() == nullptr) {
/* Create a dummy default view, it's not really used. */
DRW_view_default_set(DRW_view_create(
float4x4::identity().ptr(), float4x4::identity().ptr(), nullptr, nullptr, nullptr));
}
if (DRW_view_get_active() == nullptr) {
DRW_view_set_active(DRW_view_default_get());
}
/* Update legacy hair too, to avoid verbosity in callers. */
DRW_hair_update();

View File

@ -98,6 +98,8 @@ void DRW_pointcloud_free()
}
#include "draw_common.hh"
/* For drw_curves_get_attribute_sampler_name. */
#include "draw_curves_private.hh"
namespace blender::draw {
template<typename PassT>
@ -119,14 +121,20 @@ GPUBatch *point_cloud_sub_pass_setup_implementation(PassT &sub_ps,
sub_ps.bind_texture("ptcloud_pos_rad_tx", pos_rad_buf);
if (gpu_material != nullptr) {
/* Only single material supported for now. */
GPUBatch **geom = pointcloud_surface_shaded_get(&pointcloud, &gpu_material, 1);
return geom[0];
}
else {
GPUBatch *geom = pointcloud_surface_get(&pointcloud);
return geom;
ListBase gpu_attrs = GPU_material_attributes(gpu_material);
LISTBASE_FOREACH (GPUMaterialAttribute *, gpu_attr, &gpu_attrs) {
GPUVertBuf **attribute_buf = DRW_pointcloud_evaluated_attribute(&pointcloud, gpu_attr->name);
if (attribute_buf) {
char sampler_name[32];
/** NOTE: Reusing curve attribute function. */
drw_curves_get_attribute_sampler_name(gpu_attr->name, sampler_name);
sub_ps.bind_texture(sampler_name, attribute_buf);
}
}
}
GPUBatch *geom = pointcloud_surface_get(&pointcloud);
return geom;
}
GPUBatch *point_cloud_sub_pass_setup(PassMain::Sub &sub_ps,

View File

@ -10,7 +10,10 @@
int pointcloud_get_point_id()
{
# ifdef GPU_VERTEX_SHADER
return gl_VertexID / 32;
# endif
return 0;
}
mat3 pointcloud_get_facing_matrix(vec3 p)
@ -40,8 +43,12 @@ void pointcloud_get_pos_nor_radius(out vec3 outpos, out vec3 outnor, out float o
mat3 facing_mat = pointcloud_get_facing_matrix(p);
int vert_id = 0;
# ifdef GPU_VERTEX_SHADER
/* NOTE: Avoid modulo by non-power-of-two in shader. See Index buffer setup. */
int vert_id = gl_VertexID % 32;
vert_id = gl_VertexID % 32;
# endif
vec3 pos_inst = vec3(0.0);
switch (vert_id) {