Compare commits

..

12 Commits

Author SHA1 Message Date
42d5896841 EEVEE: Refactor closure_lit_lib.glsl
This refactor was needed for some reasons:
- closure_lit_lib.glsl was unreadable and could not be easily extended to use new features.
- It was generating ~5K LOC for any shader. Slowing down compilation.
- Some calculations were incorrect and BSDF/Closure code had lots of workaround/hacks.

What this refactor does:
- Add some macros to define the light object loops / eval.
- Clear separation between each closures which now have separate files. Each closure implements the eval functions.
- Make principled BSDF a bit more correct in some cases (specular coloring, mix between glass and opaque).
- The BSDF term are applied outside of the eval function and on the whole lighting (was separated for lights before).
- Make light iteration last to avoid carrying more data than needed.
- Makes sure that all inputs are within correct ranges before evaluating the closures (use `safe_normalize` on normals).
- Making each BSDF isolated means that we might carry duplicated data (normals for instance) but this should be optimized by compilers.
- Makes Translucent BSDF its own closure type to avoid having to disable raytraced shadows using hacks.
- Separate transmission roughness is now working on Principled BSDF.
- Makes principled shader variations using constants. Removing a lot of duplicated code. This needed `const` keyword detection in `gpu_material_library.c`.
- SSR/SSS masking and data loading is a bit more consistent and defined outside of closure eval. The loading functions will act as accumulator if the lighting is not to be separated.
- SSR pass now do a full deferred lighting evaluation, including lights, in order to avoid interference with the closure eval code. However, it seems that the cost of having a global SSR toggle uniform is making the surface shader more expensive (which is already the case, by the way).
- Principle fully black specular tint now returns black instead of white.
- This fixed some artifact issue on my AMD computer on normal surfaces (which might have been some uninitialized variables).
- This touched the Ambient Occlusion because it needs to be evaluated for each closure. But to avoid the cost of this, we use another approach to just pass the result of the occlusion on interpolated normals and modify it using the bent normal for each Closure. This tends to reduce shadowing. I'm still looking into improving this but this is out of the scope of this patch.
- Performance might be a bit worse with this patch since it is more oriented towards code modularity. But not by a lot.

Render tests needs to be updated after this.

Differential Revision: https://developer.blender.org/D10390
2021-02-13 00:54:30 +01:00
251ad8bf60 EEVEE: Fix issue with planar SSR 2021-02-09 02:02:18 +01:00
7ca2427ce2 EEVEE: Fix most regression with ambient occlusion
Occlusion had change because of the way it was applied.
Get back to almost the same thing we had previously. The only thing
that changed is the occlusion of Normal mapped surface.

It is a bit less noticeable due to a new approximation we make.
2021-02-09 01:36:20 +01:00
744427b729 EEVEE: Cleanup: Remove use of macros for the loops 2021-02-08 17:11:05 +01:00
6eaeb6272e EEVEE: Fix occlusion applied on SSR 2021-02-08 16:45:38 +01:00
1d56589f14 EEVEE: Add back support for user occlusion on eevee_specular BSDF 2021-02-08 15:43:24 +01:00
f6cc14f86e EEVEE: Split closure_lit_lib.glsl 2021-02-08 01:25:21 +01:00
1df2f7a713 EEVEE: Fix missing shadowing factor 2021-02-07 20:46:37 +01:00
bb05f6d335 EEVEE: Replace constant booleans by const float
This makes principled optimization easier and less verbose. Tests shows
no differences in performance.
2021-02-07 20:46:37 +01:00
36b066ee98 EEVEE: Fix lightgrid accumulation not using diffuse_accum
Also add safe normalize to interpolated normals.
2021-02-07 20:46:37 +01:00
60b2e410a7 EEVEE: Fix typo in macro leading to undefined behavior
Only the 1st closure radiance accumulator was initialized correctly.
2021-02-07 20:13:09 +01:00
fe00859202 EEVEE: Rewrite closure_lit_lib to reduce complexity
This rewrite improves:
- Code clarity: Less duplicated code and removes a few hacks.
- Compile time performance: Shader code was divided by 3 in average.
  I did not profile the compilation time but it is faster.
- Shading Perf: Noticed a 25% performance improvement on
  the shading pass with default dielectric principled bsdf.
- Fix Principled Tint being white if color is black
- It seems to have fixed issues on some drivers giving some incorect
  results.
- Changes Principled BSDF support to be less hacky.
2021-02-07 18:03:58 +01:00
56 changed files with 1433 additions and 1946 deletions

View File

@@ -11,6 +11,8 @@
<array>
<string>blend</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>blender file icon.icns</string>
<key>CFBundleTypeName</key>
<string>Blender File</string>
<key>CFBundleTypeOSTypes</key>
@@ -21,12 +23,6 @@
<string>Editor</string>
<key>LSIsAppleDefaultForType</key>
<true/>
<key>LSItemContentTypes</key>
<array>
<string>org.blenderfoundation.blender.file</string>
</array>
<key>CFBundleTypeIconSystemGenerated</key>
<true/>
</dict>
</array>
<key>CFBundleExecutable</key>
@@ -53,30 +49,5 @@
<string>NSApplication</string>
<key>NSHighResolutionCapable</key>
<true/>
<key>UTExportedTypeDeclarations</key>
<array>
<dict>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
</array>
<key>UTTypeIdentifier</key>
<string>org.blenderfoundation.blender.file</string>
<key>UTTypeIcons</key>
<dict>
<key>UTTypeIconName</key>
<string>Blender File</string>
<key>UTTypeIconText</key>
<string>blend</string>
</dict>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>blend</string>
</array>
</dict>
</dict>
</array>
</dict>
</plist>

View File

@@ -502,7 +502,6 @@ geometry_node_categories = [
]),
GeometryNodeCategory("GEO_INPUT", "Input", items=[
NodeItem("GeometryNodeObjectInfo"),
NodeItem("GeometryNodeCollectionInfo"),
NodeItem("FunctionNodeRandomFloat"),
NodeItem("ShaderNodeValue"),
NodeItem("FunctionNodeInputVector"),

View File

@@ -476,9 +476,3 @@ class VolumeComponent : public GeometryComponent {
static constexpr inline GeometryComponentType static_type = GeometryComponentType::Volume;
};
using ForeachGeometryCallbackConst = std::function<void(
const GeometryComponent &component, blender::Span<blender::float4x4> transforms)>;
void BKE_foreach_geometry_component_recursive(const GeometrySet &geometry_set,
const ForeachGeometryCallbackConst &callback);

View File

@@ -1364,7 +1364,6 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
#define GEO_NODE_POINT_SCALE 1020
#define GEO_NODE_ATTRIBUTE_SAMPLE_TEXTURE 1021
#define GEO_NODE_POINTS_TO_VOLUME 1022
#define GEO_NODE_COLLECTION_INFO 1023
/** \} */

View File

@@ -14,24 +14,19 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "BLI_listbase_wrapper.hh" /* TODO: Couldn't figure this out yet. */
#include "BKE_geometry_set.hh"
#include "BKE_lib_id.h"
#include "BKE_mesh.h"
#include "BKE_mesh_wrapper.h"
#include "BKE_modifier.h"
#include "BKE_pointcloud.h"
#include "BKE_volume.h"
#include "DNA_collection_types.h"
#include "DNA_object_types.h"
#include "MEM_guardedalloc.h"
using blender::float3;
using blender::float4x4;
using blender::ListBaseWrapper;
using blender::MutableSpan;
using blender::Span;
using blender::StringRef;
@@ -571,180 +566,6 @@ bool InstancesComponent::is_empty() const
return transforms_.size() == 0;
}
static GeometrySet object_get_geometry_set_for_read(const Object &object)
{
/* Objects evaluated with a nodes modifier will have a geometry set already. */
if (object.runtime.geometry_set_eval != nullptr) {
return *object.runtime.geometry_set_eval;
}
/* Otherwise, construct a new geometry set with the component based on the object type. */
GeometrySet new_geometry_set;
if (object.type == OB_MESH) {
Mesh *mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(
&const_cast<Object &>(object), false);
if (mesh != nullptr) {
BKE_mesh_wrapper_ensure_mdata(mesh);
MeshComponent &mesh_component = new_geometry_set.get_component_for_write<MeshComponent>();
mesh_component.replace(mesh, GeometryOwnershipType::ReadOnly);
mesh_component.copy_vertex_group_names_from_object(object);
}
}
// else if (object.type == OB_VOLUME) {
// Volume *volume = BKE_modifier_get_volume...
// }
/* Return by value since there is no existing geometry set owned elsewhere to use. */
return new_geometry_set;
}
static void foreach_geometry_component_recursive(const GeometrySet &geometry_set,
const ForeachGeometryCallbackConst &callback,
const float4x4 &transform);
static void foreach_collection_geometry_set_recursive(const Collection &collection,
const ForeachGeometryCallbackConst &callback,
const float4x4 &transform)
{
LISTBASE_FOREACH (const CollectionObject *, collection_object, &collection.gobject) {
BLI_assert(collection_object->ob != nullptr);
const Object &object = *collection_object->ob;
GeometrySet instance_geometry_set = object_get_geometry_set_for_read(object);
/* TODO: This seems to work-- validate this. */
const float4x4 instance_transform = transform * object.obmat;
foreach_geometry_component_recursive(instance_geometry_set, callback, instance_transform);
}
LISTBASE_FOREACH (const CollectionChild *, collection_child, &collection.children) {
BLI_assert(collection_child->collection != nullptr);
const Collection &collection = *collection_child->collection;
foreach_collection_geometry_set_recursive(collection, callback, transform);
}
}
static void foreach_geometry_component_recursive(const GeometrySet &geometry_set,
const ForeachGeometryCallbackConst &callback,
const float4x4 &transform)
{
if (geometry_set.has_mesh()) {
callback(*geometry_set.get_component_for_read<MeshComponent>(), {transform});
}
if (geometry_set.has_pointcloud()) {
callback(*geometry_set.get_component_for_read<PointCloudComponent>(), {transform});
}
if (geometry_set.has_volume()) {
callback(*geometry_set.get_component_for_read<VolumeComponent>(), {transform});
}
if (geometry_set.has_instances()) {
const InstancesComponent &instances_component =
*geometry_set.get_component_for_read<InstancesComponent>();
Span<float4x4> transforms = instances_component.transforms();
Span<InstancedData> instances = instances_component.instanced_data();
for (const int i : instances.index_range()) {
const InstancedData &data = instances[i];
const float4x4 &transform = transforms[i];
if (data.type == INSTANCE_DATA_TYPE_OBJECT) {
BLI_assert(data.data.object != nullptr);
const Object &object = *data.data.object;
GeometrySet instance_geometry_set = object_get_geometry_set_for_read(object);
foreach_geometry_component_recursive(instance_geometry_set, callback, transform);
}
else if (data.type == INSTANCE_DATA_TYPE_COLLECTION) {
BLI_assert(data.data.collection != nullptr);
const Collection &collection = *data.data.collection;
foreach_collection_geometry_set_recursive(collection, callback, transform);
}
}
}
}
void BKE_foreach_geometry_component_recursive(const GeometrySet &geometry_set,
const ForeachGeometryCallbackConst &callback)
{
float4x4 unit_transform;
unit_m4(unit_transform.values);
foreach_geometry_component_recursive(geometry_set, callback, unit_transform);
}
/* ============= API 2 =============== */
using GeometrySetGroup = std::pair<GeometrySet, Vector<float4x4>>;
static void collect_geometry_set_recursive(
const GeometrySet &geometry_set,
const float4x4 &transform,
Vector<std::pair<GeometrySet, Vector<float4x4>>> &r_sets);
static void collect_collection_geometry_set_recursive(const Collection &collection,
const float4x4 &transform,
Vector<GeometrySetGroup> &r_sets)
{
LISTBASE_FOREACH (const CollectionObject *, collection_object, &collection.gobject) {
BLI_assert(collection_object->ob != nullptr);
const Object &object = *collection_object->ob;
GeometrySet instance_geometry_set = object_get_geometry_set_for_read(object);
/* TODO: This seems to work-- validate this. */
const float4x4 instance_transform = transform * object.obmat;
collect_geometry_set_recursive(instance_geometry_set, instance_transform, r_sets);
}
LISTBASE_FOREACH (const CollectionChild *, collection_child, &collection.children) {
BLI_assert(collection_child->collection != nullptr);
const Collection &collection = *collection_child->collection;
collect_collection_geometry_set_recursive(collection, transform, r_sets);
}
}
static void collect_geometry_set_recursive(const GeometrySet &geometry_set,
const float4x4 &transform,
Vector<GeometrySetGroup> &r_sets)
{
r_sets.append({geometry_set, {transform}});
if (geometry_set.has_instances()) {
const InstancesComponent &instances_component =
*geometry_set.get_component_for_read<InstancesComponent>();
Span<float4x4> transforms = instances_component.transforms();
Span<InstancedData> instances = instances_component.instanced_data();
for (const int i : instances.index_range()) {
const InstancedData &data = instances[i];
const float4x4 &transform = transforms[i];
if (data.type == INSTANCE_DATA_TYPE_OBJECT) {
BLI_assert(data.data.object != nullptr);
const Object &object = *data.data.object;
GeometrySet instance_geometry_set = object_get_geometry_set_for_read(object);
collect_geometry_set_recursive(instance_geometry_set, transform, r_sets);
}
else if (data.type == INSTANCE_DATA_TYPE_COLLECTION) {
BLI_assert(data.data.collection != nullptr);
const Collection &collection = *data.data.collection;
collect_collection_geometry_set_recursive(collection, transform, r_sets);
}
}
}
}
Vector<GeometrySetGroup> BKE_geometry_set_gather_instanced(const GeometrySet &geometry_set)
{
Vector<GeometrySetGroup> result_vector;
float4x4 unit_transform;
unit_m4(unit_transform.values);
collect_geometry_set_recursive(geometry_set, unit_transform, result_vector);
return result_vector;
}
/** \} */
/* -------------------------------------------------------------------- */

View File

@@ -3549,9 +3549,6 @@ void nodeSetSocketAvailability(bNodeSocket *sock, bool is_available)
int nodeSocketLinkLimit(const bNodeSocket *sock)
{
bNodeSocketType *stype = sock->typeinfo;
if (sock->flag & SOCK_MULTI_INPUT) {
return 4095;
}
if (stype != nullptr && stype->use_link_limits_of_type) {
int limit = (sock->in_out == SOCK_IN) ? stype->input_link_limit : stype->output_link_limit;
return limit;
@@ -4773,7 +4770,6 @@ static void registerGeometryNodes()
register_node_type_geo_align_rotation_to_vector();
register_node_type_geo_sample_texture();
register_node_type_geo_points_to_volume();
register_node_type_geo_collection_info();
}
static void registerFunctionNodes()

View File

@@ -197,7 +197,6 @@ set(LIB
data_to_c_simple(engines/eevee/shaders/ambient_occlusion_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/background_vert.glsl SRC)
data_to_c_simple(engines/eevee/shaders/closure_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/common_uniforms_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/common_utiltex_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/lights_lib.glsl SRC)
@@ -215,7 +214,12 @@ data_to_c_simple(engines/eevee/shaders/lightprobe_grid_fill_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/lightprobe_planar_display_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/lightprobe_planar_display_vert.glsl SRC)
data_to_c_simple(engines/eevee/shaders/lookdev_world_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/closure_lit_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/closure_eval_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/closure_eval_diffuse_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/closure_eval_glossy_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/closure_eval_refraction_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/closure_eval_translucent_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/closure_type_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/effect_bloom_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/effect_dof_vert.glsl SRC)
data_to_c_simple(engines/eevee/shaders/effect_dof_frag.glsl SRC)

View File

@@ -191,6 +191,12 @@ void EEVEE_screen_raytrace_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *v
DRW_shgroup_uniform_texture_ref(grp, "hitBuffer", &effects->ssr_hit_output);
DRW_shgroup_uniform_texture_ref(grp, "pdfBuffer", &effects->ssr_pdf_output);
DRW_shgroup_uniform_texture_ref(grp, "prevColorBuffer", &txl->color_double_buffer);
DRW_shgroup_uniform_texture_ref(grp, "maxzBuffer", &txl->maxzbuffer);
DRW_shgroup_uniform_texture_ref(grp, "shadowCubeTexture", &sldata->shadow_cube_pool);
DRW_shgroup_uniform_texture_ref(grp, "shadowCascadeTexture", &sldata->shadow_cascade_pool);
DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex());
DRW_shgroup_uniform_block(grp, "light_block", sldata->light_ubo);
DRW_shgroup_uniform_block(grp, "shadow_block", sldata->shadow_ubo);
DRW_shgroup_uniform_block(grp, "grid_block", sldata->grid_ubo);
DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo);
DRW_shgroup_uniform_block(grp, "planar_block", sldata->planar_ubo);
@@ -198,7 +204,6 @@ void EEVEE_screen_raytrace_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *v
DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
DRW_shgroup_uniform_int(grp, "neighborOffset", &effects->ssr_neighbor_ofs, 1);
if ((effects->enabled_effects & EFFECT_GTAO) != 0) {
DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex());
DRW_shgroup_uniform_texture_ref(grp, "horizonBuffer", &effects->gtao_horizons);
}

View File

@@ -142,7 +142,6 @@ static struct {
struct GPUShader *volumetric_accum_sh;
/* Shader strings */
char *closure_lit_lib;
char *surface_lit_frag;
char *surface_prepass_frag;
char *surface_geom_barycentric;
@@ -184,7 +183,7 @@ extern char datatoc_bsdf_common_lib_glsl[];
extern char datatoc_bsdf_lut_frag_glsl[];
extern char datatoc_bsdf_sampling_lib_glsl[];
extern char datatoc_btdf_lut_frag_glsl[];
extern char datatoc_closure_lib_glsl[];
extern char datatoc_closure_type_lib_glsl[];
extern char datatoc_common_uniforms_lib_glsl[];
extern char datatoc_common_utiltex_lib_glsl[];
extern char datatoc_cryptomatte_frag_glsl[];
@@ -224,7 +223,11 @@ extern char datatoc_lightprobe_planar_downsample_geom_glsl[];
extern char datatoc_lightprobe_planar_downsample_vert_glsl[];
extern char datatoc_lightprobe_vert_glsl[];
extern char datatoc_lights_lib_glsl[];
extern char datatoc_closure_lit_lib_glsl[];
extern char datatoc_closure_eval_lib_glsl[];
extern char datatoc_closure_eval_diffuse_lib_glsl[];
extern char datatoc_closure_eval_glossy_lib_glsl[];
extern char datatoc_closure_eval_refraction_lib_glsl[];
extern char datatoc_closure_eval_translucent_lib_glsl[];
extern char datatoc_ltc_lib_glsl[];
extern char datatoc_object_motion_frag_glsl[];
extern char datatoc_object_motion_vert_glsl[];
@@ -279,23 +282,13 @@ static void eevee_shader_library_ensure(void)
DRW_SHADER_LIB_ADD(e_data.lib, lights_lib);
DRW_SHADER_LIB_ADD(e_data.lib, surface_lib);
DRW_SHADER_LIB_ADD(e_data.lib, volumetric_lib);
DRW_SHADER_LIB_ADD(e_data.lib, closure_lib);
DRW_SHADER_LIB_ADD(e_data.lib, ssr_lib);
/* Add one for each Closure */
e_data.closure_lit_lib = BLI_string_joinN(datatoc_closure_lit_lib_glsl,
datatoc_closure_lit_lib_glsl,
datatoc_closure_lit_lib_glsl,
datatoc_closure_lit_lib_glsl,
datatoc_closure_lit_lib_glsl,
datatoc_closure_lit_lib_glsl,
datatoc_closure_lit_lib_glsl,
datatoc_closure_lit_lib_glsl,
datatoc_closure_lit_lib_glsl,
datatoc_closure_lit_lib_glsl,
datatoc_closure_lit_lib_glsl);
DRW_shader_library_add_file(e_data.lib, e_data.closure_lit_lib, "closure_lit_lib.glsl");
DRW_SHADER_LIB_ADD(e_data.lib, closure_type_lib);
DRW_SHADER_LIB_ADD(e_data.lib, closure_eval_lib);
DRW_SHADER_LIB_ADD(e_data.lib, closure_eval_diffuse_lib);
DRW_SHADER_LIB_ADD(e_data.lib, closure_eval_glossy_lib);
DRW_SHADER_LIB_ADD(e_data.lib, closure_eval_translucent_lib);
DRW_SHADER_LIB_ADD(e_data.lib, closure_eval_refraction_lib);
e_data.surface_lit_frag = DRW_shader_library_create_shader_string(e_data.lib,
datatoc_surface_frag_glsl);
@@ -1403,7 +1396,6 @@ struct GPUMaterial *EEVEE_material_get(
void EEVEE_shaders_free(void)
{
MEM_SAFE_FREE(e_data.closure_lit_lib);
MEM_SAFE_FREE(e_data.surface_prepass_frag);
MEM_SAFE_FREE(e_data.surface_lit_frag);
MEM_SAFE_FREE(e_data.surface_geom_barycentric);

View File

@@ -191,15 +191,15 @@ void gtao_deferred(
dirs.xy = get_ao_dir(noise.x * 0.5);
dirs.zw = get_ao_dir(noise.x * 0.5 + 0.5);
bent_normal = normal * 1e-8;
visibility = 1e-8;
bent_normal = vec3(0.0);
visibility = 0.0;
horizons = unpack_horizons(horizons);
integrate_slice(normal, dirs.xy, horizons.xy, visibility, bent_normal);
integrate_slice(normal, dirs.zw, horizons.zw, visibility, bent_normal);
bent_normal = normalize(bent_normal / visibility);
bent_normal = safe_normalize(bent_normal);
visibility *= 0.5; /* We integrated 2 slices. */
}
@@ -240,13 +240,24 @@ float gtao_multibounce(float visibility, vec3 albedo)
return max(x, ((x * a + b) * x + c) * x);
}
float diffuse_occlusion(vec3 N, vec3 vis_cone_dir, float vis_cone_aperture_cos, vec3 albedo)
{
if ((int(aoSettings) & USE_AO) == 0) {
return 1.0;
}
/* If the shading normal is orthogonal to the geometric normal, it should be half lit. */
float horizon_fac = saturate(dot(N, vis_cone_dir) * 0.5 + 0.5);
float ao = vis_cone_aperture_cos * horizon_fac;
return gtao_multibounce(ao, albedo);
}
float specular_occlusion(float NV, float AO, float roughness)
{
return saturate(pow(NV + AO, roughness) - 1.0 + AO);
}
/* Use the right occlusion */
float occlusion_compute(vec3 N, vec3 vpos, float user_occlusion, vec4 rand, out vec3 bent_normal)
float occlusion_compute(vec3 N, vec3 vpos, vec4 rand, out vec3 bent_normal)
{
#ifndef USE_REFRACTION
if ((int(aoSettings) & USE_AO) != 0) {
@@ -263,10 +274,6 @@ float occlusion_compute(vec3 N, vec3 vpos, float user_occlusion, vec4 rand, out
visibility = max(1e-3, visibility);
if ((int(aoSettings) & USE_BENT_NORMAL) != 0) {
/* The bent normal will show the facet look of the mesh. Try to minimize this. */
float mix_fac = visibility * visibility * visibility;
bent_normal = normalize(mix(bent_normal, vnor, mix_fac));
bent_normal = transform_direction(ViewMatrixInverse, bent_normal);
}
else {
@@ -276,10 +283,10 @@ float occlusion_compute(vec3 N, vec3 vpos, float user_occlusion, vec4 rand, out
/* Scale by user factor */
visibility = pow(visibility, aoFactor);
return min(visibility, user_occlusion);
return visibility;
}
#endif
bent_normal = N;
return user_occlusion;
return 1.0;
}

View File

@@ -1,6 +1,15 @@
#pragma BLENDER_REQUIRE(common_math_lib.glsl)
vec3 diffuse_dominant_dir(vec3 N, vec3 vis_cone_dir, float vis_cone_aperture_cos)
{
/* TODO(fclem) revisit this. bent too much towards vis_cone_dir. */
vis_cone_aperture_cos *= sqr(vis_cone_aperture_cos);
N = mix(vis_cone_dir, N, vis_cone_aperture_cos);
return normalize(N);
}
vec3 specular_dominant_dir(vec3 N, vec3 V, float roughness)
{
vec3 R = -reflect(V, N);
@@ -79,7 +88,7 @@ vec3 F_brdf_single_scatter(vec3 f0, vec3 f90, vec2 lut)
{
/* Unreal specular matching : if specular color is below 2% intensity,
* treat as shadowning */
return saturate(50.0 * dot(f0, vec3(0.3, 0.6, 0.1))) * lut.y * abs(f90) + lut.x * f0;
return lut.y * f90 + lut.x * f0;
}
/* Multi-scattering brdf approximation from :
@@ -87,11 +96,7 @@ vec3 F_brdf_single_scatter(vec3 f0, vec3 f90, vec2 lut)
* by Carmelo J. Fdez-Agüera. */
vec3 F_brdf_multi_scatter(vec3 f0, vec3 f90, vec2 lut)
{
vec3 FssEss = F_brdf_single_scatter(f0, f90, lut);
/* Hack to avoid many more shader variations. */
if (f90.g < 0.0) {
return FssEss;
}
vec3 FssEss = lut.y * f90 + lut.x * f0;
float Ess = lut.x + lut.y;
float Ems = 1.0 - Ess;
@@ -102,8 +107,6 @@ vec3 F_brdf_multi_scatter(vec3 f0, vec3 f90, vec2 lut)
return FssEss + Fms * Ems;
}
#define F_brdf(f0, f90, lut) F_brdf_multi_scatter(f0, f90, lut)
/* GGX */
float D_ggx_opti(float NH, float a2)
{

View File

@@ -0,0 +1,85 @@
#pragma BLENDER_REQUIRE(lights_lib.glsl)
#pragma BLENDER_REQUIRE(lightprobe_lib.glsl)
#pragma BLENDER_REQUIRE(ambient_occlusion_lib.glsl)
struct ClosureInputDiffuse {
vec3 N; /** Shading normal. */
vec3 albedo; /** Used for multibounce GTAO approximation. Not applied to final radiance. */
};
#define CLOSURE_INPUT_Diffuse_DEFAULT ClosureInputDiffuse(vec3(0.0), vec3(0.0))
struct ClosureEvalDiffuse {
vec3 probe_sampling_dir; /** Direction to sample probes from. */
float ambient_occlusion; /** Final occlusion for distant lighting. */
};
/* Stubs. */
#define ClosureOutputDiffuse ClosureOutput
#define closure_Diffuse_planar_eval(cl_in, cl_eval, cl_common, data, cl_out)
#define closure_Diffuse_cubemap_eval(cl_in, cl_eval, cl_common, data, cl_out)
ClosureEvalDiffuse closure_Diffuse_eval_init(inout ClosureInputDiffuse cl_in,
ClosureEvalCommon cl_common,
out ClosureOutputDiffuse cl_out)
{
cl_in.N = safe_normalize(cl_in.N);
cl_out.radiance = vec3(0.0);
ClosureEvalDiffuse cl_eval;
cl_eval.ambient_occlusion = diffuse_occlusion(
cl_in.N, cl_common.bent_normal, cl_common.occlusion, cl_in.albedo);
cl_eval.probe_sampling_dir = diffuse_dominant_dir(
cl_in.N, cl_common.bent_normal, cl_common.occlusion);
return cl_eval;
}
void closure_Diffuse_light_eval(ClosureInputDiffuse cl_in,
ClosureEvalDiffuse cl_eval,
ClosureEvalCommon cl_common,
ClosureLightData light,
inout ClosureOutputDiffuse cl_out)
{
float radiance = light_diffuse(light.data, cl_in.N, cl_common.V, light.L);
/* TODO(fclem) We could try to shadow lights that are shadowless with the ambient_occlusion
* factor here. */
cl_out.radiance += light.data.l_color * (light.vis * light.contact_shadow * radiance);
}
void closure_Diffuse_grid_eval(ClosureInputDiffuse cl_in,
ClosureEvalDiffuse cl_eval,
ClosureEvalCommon cl_common,
ClosureGridData grid,
inout ClosureOutputDiffuse cl_out)
{
vec3 probe_radiance = probe_evaluate_grid(
grid.data, cl_common.P, cl_eval.probe_sampling_dir, grid.local_pos);
cl_out.radiance += grid.attenuation * probe_radiance;
}
void closure_Diffuse_indirect_end(ClosureInputDiffuse cl_in,
ClosureEvalDiffuse cl_eval,
ClosureEvalCommon cl_common,
inout ClosureOutputDiffuse cl_out)
{
/* If not enough light has been accumulated from probes, use the world specular cubemap
* to fill the remaining energy needed. */
if (cl_common.diffuse_accum > 0.0) {
vec3 probe_radiance = probe_evaluate_world_diff(cl_eval.probe_sampling_dir);
cl_out.radiance += cl_common.diffuse_accum * probe_radiance;
}
/* Apply occlusion on radiance before the light loop. */
cl_out.radiance *= cl_eval.ambient_occlusion;
}
void closure_Diffuse_eval_end(ClosureInputDiffuse cl_in,
ClosureEvalDiffuse cl_eval,
ClosureEvalCommon cl_common,
inout ClosureOutputDiffuse cl_out)
{
#if defined(DEPTH_SHADER) || defined(WORLD_BACKGROUND)
/* This makes shader resources become unused and avoid issues with samplers. (see T59747) */
cl_out.radiance = vec3(0.0);
return;
#endif
}

View File

@@ -0,0 +1,136 @@
#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl)
#pragma BLENDER_REQUIRE(lights_lib.glsl)
#pragma BLENDER_REQUIRE(lightprobe_lib.glsl)
#pragma BLENDER_REQUIRE(ambient_occlusion_lib.glsl)
struct ClosureInputGlossy {
vec3 N; /** Shading normal. */
float roughness; /** Input roughness, not squared. */
};
#define CLOSURE_INPUT_Glossy_DEFAULT ClosureInputGlossy(vec3(0.0), 0.0)
struct ClosureEvalGlossy {
vec4 ltc_mat; /** LTC matrix values. */
float ltc_brdf_scale; /** LTC BRDF scaling. */
vec3 probe_sampling_dir; /** Direction to sample probes from. */
float spec_occlusion; /** Specular Occlusion. */
vec3 raytrace_radiance; /** Raytrace reflection to be accumulated after occlusion. */
};
/* Stubs. */
#define ClosureOutputGlossy ClosureOutput
#define closure_Glossy_grid_eval(cl_in, cl_eval, cl_common, data, cl_out)
#ifdef STEP_RESOLVE /* SSR */
/* Prototype. */
void raytrace_resolve(ClosureInputGlossy cl_in,
inout ClosureEvalGlossy cl_eval,
inout ClosureEvalCommon cl_common,
inout ClosureOutputGlossy cl_out);
#endif
ClosureEvalGlossy closure_Glossy_eval_init(inout ClosureInputGlossy cl_in,
inout ClosureEvalCommon cl_common,
out ClosureOutputGlossy cl_out)
{
cl_in.N = safe_normalize(cl_in.N);
cl_in.roughness = clamp(cl_in.roughness, 1e-8, 0.9999);
cl_out.radiance = vec3(0.0);
float NV = dot(cl_in.N, cl_common.V);
vec2 lut_uv = lut_coords_ltc(NV, cl_in.roughness);
ClosureEvalGlossy cl_eval;
cl_eval.ltc_mat = texture(utilTex, vec3(lut_uv, LTC_MAT_LAYER));
cl_eval.probe_sampling_dir = specular_dominant_dir(cl_in.N, cl_common.V, sqr(cl_in.roughness));
cl_eval.spec_occlusion = specular_occlusion(NV, cl_common.occlusion, cl_in.roughness);
cl_eval.raytrace_radiance = vec3(0.0);
#ifdef STEP_RESOLVE /* SSR */
raytrace_resolve(cl_in, cl_eval, cl_common, cl_out);
#endif
/* The brdf split sum LUT is applied after the radiance accumulation.
* Correct the LTC so that its energy is constant. */
/* TODO(fclem) Optimize this so that only one scale factor is stored. */
vec4 ltc_brdf = texture(utilTex, vec3(lut_uv, LTC_BRDF_LAYER)).barg;
vec2 split_sum_brdf = ltc_brdf.zw;
cl_eval.ltc_brdf_scale = (ltc_brdf.x + ltc_brdf.y) / (split_sum_brdf.x + split_sum_brdf.y);
return cl_eval;
}
void closure_Glossy_light_eval(ClosureInputGlossy cl_in,
ClosureEvalGlossy cl_eval,
ClosureEvalCommon cl_common,
ClosureLightData light,
inout ClosureOutputGlossy cl_out)
{
float radiance = light_specular(light.data, cl_eval.ltc_mat, cl_in.N, cl_common.V, light.L);
radiance *= cl_eval.ltc_brdf_scale;
cl_out.radiance += light.data.l_color *
(light.data.l_spec * light.vis * light.contact_shadow * radiance);
}
void closure_Glossy_planar_eval(ClosureInputGlossy cl_in,
ClosureEvalGlossy cl_eval,
inout ClosureEvalCommon cl_common,
ClosurePlanarData planar,
inout ClosureOutputGlossy cl_out)
{
#ifndef STEP_RESOLVE /* SSR already evaluates planar reflections. */
vec3 probe_radiance = probe_evaluate_planar(
planar.id, planar.data, cl_common.P, cl_in.N, cl_common.V, cl_in.roughness);
cl_out.radiance += planar.attenuation * probe_radiance;
#else
/* HACK: Fix an issue with planar reflections still being counted inside the specular
* accumulator. This only works because we only use one Glossy closure in the resolve pass. */
cl_common.specular_accum += planar.attenuation;
#endif
}
void closure_Glossy_cubemap_eval(ClosureInputGlossy cl_in,
ClosureEvalGlossy cl_eval,
ClosureEvalCommon cl_common,
ClosureCubemapData cube,
inout ClosureOutputGlossy cl_out)
{
vec3 probe_radiance = probe_evaluate_cube(
cube.id, cl_common.P, cl_eval.probe_sampling_dir, cl_in.roughness);
cl_out.radiance += cube.attenuation * probe_radiance;
}
void closure_Glossy_indirect_end(ClosureInputGlossy cl_in,
ClosureEvalGlossy cl_eval,
ClosureEvalCommon cl_common,
inout ClosureOutputGlossy cl_out)
{
/* If not enough light has been accumulated from probes, use the world specular cubemap
* to fill the remaining energy needed. */
if (specToggle && cl_common.specular_accum > 0.0) {
vec3 probe_radiance = probe_evaluate_world_spec(cl_eval.probe_sampling_dir, cl_in.roughness);
cl_out.radiance += cl_common.specular_accum * probe_radiance;
}
/* Apply occlusion on distant lighting. */
cl_out.radiance *= cl_eval.spec_occlusion;
/* Apply Raytrace reflections after occlusion since they are direct, local reflections. */
cl_out.radiance += cl_eval.raytrace_radiance;
}
void closure_Glossy_eval_end(ClosureInputGlossy cl_in,
ClosureEvalGlossy cl_eval,
ClosureEvalCommon cl_common,
inout ClosureOutputGlossy cl_out)
{
#if defined(DEPTH_SHADER) || defined(WORLD_BACKGROUND)
/* This makes shader resources become unused and avoid issues with samplers. (see T59747) */
cl_out.radiance = vec3(0.0);
return;
#endif
if (!specToggle) {
cl_out.radiance = vec3(0.0);
}
}

View File

@@ -0,0 +1,316 @@
#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl)
#pragma BLENDER_REQUIRE(lights_lib.glsl)
#pragma BLENDER_REQUIRE(lightprobe_lib.glsl)
/**
* Extensive use of Macros to be able to change the maximum amount of evaluated closure easily.
* NOTE: GLSL does not support variadic macros.
*
* Example
* // Declare the cl_eval function
* CLOSURE_EVAL_FUNCTION_DECLARE_3(name, Diffuse, Glossy, Refraction);
* // Declare the inputs & outputs
* CLOSURE_VARS_DECLARE(Diffuse, Glossy, Refraction);
* // Specify inputs
* in_Diffuse_0.N = N;
* ...
* // Call the cl_eval function
* CLOSURE_EVAL_FUNCTION_3(name, Diffuse, Glossy, Refraction);
* // Get the cl_out
* closure.radiance = out_Diffuse_0.radiance + out_Glossy_1.radiance + out_Refraction_2.radiance;
**/
#define CLOSURE_VARS_DECLARE(t0, t1, t2, t3) \
ClosureInputCommon in_common = CLOSURE_INPUT_COMMON_DEFAULT; \
ClosureInput##t0 in_##t0##_0 = CLOSURE_INPUT_##t0##_DEFAULT; \
ClosureInput##t1 in_##t1##_1 = CLOSURE_INPUT_##t1##_DEFAULT; \
ClosureInput##t2 in_##t2##_2 = CLOSURE_INPUT_##t2##_DEFAULT; \
ClosureInput##t3 in_##t3##_3 = CLOSURE_INPUT_##t3##_DEFAULT; \
ClosureOutput##t0 out_##t0##_0; \
ClosureOutput##t1 out_##t1##_1; \
ClosureOutput##t2 out_##t2##_2; \
ClosureOutput##t3 out_##t3##_3;
#define CLOSURE_EVAL_DECLARE(t0, t1, t2, t3) \
ClosureEvalCommon cl_common = closure_Common_eval_init(in_common); \
ClosureEval##t0 eval_##t0##_0 = closure_##t0##_eval_init(in_##t0##_0, cl_common, out_##t0##_0); \
ClosureEval##t1 eval_##t1##_1 = closure_##t1##_eval_init(in_##t1##_1, cl_common, out_##t1##_1); \
ClosureEval##t2 eval_##t2##_2 = closure_##t2##_eval_init(in_##t2##_2, cl_common, out_##t2##_2); \
ClosureEval##t3 eval_##t3##_3 = closure_##t3##_eval_init(in_##t3##_3, cl_common, out_##t3##_3);
#define CLOSURE_META_SUBROUTINE(subroutine, t0, t1, t2, t3) \
closure_##t0##_##subroutine(in_##t0##_0, eval_##t0##_0, cl_common, out_##t0##_0); \
closure_##t1##_##subroutine(in_##t1##_1, eval_##t1##_1, cl_common, out_##t1##_1); \
closure_##t2##_##subroutine(in_##t2##_2, eval_##t2##_2, cl_common, out_##t2##_2); \
closure_##t3##_##subroutine(in_##t3##_3, eval_##t3##_3, cl_common, out_##t3##_3);
#define CLOSURE_META_SUBROUTINE_DATA(subroutine, sub_data, t0, t1, t2, t3) \
closure_##t0##_##subroutine(in_##t0##_0, eval_##t0##_0, cl_common, sub_data, out_##t0##_0); \
closure_##t1##_##subroutine(in_##t1##_1, eval_##t1##_1, cl_common, sub_data, out_##t1##_1); \
closure_##t2##_##subroutine(in_##t2##_2, eval_##t2##_2, cl_common, sub_data, out_##t2##_2); \
closure_##t3##_##subroutine(in_##t3##_3, eval_##t3##_3, cl_common, sub_data, out_##t3##_3);
/* Inputs are inout so that callers can get the final inputs used for evaluation. */
#define CLOSURE_EVAL_FUNCTION_DECLARE(name, t0, t1, t2, t3) \
void closure_##name##_eval(ClosureInputCommon in_common, \
inout ClosureInput##t0 in_##t0##_0, \
inout ClosureInput##t1 in_##t1##_1, \
inout ClosureInput##t2 in_##t2##_2, \
inout ClosureInput##t3 in_##t3##_3, \
out ClosureOutput##t0 out_##t0##_0, \
out ClosureOutput##t1 out_##t1##_1, \
out ClosureOutput##t2 out_##t2##_2, \
out ClosureOutput##t3 out_##t3##_3) \
{ \
CLOSURE_EVAL_DECLARE(t0, t1, t2, t3); \
\
for (int i = 0; cl_common.specular_accum > 0.0 && i < prbNumPlanar && i < MAX_PLANAR; i++) { \
ClosurePlanarData planar = closure_planar_eval_init(i, cl_common); \
if (planar.attenuation > 1e-8) { \
CLOSURE_META_SUBROUTINE_DATA(planar_eval, planar, t0, t1, t2, t3); \
} \
} \
\
/* Starts at 1 because 0 is world cubemap. */ \
for (int i = 1; cl_common.specular_accum > 0.0 && i < prbNumRenderCube && i < MAX_PROBE; \
i++) { \
ClosureCubemapData cube = closure_cubemap_eval_init(i, cl_common); \
if (cube.attenuation > 1e-8) { \
CLOSURE_META_SUBROUTINE_DATA(cubemap_eval, cube, t0, t1, t2, t3); \
} \
} \
\
/* Starts at 1 because 0 is world irradiance. */ \
for (int i = 1; cl_common.diffuse_accum > 0.0 && i < prbNumRenderGrid && i < MAX_GRID; i++) { \
ClosureGridData grid = closure_grid_eval_init(i, cl_common); \
if (grid.attenuation > 1e-8) { \
CLOSURE_META_SUBROUTINE_DATA(grid_eval, grid, t0, t1, t2, t3); \
} \
} \
\
CLOSURE_META_SUBROUTINE(indirect_end, t0, t1, t2, t3); \
\
for (int i = 0; i < laNumLight && i < MAX_LIGHT; i++) { \
ClosureLightData light = closure_light_eval_init(cl_common, i); \
if (light.vis > 1e-8) { \
CLOSURE_META_SUBROUTINE_DATA(light_eval, light, t0, t1, t2, t3); \
} \
} \
\
CLOSURE_META_SUBROUTINE(eval_end, t0, t1, t2, t3); \
}
#define CLOSURE_EVAL_FUNCTION(name, t0, t1, t2, t3) \
closure_##name##_eval(in_common, \
in_##t0##_0, \
in_##t1##_1, \
in_##t2##_2, \
in_##t3##_3, \
out_##t0##_0, \
out_##t1##_1, \
out_##t2##_2, \
out_##t3##_3)
#define CLOSURE_EVAL_FUNCTION_DECLARE_1(name, t0) \
CLOSURE_EVAL_FUNCTION_DECLARE(name, t0, Dummy, Dummy, Dummy)
#define CLOSURE_EVAL_FUNCTION_DECLARE_2(name, t0, t1) \
CLOSURE_EVAL_FUNCTION_DECLARE(name, t0, t1, Dummy, Dummy)
#define CLOSURE_EVAL_FUNCTION_DECLARE_3(name, t0, t1, t2) \
CLOSURE_EVAL_FUNCTION_DECLARE(name, t0, t1, t2, Dummy)
#define CLOSURE_EVAL_FUNCTION_DECLARE_4(name, t0, t1, t2, t3) \
CLOSURE_EVAL_FUNCTION_DECLARE(name, t0, t1, t2, t3)
#define CLOSURE_VARS_DECLARE_1(t0) CLOSURE_VARS_DECLARE(t0, Dummy, Dummy, Dummy)
#define CLOSURE_VARS_DECLARE_2(t0, t1) CLOSURE_VARS_DECLARE(t0, t1, Dummy, Dummy)
#define CLOSURE_VARS_DECLARE_3(t0, t1, t2) CLOSURE_VARS_DECLARE(t0, t1, t2, Dummy)
#define CLOSURE_VARS_DECLARE_4(t0, t1, t2, t3) CLOSURE_VARS_DECLARE(t0, t1, t2, t3)
#define CLOSURE_EVAL_FUNCTION_1(name, t0) CLOSURE_EVAL_FUNCTION(name, t0, Dummy, Dummy, Dummy)
#define CLOSURE_EVAL_FUNCTION_2(name, t0, t1) CLOSURE_EVAL_FUNCTION(name, t0, t1, Dummy, Dummy)
#define CLOSURE_EVAL_FUNCTION_3(name, t0, t1, t2) CLOSURE_EVAL_FUNCTION(name, t0, t1, t2, Dummy)
#define CLOSURE_EVAL_FUNCTION_4(name, t0, t1, t2, t3) CLOSURE_EVAL_FUNCTION(name, t0, t1, t2, t3)
/* -------------------------------------------------------------------- */
/** \name Dummy Closure
*
* Dummy closure type that will be optimized out by the compiler.
* \{ */
#define ClosureInputDummy ClosureOutput
#define ClosureOutputDummy ClosureOutput
#define ClosureEvalDummy ClosureOutput
#define CLOSURE_EVAL_DUMMY ClosureOutput(vec3(0))
#define CLOSURE_INPUT_Dummy_DEFAULT CLOSURE_EVAL_DUMMY
#define closure_Dummy_eval_init(cl_in, cl_common, cl_out) CLOSURE_EVAL_DUMMY
#define closure_Dummy_planar_eval(cl_in, cl_eval, cl_common, data, cl_out)
#define closure_Dummy_cubemap_eval(cl_in, cl_eval, cl_common, data, cl_out)
#define closure_Dummy_grid_eval(cl_in, cl_eval, cl_common, data, cl_out)
#define closure_Dummy_indirect_end(cl_in, cl_eval, cl_common, cl_out)
#define closure_Dummy_light_eval(cl_in, cl_eval, cl_common, data, cl_out)
#define closure_Dummy_eval_end(cl_in, cl_eval, cl_common, cl_out)
/** \} */
/* -------------------------------------------------------------------- */
/** \name Common cl_eval data
*
* Eval data not dependant on input parameters. All might not be used but unused ones
* will be optimized out.
* \{ */
struct ClosureInputCommon {
/** Custom occlusion value set by the user. */
float occlusion;
};
#define CLOSURE_INPUT_COMMON_DEFAULT ClosureInputCommon(1.0)
struct ClosureEvalCommon {
/** View vector. */
vec3 V;
/** Surface position. */
vec3 P;
/** Normal vector, always facing camera. */
vec3 N;
/** Normal vector, always facing camera. (viewspace) */
vec3 vN;
/** Surface position. (viewspace) */
vec3 vP;
/** Geometric normal, always facing camera. (viewspace) */
vec3 vNg;
/** Random numbers. 3 random sequences. zw is a random point on a circle. */
vec4 rand;
/** Final occlusion factor. Mix of the user occlusion and SSAO. */
float occlusion;
/** Least occluded direction in the hemisphere. */
vec3 bent_normal;
/** Specular probe accumulator. Shared between planar and cubemap probe. */
float specular_accum;
/** Diffuse probe accumulator. */
float diffuse_accum;
/** Viewspace depth to start raytracing from. */
float tracing_depth;
};
/* Common cl_out struct used by most closures. */
struct ClosureOutput {
vec3 radiance;
};
ClosureEvalCommon closure_Common_eval_init(ClosureInputCommon cl_in)
{
ClosureEvalCommon cl_eval;
cl_eval.rand = texelfetch_noise_tex(gl_FragCoord.xy);
cl_eval.V = cameraVec;
cl_eval.P = worldPosition;
cl_eval.N = safe_normalize(gl_FrontFacing ? worldNormal : -worldNormal);
cl_eval.vN = safe_normalize(gl_FrontFacing ? viewNormal : -viewNormal);
cl_eval.vP = viewPosition;
cl_eval.vNg = safe_normalize(cross(dFdx(viewPosition), dFdy(viewPosition)));
/* TODO(fclem) See if we can avoid this complicated setup. */
cl_eval.tracing_depth = gl_FragCoord.z;
/* Constant bias (due to depth buffer precision) */
/* Magic numbers for 24bits of precision.
* From http://terathon.com/gdc07_lengyel.pdf (slide 26) */
cl_eval.tracing_depth -= mix(2.4e-7, 4.8e-7, gl_FragCoord.z);
/* Convert to view Z. */
cl_eval.tracing_depth = get_view_z_from_depth(cl_eval.tracing_depth);
/* TODO(fclem) Do occlusion evaluation per Closure using shading normal. */
cl_eval.occlusion = min(
cl_in.occlusion,
occlusion_compute(cl_eval.N, cl_eval.vP, cl_eval.rand, cl_eval.bent_normal));
cl_eval.specular_accum = 1.0;
cl_eval.diffuse_accum = 1.0;
return cl_eval;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Loop data
*
* Loop datas are conveniently packed into struct to make it future proof.
* \{ */
struct ClosureLightData {
LightData data; /** Light Data. */
vec4 L; /** Non-Normalized Light Vector (surface to light) with length in W component. */
float vis; /** Light visibility. */
float contact_shadow; /** Result of contact shadow tracing. */
};
ClosureLightData closure_light_eval_init(ClosureEvalCommon cl_common, int light_id)
{
ClosureLightData light;
light.data = lights_data[light_id];
light.L.xyz = light.data.l_position - cl_common.P;
light.L.w = length(light.L.xyz);
light.vis = light_visibility(light.data, cl_common.P, light.L);
light.contact_shadow = light_contact_shadows(light.data,
cl_common.P,
cl_common.vP,
cl_common.tracing_depth,
cl_common.vNg,
cl_common.rand.x,
light.vis);
return light;
}
struct ClosureCubemapData {
int id; /** Probe id. */
float attenuation; /** Attenuation. */
};
ClosureCubemapData closure_cubemap_eval_init(int cube_id, inout ClosureEvalCommon cl_common)
{
ClosureCubemapData cube;
cube.id = cube_id;
cube.attenuation = probe_attenuation_cube(cube_id, cl_common.P);
cube.attenuation = min(cube.attenuation, cl_common.specular_accum);
cl_common.specular_accum -= cube.attenuation;
return cube;
}
struct ClosurePlanarData {
int id; /** Probe id. */
PlanarData data; /** planars_data[id]. */
float attenuation; /** Attenuation. */
};
ClosurePlanarData closure_planar_eval_init(int planar_id, inout ClosureEvalCommon cl_common)
{
ClosurePlanarData planar;
planar.id = planar_id;
planar.data = planars_data[planar_id];
planar.attenuation = probe_attenuation_planar(planar.data, cl_common.P, cl_common.N, 0.0);
planar.attenuation = min(planar.attenuation, cl_common.specular_accum);
cl_common.specular_accum -= planar.attenuation;
return planar;
}
struct ClosureGridData {
int id; /** Grid id. */
GridData data; /** grids_data[id] */
float attenuation; /** Attenuation. */
vec3 local_pos; /** Local position inside the grid. */
};
ClosureGridData closure_grid_eval_init(int id, inout ClosureEvalCommon cl_common)
{
ClosureGridData grid;
grid.id = id;
grid.data = grids_data[id];
grid.attenuation = probe_attenuation_grid(grid.data, cl_common.P, grid.local_pos);
grid.attenuation = min(grid.attenuation, cl_common.diffuse_accum);
cl_common.diffuse_accum -= grid.attenuation;
return grid;
}
/** \} */

View File

@@ -0,0 +1,128 @@
#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl)
#pragma BLENDER_REQUIRE(lights_lib.glsl)
#pragma BLENDER_REQUIRE(lightprobe_lib.glsl)
#pragma BLENDER_REQUIRE(ambient_occlusion_lib.glsl)
#pragma BLENDER_REQUIRE(ssr_lib.glsl)
struct ClosureInputRefraction {
vec3 N; /** Shading normal. */
float roughness; /** Input roughness, not squared. */
float ior; /** Index of refraction ratio. */
};
#define CLOSURE_INPUT_Refraction_DEFAULT ClosureInputRefraction(vec3(0.0), 0.0, 0.0)
struct ClosureEvalRefraction {
vec3 P; /** LTC matrix values. */
vec3 ltc_brdf; /** LTC BRDF values. */
vec3 probe_sampling_dir; /** Direction to sample probes from. */
float probes_weight; /** Factor to apply to probe radiance. */
};
/* Stubs. */
#define ClosureOutputRefraction ClosureOutput
#define closure_Refraction_grid_eval(cl_in, cl_eval, cl_common, data, cl_out)
ClosureEvalRefraction closure_Refraction_eval_init(inout ClosureInputRefraction cl_in,
ClosureEvalCommon cl_common,
out ClosureOutputRefraction cl_out)
{
cl_in.N = safe_normalize(cl_in.N);
cl_in.roughness = clamp(cl_in.roughness, 1e-8, 0.9999);
cl_in.ior = max(cl_in.ior, 1e-5);
cl_out.radiance = vec3(0.0);
ClosureEvalRefraction cl_eval;
vec3 cl_V;
float eval_ior;
/* Refract the view vector using the depth heuristic.
* Then later Refract a second time the already refracted
* ray using the inverse ior. */
if (refractionDepth > 0.0) {
eval_ior = 1.0 / cl_in.ior;
cl_V = -refract(-cl_common.V, cl_in.N, eval_ior);
vec3 plane_pos = cl_common.P - cl_in.N * refractionDepth;
cl_eval.P = line_plane_intersect(cl_common.P, cl_V, plane_pos, cl_in.N);
}
else {
eval_ior = cl_in.ior;
cl_V = cl_common.V;
cl_eval.P = cl_common.P;
}
cl_eval.probe_sampling_dir = refraction_dominant_dir(cl_in.N, cl_V, cl_in.roughness, eval_ior);
cl_eval.probes_weight = 1.0;
#ifdef USE_REFRACTION
if (ssrefractToggle && cl_in.roughness < ssrMaxRoughness + 0.2) {
/* Find approximated position of the 2nd refraction event. */
vec3 vP = (refractionDepth > 0.0) ? transform_point(ViewMatrix, cl_eval.P) : cl_common.vP;
vec4 ssr_output = screen_space_refraction(
vP, cl_in.N, cl_V, eval_ior, sqr(cl_in.roughness), cl_common.rand);
ssr_output.a *= smoothstep(ssrMaxRoughness + 0.2, ssrMaxRoughness, cl_in.roughness);
cl_out.radiance += ssr_output.rgb * ssr_output.a;
cl_eval.probes_weight -= ssr_output.a;
}
#endif
return cl_eval;
}
void closure_Refraction_light_eval(ClosureInputRefraction cl_in,
ClosureEvalRefraction cl_eval,
ClosureEvalCommon cl_common,
ClosureLightData light,
inout ClosureOutputRefraction cl_out)
{
/* Not implemented yet. */
}
void closure_Refraction_planar_eval(ClosureInputRefraction cl_in,
ClosureEvalRefraction cl_eval,
ClosureEvalCommon cl_common,
ClosurePlanarData planar,
inout ClosureOutputRefraction cl_out)
{
/* Not implemented yet. */
}
void closure_Refraction_cubemap_eval(ClosureInputRefraction cl_in,
ClosureEvalRefraction cl_eval,
ClosureEvalCommon cl_common,
ClosureCubemapData cube,
inout ClosureOutputRefraction cl_out)
{
vec3 probe_radiance = probe_evaluate_cube(
cube.id, cl_eval.P, cl_eval.probe_sampling_dir, sqr(cl_in.roughness));
cl_out.radiance += (cube.attenuation * cl_eval.probes_weight) * probe_radiance;
}
void closure_Refraction_indirect_end(ClosureInputRefraction cl_in,
ClosureEvalRefraction cl_eval,
ClosureEvalCommon cl_common,
inout ClosureOutputRefraction cl_out)
{
/* If not enough light has been accumulated from probes, use the world specular cubemap
* to fill the remaining energy needed. */
if (specToggle && cl_common.specular_accum > 0.0) {
vec3 probe_radiance = probe_evaluate_world_spec(cl_eval.probe_sampling_dir,
sqr(cl_in.roughness));
cl_out.radiance += (cl_common.specular_accum * cl_eval.probes_weight) * probe_radiance;
}
}
void closure_Refraction_eval_end(ClosureInputRefraction cl_in,
ClosureEvalRefraction cl_eval,
ClosureEvalCommon cl_common,
inout ClosureOutputRefraction cl_out)
{
#if defined(DEPTH_SHADER) || defined(WORLD_BACKGROUND)
/* This makes shader resources become unused and avoid issues with samplers. (see T59747) */
cl_out.radiance = vec3(0.0);
return;
#endif
if (!specToggle) {
cl_out.radiance = vec3(0.0);
}
}

View File

@@ -0,0 +1,71 @@
#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl)
#pragma BLENDER_REQUIRE(lights_lib.glsl)
#pragma BLENDER_REQUIRE(lightprobe_lib.glsl)
#pragma BLENDER_REQUIRE(ambient_occlusion_lib.glsl)
struct ClosureInputTranslucent {
vec3 N; /** Shading normal. */
};
#define CLOSURE_INPUT_Translucent_DEFAULT ClosureInputTranslucent(vec3(0.0))
/* Stubs. */
#define ClosureEvalTranslucent ClosureEvalDummy
#define ClosureOutputTranslucent ClosureOutput
#define closure_Translucent_planar_eval(cl_in, cl_eval, cl_common, data, cl_out)
#define closure_Translucent_cubemap_eval(cl_in, cl_eval, cl_common, data, cl_out)
ClosureEvalTranslucent closure_Translucent_eval_init(inout ClosureInputTranslucent cl_in,
ClosureEvalCommon cl_common,
out ClosureOutputTranslucent cl_out)
{
cl_in.N = safe_normalize(cl_in.N);
cl_out.radiance = vec3(0.0);
return CLOSURE_EVAL_DUMMY;
}
void closure_Translucent_light_eval(ClosureInputTranslucent cl_in,
ClosureEvalTranslucent cl_eval,
ClosureEvalCommon cl_common,
ClosureLightData light,
inout ClosureOutputTranslucent cl_out)
{
float radiance = light_diffuse(light.data, cl_in.N, cl_common.V, light.L);
cl_out.radiance += light.data.l_color * (light.vis * radiance);
}
void closure_Translucent_grid_eval(ClosureInputTranslucent cl_in,
ClosureEvalTranslucent cl_eval,
ClosureEvalCommon cl_common,
ClosureGridData grid,
inout ClosureOutputTranslucent cl_out)
{
vec3 probe_radiance = probe_evaluate_grid(grid.data, cl_common.P, cl_in.N, grid.local_pos);
cl_out.radiance += grid.attenuation * probe_radiance;
}
void closure_Translucent_indirect_end(ClosureInputTranslucent cl_in,
ClosureEvalTranslucent cl_eval,
ClosureEvalCommon cl_common,
inout ClosureOutputTranslucent cl_out)
{
/* If not enough light has been accumulated from probes, use the world specular cubemap
* to fill the remaining energy needed. */
if (cl_common.diffuse_accum > 0.0) {
vec3 probe_radiance = probe_evaluate_world_diff(cl_in.N);
cl_out.radiance += cl_common.diffuse_accum * probe_radiance;
}
}
void closure_Translucent_eval_end(ClosureInputTranslucent cl_in,
ClosureEvalTranslucent cl_eval,
ClosureEvalCommon cl_common,
inout ClosureOutputTranslucent cl_out)
{
#if defined(DEPTH_SHADER) || defined(WORLD_BACKGROUND)
/* This makes shader resources become unused and avoid issues with samplers. (see T59747) */
cl_out.radiance = vec3(0.0);
return;
#endif
}

View File

@@ -1,545 +0,0 @@
#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl)
#pragma BLENDER_REQUIRE(lightprobe_lib.glsl)
#pragma BLENDER_REQUIRE(ambient_occlusion_lib.glsl)
#pragma BLENDER_REQUIRE(ssr_lib.glsl)
/**
* AUTO CONFIG
* We include the file multiple times each time with a different configuration.
* This leads to a lot of deadcode. Better idea would be to only generate the one needed.
*/
#if !defined(SURFACE_DEFAULT)
# define SURFACE_DEFAULT
# define CLOSURE_NAME eevee_closure_default
# define CLOSURE_DIFFUSE
# define CLOSURE_GLOSSY
#endif /* SURFACE_DEFAULT */
#if !defined(SURFACE_DEFAULT_CLEARCOAT) && !defined(CLOSURE_NAME)
# define SURFACE_DEFAULT_CLEARCOAT
# define CLOSURE_NAME eevee_closure_default_clearcoat
# define CLOSURE_DIFFUSE
# define CLOSURE_GLOSSY
# define CLOSURE_CLEARCOAT
#endif /* SURFACE_DEFAULT_CLEARCOAT */
#if !defined(SURFACE_PRINCIPLED) && !defined(CLOSURE_NAME)
# define SURFACE_PRINCIPLED
# define CLOSURE_NAME eevee_closure_principled
# define CLOSURE_DIFFUSE
# define CLOSURE_GLOSSY
# define CLOSURE_CLEARCOAT
# define CLOSURE_REFRACTION
# define CLOSURE_SUBSURFACE
#endif /* SURFACE_PRINCIPLED */
#if !defined(SURFACE_CLEARCOAT) && !defined(CLOSURE_NAME)
# define SURFACE_CLEARCOAT
# define CLOSURE_NAME eevee_closure_clearcoat
# define CLOSURE_GLOSSY
# define CLOSURE_CLEARCOAT
#endif /* SURFACE_CLEARCOAT */
#if !defined(SURFACE_DIFFUSE) && !defined(CLOSURE_NAME)
# define SURFACE_DIFFUSE
# define CLOSURE_NAME eevee_closure_diffuse
# define CLOSURE_DIFFUSE
#endif /* SURFACE_DIFFUSE */
#if !defined(SURFACE_SUBSURFACE) && !defined(CLOSURE_NAME)
# define SURFACE_SUBSURFACE
# define CLOSURE_NAME eevee_closure_subsurface
# define CLOSURE_DIFFUSE
# define CLOSURE_SUBSURFACE
#endif /* SURFACE_SUBSURFACE */
#if !defined(SURFACE_SKIN) && !defined(CLOSURE_NAME)
# define SURFACE_SKIN
# define CLOSURE_NAME eevee_closure_skin
# define CLOSURE_DIFFUSE
# define CLOSURE_SUBSURFACE
# define CLOSURE_GLOSSY
#endif /* SURFACE_SKIN */
#if !defined(SURFACE_GLOSSY) && !defined(CLOSURE_NAME)
# define SURFACE_GLOSSY
# define CLOSURE_NAME eevee_closure_glossy
# define CLOSURE_GLOSSY
#endif /* SURFACE_GLOSSY */
#if !defined(SURFACE_REFRACT) && !defined(CLOSURE_NAME)
# define SURFACE_REFRACT
# define CLOSURE_NAME eevee_closure_refraction
# define CLOSURE_REFRACTION
#endif /* SURFACE_REFRACT */
#if !defined(SURFACE_GLASS) && !defined(CLOSURE_NAME)
# define SURFACE_GLASS
# define CLOSURE_NAME eevee_closure_glass
# define CLOSURE_GLOSSY
# define CLOSURE_REFRACTION
#endif /* SURFACE_GLASS */
/* Safety : CLOSURE_CLEARCOAT implies CLOSURE_GLOSSY */
#ifdef CLOSURE_CLEARCOAT
# ifndef CLOSURE_GLOSSY
# define CLOSURE_GLOSSY
# endif
#endif /* CLOSURE_CLEARCOAT */
void CLOSURE_NAME(vec3 N
#ifdef CLOSURE_DIFFUSE
,
vec3 albedo
#endif
#ifdef CLOSURE_GLOSSY
,
vec3 f0,
vec3 f90,
int ssr_id
#endif
#if defined(CLOSURE_GLOSSY) || defined(CLOSURE_REFRACTION)
,
float roughness
#endif
#ifdef CLOSURE_CLEARCOAT
,
vec3 C_N,
float C_intensity,
float C_roughness
#endif
#if defined(CLOSURE_GLOSSY) || defined(CLOSURE_DIFFUSE)
,
float ao
#endif
#ifdef CLOSURE_SUBSURFACE
,
float sss_scale
#endif
#ifdef CLOSURE_REFRACTION
,
float ior
#endif
,
const bool use_contact_shadows
#ifdef CLOSURE_DIFFUSE
,
out vec3 out_diff
#endif
#ifdef CLOSURE_GLOSSY
,
out vec3 out_spec
#endif
#ifdef CLOSURE_REFRACTION
,
out vec3 out_refr
#endif
#ifdef CLOSURE_GLOSSY
,
out vec3 ssr_spec
#endif
)
{
#ifdef CLOSURE_DIFFUSE
out_diff = vec3(0.0);
#endif
#ifdef CLOSURE_GLOSSY
out_spec = vec3(0.0);
#endif
#ifdef CLOSURE_REFRACTION
out_refr = vec3(0.0);
#endif
#if defined(DEPTH_SHADER) || defined(WORLD_BACKGROUND)
/* This makes shader resources become unused and avoid issues with samplers. (see T59747) */
return;
#else
/* Zero length vectors cause issues, see: T51979. */
float len = length(N);
if (isnan(len)) {
return;
}
N /= len;
# ifdef CLOSURE_CLEARCOAT
len = length(C_N);
if (isnan(len)) {
return;
}
C_N /= len;
# endif
# if defined(CLOSURE_GLOSSY) || defined(CLOSURE_REFRACTION)
roughness = clamp(roughness, 1e-8, 0.9999);
float roughnessSquared = roughness * roughness;
# endif
# ifdef CLOSURE_CLEARCOAT
C_roughness = clamp(C_roughness, 1e-8, 0.9999);
float C_roughnessSquared = C_roughness * C_roughness;
# endif
vec3 V = cameraVec;
vec4 rand = texelfetch_noise_tex(gl_FragCoord.xy);
/* ---------------------------------------------------------------- */
/* -------------------- SCENE LIGHTS LIGHTING --------------------- */
/* ---------------------------------------------------------------- */
# ifdef CLOSURE_GLOSSY
vec2 lut_uv = lut_coords_ltc(dot(N, V), roughness);
vec4 ltc_mat = texture(utilTex, vec3(lut_uv, 0.0)).rgba;
# endif
# ifdef CLOSURE_CLEARCOAT
vec2 lut_uv_clear = lut_coords_ltc(dot(C_N, V), C_roughness);
vec4 ltc_mat_clear = texture(utilTex, vec3(lut_uv_clear, 0.0)).rgba;
vec3 out_spec_clear = vec3(0.0);
# endif
float tracing_depth = gl_FragCoord.z;
/* Constant bias (due to depth buffer precision) */
/* Magic numbers for 24bits of precision.
* From http://terathon.com/gdc07_lengyel.pdf (slide 26) */
tracing_depth -= mix(2.4e-7, 4.8e-7, gl_FragCoord.z);
/* Convert to view Z. */
tracing_depth = get_view_z_from_depth(tracing_depth);
vec3 true_normal = normalize(cross(dFdx(viewPosition), dFdy(viewPosition)));
for (int i = 0; i < MAX_LIGHT && i < laNumLight; i++) {
LightData ld = lights_data[i];
vec4 l_vector; /* Non-Normalized Light Vector with length in last component. */
l_vector.xyz = ld.l_position - worldPosition;
l_vector.w = length(l_vector.xyz);
float l_vis = light_visibility(ld,
worldPosition,
viewPosition,
tracing_depth,
true_normal,
rand.x,
use_contact_shadows,
l_vector);
if (l_vis < 1e-8) {
continue;
}
vec3 l_color_vis = ld.l_color * l_vis;
# ifdef CLOSURE_DIFFUSE
out_diff += l_color_vis * light_diffuse(ld, N, V, l_vector);
# endif
# ifdef CLOSURE_GLOSSY
out_spec += l_color_vis * light_specular(ld, ltc_mat, N, V, l_vector) * ld.l_spec;
# endif
# ifdef CLOSURE_CLEARCOAT
out_spec_clear += l_color_vis * light_specular(ld, ltc_mat_clear, C_N, V, l_vector) *
ld.l_spec;
# endif
}
# ifdef CLOSURE_GLOSSY
vec2 brdf_lut_lights = texture(utilTex, vec3(lut_uv, 1.0)).ba;
out_spec *= F_brdf(f0, f90, brdf_lut_lights.xy);
# endif
# ifdef CLOSURE_CLEARCOAT
vec2 brdf_lut_lights_clear = texture(utilTex, vec3(lut_uv_clear, 1.0)).ba;
out_spec_clear *= F_brdf(vec3(0.04), vec3(1.0), brdf_lut_lights_clear.xy);
out_spec += out_spec_clear * C_intensity;
# endif
/* ---------------------------------------------------------------- */
/* ---------------- SPECULAR ENVIRONMENT LIGHTING ----------------- */
/* ---------------------------------------------------------------- */
/* Accumulate incoming light from all sources until accumulator is full. Then apply Occlusion and
* BRDF. */
# ifdef CLOSURE_GLOSSY
vec4 spec_accum = vec4(0.0);
# endif
# ifdef CLOSURE_CLEARCOAT
vec4 C_spec_accum = vec4(0.0);
# endif
# ifdef CLOSURE_REFRACTION
vec4 refr_accum = vec4(0.0);
# endif
# ifdef CLOSURE_GLOSSY
/* ---------------------------- */
/* Planar Reflections */
/* ---------------------------- */
for (int i = 0; i < MAX_PLANAR && i < prbNumPlanar && spec_accum.a < 0.999; i++) {
PlanarData pd = planars_data[i];
/* Fade on geometric normal. */
float fade = probe_attenuation_planar(
pd, worldPosition, (gl_FrontFacing) ? worldNormal : -worldNormal, roughness);
if (fade > 0.0) {
if (!(ssrToggle && ssr_id == outputSsrId)) {
vec3 spec = probe_evaluate_planar(float(i), pd, worldPosition, N, V, roughness, fade);
accumulate_light(spec, fade, spec_accum);
}
# ifdef CLOSURE_CLEARCOAT
vec3 C_spec = probe_evaluate_planar(float(i), pd, worldPosition, C_N, V, C_roughness, fade);
accumulate_light(C_spec, fade, C_spec_accum);
# endif
}
}
# endif
# ifdef CLOSURE_GLOSSY
vec3 spec_dir = specular_dominant_dir(N, V, roughnessSquared);
# endif
# ifdef CLOSURE_CLEARCOAT
vec3 C_spec_dir = specular_dominant_dir(C_N, V, C_roughnessSquared);
# endif
# ifdef CLOSURE_REFRACTION
/* Refract the view vector using the depth heuristic.
* Then later Refract a second time the already refracted
* ray using the inverse ior. */
float final_ior = (refractionDepth > 0.0) ? 1.0 / ior : ior;
vec3 refr_V = (refractionDepth > 0.0) ? -refract(-V, N, final_ior) : V;
vec3 refr_pos = (refractionDepth > 0.0) ?
line_plane_intersect(
worldPosition, refr_V, worldPosition - N * refractionDepth, N) :
worldPosition;
vec3 refr_dir = refraction_dominant_dir(N, refr_V, roughness, final_ior);
# endif
# ifdef CLOSURE_REFRACTION
/* ---------------------------- */
/* Screen Space Refraction */
/* ---------------------------- */
# ifdef USE_REFRACTION
if (ssrefractToggle && roughness < ssrMaxRoughness + 0.2) {
/* Find approximated position of the 2nd refraction event. */
vec3 refr_vpos = (refractionDepth > 0.0) ? transform_point(ViewMatrix, refr_pos) :
viewPosition;
vec4 trans = screen_space_refraction(refr_vpos, N, refr_V, final_ior, roughnessSquared, rand);
trans.a *= smoothstep(ssrMaxRoughness + 0.2, ssrMaxRoughness, roughness);
accumulate_light(trans.rgb, trans.a, refr_accum);
}
# endif
# endif
/* ---------------------------- */
/* Specular probes */
/* ---------------------------- */
# if defined(CLOSURE_GLOSSY) || defined(CLOSURE_REFRACTION)
# if defined(CLOSURE_GLOSSY) && defined(CLOSURE_REFRACTION)
# define GLASS_ACCUM 1
# define ACCUM min(refr_accum.a, spec_accum.a)
# elif defined(CLOSURE_REFRACTION)
# define GLASS_ACCUM 0
# define ACCUM refr_accum.a
# else
# define GLASS_ACCUM 0
# define ACCUM spec_accum.a
# endif
/* Starts at 1 because 0 is world probe */
for (int i = 1; ACCUM < 0.999 && i < prbNumRenderCube && i < MAX_PROBE; i++) {
float fade = probe_attenuation_cube(i, worldPosition);
if (fade > 0.0) {
# if GLASS_ACCUM
if (spec_accum.a < 0.999) {
# endif
# ifdef CLOSURE_GLOSSY
if (!(ssrToggle && ssr_id == outputSsrId)) {
vec3 spec = probe_evaluate_cube(i, worldPosition, spec_dir, roughness);
accumulate_light(spec, fade, spec_accum);
}
# endif
# ifdef CLOSURE_CLEARCOAT
vec3 C_spec = probe_evaluate_cube(i, worldPosition, C_spec_dir, C_roughness);
accumulate_light(C_spec, fade, C_spec_accum);
# endif
# if GLASS_ACCUM
}
# endif
# if GLASS_ACCUM
if (refr_accum.a < 0.999) {
# endif
# ifdef CLOSURE_REFRACTION
vec3 trans = probe_evaluate_cube(i, refr_pos, refr_dir, roughnessSquared);
accumulate_light(trans, fade, refr_accum);
# endif
# if GLASS_ACCUM
}
# endif
}
}
# undef GLASS_ACCUM
# undef ACCUM
/* ---------------------------- */
/* World Probe */
/* ---------------------------- */
# ifdef CLOSURE_GLOSSY
if (spec_accum.a < 0.999) {
if (!(ssrToggle && ssr_id == outputSsrId)) {
vec3 spec = probe_evaluate_world_spec(spec_dir, roughness);
accumulate_light(spec, 1.0, spec_accum);
}
# ifdef CLOSURE_CLEARCOAT
vec3 C_spec = probe_evaluate_world_spec(C_spec_dir, C_roughness);
accumulate_light(C_spec, 1.0, C_spec_accum);
# endif
}
# endif
# ifdef CLOSURE_REFRACTION
if (refr_accum.a < 0.999) {
vec3 trans = probe_evaluate_world_spec(refr_dir, roughnessSquared);
accumulate_light(trans, 1.0, refr_accum);
}
# endif
# endif /* Specular probes */
/* ---------------------------- */
/* Ambient Occlusion */
/* ---------------------------- */
# if defined(CLOSURE_GLOSSY) || defined(CLOSURE_DIFFUSE)
if (!use_contact_shadows) {
/* HACK: Fix for translucent BSDF. (see T65631) */
N = -N;
}
vec3 bent_normal;
float final_ao = occlusion_compute(N, viewPosition, ao, rand, bent_normal);
if (!use_contact_shadows) {
N = -N;
/* Bypass bent normal. */
bent_normal = N;
}
# endif
/* ---------------------------- */
/* Specular Output */
/* ---------------------------- */
float NV = dot(N, V);
# ifdef CLOSURE_GLOSSY
vec2 uv = lut_coords(NV, roughness);
vec2 brdf_lut = texture(utilTex, vec3(uv, 1.0)).rg;
/* This factor is outputted to be used by SSR in order
* to match the intensity of the regular reflections. */
ssr_spec = F_brdf(f0, f90, brdf_lut);
float spec_occlu = specular_occlusion(NV, final_ao, roughness);
/* The SSR pass recompute the occlusion to not apply it to the SSR */
if (ssrToggle && ssr_id == outputSsrId) {
spec_occlu = 1.0;
}
out_spec += spec_accum.rgb * ssr_spec * spec_occlu;
# endif
# ifdef CLOSURE_REFRACTION
float btdf = get_btdf_lut(NV, roughness, ior);
out_refr += refr_accum.rgb * btdf;
/* Global toggle for lightprobe baking. */
out_refr *= float(specToggle);
# endif
# ifdef CLOSURE_CLEARCOAT
NV = dot(C_N, V);
vec2 C_uv = lut_coords(NV, C_roughness);
vec2 C_brdf_lut = texture(utilTex, vec3(C_uv, 1.0)).rg;
vec3 C_fresnel = F_brdf(vec3(0.04), vec3(1.0), C_brdf_lut) *
specular_occlusion(NV, final_ao, C_roughness);
out_spec += C_spec_accum.rgb * C_fresnel * C_intensity;
# endif
# ifdef CLOSURE_GLOSSY
/* Global toggle for lightprobe baking. */
out_spec *= float(specToggle);
# endif
/* ---------------------------------------------------------------- */
/* ---------------- DIFFUSE ENVIRONMENT LIGHTING ------------------ */
/* ---------------------------------------------------------------- */
/* Accumulate light from all sources until accumulator is full. Then apply Occlusion and BRDF. */
# ifdef CLOSURE_DIFFUSE
vec4 diff_accum = vec4(0.0);
/* ---------------------------- */
/* Irradiance Grids */
/* ---------------------------- */
/* Start at 1 because 0 is world irradiance */
for (int i = 1; i < MAX_GRID && i < prbNumRenderGrid && diff_accum.a < 0.999; i++) {
GridData gd = grids_data[i];
vec3 localpos;
float fade = probe_attenuation_grid(gd, grids_data[i].localmat, worldPosition, localpos);
if (fade > 0.0) {
vec3 diff = probe_evaluate_grid(gd, worldPosition, bent_normal, localpos);
accumulate_light(diff, fade, diff_accum);
}
}
/* ---------------------------- */
/* World Diffuse */
/* ---------------------------- */
if (diff_accum.a < 0.999 && prbNumRenderGrid > 0) {
vec3 diff = probe_evaluate_world_diff(bent_normal);
accumulate_light(diff, 1.0, diff_accum);
}
out_diff += diff_accum.rgb * gtao_multibounce(final_ao, albedo);
# endif
#endif
}
/* Cleanup for next configuration */
#undef CLOSURE_NAME
#ifdef CLOSURE_DIFFUSE
# undef CLOSURE_DIFFUSE
#endif
#ifdef CLOSURE_GLOSSY
# undef CLOSURE_GLOSSY
#endif
#ifdef CLOSURE_CLEARCOAT
# undef CLOSURE_CLEARCOAT
#endif
#ifdef CLOSURE_REFRACTION
# undef CLOSURE_REFRACTION
#endif
#ifdef CLOSURE_SUBSURFACE
# undef CLOSURE_SUBSURFACE
#endif

View File

@@ -147,17 +147,27 @@ Closure closure_emission(vec3 rgb)
#ifndef VOLUMETRICS
/* Let radiance passthrough or replace it to get the BRDF and color
* to applied to the SSR result. */
vec3 closure_mask_ssr_radiance(vec3 radiance, float ssr_id)
{
return (ssrToggle && int(ssr_id) == outputSsrId) ? vec3(1.0) : radiance;
}
void closure_load_ssr_data(
vec3 ssr_spec, float roughness, vec3 N, vec3 viewVec, int ssr_id, inout Closure cl)
vec3 ssr_radiance, float roughness, vec3 N, float ssr_id, inout Closure cl)
{
/* Still encode to avoid artifacts in the SSR pass. */
vec3 vN = normalize(mat3(ViewMatrix) * N);
cl.ssr_normal = normal_encode(vN, viewVec);
cl.ssr_normal = normal_encode(vN, viewCameraVec);
if (ssr_id == outputSsrId) {
cl.ssr_data = vec4(ssr_spec, roughness);
if (ssrToggle && int(ssr_id) == outputSsrId) {
cl.ssr_data = vec4(ssr_radiance, roughness);
cl.flag |= CLOSURE_SSR_FLAG;
}
else {
cl.radiance += ssr_radiance;
}
}
void closure_load_sss_data(
@@ -169,13 +179,11 @@ void closure_load_sss_data(
cl.sss_radius = radius;
cl.sss_albedo = sss_albedo;
cl.flag |= CLOSURE_SSS_FLAG;
cl.radiance += render_pass_diffuse_mask(sss_albedo, vec3(0));
/* Irradiance will be convolved by SSSS pass. Do not add to radiance. */
sss_irradiance = vec3(0);
}
else
# endif
{
cl.radiance += render_pass_diffuse_mask(sss_albedo, sss_irradiance * sss_albedo);
}
cl.radiance += render_pass_diffuse_mask(vec3(1), sss_irradiance) * sss_albedo;
}
#endif

View File

@@ -12,11 +12,20 @@ uniform sampler2DArray utilTex;
#define LUT_SIZE 64
#define texelfetch_noise_tex(coord) texelFetch(utilTex, ivec3(ivec2(coord) % LUT_SIZE, 2.0), 0)
#define LTC_MAT_LAYER 0
#define LTC_BRDF_LAYER 1
#define BRDF_LUT_LAYER 1
#define NOISE_LAYER 2
#define LTC_DISK_LAYER 3 /* UNUSED */
/* Layers 4 to 20 are for BTDF Lut. */
#define texelfetch_noise_tex(coord) \
texelFetch(utilTex, ivec3(ivec2(coord) % LUT_SIZE, NOISE_LAYER), 0)
/* Return texture coordinates to sample Surface LUT */
vec2 lut_coords(float cosTheta, float roughness)
{
/* TODO(fclem) Ugly Acos here. Get rid ot this. Should use same mapping as lut_coords_ltc. */
float theta = acos(cosTheta);
vec2 coords = vec2(roughness, theta / M_PI_2);
@@ -32,6 +41,11 @@ vec2 lut_coords_ltc(float cosTheta, float roughness)
return coords * (LUT_SIZE - 1.0) / LUT_SIZE + 0.5 / LUT_SIZE;
}
vec2 brdf_lut(float cosTheta, float roughness)
{
return textureLod(utilTex, vec3(lut_coords(cosTheta, roughness), BRDF_LUT_LAYER), 0.0).rg;
}
float get_btdf_lut(float NV, float roughness, float ior)
{
const vec3 lut_scale_bias_texel_size = vec3((LUT_SIZE - 1.0), 0.5, 1.5) / LUT_SIZE;

View File

@@ -2,6 +2,8 @@
#pragma BLENDER_REQUIRE(common_math_lib.glsl)
#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl)
#pragma BLENDER_REQUIRE(closure_eval_glossy_lib.glsl)
#pragma BLENDER_REQUIRE(closure_eval_lib.glsl)
#pragma BLENDER_REQUIRE(raytrace_lib.glsl)
#pragma BLENDER_REQUIRE(lightprobe_lib.glsl)
#pragma BLENDER_REQUIRE(ssr_lib.glsl)
@@ -170,7 +172,7 @@ void main()
/* Importance sampling bias */
rand.x = mix(rand.x, 0.0, ssrBrdfBias);
vec3 worldPosition = transform_point(ViewMatrixInverse, viewPosition);
vec3 W = transform_point(ViewMatrixInverse, viewPosition);
vec3 wN = transform_direction(ViewMatrixInverse, N);
vec3 T, B;
@@ -180,12 +182,12 @@ void main()
for (int i = 0; i < MAX_PLANAR && i < prbNumPlanar; i++) {
PlanarData pd = planars_data[i];
float fade = probe_attenuation_planar(pd, worldPosition, wN, 0.0);
float fade = probe_attenuation_planar(pd, W, wN, 0.0);
if (fade > 0.5) {
/* Find view vector / reflection plane intersection. */
/* TODO optimize, use view space for all. */
vec3 tracePosition = line_plane_intersect(worldPosition, cameraVec, pd.pl_plane_eq);
vec3 tracePosition = line_plane_intersect(W, cameraVec, pd.pl_plane_eq);
tracePosition = transform_point(ViewMatrix, tracePosition);
vec3 planeNormal = transform_direction(ViewMatrix, pd.pl_normal);
@@ -213,6 +215,8 @@ uniform sampler2D pdfBuffer;
uniform int neighborOffset;
in vec4 uvcoordsvar;
const ivec2 neighbors[32] = ivec2[32](ivec2(0, 0),
ivec2(1, 1),
ivec2(-2, 0),
@@ -298,7 +302,7 @@ float get_sample_depth(vec2 hit_co, bool is_planar, float planar_index)
vec3 get_hit_vector(vec3 hit_pos,
PlanarData pd,
vec3 worldPosition,
vec3 P,
vec3 N,
vec3 V,
bool is_planar,
@@ -309,7 +313,7 @@ vec3 get_hit_vector(vec3 hit_pos,
if (is_planar) {
/* Reflect back the hit position to have it in non-reflected world space */
vec3 trace_pos = line_plane_intersect(worldPosition, V, pd.pl_plane_eq);
vec3 trace_pos = line_plane_intersect(P, V, pd.pl_plane_eq);
hit_vec = hit_pos - trace_pos;
hit_vec = reflect(hit_vec, pd.pl_normal);
/* Modify here so mip texel alignment is correct. */
@@ -317,8 +321,8 @@ vec3 get_hit_vector(vec3 hit_pos,
}
else {
/* Find hit position in previous frame. */
hit_co = get_reprojected_reflection(hit_pos, worldPosition, N);
hit_vec = hit_pos - worldPosition;
hit_co = get_reprojected_reflection(hit_pos, P, N);
hit_vec = hit_pos - P;
}
mask = screen_border_mask(hit_co);
@@ -339,7 +343,7 @@ vec4 get_ssr_samples(vec4 hit_pdf,
ivec4 hit_data[2],
PlanarData pd,
float planar_index,
vec3 worldPosition,
vec3 P,
vec3 N,
vec3 V,
float roughnessSquared,
@@ -379,14 +383,10 @@ vec4 get_ssr_samples(vec4 hit_pdf,
/* Get actual hit vector and hit coordinate (from last frame). */
vec4 mask = vec4(1.0);
hit_pos[0] = get_hit_vector(
hit_pos[0], pd, worldPosition, N, V, is_planar.x, hit_co[0].xy, mask.x);
hit_pos[1] = get_hit_vector(
hit_pos[1], pd, worldPosition, N, V, is_planar.y, hit_co[0].zw, mask.y);
hit_pos[2] = get_hit_vector(
hit_pos[2], pd, worldPosition, N, V, is_planar.z, hit_co[1].xy, mask.z);
hit_pos[3] = get_hit_vector(
hit_pos[3], pd, worldPosition, N, V, is_planar.w, hit_co[1].zw, mask.w);
hit_pos[0] = get_hit_vector(hit_pos[0], pd, P, N, V, is_planar.x, hit_co[0].xy, mask.x);
hit_pos[1] = get_hit_vector(hit_pos[1], pd, P, N, V, is_planar.y, hit_co[0].zw, mask.y);
hit_pos[2] = get_hit_vector(hit_pos[2], pd, P, N, V, is_planar.z, hit_co[1].xy, mask.z);
hit_pos[3] = get_hit_vector(hit_pos[3], pd, P, N, V, is_planar.w, hit_co[1].zw, mask.w);
vec4 hit_dist;
hit_dist.x = length(hit_pos[0]);
@@ -476,47 +476,30 @@ vec4 get_ssr_samples(vec4 hit_pdf,
return accum;
}
void main()
void raytrace_resolve(ClosureInputGlossy cl_in,
inout ClosureEvalGlossy cl_eval,
inout ClosureEvalCommon cl_common,
inout ClosureOutputGlossy cl_out)
{
ivec2 fullres_texel = ivec2(gl_FragCoord.xy);
# ifdef FULLRES
ivec2 halfres_texel = fullres_texel;
ivec2 texel = ivec2(gl_FragCoord.xy);
# else
ivec2 halfres_texel = ivec2(gl_FragCoord.xy / 2.0);
ivec2 texel = ivec2(gl_FragCoord.xy / 2.0);
# endif
vec2 uvs = gl_FragCoord.xy / vec2(textureSize(depthBuffer, 0));
float depth = textureLod(depthBuffer, uvs, 0.0).r;
/* Early out */
if (depth == 1.0) {
discard;
}
/* Using world space */
vec3 viewPosition = get_view_space_from_depth(uvs, depth); /* Needed for viewCameraVec */
vec3 worldPosition = transform_point(ViewMatrixInverse, viewPosition);
vec3 V = cameraVec;
vec3 vN = normal_decode(texelFetch(normalBuffer, fullres_texel, 0).rg, viewCameraVec);
vec3 N = transform_direction(ViewMatrixInverse, vN);
vec4 speccol_roughness = texelFetch(specroughBuffer, fullres_texel, 0).rgba;
vec3 V = cl_common.V;
vec3 N = cl_in.N;
vec3 P = cl_common.P;
/* Early out */
if (dot(speccol_roughness.rgb, vec3(1.0)) == 0.0) {
discard;
}
float roughness = speccol_roughness.a;
float roughnessSquared = max(1e-3, roughness * roughness);
vec4 spec_accum = vec4(0.0);
float roughness = cl_in.roughness;
float roughnessSquared = max(1e-3, sqr(roughness));
/* Resolve SSR */
float cone_cos = cone_cosine(roughnessSquared);
float cone_tan = sqrt(1 - cone_cos * cone_cos) / cone_cos;
cone_tan *= mix(saturate(dot(N, -V) * 2.0), 1.0, roughness); /* Elongation fit */
vec2 source_uvs = project_point(pastViewProjectionMatrix, worldPosition).xy * 0.5 + 0.5;
vec2 source_uvs = project_point(pastViewProjectionMatrix, P).xy * 0.5 + 0.5;
vec4 ssr_accum = vec4(0.0);
float weight_acc = 0.0;
@@ -525,16 +508,16 @@ void main()
/* TODO optimize with textureGather */
/* Doing these fetches early to hide latency. */
vec4 hit_pdf;
hit_pdf.x = texelFetch(pdfBuffer, halfres_texel + neighbors[0 + neighborOffset], 0).r;
hit_pdf.y = texelFetch(pdfBuffer, halfres_texel + neighbors[1 + neighborOffset], 0).r;
hit_pdf.z = texelFetch(pdfBuffer, halfres_texel + neighbors[2 + neighborOffset], 0).r;
hit_pdf.w = texelFetch(pdfBuffer, halfres_texel + neighbors[3 + neighborOffset], 0).r;
hit_pdf.x = texelFetch(pdfBuffer, texel + neighbors[0 + neighborOffset], 0).r;
hit_pdf.y = texelFetch(pdfBuffer, texel + neighbors[1 + neighborOffset], 0).r;
hit_pdf.z = texelFetch(pdfBuffer, texel + neighbors[2 + neighborOffset], 0).r;
hit_pdf.w = texelFetch(pdfBuffer, texel + neighbors[3 + neighborOffset], 0).r;
ivec4 hit_data[2];
hit_data[0].xy = texelFetch(hitBuffer, halfres_texel + neighbors[0 + neighborOffset], 0).rg;
hit_data[0].zw = texelFetch(hitBuffer, halfres_texel + neighbors[1 + neighborOffset], 0).rg;
hit_data[1].xy = texelFetch(hitBuffer, halfres_texel + neighbors[2 + neighborOffset], 0).rg;
hit_data[1].zw = texelFetch(hitBuffer, halfres_texel + neighbors[3 + neighborOffset], 0).rg;
hit_data[0].xy = texelFetch(hitBuffer, texel + neighbors[0 + neighborOffset], 0).rg;
hit_data[0].zw = texelFetch(hitBuffer, texel + neighbors[1 + neighborOffset], 0).rg;
hit_data[1].xy = texelFetch(hitBuffer, texel + neighbors[2 + neighborOffset], 0).rg;
hit_data[1].zw = texelFetch(hitBuffer, texel + neighbors[3 + neighborOffset], 0).rg;
/* Find Planar Reflections affecting this pixel */
PlanarData pd;
@@ -542,7 +525,7 @@ void main()
for (int i = 0; i < MAX_PLANAR && i < prbNumPlanar; i++) {
pd = planars_data[i];
float fade = probe_attenuation_planar(pd, worldPosition, N, 0.0);
float fade = probe_attenuation_planar(pd, P, N, 0.0);
if (fade > 0.5) {
planar_index = float(i);
@@ -554,7 +537,7 @@ void main()
hit_data,
pd,
planar_index,
worldPosition,
P,
N,
V,
roughnessSquared,
@@ -564,19 +547,51 @@ void main()
}
/* Compute SSR contribution */
if (weight_acc > 0.0) {
ssr_accum /= weight_acc;
/* fade between 0.5 and 1.0 roughness */
ssr_accum.a *= smoothstep(ssrMaxRoughness + 0.2, ssrMaxRoughness, roughness);
accumulate_light(ssr_accum.rgb, ssr_accum.a, spec_accum);
ssr_accum *= (weight_acc == 0.0) ? 0.0 : (1.0 / weight_acc);
/* fade between 0.5 and 1.0 roughness */
ssr_accum.a *= smoothstep(ssrMaxRoughness + 0.2, ssrMaxRoughness, roughness);
cl_eval.raytrace_radiance = ssr_accum.rgb * ssr_accum.a;
cl_common.specular_accum -= ssr_accum.a;
}
CLOSURE_EVAL_FUNCTION_DECLARE_1(ssr_resolve, Glossy)
void main()
{
ivec2 texel = ivec2(gl_FragCoord.xy);
float depth = texelFetch(depthBuffer, texel, 0).r;
if (depth == 1.0) {
discard;
}
/* If SSR contribution is not 1.0, blend with cubemaps */
if (spec_accum.a < 1.0) {
fallback_cubemap(N, V, worldPosition, viewPosition, roughness, roughnessSquared, spec_accum);
vec4 speccol_roughness = texelFetch(specroughBuffer, texel, 0).rgba;
vec3 brdf = speccol_roughness.rgb;
float roughness = speccol_roughness.a;
if (max_v3(brdf) <= 0.0) {
discard;
}
fragColor = vec4(spec_accum.rgb * speccol_roughness.rgb, 1.0);
viewPosition = get_view_space_from_depth(uvcoordsvar.xy, depth);
worldPosition = transform_point(ViewMatrixInverse, viewPosition);
vec2 normal_encoded = texelFetch(normalBuffer, texel, 0).rg;
viewNormal = normal_decode(normal_encoded, viewCameraVec);
worldNormal = transform_direction(ViewMatrixInverse, viewNormal);
CLOSURE_VARS_DECLARE_1(Glossy);
in_Glossy_0.N = worldNormal;
in_Glossy_0.roughness = roughness;
/* Do a full deferred evaluation of the glossy BSDF. The only difference is that we inject the
* SSR resolve before the cubemap iter. BRDF term is already computed during main pass and is
* passed as specular color. */
CLOSURE_EVAL_FUNCTION_1(ssr_resolve, Glossy);
fragColor = vec4(out_Glossy_0.radiance * brdf, 1.0);
}
#endif

View File

@@ -137,9 +137,9 @@ float probe_attenuation_planar(PlanarData pd, vec3 W, vec3 N, float roughness)
return fac;
}
float probe_attenuation_grid(GridData gd, mat4 localmat, vec3 W, out vec3 localpos)
float probe_attenuation_grid(GridData gd, vec3 W, out vec3 localpos)
{
localpos = transform_point(localmat, W);
localpos = transform_point(gd.localmat, W);
vec3 pos_to_edge = max(vec3(0.0), abs(localpos) - 1.0);
float fade = length(pos_to_edge);
return saturate(-fade * gd.g_atten_scale + gd.g_atten_bias);
@@ -183,8 +183,7 @@ vec3 probe_evaluate_world_spec(vec3 R, float roughness)
return textureLod_cubemapArray(probeCubes, vec4(R, 0.0), roughness * prbLodCubeMax).rgb;
}
vec3 probe_evaluate_planar(
float id, PlanarData pd, vec3 W, vec3 N, vec3 V, float roughness, inout float fade)
vec3 probe_evaluate_planar(int id, PlanarData pd, vec3 W, vec3 N, vec3 V, float roughness)
{
/* Find view vector / reflection plane intersection. */
vec3 point_on_plane = line_plane_intersect(W, V, pd.pl_plane_eq);
@@ -226,7 +225,7 @@ void fallback_cubemap(vec3 N,
#ifdef SSR_AO
vec4 rand = texelfetch_noise_tex(gl_FragCoord.xy);
vec3 bent_normal;
float final_ao = occlusion_compute(N, viewPosition, 1.0, rand, bent_normal);
float final_ao = occlusion_compute(N, viewPosition, rand, bent_normal);
final_ao = specular_occlusion(dot(N, V), final_ao, roughness);
#else
const float final_ao = 1.0;

View File

@@ -252,32 +252,29 @@ float light_attenuation(LightData ld, vec4 l_vector)
return vis;
}
float light_shadowing(LightData ld,
vec3 W,
#ifndef VOLUMETRICS
vec3 viewPosition,
float tracing_depth,
vec3 true_normal,
float rand_x,
const bool use_contact_shadows,
#endif
float vis)
float light_shadowing(LightData ld, vec3 W, float vis)
{
#if !defined(VOLUMETRICS) || defined(VOLUME_SHADOW)
/* shadowing */
if (ld.l_shadowid >= 0.0 && vis > 0.001) {
if (ld.l_type == SUN) {
vis *= sample_cascade_shadow(int(ld.l_shadowid), W);
}
else {
vis *= sample_cube_shadow(int(ld.l_shadowid), W);
}
}
#endif
return vis;
}
# ifndef VOLUMETRICS
#ifndef VOLUMETRICS
float light_contact_shadows(
LightData ld, vec3 P, vec3 vP, float tracing_depth, vec3 vNg, float rand_x, float vis)
{
if (ld.l_shadowid >= 0.0 && vis > 0.001) {
ShadowData sd = shadows_data[int(ld.l_shadowid)];
/* Only compute if not already in shadow. */
if (use_contact_shadows && sd.sh_contact_dist > 0.0 && vis > 1e-8) {
if (sd.sh_contact_dist > 0.0) {
/* Contact Shadows. */
vec3 ray_ori, ray_dir;
float trace_distance;
@@ -287,54 +284,34 @@ float light_shadowing(LightData ld,
ray_dir = shadows_cascade_data[int(sd.sh_data_index)].sh_shadow_vec * trace_distance;
}
else {
ray_dir = shadows_cube_data[int(sd.sh_data_index)].position.xyz - W;
ray_dir = shadows_cube_data[int(sd.sh_data_index)].position.xyz - P;
float len = length(ray_dir);
trace_distance = min(sd.sh_contact_dist, len);
ray_dir *= trace_distance / len;
}
ray_dir = transform_direction(ViewMatrix, ray_dir);
ray_ori = vec3(viewPosition.xy, tracing_depth) + true_normal * sd.sh_contact_offset;
ray_ori = vec3(vP.xy, tracing_depth) + vNg * sd.sh_contact_offset;
vec3 hit_pos = raycast(
-1, ray_ori, ray_dir, sd.sh_contact_thickness, rand_x, 0.1, 0.001, false);
if (hit_pos.z > 0.0) {
hit_pos = get_view_space_from_depth(hit_pos.xy, hit_pos.z);
float hit_dist = distance(viewPosition, hit_pos);
float hit_dist = distance(vP, hit_pos);
float dist_ratio = hit_dist / trace_distance;
return vis * saturate(dist_ratio * 3.0 - 2.0);
return saturate(dist_ratio * 3.0 - 2.0);
}
}
# endif /* VOLUMETRICS */
}
#endif
return vis;
return 1.0;
}
#endif /* VOLUMETRICS */
float light_visibility(LightData ld,
vec3 W,
#ifndef VOLUMETRICS
vec3 viewPosition,
float tracing_depth,
vec3 true_normal,
float rand_x,
const bool use_contact_shadows,
#endif
vec4 l_vector)
float light_visibility(LightData ld, vec3 W, vec4 l_vector)
{
float l_atten = light_attenuation(ld, l_vector);
return light_shadowing(ld,
W,
#ifndef VOLUMETRICS
viewPosition,
tracing_depth,
true_normal,
rand_x,
use_contact_shadows,
#endif
l_atten);
return light_shadowing(ld, W, l_atten);
}
float light_diffuse(LightData ld, vec3 N, vec3 V, vec4 l_vector)

View File

@@ -5,8 +5,12 @@
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
#pragma BLENDER_REQUIRE(common_uniforms_lib.glsl)
#pragma BLENDER_REQUIRE(closure_lib.glsl)
#pragma BLENDER_REQUIRE(closure_lit_lib.glsl)
#pragma BLENDER_REQUIRE(closure_type_lib.glsl)
#pragma BLENDER_REQUIRE(closure_eval_lib.glsl)
#pragma BLENDER_REQUIRE(closure_eval_diffuse_lib.glsl)
#pragma BLENDER_REQUIRE(closure_eval_glossy_lib.glsl)
#pragma BLENDER_REQUIRE(closure_eval_translucent_lib.glsl)
#pragma BLENDER_REQUIRE(closure_eval_refraction_lib.glsl)
#pragma BLENDER_REQUIRE(surface_lib.glsl)
#ifdef USE_ALPHA_HASH

View File

@@ -39,7 +39,7 @@ void main()
vec3 viewPosition = get_view_space_from_depth(uvs, depth);
vec3 worldPosition = transform_point(ViewMatrixInverse, viewPosition);
vec3 true_normal = normalize(cross(dFdx(viewPosition), dFdy(viewPosition)));
vec3 true_normal = safe_normalize(cross(dFdx(viewPosition), dFdy(viewPosition)));
for (int i = 0; i < MAX_LIGHT && i < laNumLight; i++) {
LightData ld = lights_data[i];
@@ -48,8 +48,10 @@ void main()
l_vector.xyz = ld.l_position - worldPosition;
l_vector.w = length(l_vector.xyz);
float l_vis = light_shadowing(
ld, worldPosition, viewPosition, tracing_depth, true_normal, rand.x, true, 1.0);
float l_vis = light_shadowing(ld, worldPosition, 1.0);
l_vis *= light_contact_shadows(
ld, worldPosition, viewPosition, tracing_depth, true_normal, rand.x, 1.0);
accum_light += l_vis;
}

View File

@@ -3,8 +3,13 @@
#pragma BLENDER_REQUIRE(common_hair_lib.glsl)
#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl)
#pragma BLENDER_REQUIRE(closure_lib.glsl)
#pragma BLENDER_REQUIRE(closure_lit_lib.glsl)
#pragma BLENDER_REQUIRE(closure_type_lib.glsl)
#pragma BLENDER_REQUIRE(closure_eval_lib.glsl)
#pragma BLENDER_REQUIRE(closure_eval_diffuse_lib.glsl)
#pragma BLENDER_REQUIRE(closure_eval_glossy_lib.glsl)
#pragma BLENDER_REQUIRE(closure_eval_translucent_lib.glsl)
#pragma BLENDER_REQUIRE(closure_eval_refraction_lib.glsl)
#pragma BLENDER_REQUIRE(surface_lib.glsl)
#pragma BLENDER_REQUIRE(volumetric_lib.glsl)

View File

@@ -13,7 +13,15 @@ uniform float refractionDepth;
vec3 worldNormal; \
vec3 viewNormal;
#ifdef GPU_GEOMETRY_SHADER
#if defined(STEP_RESOLVE) || defined(STEP_RAYTRACE)
/* SSR will set these global variables itself.
* Also make false positive compiler warnings disapear by setting values. */
vec3 worldPosition = vec3(0);
vec3 viewPosition = vec3(0);
vec3 worldNormal = vec3(0);
vec3 viewNormal = vec3(0);
#elif defined(GPU_GEOMETRY_SHADER)
in ShaderStageInterface{SURFACE_INTERFACE} dataIn[];
out ShaderStageInterface{SURFACE_INTERFACE} dataOut;
@@ -24,7 +32,7 @@ out ShaderStageInterface{SURFACE_INTERFACE} dataOut;
dataOut.worldNormal = dataIn[vert].worldNormal; \
dataOut.viewNormal = dataIn[vert].viewNormal;
#else
#else /* GPU_VERTEX_SHADER || GPU_FRAGMENT_SHADER*/
IN_OUT ShaderStageInterface{SURFACE_INTERFACE};

View File

@@ -1,6 +1,6 @@
#pragma BLENDER_REQUIRE(volumetric_lib.glsl)
#pragma BLENDER_REQUIRE(closure_lib.glsl)
#pragma BLENDER_REQUIRE(closure_type_lib.glsl)
/* Based on Frosbite Unified Volumetric.
* https://www.ea.com/frostbite/news/physically-based-unified-volumetric-rendering-in-frostbite */

View File

@@ -72,6 +72,9 @@ float sum(vec4 v) { return dot(vec4(1.0), v); }
float avg(vec2 v) { return dot(vec2(1.0 / 2.0), v); }
float avg(vec3 v) { return dot(vec3(1.0 / 3.0), v); }
float avg(vec4 v) { return dot(vec4(1.0 / 4.0), v); }
float sqr(float v) { return v * v; }
/* clang-format on */
#define saturate(a) clamp(a, 0.0, 1.0)
@@ -93,6 +96,15 @@ float len_squared(vec3 a)
return dot(a, a);
}
vec3 safe_normalize(vec3 v)
{
float len = length(v);
if (isnan(len) || len == 0.0) {
return vec3(1.0, 0.0, 0.0);
}
return v / len;
}
/** \} */
/* ---------------------------------------------------------------------- */

View File

@@ -3374,13 +3374,6 @@ static void node_geometry_buts_points_to_volume(uiLayout *layout,
uiItemR(layout, ptr, "input_type_radius", DEFAULT_FLAGS, IFACE_("Radius"), ICON_NONE);
}
static void node_geometry_buts_collection_info(uiLayout *layout,
bContext *UNUSED(C),
PointerRNA *ptr)
{
uiItemR(layout, ptr, "transform_space", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
}
static void node_geometry_set_butfunc(bNodeType *ntype)
{
switch (ntype->type) {
@@ -3441,9 +3434,6 @@ static void node_geometry_set_butfunc(bNodeType *ntype)
case GEO_NODE_POINTS_TO_VOLUME:
ntype->draw_buttons = node_geometry_buts_points_to_volume;
break;
case GEO_NODE_COLLECTION_INFO:
ntype->draw_buttons = node_geometry_buts_collection_info;
break;
}
}

View File

@@ -51,7 +51,6 @@ typedef struct bNodeLinkDrag {
* This way the links can be added to the node tree while being stored in this list.
*/
ListBase links;
bool from_multi_input_socket;
int in_out;
} bNodeLinkDrag;

View File

@@ -836,38 +836,31 @@ static bNodeLinkDrag *node_link_init(Main *bmain, SpaceNode *snode, float cursor
nldrag = MEM_callocN(sizeof(bNodeLinkDrag), "drag link op customdata");
const int num_links = nodeCountSocketLinks(snode->edittree, sock);
if (num_links > 0) {
int link_limit = nodeSocketLinkLimit(sock);
if (num_links > 0 && (num_links >= link_limit || detach)) {
/* dragged links are fixed on output side */
nldrag->in_out = SOCK_OUT;
/* detach current links and store them in the operator data */
bNodeLink *link_to_pick;
LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &snode->edittree->links) {
if (link->tosock == sock) {
if (sock->flag & SOCK_MULTI_INPUT) {
nldrag->from_multi_input_socket = true;
LinkData *linkdata = MEM_callocN(sizeof(LinkData), "drag link op link data");
bNodeLink *oplink = MEM_callocN(sizeof(bNodeLink), "drag link op link");
linkdata->data = oplink;
*oplink = *link;
oplink->next = oplink->prev = NULL;
oplink->flag |= NODE_LINK_VALID;
oplink->flag &= ~NODE_LINK_TEST;
if (node_connected_to_output(bmain, snode->edittree, link->tonode)) {
oplink->flag |= NODE_LINK_TEST;
}
link_to_pick = link;
}
}
if (link_to_pick != NULL && !nldrag->from_multi_input_socket) {
LinkData *linkdata = MEM_callocN(sizeof(LinkData), "drag link op link data");
bNodeLink *oplink = MEM_callocN(sizeof(bNodeLink), "drag link op link");
linkdata->data = oplink;
*oplink = *link_to_pick;
oplink->next = oplink->prev = NULL;
oplink->flag |= NODE_LINK_VALID;
oplink->flag &= ~NODE_LINK_TEST;
if (node_connected_to_output(bmain, snode->edittree, link_to_pick->tonode)) {
oplink->flag |= NODE_LINK_TEST;
}
BLI_addtail(&nldrag->links, linkdata);
nodeRemLink(snode->edittree, link);
BLI_addtail(&nldrag->links, linkdata);
nodeRemLink(snode->edittree, link_to_pick);
/* send changed event to original link->tonode */
if (node) {
snode_update(snode, node);
/* send changed event to original link->tonode */
if (node) {
snode_update(snode, node);
}
}
}
}
@@ -903,8 +896,6 @@ static int node_link_invoke(bContext *C, wmOperator *op, const wmEvent *event)
float cursor[2];
UI_view2d_region_to_view(&region->v2d, event->mval[0], event->mval[1], &cursor[0], &cursor[1]);
RNA_float_set_array(op->ptr, "drag_start", cursor);
RNA_boolean_set(op->ptr, "has_link_picked", false);
ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
@@ -950,28 +941,7 @@ void NODE_OT_link(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
PropertyRNA *prop;
RNA_def_boolean(ot->srna, "detach", false, "Detach", "Detach and redirect existing links");
prop = RNA_def_boolean(
ot->srna,
"has_link_picked",
false,
"Has Link Picked",
"The operation has placed a link. Only used for multi-input sockets, where the "
"link is picked later");
RNA_def_property_flag(prop, PROP_HIDDEN);
RNA_def_float_array(ot->srna,
"drag_start",
2,
0,
-UI_PRECISION_FLOAT_MAX,
UI_PRECISION_FLOAT_MAX,
"Drag Start",
"The position of the mouse cursor at the start of the operation.",
-UI_PRECISION_FLOAT_MAX,
UI_PRECISION_FLOAT_MAX);
RNA_def_property_flag(prop, PROP_HIDDEN);
}
/* ********************** Make Link operator ***************** */

View File

@@ -755,6 +755,10 @@ static void gpu_parse_material_library(GHash *hash, GPUMaterialLibrary *library)
/* get parameters */
while (*code && *code != ')') {
if (BLI_str_startswith(code, "const ")) {
code = gpu_str_skip_token(code, NULL, 0);
}
/* test if it's an input or output */
qual = FUNCTION_QUAL_IN;
if (BLI_str_startswith(code, "out ")) {

View File

@@ -4,7 +4,7 @@ void node_ambient_occlusion(
{
vec3 bent_normal;
vec4 rand = texelfetch_noise_tex(gl_FragCoord.xy);
result_ao = occlusion_compute(normalize(normal), viewPosition, 1.0, rand, bent_normal);
result_ao = occlusion_compute(normalize(normal), viewPosition, rand, bent_normal);
result_color = result_ao * color;
}
#else

View File

@@ -1,12 +1,27 @@
#ifndef VOLUMETRICS
CLOSURE_EVAL_FUNCTION_DECLARE_1(node_bsdf_diffuse, Diffuse)
void node_bsdf_diffuse(vec4 color, float roughness, vec3 N, out Closure result)
{
N = normalize(N);
CLOSURE_VARS_DECLARE_1(Diffuse);
in_Diffuse_0.N = N; /* Normalized during eval. */
in_Diffuse_0.albedo = color.rgb;
CLOSURE_EVAL_FUNCTION_1(node_bsdf_diffuse, Diffuse);
result = CLOSURE_DEFAULT;
eevee_closure_diffuse(N, color.rgb, 1.0, true, result.radiance);
result.radiance = render_pass_diffuse_mask(color.rgb, result.radiance * color.rgb);
closure_load_ssr_data(vec3(0.0), 0.0, N, viewCameraVec, -1, result);
out_Diffuse_0.radiance = render_pass_diffuse_mask(vec3(1.0), out_Diffuse_0.radiance);
out_Diffuse_0.radiance *= color.rgb;
result.radiance = out_Diffuse_0.radiance;
/* TODO(fclem) Try to not use this. */
closure_load_ssr_data(vec3(0.0), 0.0, in_Diffuse_0.N, -1.0, result);
}
#else
/* Stub diffuse because it is not compatible with volumetrics. */
# define node_bsdf_diffuse(a, b, c, d) (d = CLOSURE_DEFAULT)

View File

@@ -1,4 +1,7 @@
#ifndef VOLUMETRICS
CLOSURE_EVAL_FUNCTION_DECLARE_3(node_eevee_specular, Diffuse, Glossy, Glossy)
void node_eevee_specular(vec4 diffuse,
vec4 specular,
float roughness,
@@ -12,34 +15,63 @@ void node_eevee_specular(vec4 diffuse,
float ssr_id,
out Closure result)
{
normal = normalize(normal);
CLOSURE_VARS_DECLARE_3(Diffuse, Glossy, Glossy);
vec3 out_diff, out_spec, ssr_spec;
eevee_closure_default_clearcoat(normal,
diffuse.rgb,
specular.rgb,
vec3(1.0),
int(ssr_id),
roughness,
clearcoat_normal,
clearcoat * 0.25,
clearcoat_roughness,
occlusion,
true,
out_diff,
out_spec,
ssr_spec);
in_common.occlusion = occlusion;
in_Diffuse_0.N = normal; /* Normalized during eval. */
in_Diffuse_0.albedo = diffuse.rgb;
in_Glossy_1.N = normal; /* Normalized during eval. */
in_Glossy_1.roughness = roughness;
in_Glossy_2.N = clearcoat_normal; /* Normalized during eval. */
in_Glossy_2.roughness = clearcoat_roughness;
CLOSURE_EVAL_FUNCTION_3(node_eevee_specular, Diffuse, Glossy, Glossy);
float alpha = 1.0 - transp;
result = CLOSURE_DEFAULT;
result.radiance = render_pass_diffuse_mask(diffuse.rgb, out_diff * diffuse.rgb);
result.radiance += render_pass_glossy_mask(vec3(1.0), out_spec);
result.radiance += render_pass_emission_mask(emissive.rgb);
result.radiance *= alpha;
result.transmittance = vec3(transp);
closure_load_ssr_data(ssr_spec * alpha, roughness, normal, viewCameraVec, int(ssr_id), result);
{
/* Diffuse. */
out_Diffuse_0.radiance = render_pass_diffuse_mask(vec3(1), out_Diffuse_0.radiance);
out_Diffuse_0.radiance *= in_Diffuse_0.albedo;
result += out_Diffuse_0.radiance;
}
{
/* Glossy. */
float NV = dot(in_Glossy_1.N, cameraVec);
vec2 split_sum = brdf_lut(NV, in_Glossy_1.roughness);
vec3 brdf = F_brdf_single_scatter(specular.rgb, vec3(1.0), split_sum);
out_Glossy_1.radiance = closure_mask_ssr_radiance(out_Glossy_1.radiance, ssr_id);
out_Glossy_1.radiance *= brdf;
out_Glossy_1.radiance = render_pass_glossy_mask(spec_color, out_Glossy_1.radiance);
closure_load_ssr_data(
out_Glossy_1.radiance, in_Glossy_1.roughness, in_Glossy_1.N, ssr_id, result);
}
{
/* Clearcoat. */
float NV = dot(in_Glossy_2.N, cameraVec);
vec2 split_sum = brdf_lut(NV, in_Glossy_2.roughness);
vec3 brdf = F_brdf_single_scatter(vec3(0.04), vec3(1.0), split_sum);
out_Glossy_2.radiance *= brdf * clearcoat * 0.25;
out_Glossy_2.radiance = render_pass_glossy_mask(vec3(1), out_Glossy_2.radiance);
result.radiance += out_Glossy_2.radiance;
}
{
/* Emission. */
vec3 out_emission_radiance = render_pass_emission_mask(emission.rgb);
result.radiance += out_emission_radiance;
}
float trans = 1.0 - trans;
result.transmittance = vec3(trans);
result.radiance *= alpha;
result.ssr_data.rgb *= alpha;
}
#else
/* Stub specular because it is not compatible with volumetrics. */
# define node_eevee_specular(a, b, c, d, e, f, g, h, i, j, k, result) (result = CLOSURE_DEFAULT)

View File

@@ -1,4 +1,7 @@
#ifndef VOLUMETRICS
CLOSURE_EVAL_FUNCTION_DECLARE_2(node_bsdf_glass, Glossy, Refraction)
void node_bsdf_glass(vec4 color,
float roughness,
float ior,
@@ -7,32 +10,38 @@ void node_bsdf_glass(vec4 color,
float ssr_id,
out Closure result)
{
N = normalize(N);
vec3 out_spec, out_refr, ssr_spec;
vec3 refr_color = (refractionDepth > 0.0) ? color.rgb * color.rgb :
color.rgb; /* Simulate 2 transmission event */
eevee_closure_glass(N,
vec3(1.0),
/* HACK: Pass the multiscatter flag as the sign to not add closure
* variations or increase register usage. */
(use_multiscatter != 0.0) ? vec3(1.0) : -vec3(1.0),
int(ssr_id),
roughness,
1.0,
ior,
true,
out_spec,
out_refr,
ssr_spec);
float fresnel = F_eta(ior, dot(N, cameraVec));
vec3 vN = mat3(ViewMatrix) * N;
result = CLOSURE_DEFAULT;
result.radiance = render_pass_glossy_mask(refr_color, out_refr * refr_color) * (1.0 - fresnel);
result.radiance += render_pass_glossy_mask(color.rgb, out_spec * color.rgb) * fresnel;
CLOSURE_VARS_DECLARE_2(Glossy, Refraction);
in_Glossy_0.N = N; /* Normalized during eval. */
in_Glossy_0.roughness = roughness;
in_Refraction_1.N = N; /* Normalized during eval. */
in_Refraction_1.roughness = roughness;
in_Refraction_1.ior = ior;
CLOSURE_EVAL_FUNCTION_2(node_bsdf_glass, Glossy, Refraction);
result = CLOSURE_DEFAULT;
float fresnel = F_eta(in_Refraction_1.ior, dot(in_Glossy_0.N, cameraVec));
vec2 split_sum = brdf_lut(dot(in_Glossy_0.N, cameraVec), in_Glossy_0.roughness);
vec3 brdf = (use_multiscatter != 0.0) ? F_brdf_multi_scatter(vec3(1.0), vec3(1.0), split_sum) :
F_brdf_single_scatter(vec3(1.0), vec3(1.0), split_sum);
out_Glossy_0.radiance = closure_mask_ssr_radiance(out_Glossy_0.radiance, ssr_id);
out_Glossy_0.radiance *= brdf;
out_Glossy_0.radiance = render_pass_glossy_mask(vec3(1.0), out_Glossy_0.radiance);
out_Glossy_0.radiance *= color.rgb * fresnel;
closure_load_ssr_data(
ssr_spec * color.rgb * fresnel, roughness, N, viewCameraVec, int(ssr_id), result);
out_Glossy_0.radiance, in_Glossy_0.roughness, in_Glossy_0.N, ssr_id, result);
out_Refraction_1.radiance = render_pass_glossy_mask(vec3(1.0), out_Refraction_1.radiance);
out_Refraction_1.radiance *= color.rgb * (1.0 - fresnel);
/* Simulate 2nd absorption event. */
out_Refraction_1.radiance *= (refractionDepth > 0.0) ? color.rgb : vec3(1.0);
result.radiance += out_Refraction_1.radiance;
}
#else
/* Stub glass because it is not compatible with volumetrics. */
# define node_bsdf_glass(a, b, c, d, e, f, result) (result = CLOSURE_DEFAULT)

View File

@@ -1,23 +1,32 @@
#ifndef VOLUMETRICS
CLOSURE_EVAL_FUNCTION_DECLARE_1(node_bsdf_glossy, Glossy)
void node_bsdf_glossy(
vec4 color, float roughness, vec3 N, float use_multiscatter, float ssr_id, out Closure result)
{
N = normalize(N);
vec3 out_spec, ssr_spec;
eevee_closure_glossy(N,
vec3(1.0),
use_multiscatter != 0.0 ? vec3(1.0) : vec3(-1.0), /* HACK */
int(ssr_id),
roughness,
1.0,
true,
out_spec,
ssr_spec);
vec3 vN = mat3(ViewMatrix) * N;
bool do_ssr = (ssrToggle && int(ssr_id) == outputSsrId);
CLOSURE_VARS_DECLARE_1(Glossy);
in_Glossy_0.N = N; /* Normalized during eval. */
in_Glossy_0.roughness = roughness;
CLOSURE_EVAL_FUNCTION_1(node_bsdf_glossy, Glossy);
result = CLOSURE_DEFAULT;
result.radiance = render_pass_glossy_mask(vec3(1.0), out_spec) * color.rgb;
closure_load_ssr_data(ssr_spec * color.rgb, roughness, N, viewCameraVec, int(ssr_id), result);
vec2 split_sum = brdf_lut(dot(in_Glossy_0.N, cameraVec), in_Glossy_0.roughness);
vec3 brdf = (use_multiscatter != 0.0) ? F_brdf_multi_scatter(vec3(1.0), vec3(1.0), split_sum) :
F_brdf_single_scatter(vec3(1.0), vec3(1.0), split_sum);
out_Glossy_0.radiance = closure_mask_ssr_radiance(out_Glossy_0.radiance, ssr_id);
out_Glossy_0.radiance *= brdf;
out_Glossy_0.radiance = render_pass_glossy_mask(vec3(1.0), out_Glossy_0.radiance);
out_Glossy_0.radiance *= color.rgb;
closure_load_ssr_data(
out_Glossy_0.radiance, in_Glossy_0.roughness, in_Glossy_0.N, ssr_id, result);
}
#else
/* Stub glossy because it is not compatible with volumetrics. */
# define node_bsdf_glossy(a, b, c, d, e, result) (result = CLOSURE_DEFAULT)

View File

@@ -1,41 +1,20 @@
#ifndef VOLUMETRICS
vec3 tint_from_color(vec3 color)
{
float lum = dot(color, vec3(0.3, 0.6, 0.1)); /* luminance approx. */
return (lum > 0) ? color / lum : vec3(1.0); /* normalize lum. to isolate hue+sat */
float lum = dot(color, vec3(0.3, 0.6, 0.1)); /* luminance approx. */
return (lum > 0.0) ? color / lum : vec3(0.0); /* normalize lum. to isolate hue+sat */
}
void convert_metallic_to_specular_tinted(vec3 basecol,
vec3 basecol_tint,
float metallic,
float specular_fac,
float specular_tint,
out vec3 diffuse,
out vec3 f0)
{
vec3 tmp_col = mix(vec3(1.0), basecol_tint, specular_tint);
f0 = mix((0.08 * specular_fac) * tmp_col, basecol, metallic);
diffuse = basecol * (1.0 - metallic);
}
/* Output sheen is to be multiplied by sheen_color. */
void principled_sheen(float NV,
vec3 basecol_tint,
float sheen,
float sheen_tint,
out float out_sheen,
out vec3 sheen_color)
float principled_sheen(float NV)
{
float f = 1.0 - NV;
/* Temporary fix for T59784. Normal map seems to contain NaNs for tangent space normal maps,
* therefore we need to clamp value. */
f = clamp(f, 0.0, 1.0);
/* Empirical approximation (manual curve fitting). Can be refined. */
out_sheen = f * f * f * 0.077 + f * 0.01 + 0.00026;
sheen_color = sheen * mix(vec3(1.0), basecol_tint, sheen_tint);
float sheen = f * f * f * 0.077 + f * 0.01 + 0.00026;
return sheen;
}
CLOSURE_EVAL_FUNCTION_DECLARE_4(node_bsdf_principled, Diffuse, Glossy, Glossy, Refraction)
void node_bsdf_principled(vec4 base_color,
float subsurface,
vec3 subsurface_radius,
@@ -59,434 +38,163 @@ void node_bsdf_principled(vec4 base_color,
vec3 N,
vec3 CN,
vec3 T,
vec3 I,
float use_multiscatter,
const float do_diffuse,
const float do_clearcoat,
const float do_refraction,
const float do_multiscatter,
float ssr_id,
float sss_id,
vec3 sss_scale,
out Closure result)
{
N = normalize(N);
ior = max(ior, 1e-5);
/* Match cycles. */
metallic = saturate(metallic);
transmission = saturate(transmission);
float m_transmission = 1.0 - transmission;
float diffuse_weight = (1.0 - transmission) * (1.0 - metallic);
transmission *= (1.0 - metallic);
float specular_weight = (1.0 - transmission);
clearcoat = max(clearcoat, 0.0);
transmission_roughness = 1.0 - (1.0 - roughness) * (1.0 - transmission_roughness);
float dielectric = 1.0 - metallic;
transmission *= dielectric;
sheen *= dielectric;
subsurface_color *= dielectric;
CLOSURE_VARS_DECLARE_4(Diffuse, Glossy, Glossy, Refraction);
vec3 diffuse, f0, out_diff, out_spec, out_refr, ssr_spec, sheen_color;
float out_sheen;
vec3 ctint = tint_from_color(base_color.rgb);
convert_metallic_to_specular_tinted(
base_color.rgb, ctint, metallic, specular, specular_tint, diffuse, f0);
in_Diffuse_0.N = N; /* Normalized during eval. */
in_Diffuse_0.albedo = mix(base_color.rgb, subsurface_color.rgb, subsurface);
float NV = dot(N, cameraVec);
principled_sheen(NV, ctint, sheen, sheen_tint, out_sheen, sheen_color);
in_Glossy_1.N = N; /* Normalized during eval. */
in_Glossy_1.roughness = roughness;
vec3 f90 = mix(vec3(1.0), f0, (1.0 - specular) * metallic);
in_Glossy_2.N = CN; /* Normalized during eval. */
in_Glossy_2.roughness = clearcoat_roughness;
/* Far from being accurate, but 2 glossy evaluation is too expensive.
* Most noticeable difference is at grazing angles since the bsdf lut
* f0 color interpolation is done on top of this interpolation. */
vec3 f0_glass = mix(vec3(1.0), base_color.rgb, specular_tint);
float fresnel = F_eta(ior, NV);
vec3 spec_col = F_color_blend(ior, fresnel, f0_glass) * fresnel;
f0 = mix(f0, spec_col, transmission);
f90 = mix(f90, spec_col, transmission);
in_Refraction_3.N = N; /* Normalized during eval. */
in_Refraction_3.roughness = do_multiscatter != 0.0 ? roughness : transmission_roughness;
in_Refraction_3.ior = ior;
/* Really poor approximation but needed to workaround issues with renderpasses. */
spec_col = mix(vec3(1.0), spec_col, transmission);
/* Match cycles. */
spec_col += float(clearcoat > 1e-5);
vec3 mixed_ss_base_color = mix(diffuse, subsurface_color.rgb, subsurface);
float sss_scalef = avg(sss_scale) * subsurface;
eevee_closure_principled(N,
mixed_ss_base_color,
f0,
/* HACK: Pass the multiscatter flag as the sign to not add closure
* variations or increase register usage. */
(use_multiscatter != 0.0) ? f90 : -f90,
int(ssr_id),
roughness,
CN,
clearcoat * 0.25,
clearcoat_roughness,
1.0,
sss_scalef,
ior,
true,
out_diff,
out_spec,
out_refr,
ssr_spec);
vec3 refr_color = base_color.rgb;
refr_color *= (refractionDepth > 0.0) ? refr_color :
vec3(1.0); /* Simulate 2 transmission event */
refr_color *= saturate(1.0 - fresnel) * transmission;
sheen_color *= m_transmission;
mixed_ss_base_color *= m_transmission;
CLOSURE_EVAL_FUNCTION_4(node_bsdf_principled, Diffuse, Glossy, Glossy, Refraction);
result = CLOSURE_DEFAULT;
result.radiance = render_pass_glossy_mask(refr_color, out_refr * refr_color);
result.radiance += render_pass_glossy_mask(spec_col, out_spec);
/* Coarse approx. */
result.radiance += render_pass_diffuse_mask(sheen_color, out_diff * out_sheen * sheen_color);
result.radiance += render_pass_emission_mask(emission.rgb * emission_strength);
result.radiance *= alpha;
closure_load_ssr_data(ssr_spec * alpha, roughness, N, viewCameraVec, int(ssr_id), result);
mixed_ss_base_color *= alpha;
closure_load_sss_data(sss_scalef, out_diff, mixed_ss_base_color, int(sss_id), result);
result.transmittance = vec3(1.0 - alpha);
}
/* This will tag the whole eval for optimisation. */
if (do_diffuse == 0.0) {
out_Diffuse_0.radiance = vec3(0);
}
if (do_clearcoat == 0.0) {
out_Glossy_2.radiance = vec3(0);
}
if (do_refraction == 0.0) {
out_Refraction_3.radiance = vec3(0);
}
void node_bsdf_principled_dielectric(vec4 base_color,
float subsurface,
vec3 subsurface_radius,
vec4 subsurface_color,
float metallic,
float specular,
float specular_tint,
float roughness,
float anisotropic,
float anisotropic_rotation,
float sheen,
float sheen_tint,
float clearcoat,
float clearcoat_roughness,
float ior,
float transmission,
float transmission_roughness,
vec4 emission,
float emission_strength,
float alpha,
vec3 N,
vec3 CN,
vec3 T,
vec3 I,
float use_multiscatter,
float ssr_id,
float sss_id,
vec3 sss_scale,
out Closure result)
{
N = normalize(N);
metallic = saturate(metallic);
float dielectric = 1.0 - metallic;
/* Glossy_1 will always be evaluated. */
float NV = dot(in_Glossy_1.N, cameraVec);
vec3 diffuse, f0, out_diff, out_spec, ssr_spec, sheen_color;
float out_sheen;
vec3 ctint = tint_from_color(base_color.rgb);
convert_metallic_to_specular_tinted(
base_color.rgb, ctint, metallic, specular, specular_tint, diffuse, f0);
vec3 base_color_tint = tint_from_color(base_color.rgb);
vec3 f90 = mix(vec3(1.0), f0, (1.0 - specular) * metallic);
/* TODO(fclem) This isn't good for rough glass using multiscatter (since the fresnel is applied
* on each microfacet in cycles). */
float fresnel = F_eta(in_Refraction_3.ior, NV);
float NV = dot(N, cameraVec);
principled_sheen(NV, ctint, sheen, sheen_tint, out_sheen, sheen_color);
{
/* Glossy reflections.
* Separate Glass reflections and main specular reflections to match Cycles renderpasses. */
out_Glossy_1.radiance = closure_mask_ssr_radiance(out_Glossy_1.radiance, ssr_id);
eevee_closure_default(N,
diffuse,
f0,
/* HACK: Pass the multiscatter flag as the sign to not add closure
* variations or increase register usage. */
(use_multiscatter != 0.0) ? f90 : -f90,
int(ssr_id),
roughness,
1.0,
true,
out_diff,
out_spec,
ssr_spec);
vec2 split_sum = brdf_lut(NV, roughness);
result = CLOSURE_DEFAULT;
result.radiance = render_pass_glossy_mask(vec3(1.0), out_spec);
result.radiance += render_pass_diffuse_mask(sheen_color, out_diff * out_sheen * sheen_color);
result.radiance += render_pass_diffuse_mask(diffuse, out_diff * diffuse);
result.radiance += render_pass_emission_mask(emission.rgb * emission_strength);
result.radiance *= alpha;
closure_load_ssr_data(ssr_spec * alpha, roughness, N, viewCameraVec, int(ssr_id), result);
vec3 glossy_radiance_final = vec3(0.0);
if (transmission > 1e-5) {
/* Glass Reflection: Reuse radiance from Glossy1. */
vec3 out_glass_refl_radiance = out_Glossy_1.radiance;
/* Poor approximation since we baked the LUT using a fixed IOR. */
vec3 f0 = mix(vec3(1.0), base_color.rgb, specular_tint);
vec3 f90 = vec3(1);
vec3 brdf = (do_multiscatter != 0.0) ? F_brdf_multi_scatter(f0, f90, split_sum) :
F_brdf_single_scatter(f0, f90, split_sum);
out_glass_refl_radiance *= brdf;
out_glass_refl_radiance = render_pass_glossy_mask(vec3(1), out_glass_refl_radiance);
out_glass_refl_radiance *= fresnel * transmission;
glossy_radiance_final += out_glass_refl_radiance;
}
if (specular_weight > 1e-5) {
vec3 dielectric_f0_color = mix(vec3(1.0), base_color_tint, specular_tint);
vec3 metallic_f0_color = base_color.rgb;
vec3 f0 = mix((0.08 * specular) * dielectric_f0_color, metallic_f0_color, metallic);
/* Cycles does this blending using the microfacet fresnel factor. However, our fresnel
* is already baked inside the split sum LUT. We approximate using by modifying the
* changing the f90 color directly in a non linear fashion. */
vec3 f90 = mix(f0, vec3(1), fast_sqrt(specular));
vec3 brdf = (do_multiscatter != 0.0) ? F_brdf_multi_scatter(f0, f90, split_sum) :
F_brdf_single_scatter(f0, f90, split_sum);
out_Glossy_1.radiance *= brdf;
out_Glossy_1.radiance = render_pass_glossy_mask(vec3(1), out_Glossy_1.radiance);
out_Glossy_1.radiance *= specular_weight;
glossy_radiance_final += out_Glossy_1.radiance;
}
closure_load_ssr_data(
glossy_radiance_final, in_Glossy_1.roughness, in_Glossy_1.N, ssr_id, result);
}
if (diffuse_weight > 1e-5) {
/* Mask over all diffuse radiance. */
out_Diffuse_0.radiance *= diffuse_weight;
/* Sheen Coarse approximation: We reuse the diffuse radiance and just scale it. */
vec3 sheen_color = mix(vec3(1), base_color_tint, sheen_tint);
vec3 out_sheen_radiance = out_Diffuse_0.radiance * principled_sheen(NV);
out_sheen_radiance = render_pass_diffuse_mask(vec3(1), out_sheen_radiance);
out_sheen_radiance *= sheen * sheen_color;
result.radiance += out_sheen_radiance;
/* Diffuse / Subsurface. */
float scale = avg(sss_scale) * subsurface;
closure_load_sss_data(scale, out_Diffuse_0.radiance, in_Diffuse_0.albedo, int(sss_id), result);
}
if (transmission > 1e-5) {
/* TODO(fclem) This could be going to a transmission render pass instead. */
out_Refraction_3.radiance = render_pass_glossy_mask(vec3(1), out_Refraction_3.radiance);
out_Refraction_3.radiance *= base_color.rgb;
/* Simulate 2nd transmission event. */
out_Refraction_3.radiance *= (refractionDepth > 0.0) ? base_color.rgb : vec3(1);
out_Refraction_3.radiance *= (1.0 - fresnel) * transmission;
result.radiance += out_Refraction_3.radiance;
}
if (clearcoat > 1e-5) {
float NV = dot(in_Glossy_2.N, cameraVec);
vec2 split_sum = brdf_lut(NV, in_Glossy_2.roughness);
vec3 brdf = F_brdf_single_scatter(vec3(0.04), vec3(1.0), split_sum);
out_Glossy_2.radiance *= brdf * clearcoat * 0.25;
out_Glossy_2.radiance = render_pass_glossy_mask(vec3(1), out_Glossy_2.radiance);
result.radiance += out_Glossy_2.radiance;
}
{
vec3 out_emission_radiance = render_pass_emission_mask(emission.rgb);
out_emission_radiance *= emission_strength;
result.radiance += out_emission_radiance;
}
result.transmittance = vec3(1.0 - alpha);
}
void node_bsdf_principled_metallic(vec4 base_color,
float subsurface,
vec3 subsurface_radius,
vec4 subsurface_color,
float metallic,
float specular,
float specular_tint,
float roughness,
float anisotropic,
float anisotropic_rotation,
float sheen,
float sheen_tint,
float clearcoat,
float clearcoat_roughness,
float ior,
float transmission,
float transmission_roughness,
vec4 emission,
float emission_strength,
float alpha,
vec3 N,
vec3 CN,
vec3 T,
vec3 I,
float use_multiscatter,
float ssr_id,
float sss_id,
vec3 sss_scale,
out Closure result)
{
N = normalize(N);
vec3 out_spec, ssr_spec;
vec3 f90 = mix(vec3(1.0), base_color.rgb, (1.0 - specular) * metallic);
eevee_closure_glossy(N,
base_color.rgb,
/* HACK: Pass the multiscatter flag as the sign to not add closure
* variations or increase register usage. */
(use_multiscatter != 0.0) ? f90 : -f90,
int(ssr_id),
roughness,
1.0,
true,
out_spec,
ssr_spec);
result = CLOSURE_DEFAULT;
result.radiance = render_pass_glossy_mask(vec3(1.0), out_spec);
result.radiance += render_pass_emission_mask(emission.rgb * emission_strength);
result.radiance *= alpha;
closure_load_ssr_data(ssr_spec * alpha, roughness, N, viewCameraVec, int(ssr_id), result);
result.transmittance = vec3(1.0 - alpha);
result.ssr_data.rgb *= alpha;
# ifdef USE_SSS
result.sss_irradiance *= alpha;
# endif
}
void node_bsdf_principled_clearcoat(vec4 base_color,
float subsurface,
vec3 subsurface_radius,
vec4 subsurface_color,
float metallic,
float specular,
float specular_tint,
float roughness,
float anisotropic,
float anisotropic_rotation,
float sheen,
float sheen_tint,
float clearcoat,
float clearcoat_roughness,
float ior,
float transmission,
float transmission_roughness,
vec4 emission,
float emission_strength,
float alpha,
vec3 N,
vec3 CN,
vec3 T,
vec3 I,
float use_multiscatter,
float ssr_id,
float sss_id,
vec3 sss_scale,
out Closure result)
{
vec3 out_spec, ssr_spec;
N = normalize(N);
vec3 f90 = mix(vec3(1.0), base_color.rgb, (1.0 - specular) * metallic);
eevee_closure_clearcoat(N,
base_color.rgb,
/* HACK: Pass the multiscatter flag as the sign to not add closure
* variations or increase register usage. */
(use_multiscatter != 0.0) ? f90 : -f90,
int(ssr_id),
roughness,
CN,
clearcoat * 0.25,
clearcoat_roughness,
1.0,
true,
out_spec,
ssr_spec);
/* Match cycles. */
float spec_col = 1.0 + float(clearcoat > 1e-5);
result = CLOSURE_DEFAULT;
result.radiance = render_pass_glossy_mask(vec3(spec_col), out_spec);
result.radiance += render_pass_emission_mask(emission.rgb * emission_strength);
result.radiance *= alpha;
closure_load_ssr_data(ssr_spec * alpha, roughness, N, viewCameraVec, int(ssr_id), result);
result.transmittance = vec3(1.0 - alpha);
}
void node_bsdf_principled_subsurface(vec4 base_color,
float subsurface,
vec3 subsurface_radius,
vec4 subsurface_color,
float metallic,
float specular,
float specular_tint,
float roughness,
float anisotropic,
float anisotropic_rotation,
float sheen,
float sheen_tint,
float clearcoat,
float clearcoat_roughness,
float ior,
float transmission,
float transmission_roughness,
vec4 emission,
float emission_strength,
float alpha,
vec3 N,
vec3 CN,
vec3 T,
vec3 I,
float use_multiscatter,
float ssr_id,
float sss_id,
vec3 sss_scale,
out Closure result)
{
metallic = saturate(metallic);
N = normalize(N);
vec3 diffuse, f0, out_diff, out_spec, ssr_spec, sheen_color;
float out_sheen;
vec3 ctint = tint_from_color(base_color.rgb);
convert_metallic_to_specular_tinted(
base_color.rgb, ctint, metallic, specular, specular_tint, diffuse, f0);
subsurface_color = subsurface_color * (1.0 - metallic);
vec3 mixed_ss_base_color = mix(diffuse, subsurface_color.rgb, subsurface);
float sss_scalef = avg(sss_scale) * subsurface;
float NV = dot(N, cameraVec);
principled_sheen(NV, ctint, sheen, sheen_tint, out_sheen, sheen_color);
vec3 f90 = mix(vec3(1.0), base_color.rgb, (1.0 - specular) * metallic);
eevee_closure_skin(N,
mixed_ss_base_color,
f0,
/* HACK: Pass the multiscatter flag as the sign to not add closure variations
* or increase register usage. */
(use_multiscatter != 0.0) ? f90 : -f90,
int(ssr_id),
roughness,
1.0,
sss_scalef,
true,
out_diff,
out_spec,
ssr_spec);
result = CLOSURE_DEFAULT;
result.radiance = render_pass_glossy_mask(vec3(1.0), out_spec);
result.radiance += render_pass_diffuse_mask(sheen_color, out_diff * out_sheen * sheen_color);
result.radiance += render_pass_emission_mask(emission.rgb * emission_strength);
result.radiance *= alpha;
closure_load_ssr_data(ssr_spec * alpha, roughness, N, viewCameraVec, int(ssr_id), result);
mixed_ss_base_color *= alpha;
closure_load_sss_data(sss_scalef, out_diff, mixed_ss_base_color, int(sss_id), result);
result.transmittance = vec3(1.0 - alpha);
}
void node_bsdf_principled_glass(vec4 base_color,
float subsurface,
vec3 subsurface_radius,
vec4 subsurface_color,
float metallic,
float specular,
float specular_tint,
float roughness,
float anisotropic,
float anisotropic_rotation,
float sheen,
float sheen_tint,
float clearcoat,
float clearcoat_roughness,
float ior,
float transmission,
float transmission_roughness,
vec4 emission,
float emission_strength,
float alpha,
vec3 N,
vec3 CN,
vec3 T,
vec3 I,
float use_multiscatter,
float ssr_id,
float sss_id,
vec3 sss_scale,
out Closure result)
{
ior = max(ior, 1e-5);
N = normalize(N);
vec3 f0, out_spec, out_refr, ssr_spec;
f0 = mix(vec3(1.0), base_color.rgb, specular_tint);
eevee_closure_glass(N,
vec3(1.0),
vec3((use_multiscatter != 0.0) ? 1.0 : -1.0),
int(ssr_id),
roughness,
1.0,
ior,
true,
out_spec,
out_refr,
ssr_spec);
vec3 refr_color = base_color.rgb;
refr_color *= (refractionDepth > 0.0) ? refr_color :
vec3(1.0); /* Simulate 2 transmission events */
float fresnel = F_eta(ior, dot(N, cameraVec));
vec3 spec_col = F_color_blend(ior, fresnel, f0);
spec_col *= fresnel;
refr_color *= (1.0 - fresnel);
ssr_spec *= spec_col;
result = CLOSURE_DEFAULT;
result.radiance = render_pass_glossy_mask(refr_color, out_refr * refr_color);
result.radiance += render_pass_glossy_mask(spec_col, out_spec * spec_col);
result.radiance += render_pass_emission_mask(emission.rgb * emission_strength);
result.radiance *= alpha;
closure_load_ssr_data(ssr_spec * alpha, roughness, N, viewCameraVec, int(ssr_id), result);
result.transmittance = vec3(1.0 - alpha);
}
#else
/* clang-format off */
/* Stub principled because it is not compatible with volumetrics. */
# define node_bsdf_principled(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, aa, bb, result) (result = CLOSURE_DEFAULT)
# define node_bsdf_principled_dielectric(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, aa, bb, result) (result = CLOSURE_DEFAULT)
# define node_bsdf_principled_metallic(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, aa, bb, result) (result = CLOSURE_DEFAULT)
# define node_bsdf_principled_clearcoat(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, aa, bb, result) (result = CLOSURE_DEFAULT)
# define node_bsdf_principled_subsurface(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, aa, bb, result) (result = CLOSURE_DEFAULT)
# define node_bsdf_principled_glass(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, aa, bb, result) (result = CLOSURE_DEFAULT)
# define node_bsdf_principled(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, aa, bb, cc, dd, result) (result = CLOSURE_DEFAULT)
/* clang-format on */
#endif

View File

@@ -1,15 +1,30 @@
#ifndef VOLUMETRICS
CLOSURE_EVAL_FUNCTION_DECLARE_1(node_bsdf_refraction, Refraction)
void node_bsdf_refraction(vec4 color, float roughness, float ior, vec3 N, out Closure result)
{
N = normalize(N);
vec3 out_refr;
color.rgb *= (refractionDepth > 0.0) ? color.rgb : vec3(1.0); /* Simulate 2 absorption event. */
eevee_closure_refraction(N, roughness, ior, true, out_refr);
vec3 vN = mat3(ViewMatrix) * N;
CLOSURE_VARS_DECLARE_1(Refraction);
in_Refraction_0.N = N; /* Normalized during eval. */
in_Refraction_0.roughness = roughness;
in_Refraction_0.ior = ior;
CLOSURE_EVAL_FUNCTION_1(node_bsdf_refraction, Refraction);
result = CLOSURE_DEFAULT;
result.ssr_normal = normal_encode(vN, viewCameraVec);
result.radiance = render_pass_glossy_mask(color.rgb, out_refr * color.rgb);
out_Refraction_0.radiance = render_pass_glossy_mask(vec3(1.0), out_Refraction_0.radiance);
out_Refraction_0.radiance *= color.rgb;
/* Simulate 2nd absorption event. */
out_Refraction_0.radiance *= (refractionDepth > 0.0) ? color.rgb : vec3(1.0);
result.radiance = out_Refraction_0.radiance;
/* TODO(fclem) Try to not use this. */
result.ssr_normal = normal_encode(mat3(ViewMatrix) * in_Refraction_0.N, viewCameraVec);
}
#else
/* Stub refraction because it is not compatible with volumetrics. */
# define node_bsdf_refraction(a, b, c, d, e) (e = CLOSURE_DEFAULT)

View File

@@ -1,4 +1,7 @@
#ifndef VOLUMETRICS
CLOSURE_EVAL_FUNCTION_DECLARE_1(node_subsurface_scattering, Diffuse)
void node_subsurface_scattering(vec4 color,
float scale,
vec3 radius,
@@ -8,20 +11,29 @@ void node_subsurface_scattering(vec4 color,
float sss_id,
out Closure result)
{
N = normalize(N);
vec3 out_diff;
vec3 vN = mat3(ViewMatrix) * N;
CLOSURE_VARS_DECLARE_1(Diffuse);
in_Diffuse_0.N = N; /* Normalized during eval. */
in_Diffuse_0.albedo = color.rgb;
CLOSURE_EVAL_FUNCTION_1(node_subsurface_scattering, Diffuse);
result = CLOSURE_DEFAULT;
closure_load_ssr_data(vec3(0.0), 0.0, N, viewCameraVec, -1, result);
eevee_closure_subsurface(N, color.rgb, 1.0, scale, true, out_diff);
/* Not perfect for texture_blur values between 0.0 and 1.0.
* Interpolate between separated color and color applied on irradiance. */
float one_minus_texture_blur = 1.0 - texture_blur;
vec3 sss_albedo = color.rgb * texture_blur + one_minus_texture_blur;
vec3 radiance_tint = color.rgb * one_minus_texture_blur + texture_blur;
/* Consider output radiance as irradiance. */
out_Diffuse_0.radiance *= radiance_tint;
/* Not perfect for texture_blur not exactly equal to 0.0 or 1.0. */
vec3 sss_albedo = mix(color.rgb, vec3(1.0), texture_blur);
out_diff *= mix(vec3(1.0), color.rgb, texture_blur);
result.radiance = render_pass_sss_mask(sss_albedo);
closure_load_sss_data(scale, out_diff, sss_albedo, int(sss_id), result);
closure_load_sss_data(scale, out_Diffuse_0.radiance, sss_albedo, int(sss_id), result);
/* TODO(fclem) Try to not use this. */
closure_load_ssr_data(vec3(0.0), 0.0, in_Diffuse_0.N, -1.0, result);
}
#else
/* Stub subsurface scattering because it is not compatible with volumetrics. */
# define node_subsurface_scattering(a, b, c, d, e, f, g, h) (h = CLOSURE_DEFAULT)

View File

@@ -1,12 +1,20 @@
#ifndef VOLUMETRICS
CLOSURE_EVAL_FUNCTION_DECLARE_1(node_bsdf_translucent, Translucent)
void node_bsdf_translucent(vec4 color, vec3 N, out Closure result)
{
N = normalize(N);
CLOSURE_VARS_DECLARE_1(Translucent);
in_Translucent_0.N = -N; /* Normalized during eval. */
CLOSURE_EVAL_FUNCTION_1(node_bsdf_translucent, Translucent);
result = CLOSURE_DEFAULT;
eevee_closure_diffuse(-N, color.rgb, 1.0, false, result.radiance);
closure_load_ssr_data(vec3(0.0), 0.0, N, viewCameraVec, -1, result);
result.radiance = render_pass_diffuse_mask(color.rgb, result.radiance * color.rgb);
closure_load_ssr_data(vec3(0.0), 0.0, -in_Translucent_0.N, -1.0, result);
result.radiance = render_pass_diffuse_mask(color.rgb, out_Translucent_0.radiance * color.rgb);
}
#else
/* Stub translucent because it is not compatible with volumetrics. */
# define node_bsdf_translucent(a, b, c) (c = CLOSURE_DEFAULT)

View File

@@ -200,8 +200,6 @@ typedef enum eNodeSocketFlag {
SOCK_NO_INTERNAL_LINK = (1 << 9),
/** Draw socket in a more compact form. */
SOCK_COMPACT = (1 << 10),
/** Make the input socket accept multiple incoming links in the UI. */
SOCK_MULTI_INPUT = (1 << 11),
} eNodeSocketFlag;
/* limit data in bNode to what we want to see saved? */
@@ -1195,13 +1193,6 @@ typedef struct NodeGeometryPointsToVolume {
char _pad[6];
} NodeGeometryPointsToVolume;
typedef struct NodeGeometryCollectionInfo {
/* GeometryNodeTransformSpace. */
uint8_t transform_space;
char _pad[7];
} NodeGeometryCollectionInfo;
/* script node mode */
#define NODE_SCRIPT_INTERNAL 0
#define NODE_SCRIPT_EXTERNAL 1

View File

@@ -8911,33 +8911,6 @@ static void def_geo_points_to_volume(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
static void def_geo_collection_info(StructRNA *srna)
{
PropertyRNA *prop;
static const EnumPropertyItem rna_node_geometry_collection_info_transform_space_items[] = {
{GEO_NODE_TRANSFORM_SPACE_ORIGINAL,
"ORIGINAL",
0,
"Original",
"Output the geometry relative to the collection offset"},
{GEO_NODE_TRANSFORM_SPACE_RELATIVE,
"RELATIVE",
0,
"Relative",
"Bring the input collection geometry into the modified object, maintaining the relative "
"position between the objects in the scene."},
{0, NULL, 0, NULL, NULL},
};
RNA_def_struct_sdna_from(srna, "NodeGeometryCollectionInfo", "storage");
prop = RNA_def_property(srna, "transform_space", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, rna_node_geometry_collection_info_transform_space_items);
RNA_def_property_ui_text(prop, "Transform Space", "The transformation of the geometry output");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
/* -------------------------------------------------------------------------- */
static void rna_def_shader_node(BlenderRNA *brna)

View File

@@ -215,7 +215,7 @@ static bool isDisabled(const struct Scene *UNUSED(scene),
class GeometryNodesEvaluator {
private:
blender::LinearAllocator<> allocator_;
Map<std::pair<const DInputSocket *, const DOutputSocket *>, GMutablePointer> value_by_input_;
Map<const DInputSocket *, GMutablePointer> value_by_input_;
Vector<const DInputSocket *> group_outputs_;
blender::nodes::MultiFunctionByNode &mf_by_node_;
const blender::nodes::DataTypeConversions &conversions_;
@@ -246,8 +246,8 @@ class GeometryNodesEvaluator {
{
Vector<GMutablePointer> results;
for (const DInputSocket *group_output : group_outputs_) {
Vector<GMutablePointer> result = this->get_input_values(*group_output);
results.append(result[0]);
GMutablePointer result = this->get_input_value(*group_output);
results.append(result);
}
for (GMutablePointer value : value_by_input_.values()) {
value.destruct();
@@ -256,53 +256,32 @@ class GeometryNodesEvaluator {
}
private:
Vector<GMutablePointer> get_input_values(const DInputSocket &socket_to_compute)
GMutablePointer get_input_value(const DInputSocket &socket_to_compute)
{
std::optional<GMutablePointer> value = value_by_input_.pop_try(&socket_to_compute);
if (value.has_value()) {
/* This input has been computed before, return it directly. */
return *value;
}
Span<const DOutputSocket *> from_sockets = socket_to_compute.linked_sockets();
Span<const DGroupInput *> from_group_inputs = socket_to_compute.linked_group_inputs();
const int total_inputs = from_sockets.size() + from_group_inputs.size();
BLI_assert(total_inputs <= 1);
if (total_inputs == 0) {
/* The input is not connected, use the value from the socket itself. */
return {get_unlinked_input_value(socket_to_compute)};
return get_unlinked_input_value(socket_to_compute);
}
if (from_group_inputs.size() == 1) {
return {get_unlinked_input_value(socket_to_compute)};
}
/* Multi-input sockets contain a vector of inputs. */
if (socket_to_compute.is_multi_input_socket()) {
Vector<GMutablePointer> values;
for (const DOutputSocket *from_socket : from_sockets) {
const std::pair<const DInputSocket *, const DOutputSocket *> key = std::make_pair(
&socket_to_compute, from_socket);
std::optional<GMutablePointer> value = value_by_input_.pop_try(key);
if (value.has_value()) {
values.append(value.value());
}
else {
this->compute_output_and_forward(*from_socket);
GMutablePointer value = value_by_input_.pop(key);
values.append(value);
}
}
return values;
}
const DOutputSocket &from_socket = *from_sockets[0];
const std::pair<const DInputSocket *, const DOutputSocket *> key = std::make_pair(
&socket_to_compute, &from_socket);
std::optional<GMutablePointer> value = value_by_input_.pop_try(key);
if (value.has_value()) {
/* This input has been computed before, return it directly. */
return {*value};
/* The input gets its value from the input of a group that is not further connected. */
return get_unlinked_input_value(socket_to_compute);
}
/* Compute the socket now. */
const DOutputSocket &from_socket = *from_sockets[0];
this->compute_output_and_forward(from_socket);
return {value_by_input_.pop(key)};
return value_by_input_.pop(&socket_to_compute);
}
void compute_output_and_forward(const DOutputSocket &socket_to_compute)
@@ -323,14 +302,8 @@ class GeometryNodesEvaluator {
GValueMap<StringRef> node_inputs_map{allocator_};
for (const DInputSocket *input_socket : node.inputs()) {
if (input_socket->is_available()) {
Vector<GMutablePointer> values = this->get_input_values(*input_socket);
for (int i = 0; i < values.size(); ++i) {
/* Values from Multi Input Sockets are stored in input map with the format
* <identifier>[<index>]. */
blender::StringRefNull key = allocator_.copy_string(
input_socket->identifier() + (i > 0 ? ("[" + std::to_string(i)) + "]" : ""));
node_inputs_map.add_new_direct(key, std::move(values[i]));
}
GMutablePointer value = this->get_input_value(*input_socket);
node_inputs_map.add_new_direct(input_socket->identifier(), value);
}
}
@@ -420,15 +393,13 @@ class GeometryNodesEvaluator {
void forward_to_inputs(const DOutputSocket &from_socket, GMutablePointer value_to_forward)
{
/* For all sockets that are linked with the from_socket push the value to their node. */
Span<const DInputSocket *> to_sockets_all = from_socket.linked_sockets();
const CPPType &from_type = *value_to_forward.type();
Vector<const DInputSocket *> to_sockets_same_type;
for (const DInputSocket *to_socket : to_sockets_all) {
const CPPType &to_type = *blender::nodes::socket_cpp_type_get(*to_socket->typeinfo());
const std::pair<const DInputSocket *, const DOutputSocket *> key = std::make_pair(
to_socket, &from_socket);
if (from_type == to_type) {
to_sockets_same_type.append(to_socket);
}
@@ -440,7 +411,7 @@ class GeometryNodesEvaluator {
else {
to_type.copy_to_uninitialized(to_type.default_value(), buffer);
}
add_value_to_input_socket(key, GMutablePointer{to_type, buffer});
value_by_input_.add_new(to_socket, GMutablePointer{to_type, buffer});
}
}
@@ -451,35 +422,23 @@ class GeometryNodesEvaluator {
else if (to_sockets_same_type.size() == 1) {
/* This value is only used on one input socket, no need to copy it. */
const DInputSocket *to_socket = to_sockets_same_type[0];
const std::pair<const DInputSocket *, const DOutputSocket *> key = std::make_pair(
to_socket, &from_socket);
add_value_to_input_socket(key, value_to_forward);
value_by_input_.add_new(to_socket, value_to_forward);
}
else {
/* Multiple inputs use the value, make a copy for every input except for one. */
const DInputSocket *first_to_socket = to_sockets_same_type[0];
Span<const DInputSocket *> other_to_sockets = to_sockets_same_type.as_span().drop_front(1);
const CPPType &type = *value_to_forward.type();
const std::pair<const DInputSocket *, const DOutputSocket *> first_key = std::make_pair(
first_to_socket, &from_socket);
add_value_to_input_socket(first_key, value_to_forward);
value_by_input_.add_new(first_to_socket, value_to_forward);
for (const DInputSocket *to_socket : other_to_sockets) {
const std::pair<const DInputSocket *, const DOutputSocket *> key = std::make_pair(
to_socket, &from_socket);
void *buffer = allocator_.allocate(type.size(), type.alignment());
type.copy_to_uninitialized(value_to_forward.get(), buffer);
add_value_to_input_socket(key, GMutablePointer{type, buffer});
value_by_input_.add_new(to_socket, GMutablePointer{type, buffer});
}
}
}
void add_value_to_input_socket(const std::pair<const DInputSocket *, const DOutputSocket *> key,
GMutablePointer value)
{
value_by_input_.add_new(key, value);
}
GMutablePointer get_unlinked_input_value(const DInputSocket &socket)
{
bNodeSocket *bsocket;

View File

@@ -150,7 +150,6 @@ set(SRC
geometry/nodes/node_geo_attribute_randomize.cc
geometry/nodes/node_geo_attribute_vector_math.cc
geometry/nodes/node_geo_boolean.cc
geometry/nodes/node_geo_collection_info.cc
geometry/nodes/node_geo_common.cc
geometry/nodes/node_geo_edge_split.cc
geometry/nodes/node_geo_join_geometry.cc

View File

@@ -80,7 +80,6 @@ class DInputSocket : public DSocket {
private:
Vector<DOutputSocket *> linked_sockets_;
Vector<DGroupInput *> linked_group_inputs_;
bool is_multi_input_socket_;
friend DerivedNodeTree;
@@ -91,7 +90,6 @@ class DInputSocket : public DSocket {
Span<const DGroupInput *> linked_group_inputs() const;
bool is_linked() const;
bool is_multi_input_socket() const;
};
class DOutputSocket : public DSocket {
@@ -364,11 +362,6 @@ inline bool DInputSocket::is_linked() const
return linked_sockets_.size() > 0 || linked_group_inputs_.size() > 0;
}
inline bool DInputSocket::is_multi_input_socket() const
{
return is_multi_input_socket_;
}
/* --------------------------------------------------------------------
* DOutputSocket inline methods.
*/

View File

@@ -49,7 +49,6 @@ void register_node_type_geo_point_rotate(void);
void register_node_type_geo_align_rotation_to_vector(void);
void register_node_type_geo_sample_texture(void);
void register_node_type_geo_points_to_volume(void);
void register_node_type_geo_collection_info(void);
#ifdef __cplusplus
}

View File

@@ -103,25 +103,6 @@ class GeoNodeExecParams {
return input_values_.extract<T>(identifier);
}
/**
* Get input as vector for multi input socket with the given identifier.
*
* This method can only be called once for each identifier.
*/
template<typename T> Vector<T> extract_multi_input(StringRef identifier)
{
Vector<T> values;
values.append(input_values_.extract<T>(identifier));
int i = 1;
std::string sub_identifier = identifier + "[1]";
while (input_values_.contains(sub_identifier)) {
values.append(input_values_.extract<T>(sub_identifier));
i++;
sub_identifier = identifier + "[" + std::to_string(i) + "]";
}
return values;
}
/**
* Get the input value for the input socket with the given identifier.
*

View File

@@ -291,7 +291,6 @@ DefNode(GeometryNode, GEO_NODE_POINT_SCALE, def_geo_point_scale, "POINT_SCALE",
DefNode(GeometryNode, GEO_NODE_POINT_TRANSLATE, def_geo_point_translate, "POINT_TRANSLATE", PointTranslate, "Point Translate", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_SAMPLE_TEXTURE, def_geo_attribute_sample_texture, "ATTRIBUTE_SAMPLE_TEXTURE", AttributeSampleTexture, "Attribute Sample Texture", "")
DefNode(GeometryNode, GEO_NODE_POINTS_TO_VOLUME, def_geo_points_to_volume, "POINTS_TO_VOLUME", PointsToVolume, "Points to Volume", "")
DefNode(GeometryNode, GEO_NODE_COLLECTION_INFO, def_geo_collection_info, "COLLECTION_INFO", CollectionInfo, "Collection Info", "")
/* undefine macros */
#undef DefNode

View File

@@ -1,99 +0,0 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "node_geometry_util.hh"
#include "BLI_math_matrix.h"
#include "DNA_collection_types.h"
static bNodeSocketTemplate geo_node_collection_info_in[] = {
{SOCK_COLLECTION, N_("Collection")},
{-1, ""},
};
static bNodeSocketTemplate geo_node_collection_info_out[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{-1, ""},
};
namespace blender::nodes {
static void geo_node_collection_info_exec(GeoNodeExecParams params)
{
bke::PersistentCollectionHandle collection_handle =
params.extract_input<bke::PersistentCollectionHandle>("Collection");
Collection *collection = params.handle_map().lookup(collection_handle);
GeometrySet geometry_set_out;
if (collection == nullptr) {
params.set_output("Geometry", geometry_set_out);
return;
}
const bNode &bnode = params.node();
NodeGeometryCollectionInfo *node_storage = (NodeGeometryCollectionInfo *)bnode.storage;
const bool transform_space_relative = (node_storage->transform_space ==
GEO_NODE_TRANSFORM_SPACE_RELATIVE);
InstancedData instance;
instance.type = INSTANCE_DATA_TYPE_COLLECTION;
instance.data.collection = collection;
InstancesComponent &instances = geometry_set_out.get_component_for_write<InstancesComponent>();
float transform_mat[4][4];
unit_m4(transform_mat);
const Object *self_object = params.self_object();
if (transform_space_relative) {
copy_v3_v3(transform_mat[3], collection->instance_offset);
mul_m4_m4_pre(transform_mat, self_object->imat);
float3 self_loc;
copy_v3_v3(self_loc, self_object->obmat[3]);
}
instances.add_instance(instance, transform_mat, -1);
params.set_output("Geometry", geometry_set_out);
}
static void geo_node_collection_info_node_init(bNodeTree *UNUSED(tree), bNode *node)
{
NodeGeometryCollectionInfo *data = (NodeGeometryCollectionInfo *)MEM_callocN(
sizeof(NodeGeometryCollectionInfo), __func__);
data->transform_space = GEO_NODE_TRANSFORM_SPACE_ORIGINAL;
node->storage = data;
}
} // namespace blender::nodes
void register_node_type_geo_collection_info()
{
static bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_COLLECTION_INFO, "Collection Info", NODE_CLASS_INPUT, 0);
node_type_socket_templates(&ntype, geo_node_collection_info_in, geo_node_collection_info_out);
node_type_init(&ntype, blender::nodes::geo_node_collection_info_node_init);
node_type_storage(&ntype,
"NodeGeometryCollectionInfo",
node_free_standard_storage,
node_copy_standard_storage);
ntype.geometry_node_execute = blender::nodes::geo_node_collection_info_exec;
nodeRegisterType(&ntype);
}

View File

@@ -69,15 +69,35 @@ static void geo_node_object_info_exec(GeoNodeExecParams params)
quat_to_eul(rotation, quaternion);
if (object != self_object) {
InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>();
if (object->type == OB_MESH) {
Mesh *mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(object, false);
if (mesh != nullptr) {
BKE_mesh_wrapper_ensure_mdata(mesh);
if (transform_space_relative) {
instances.add_instance(object, transform);
/* Make a copy because the life time of the other mesh might be shorter. */
Mesh *copied_mesh = BKE_mesh_copy_for_eval(mesh, false);
if (transform_space_relative) {
/* Transform into the local space of the object that is being modified. */
BKE_mesh_transform(copied_mesh, transform, true);
}
MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
mesh_component.replace(copied_mesh);
mesh_component.copy_vertex_group_names_from_object(*object);
}
}
else {
float unit_transform[4][4];
unit_m4(unit_transform);
instances.add_instance(object, unit_transform);
if (object->type == OB_VOLUME) {
InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>();
if (transform_space_relative) {
instances.add_instance(object, transform);
}
else {
float unit_transform[4][4];
unit_m4(unit_transform);
instances.add_instance(object, unit_transform);
}
}
}
}

View File

@@ -79,7 +79,6 @@ static Span<MLoopTri> get_mesh_looptris(const Mesh &mesh)
}
static void sample_mesh_surface(const Mesh &mesh,
const float4x4 &transform,
const float base_density,
const FloatReadAttribute *density_factors,
const int seed,
@@ -94,10 +93,9 @@ static void sample_mesh_surface(const Mesh &mesh,
const int v0_index = mesh.mloop[looptri.tri[0]].v;
const int v1_index = mesh.mloop[looptri.tri[1]].v;
const int v2_index = mesh.mloop[looptri.tri[2]].v;
const float3 v0_pos = transform * float3(mesh.mvert[v0_index].co);
const float3 v1_pos = transform * float3(mesh.mvert[v1_index].co);
const float3 v2_pos = transform * float3(mesh.mvert[v2_index].co);
const float3 v0_pos = mesh.mvert[v0_index].co;
const float3 v1_pos = mesh.mvert[v1_index].co;
const float3 v2_pos = mesh.mvert[v2_index].co;
float looptri_density_factor = 1.0f;
if (density_factors != nullptr) {
@@ -196,8 +194,8 @@ BLI_NOINLINE static void update_elimination_mask_based_on_density_factors(
const float v1_density_factor = std::max(0.0f, density_factors[v1_index]);
const float v2_density_factor = std::max(0.0f, density_factors[v2_index]);
const float probablity = attribute_math::mix3<float>(
bary_coord, v0_density_factor, v1_density_factor, v2_density_factor);
const float probablity = v0_density_factor * bary_coord.x + v1_density_factor * bary_coord.y +
v2_density_factor * bary_coord.z;
const float hash = BLI_hash_int_01(bary_coord.hash());
if (hash > probablity) {
@@ -388,174 +386,77 @@ BLI_NOINLINE static void add_remaining_point_attributes(const MeshComponent &mes
*mesh_component.get_for_read(), component, bary_coords, looptri_indices);
}
struct AttributeInfo {
std::string name;
Vector<CustomDataType> data_types;
Vector<AttributeDomain> domains;
};
struct ScatterPointsOnMeshOp {
/* Input data. */
const GeometryNodePointDistributeMethod distribute_method;
const std::string &density_attribute_name;
const int seed;
const float density;
/* Output data. */
Vector<float3> &positions;
Vector<float3> &bary_coords;
Vector<int> &looptri_indices;
Set<AttributeInfo> &attributes;
void operator()(const GeometryComponent &component, blender::Span<blender::float4x4> transforms)
{
if (component.type() != GeometryComponentType::Mesh) {
return;
}
const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
if (!mesh_component.has_mesh()) {
return;
}
const Mesh &mesh = *mesh_component.get_for_read();
for (const float4x4 &transform : transforms) {
switch (distribute_method) {
case GEO_NODE_POINT_DISTRIBUTE_RANDOM: {
const FloatReadAttribute density_factors = mesh_component.attribute_get_for_read<float>(
density_attribute_name, ATTR_DOMAIN_POINT, 1.0f);
sample_mesh_surface(mesh,
transform,
density,
&density_factors,
seed,
positions,
bary_coords,
looptri_indices);
break;
}
case GEO_NODE_POINT_DISTRIBUTE_POISSON:
sample_mesh_surface(
mesh, transform, density, nullptr, seed, positions, bary_coords, looptri_indices);
break;
}
}
}
};
struct PoissonEliminateFromDensityOp {
/* Input data. */
const std::string &density_attribute_name;
const float density;
Span<float3> bary_coords;
Span<int> looptri_indices;
/* Output data. */
MutableSpan<bool> elimination_mask;
void operator()(const GeometryComponent &component, blender::Span<blender::float4x4> transforms)
{
if (component.type() != GeometryComponentType::Mesh) {
return;
}
const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
if (!mesh_component.has_mesh()) {
return;
}
const Mesh &mesh = *mesh_component.get_for_read();
for (const float4x4 &transform : transforms) {
const FloatReadAttribute density_factors = mesh_component.attribute_get_for_read<float>(
density_attribute_name, ATTR_DOMAIN_POINT, 1.0f);
update_elimination_mask_based_on_density_factors(
mesh, density_factors, bary_coords, looptri_indices, elimination_mask);
}
}
};
struct AttributeInterpolateOp {
/* Input data. */
const std::string &density_attribute_name;
const float density;
Span<float3> bary_coords;
Span<int> looptri_indices;
int index_offset = 0;
/* Output data. */
MutableSpan<bool> elimination_mask;
void operator()(const GeometryComponent &component, blender::Span<blender::float4x4> transforms)
{
if (component.type() != GeometryComponentType::Mesh) {
return;
}
const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
if (!mesh_component.has_mesh()) {
return;
}
const Mesh &mesh = *mesh_component.get_for_read();
for (const float4x4 &transform : transforms) {
const FloatReadAttribute density_factors = mesh_component.attribute_get_for_read<float>(
density_attribute_name, ATTR_DOMAIN_POINT, 1.0f);
update_elimination_mask_based_on_density_factors(
mesh, density_factors, bary_coords, looptri_indices, elimination_mask);
}
}
};
static void sample_mesh_surface_with_minimum_distance(const Mesh &mesh,
const float max_density,
const float minimum_distance,
const FloatReadAttribute &density_factors,
const int seed,
Vector<float3> &r_positions,
Vector<float3> &r_bary_coords,
Vector<int> &r_looptri_indices)
{
sample_mesh_surface(
mesh, max_density, nullptr, seed, r_positions, r_bary_coords, r_looptri_indices);
Array<bool> elimination_mask(r_positions.size(), false);
update_elimination_mask_for_close_points(r_positions, minimum_distance, elimination_mask);
update_elimination_mask_based_on_density_factors(
mesh, density_factors, r_bary_coords, r_looptri_indices, elimination_mask);
eliminate_points_based_on_mask(elimination_mask, r_positions, r_bary_coords, r_looptri_indices);
}
static void geo_node_point_distribute_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
GeometrySet geometry_set_out;
const GeometryNodePointDistributeMethod distribute_method =
GeometryNodePointDistributeMethod distribute_method =
static_cast<GeometryNodePointDistributeMethod>(params.node().custom1);
if (!geometry_set.has_mesh() && !geometry_set.has_instances()) {
if (!geometry_set.has_mesh()) {
params.set_output("Geometry", std::move(geometry_set_out));
return;
}
const float density = params.extract_input<float>("Density Max");
const std::string density_attribute_name = params.extract_input<std::string>(
"Density Attribute");
const std::string density_attribute = params.extract_input<std::string>("Density Attribute");
if (density <= 0.0f) {
params.set_output("Geometry", std::move(geometry_set_out));
return;
}
const MeshComponent &mesh_component = *geometry_set.get_component_for_read<MeshComponent>();
const Mesh *mesh_in = mesh_component.get_for_read();
if (mesh_in == nullptr || mesh_in->mpoly == nullptr) {
params.set_output("Geometry", std::move(geometry_set_out));
return;
}
const FloatReadAttribute density_factors = mesh_component.attribute_get_for_read<float>(
density_attribute, ATTR_DOMAIN_POINT, 1.0f);
const int seed = params.get_input<int>("Seed");
Vector<float3> positions;
Vector<float3> bary_coords;
Vector<int> looptri_indices;
Set<AttributeInfo> attributes;
ScatterPointsOnMeshOp scatter_points_op{distribute_method,
density_attribute_name,
seed,
density,
positions,
bary_coords,
looptri_indices,
attributes};
BKE_foreach_geometry_component_recursive(geometry_set, scatter_points_op);
/* Eliminate points based on the minimum distance for the poisson disk case. */
if (distribute_method == GEO_NODE_POINT_DISTRIBUTE_POISSON) {
Array<bool> elimination_mask(positions.size(), false);
const float minimum_distance = params.get_input<float>("Distance Min");
update_elimination_mask_for_close_points(positions, minimum_distance, elimination_mask);
PoissonEliminateFromDensityOp eliminate_density_op{
density_attribute_name, density, bary_coords, looptri_indices};
BKE_foreach_geometry_component_recursive(geometry_set, eliminate_density_op);
eliminate_points_based_on_mask(elimination_mask, positions, bary_coords, looptri_indices);
switch (distribute_method) {
case GEO_NODE_POINT_DISTRIBUTE_RANDOM:
sample_mesh_surface(
*mesh_in, density, &density_factors, seed, positions, bary_coords, looptri_indices);
break;
case GEO_NODE_POINT_DISTRIBUTE_POISSON:
const float minimum_distance = params.extract_input<float>("Distance Min");
sample_mesh_surface_with_minimum_distance(*mesh_in,
density,
minimum_distance,
density_factors,
seed,
positions,
bary_coords,
looptri_indices);
break;
}
const int tot_points = positions.size();
PointCloud *pointcloud = BKE_pointcloud_new_nomain(tot_points);
@@ -569,7 +470,7 @@ static void geo_node_point_distribute_exec(GeoNodeExecParams params)
geometry_set_out.get_component_for_write<PointCloudComponent>();
point_component.replace(pointcloud);
// add_remaining_point_attributes(mesh_component, point_component, bary_coords, looptri_indices);
add_remaining_point_attributes(mesh_component, point_component, bary_coords, looptri_indices);
params.set_output("Geometry", std::move(geometry_set_out));
}

View File

@@ -91,7 +91,7 @@ DNode &DerivedNodeTree::create_node(const NodeRef &node_ref,
for (int i : node.inputs_.index_range()) {
const InputSocketRef &socket_ref = node_ref.input(i);
DInputSocket &socket = *node.inputs_[i];
socket.is_multi_input_socket_ = socket_ref.bsocket()->flag & SOCK_MULTI_INPUT;
socket.id_ = UNINITIALIZED_ID;
socket.node_ = &node;
socket.socket_ref_ = &socket_ref;

View File

@@ -281,30 +281,25 @@ static int node_count_links(bNodeTree *ntree, bNodeSocket *sock)
return count;
}
/* Find an eligible socket for linking. */
/* find an eligible socket for linking */
static bNodeSocket *node_find_linkable_socket(bNodeTree *ntree, bNode *node, bNodeSocket *cur)
{
/* link swapping: try to find a free slot with a matching name */
bNodeSocket *first = cur->in_out == SOCK_IN ? node->inputs.first : node->outputs.first;
bNodeSocket *sock;
/* Iterate over all sockets of the target node, to find one that matches the same socket type.
* The idea behind this is: When a user connects an input to a socket that is
* already linked (and if its not an Multi Input Socket), we try to find a replacement socket for
* the link that we try to overwrite and connect that previous link to the new socket. */
sock = cur->next ? cur->next : first; /* Wrap around the list end. */
sock = cur->next ? cur->next : first; /* wrap around the list end */
while (sock != cur) {
if (!nodeSocketIsHidden(sock) && node_link_socket_match(sock, cur)) {
break;
int link_count = node_count_links(ntree, sock);
/* take +1 into account since we would add a new link */
if (link_count + 1 <= nodeSocketLinkLimit(sock)) {
return sock; /* found a valid free socket we can swap to */
}
}
sock = sock->next ? sock->next : first; /* Wrap around the list end. */
}
if (!nodeSocketIsHidden(sock) && node_link_socket_match(sock, cur)) {
int link_count = node_count_links(ntree, sock);
/* Take +1 into account since we would add a new link. */
if (link_count + 1 <= nodeSocketLinkLimit(sock)) {
return sock; /* Found a valid free socket we can swap to. */
}
sock = sock->next ? sock->next : first; /* wrap around the list end */
}
return NULL;
}
@@ -314,6 +309,7 @@ void node_insert_link_default(bNodeTree *ntree, bNode *node, bNodeLink *link)
bNodeSocket *sock = link->tosock;
bNodeLink *tlink, *tlink_next;
/* inputs can have one link only, outputs can have unlimited links */
if (node != link->tonode) {
return;
}

View File

@@ -134,54 +134,32 @@ static int node_shader_gpu_bsdf_principled(GPUMaterial *mat,
GPU_link(mat, "set_rgb_one", &sss_scale);
}
/* Due to the manual effort done per config, we only optimize the most common permutations. */
char *node_name;
uint flag = 0;
if (!use_subsurf && use_diffuse && !use_refract && !use_clear) {
static char name[] = "node_bsdf_principled_dielectric";
node_name = name;
flag = GPU_MATFLAG_DIFFUSE | GPU_MATFLAG_GLOSSY;
uint flag = GPU_MATFLAG_GLOSSY;
if (use_diffuse) {
flag |= GPU_MATFLAG_DIFFUSE;
}
else if (!use_subsurf && !use_diffuse && !use_refract && !use_clear) {
static char name[] = "node_bsdf_principled_metallic";
node_name = name;
flag = GPU_MATFLAG_GLOSSY;
if (use_refract) {
flag |= GPU_MATFLAG_REFRACT;
}
else if (!use_subsurf && !use_diffuse && !use_refract && use_clear) {
static char name[] = "node_bsdf_principled_clearcoat";
node_name = name;
flag = GPU_MATFLAG_GLOSSY;
}
else if (use_subsurf && use_diffuse && !use_refract && !use_clear) {
static char name[] = "node_bsdf_principled_subsurface";
node_name = name;
flag = GPU_MATFLAG_DIFFUSE | GPU_MATFLAG_GLOSSY;
}
else if (!use_subsurf && !use_diffuse && use_refract && !use_clear && !socket_not_zero(4)) {
static char name[] = "node_bsdf_principled_glass";
node_name = name;
flag = GPU_MATFLAG_GLOSSY | GPU_MATFLAG_REFRACT;
}
else {
static char name[] = "node_bsdf_principled";
node_name = name;
flag = GPU_MATFLAG_DIFFUSE | GPU_MATFLAG_GLOSSY | GPU_MATFLAG_REFRACT;
}
if (use_subsurf) {
flag |= GPU_MATFLAG_SSS;
}
float f_use_diffuse = use_diffuse ? 1.0f : 0.0f;
float f_use_clearcoat = use_clear ? 1.0f : 0.0f;
float f_use_refraction = use_refract ? 1.0f : 0.0f;
float use_multi_scatter = (node->custom1 == SHD_GLOSSY_MULTI_GGX) ? 1.0f : 0.0f;
GPU_material_flag_set(mat, flag);
return GPU_stack_link(mat,
node,
node_name,
"node_bsdf_principled",
in,
out,
GPU_builtin(GPU_VIEW_POSITION),
GPU_constant(&f_use_diffuse),
GPU_constant(&f_use_clearcoat),
GPU_constant(&f_use_refraction),
GPU_constant(&use_multi_scatter),
GPU_constant(&node->ssr_id),
GPU_constant(&node->sss_id),