Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClément Foucault <foucault.clem@gmail.com>2022-04-21 11:28:30 +0300
committerClément Foucault <foucault.clem@gmail.com>2022-04-23 00:12:05 +0300
commita60215f058845e14084132c82ddce3c3b028c80f (patch)
tree589dc5898e5db74b628d45ee2aad89e987c47d08 /source/blender/draw/engines/eevee_next/shaders
parent2882cbe685e7eca2a79438998354035c5d665205 (diff)
EEVEE: Rewrite: Implement nodetree support with every geometry typestmp-eevee-next-merge
Diffstat (limited to 'source/blender/draw/engines/eevee_next/shaders')
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_attributes_lib.glsl305
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_geom_curves_vert.glsl37
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_geom_gpencil_vert.glsl46
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_geom_mesh_vert.glsl20
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_geom_world_vert.glsl21
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl358
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_surf_deferred_frag.glsl18
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_surf_depth_frag.glsl71
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_surf_forward_frag.glsl36
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_surf_lib.glsl106
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_surf_world_frag.glsl29
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh176
12 files changed, 1223 insertions, 0 deletions
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_attributes_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_attributes_lib.glsl
new file mode 100644
index 00000000000..d315fe92400
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_attributes_lib.glsl
@@ -0,0 +1,305 @@
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(gpu_shader_codegen_lib.glsl)
+
+#if defined(MAT_GEOM_MESH)
+
+/* -------------------------------------------------------------------- */
+/** \name Mesh
+ *
+ * Mesh objects attributes are loaded using vertex input attributes.
+ * \{ */
+
+# ifdef OBINFO_LIB
+vec3 attr_load_orco(vec4 orco)
+{
+ /* We know when there is no orco layer when orco.w is 1.0 because it uses the generic vertex
+ * attrib (which is [0,0,0,1]). */
+ if (orco.w == 1.0) {
+ /* If the object does not have any deformation, the orco layer calculation is done on the fly
+ * using the orco_madd factors. */
+ return OrcoTexCoFactors[0].xyz + pos * OrcoTexCoFactors[1].xyz;
+ }
+ return orco.xyz * 0.5 + 0.5;
+}
+# endif
+vec4 attr_load_tangent(vec4 tangent)
+{
+ tangent.xyz = safe_normalize(normal_object_to_world(tangent.xyz));
+ return tangent;
+}
+vec4 attr_load_vec4(vec4 attr)
+{
+ return attr;
+}
+vec3 attr_load_vec3(vec3 attr)
+{
+ return attr;
+}
+vec2 attr_load_vec2(vec2 attr)
+{
+ return attr;
+}
+float attr_load_float(float attr)
+{
+ return attr;
+}
+vec4 attr_load_color(vec4 attr)
+{
+ return attr;
+}
+vec3 attr_load_uv(vec3 attr)
+{
+ return attr;
+}
+
+/** \} */
+
+#elif defined(MAT_GEOM_GPENCIL)
+
+/* -------------------------------------------------------------------- */
+/** \name Grease Pencil
+ *
+ * Grease Pencil objects have one uv and one color attribute layer.
+ * \{ */
+
+/* Globals to feed the load functions. */
+vec2 g_uvs;
+vec4 g_color;
+
+# ifdef OBINFO_LIB
+vec3 attr_load_orco(vec4 orco)
+{
+ vec3 lP = point_world_to_object(interp.P);
+ return OrcoTexCoFactors[0].xyz + lP * OrcoTexCoFactors[1].xyz;
+}
+# endif
+vec4 attr_load_tangent(vec4 tangent)
+{
+ return vec4(0.0, 0.0, 0.0, 1.0);
+}
+vec3 attr_load_uv(vec3 dummy)
+{
+ return vec3(g_uvs, 0.0);
+}
+vec4 attr_load_color(vec4 dummy)
+{
+ return g_color;
+}
+vec4 attr_load_vec4(vec4 attr)
+{
+ return vec4(0.0);
+}
+vec3 attr_load_vec3(vec3 attr)
+{
+ return vec3(0.0);
+}
+vec2 attr_load_vec2(vec2 attr)
+{
+ return vec2(0.0);
+}
+float attr_load_float(float attr)
+{
+ return 0.0;
+}
+
+/** \} */
+
+#elif defined(MAT_GEOM_CURVES)
+
+/* -------------------------------------------------------------------- */
+/** \name Curve
+ *
+ * Curve objects loads attributes from buffers through sampler buffers.
+ * \{ */
+
+# ifdef OBINFO_LIB
+vec3 attr_load_orco(samplerBuffer cd_buf)
+{
+ vec3 P = hair_get_strand_pos();
+ vec3 lP = transform_point(ModelMatrixInverse, P);
+ return OrcoTexCoFactors[0].xyz + lP * OrcoTexCoFactors[1].xyz;
+}
+# endif
+vec4 attr_load_tangent(samplerBuffer cd_buf)
+{
+ /* Not supported for the moment. */
+ return vec4(0.0, 0.0, 0.0, 1.0);
+}
+vec3 attr_load_uv(samplerBuffer cd_buf)
+{
+ return texelFetch(cd_buf, interp.curves_strand_id).rgb;
+}
+vec4 attr_load_color(samplerBuffer cd_buf)
+{
+ return texelFetch(cd_buf, interp.curves_strand_id).rgba;
+}
+vec4 attr_load_vec4(samplerBuffer cd_buf)
+{
+ return texelFetch(cd_buf, interp.curves_strand_id).rgba;
+}
+vec3 attr_load_vec3(samplerBuffer cd_buf)
+{
+ return texelFetch(cd_buf, interp.curves_strand_id).rgb;
+}
+vec2 attr_load_vec2(samplerBuffer cd_buf)
+{
+ return texelFetch(cd_buf, interp.curves_strand_id).rg;
+}
+float attr_load_float(samplerBuffer cd_buf)
+{
+ return texelFetch(cd_buf, interp.curves_strand_id).r;
+}
+
+/** \} */
+
+#elif defined(MAT_GEOM_VOLUME)
+
+/* -------------------------------------------------------------------- */
+/** \name Volume
+ *
+ * Volume objects loads attributes from "grids" in the form of 3D textures.
+ * Per grid transform order is following loading order.
+ * \{ */
+
+# ifndef OBINFO_LIB
+# error "draw_object_infos is mandatory for volume objects"
+# endif
+
+vec3 g_orco;
+int g_attr_id = 0;
+
+vec3 grid_coordinates()
+{
+ vec3 co = g_orco;
+ /* Optional per-grid transform. */
+ if (drw_volume.grids_xform[g_attr_id][3][3] != 0.0) {
+ co = (drw_volume.grids_xform[g_attr_id] * vec4(objectPosition, 1.0)).xyz;
+ }
+ g_attr_id += 1;
+ return co;
+}
+
+vec3 attr_load_orco(sampler3D tex)
+{
+ g_attr_id += 1;
+ return g_orco;
+}
+vec4 attr_load_tangent(sampler3D tex)
+{
+ attr_id += 1;
+ return vec4(0);
+}
+vec4 attr_load_vec4(sampler3D tex)
+{
+ return texture(tex, grid_coordinates());
+}
+vec3 attr_load_vec3(sampler3D tex)
+{
+ return texture(tex, grid_coordinates()).rgb;
+}
+vec2 attr_load_vec2(sampler3D tex)
+{
+ return texture(tex, grid_coordinates()).rg;
+}
+float attr_load_float(sampler3D tex)
+{
+ return texture(tex, grid_coordinates()).r;
+}
+vec4 attr_load_color(sampler3D tex)
+{
+ return texture(tex, grid_coordinates());
+}
+vec3 attr_load_uv(sampler3D attr)
+{
+ attr_id += 1;
+ return vec3(0);
+}
+
+/** \} */
+
+#elif defined(MAT_GEOM_WORLD)
+
+/* -------------------------------------------------------------------- */
+/** \name World
+ *
+ * World has no attributes other than orco.
+ * \{ */
+
+vec3 attr_load_orco(vec4 orco)
+{
+ return -g_data.N;
+}
+vec4 attr_load_tangent(vec4 tangent)
+{
+ return vec4(0);
+}
+vec4 attr_load_vec4(vec4 attr)
+{
+ return vec4(0);
+}
+vec3 attr_load_vec3(vec3 attr)
+{
+ return vec3(0);
+}
+vec2 attr_load_vec2(vec2 attr)
+{
+ return vec2(0);
+}
+float attr_load_float(float attr)
+{
+ return 0.0;
+}
+vec4 attr_load_color(vec4 attr)
+{
+ return vec4(0);
+}
+vec3 attr_load_uv(vec3 attr)
+{
+ return vec3(0);
+}
+
+/** \} */
+
+#endif
+
+/* -------------------------------------------------------------------- */
+/** \name Volume Attribute post
+ *
+ * TODO(@fclem): These implementation details should concern the DRWManager and not be a fix on
+ * the engine side. But as of now, the engines are reponsible for loading the attributes.
+ *
+ * \{ */
+
+#if defined(MAT_GEOM_VOLUME)
+
+float attr_load_temperature_post(float attr)
+{
+ /* Bring the into standard range without having to modify the grid values */
+ attr = (attr > 0.01) ? (attr * drw_volume.temperature_mul + drw_volume.temperature_bias) : 0.0;
+ return attr;
+}
+vec4 attr_load_color_post(vec4 attr)
+{
+ /* Density is premultiplied for interpolation, divide it out here. */
+ attr.rgb *= safe_rcp(attr.a);
+ attr.rgb *= drw_volume.color_mul.rgb;
+ attr.a = 1.0;
+ return attr;
+}
+
+#else /* Noop for any other surface. */
+
+float attr_load_temperature_post(float attr)
+{
+ return attr;
+}
+vec4 attr_load_color_post(vec4 attr)
+{
+ return attr;
+}
+
+#endif
+
+/** \} */
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_geom_curves_vert.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_geom_curves_vert.glsl
new file mode 100644
index 00000000000..4c123031c83
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_geom_curves_vert.glsl
@@ -0,0 +1,37 @@
+
+#pragma BLENDER_REQUIRE(eevee_attributes_lib.glsl)
+#pragma BLENDER_REQUIRE(common_hair_lib.glsl) /* TODO rename to curve. */
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_nodetree_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_surf_lib.glsl)
+
+void main(void)
+{
+ init_interface();
+
+ vec3 T;
+
+ bool is_persp = (ProjectionMatrix[3][3] == 0.0);
+ hair_get_pos_tan_binor_time(is_persp,
+ ModelMatrixInverse,
+ ViewMatrixInverse[3].xyz,
+ ViewMatrixInverse[2].xyz,
+ interp.P,
+ T,
+ interp.curves_binormal,
+ interp.curves_time,
+ interp.curves_thickness,
+ interp.curves_time_width);
+
+ interp.N = cross(T, interp.curves_binormal);
+ interp.curves_strand_id = hair_get_strand_id();
+ interp.barycentric_coords = hair_get_barycentric();
+
+ init_globals();
+ attrib_load();
+
+ interp.P += nodetree_displacement();
+
+ gl_Position = point_world_to_ndc(interp.P);
+}
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_geom_gpencil_vert.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_geom_gpencil_vert.glsl
new file mode 100644
index 00000000000..8e7201b9bce
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_geom_gpencil_vert.glsl
@@ -0,0 +1,46 @@
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_gpencil_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_surf_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_attributes_lib.glsl)
+
+void main(void)
+{
+ init_interface();
+
+ /* TODO(fclem): Expose through a node? */
+ vec4 sspos;
+ vec2 aspect;
+ float strength;
+ float hardness;
+ vec2 thickness;
+
+ gl_Position = gpencil_vertex(ma,
+ ma1,
+ ma2,
+ ma3,
+ pos,
+ pos1,
+ pos2,
+ pos3,
+ uv1,
+ uv2,
+ col1,
+ col2,
+ fcol1,
+ vec4(drw_view.viewport_size, drw_view.viewport_size_inverse),
+ interp.P,
+ interp.N,
+ g_color,
+ strength,
+ g_uvs,
+ sspos,
+ aspect,
+ thickness,
+ hardness);
+
+ init_globals();
+ attrib_load();
+
+ interp.P += nodetree_displacement();
+}
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_geom_mesh_vert.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_geom_mesh_vert.glsl
new file mode 100644
index 00000000000..24d5a2c60b0
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_geom_mesh_vert.glsl
@@ -0,0 +1,20 @@
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_attributes_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_nodetree_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_surf_lib.glsl)
+
+void main(void)
+{
+ init_interface();
+
+ interp.P = point_object_to_world(pos);
+ interp.N = normal_object_to_world(nor);
+
+ init_globals();
+ attrib_load();
+
+ interp.P += nodetree_displacement();
+
+ gl_Position = point_world_to_ndc(interp.P);
+}
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_geom_world_vert.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_geom_world_vert.glsl
new file mode 100644
index 00000000000..6ce98f26c7e
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_geom_world_vert.glsl
@@ -0,0 +1,21 @@
+
+/**
+ * Custom fullscreen triangle with placeholders varyings.
+ **/
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_nodetree_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_surf_lib.glsl)
+
+void main(void)
+{
+ /* Fullscreen triangle. */
+ int v = gl_VertexID % 3;
+ float x = float((v & 1) << 2) - 1.0;
+ float y = float((v & 2) << 1) - 1.0;
+ gl_Position = vec4(x, y, 1.0, 1.0);
+
+ /* Pass view position to keep accuracy. */
+ interp.P = project_point(ProjectionMatrixInverse, gl_Position.xyz);
+ interp.N = vec3(1);
+}
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl
new file mode 100644
index 00000000000..0110a2bd930
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl
@@ -0,0 +1,358 @@
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(gpu_shader_codegen_lib.glsl)
+
+vec3 g_emission;
+vec3 g_transmittance;
+float g_holdout;
+
+/* The Closure type is never used. Use float as dummy type. */
+#define Closure float
+
+/* Sampled closure parameters. */
+ClosureDiffuse g_diffuse_data;
+ClosureReflection g_reflection_data;
+ClosureRefraction g_refraction_data;
+/* Weight accumulation per sampled closure type. */
+float g_diffuse_weight;
+float g_reflection_weight;
+float g_refraction_weight;
+/* Random number per sampled closure type. */
+float g_diffuse_rand;
+float g_reflection_rand;
+float g_refraction_rand;
+
+/**
+ * Returns true if the closure is to be selected based on the input weight.
+ */
+bool closure_select(float weight, inout float total_weight, inout float r)
+{
+ total_weight += weight;
+ float x = weight / total_weight;
+ bool chosen = (r < x);
+ /* Assuming that if r is in the interval [0,x] or [x,1], it's still uniformly distributed within
+ * that interval, so you remaping to [0,1] again to explore this space of probability. */
+ r = (chosen) ? (r / x) : ((r - x) / (1.0 - x));
+ return chosen;
+}
+
+#define SELECT_CLOSURE(destination, weight_total, random, candidate) \
+ if (closure_select(candidate.weight, weight_total, random)) { \
+ destination = candidate; \
+ }
+
+void closure_weights_reset()
+{
+ g_diffuse_weight = 0.0;
+ g_reflection_weight = 0.0;
+ g_refraction_weight = 0.0;
+
+ g_diffuse_data.weight = 0.0;
+ g_diffuse_data.color = vec3(0.0);
+ g_diffuse_data.N = vec3(0.0);
+ g_diffuse_data.sss_radius = vec3(0.0);
+ g_diffuse_data.sss_id = uint(0);
+
+ g_reflection_data.weight = 0.0;
+ g_reflection_data.color = vec3(0.0);
+ g_reflection_data.N = vec3(0.0);
+ g_reflection_data.roughness = 0.0;
+
+ g_refraction_data.weight = 0.0;
+ g_refraction_data.color = vec3(0.0);
+ g_refraction_data.N = vec3(0.0);
+ g_refraction_data.roughness = 0.0;
+ g_refraction_data.ior = 0.0;
+
+ /* TODO */
+ g_diffuse_rand = 0.5;
+ g_reflection_rand = 0.5;
+ g_refraction_rand = 0.5;
+
+ g_emission = vec3(0.0);
+ g_transmittance = vec3(0.0);
+ g_holdout = 0.0;
+}
+
+/* Single BSDFs. */
+Closure closure_eval(ClosureDiffuse diffuse)
+{
+ SELECT_CLOSURE(g_diffuse_data, g_diffuse_weight, g_diffuse_rand, diffuse);
+ return Closure(0);
+}
+
+Closure closure_eval(ClosureTranslucent translucent)
+{
+ /* TODO */
+ return Closure(0);
+}
+
+Closure closure_eval(ClosureReflection reflection)
+{
+ SELECT_CLOSURE(g_reflection_data, g_reflection_weight, g_reflection_rand, reflection);
+ return Closure(0);
+}
+
+Closure closure_eval(ClosureRefraction refraction)
+{
+ SELECT_CLOSURE(g_refraction_data, g_refraction_weight, g_refraction_rand, refraction);
+ return Closure(0);
+}
+
+Closure closure_eval(ClosureEmission emission)
+{
+ g_emission += emission.emission * emission.weight;
+ return Closure(0);
+}
+
+Closure closure_eval(ClosureTransparency transparency)
+{
+ g_transmittance += transparency.transmittance * transparency.weight;
+ g_holdout += transparency.holdout * transparency.weight;
+ return Closure(0);
+}
+
+Closure closure_eval(ClosureVolumeScatter volume_scatter)
+{
+ /* TODO */
+ return Closure(0);
+}
+
+Closure closure_eval(ClosureVolumeAbsorption volume_absorption)
+{
+ /* TODO */
+ return Closure(0);
+}
+
+Closure closure_eval(ClosureHair hair)
+{
+ /* TODO */
+ return Closure(0);
+}
+
+/* Glass BSDF. */
+Closure closure_eval(ClosureReflection reflection, ClosureRefraction refraction)
+{
+ SELECT_CLOSURE(g_reflection_data, g_reflection_weight, g_reflection_rand, reflection);
+ SELECT_CLOSURE(g_refraction_data, g_refraction_weight, g_refraction_rand, refraction);
+ return Closure(0);
+}
+
+/* Dielectric BSDF. */
+Closure closure_eval(ClosureDiffuse diffuse, ClosureReflection reflection)
+{
+ SELECT_CLOSURE(g_diffuse_data, g_diffuse_weight, g_diffuse_rand, diffuse);
+ SELECT_CLOSURE(g_reflection_data, g_reflection_weight, g_reflection_rand, reflection);
+ return Closure(0);
+}
+
+/* ClearCoat BSDF. */
+Closure closure_eval(ClosureReflection reflection, ClosureReflection clearcoat)
+{
+ SELECT_CLOSURE(g_reflection_data, g_reflection_weight, g_reflection_rand, reflection);
+ SELECT_CLOSURE(g_reflection_data, g_reflection_weight, g_reflection_rand, clearcoat);
+ return Closure(0);
+}
+
+/* Volume BSDF. */
+Closure closure_eval(ClosureVolumeScatter volume_scatter,
+ ClosureVolumeAbsorption volume_absorption,
+ ClosureEmission emission)
+{
+ /* TODO */
+ return Closure(0);
+}
+
+/* Specular BSDF. */
+Closure closure_eval(ClosureDiffuse diffuse,
+ ClosureReflection reflection,
+ ClosureReflection clearcoat)
+{
+ SELECT_CLOSURE(g_diffuse_data, g_diffuse_weight, g_diffuse_rand, diffuse);
+ SELECT_CLOSURE(g_reflection_data, g_reflection_weight, g_reflection_rand, reflection);
+ SELECT_CLOSURE(g_reflection_data, g_reflection_weight, g_reflection_rand, clearcoat);
+ return Closure(0);
+}
+
+/* Principled BSDF. */
+Closure closure_eval(ClosureDiffuse diffuse,
+ ClosureReflection reflection,
+ ClosureReflection clearcoat,
+ ClosureRefraction refraction)
+{
+ SELECT_CLOSURE(g_diffuse_data, g_diffuse_weight, g_diffuse_rand, diffuse);
+ SELECT_CLOSURE(g_reflection_data, g_reflection_weight, g_reflection_rand, reflection);
+ SELECT_CLOSURE(g_reflection_data, g_reflection_weight, g_reflection_rand, clearcoat);
+ SELECT_CLOSURE(g_refraction_data, g_refraction_weight, g_refraction_rand, refraction);
+ return Closure(0);
+}
+
+/* Noop since we are sampling closures. */
+Closure closure_add(Closure cl1, Closure cl2)
+{
+ return Closure(0);
+}
+Closure closure_mix(Closure cl1, Closure cl2, float fac)
+{
+ return Closure(0);
+}
+
+float ambient_occlusion_eval(vec3 normal,
+ float distance,
+ const float inverted,
+ const float sample_count)
+{
+ /* TODO */
+ return 0.0;
+}
+
+#ifndef GPU_METAL
+void attrib_load();
+Closure nodetree_surface();
+Closure nodetree_volume();
+vec3 nodetree_displacement();
+float nodetree_thickness();
+#endif
+
+/* Stubs. */
+vec2 btdf_lut(float a, float b, float c)
+{
+ return vec2(1, 0);
+}
+vec2 brdf_lut(float a, float b)
+{
+ return vec2(1, 0);
+}
+vec3 F_brdf_multi_scatter(vec3 a, vec3 b, vec2 c)
+{
+ return a;
+}
+vec3 F_brdf_single_scatter(vec3 a, vec3 b, vec2 c)
+{
+ return a;
+}
+float F_eta(float a, float b)
+{
+ return a;
+}
+vec4 closure_to_rgba(Closure cl)
+{
+ return vec4(0.0);
+}
+void output_aov(vec4 color, float value, uint hash)
+{
+}
+
+#ifdef EEVEE_MATERIAL_STUBS
+# define attrib_load()
+# define nodetree_displacement() vec3(0.0)
+# define nodetree_surface() Closure(0)
+# define nodetree_volume() Closure(0)
+# define nodetree_thickness() 0.1
+#endif
+
+/* -------------------------------------------------------------------- */
+/** \name Fragment Displacement
+ *
+ * Displacement happening in the fragment shader.
+ * Can be used in conjunction with a per vertex displacement.
+ *
+ * \{ */
+
+#ifdef MAT_DISPLACEMENT_BUMP
+/* Return new shading normal. */
+vec3 displacement_bump()
+{
+# ifdef GPU_FRAGMENT_SHADER
+ vec2 dHd;
+ dF_branch(dot(nodetree_displacement(), g_data.N + dF_impl(g_data.N)), dHd);
+
+ vec3 dPdx = dFdx(g_data.P);
+ vec3 dPdy = dFdy(g_data.P);
+
+ /* Get surface tangents from normal. */
+ vec3 Rx = cross(dPdy, g_data.N);
+ vec3 Ry = cross(g_data.N, dPdx);
+
+ /* Compute surface gradient and determinant. */
+ float det = dot(dPdx, Rx);
+
+ vec3 surfgrad = dHd.x * Rx + dHd.y * Ry;
+
+ float facing = FrontFacing ? 1.0 : -1.0;
+ return normalize(abs(det) * g_data.N - facing * sign(det) * surfgrad);
+# else
+ return g_data.N;
+# endif
+}
+#endif
+
+void fragment_displacement()
+{
+#ifdef MAT_DISPLACEMENT_BUMP
+ g_data.N = displacement_bump();
+#endif
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Coordinate implementations
+ *
+ * Callbacks for the texture coordinate node.
+ *
+ * \{ */
+
+vec3 coordinate_camera(vec3 P)
+{
+ vec3 vP;
+ if (false /* probe */) {
+ /* Unsupported. It would make the probe camera-dependent. */
+ vP = P;
+ }
+ else {
+#ifdef MAT_WORLD
+ vP = transform_direction(ViewMatrix, P);
+#else
+ vP = transform_point(ViewMatrix, P);
+#endif
+ }
+ vP.z = -vP.z;
+ return vP;
+}
+
+vec3 coordinate_screen(vec3 P)
+{
+ vec3 window = vec3(0.0);
+ if (false /* probe */) {
+ /* Unsupported. It would make the probe camera-dependent. */
+ window.xy = vec2(0.5);
+ }
+ else {
+ /* TODO(fclem): Actual camera tranform. */
+ window.xy = project_point(ViewProjectionMatrix, P).xy * 0.5 + 0.5;
+ window.xy = window.xy * CameraTexCoFactors.xy + CameraTexCoFactors.zw;
+ }
+ return window;
+}
+
+vec3 coordinate_reflect(vec3 P, vec3 N)
+{
+#ifdef MAT_WORLD
+ return N;
+#else
+ return -reflect(cameraVec(P), N);
+#endif
+}
+
+vec3 coordinate_incoming(vec3 P)
+{
+#ifdef MAT_WORLD
+ return -P;
+#else
+ return cameraVec(P);
+#endif
+}
+
+/** \} */
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_deferred_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_deferred_frag.glsl
new file mode 100644
index 00000000000..fbc5be853a0
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_deferred_frag.glsl
@@ -0,0 +1,18 @@
+
+/**
+ * Deferred lighting evaluation: Lighting is evaluated in a separate pass.
+ *
+ * Outputs shading parameter per pixel using a randomized set of BSDFs.
+ **/
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_surf_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_nodetree_lib.glsl)
+
+void main(void)
+{
+ init_globals();
+
+ nodetree_surface();
+}
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_depth_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_depth_frag.glsl
new file mode 100644
index 00000000000..a7656d70c1b
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_depth_frag.glsl
@@ -0,0 +1,71 @@
+
+/**
+ * Depth shader that can stochastically discard transparent pixel.
+ */
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_nodetree_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_surf_lib.glsl)
+
+/* From the paper "Hashed Alpha Testing" by Chris Wyman and Morgan McGuire. */
+float hash(vec2 a)
+{
+ return fract(1e4 * sin(17.0 * a.x + 0.1 * a.y) * (0.1 + abs(sin(13.0 * a.y + a.x))));
+}
+
+float hash3d(vec3 a)
+{
+ return hash(vec2(hash(a.xy), a.z));
+}
+
+float hashed_alpha_threshold(float hash_scale, float hash_offset, vec3 P)
+{
+ /* Find the discretized derivatives of our coordinates. */
+ float max_deriv = max(length(dFdx(P)), length(dFdy(P)));
+ float pix_scale = 1.0 / (hash_scale * max_deriv);
+ /* Find two nearest log-discretized noise scales. */
+ float pix_scale_log = log2(pix_scale);
+ vec2 pix_scales;
+ pix_scales.x = exp2(floor(pix_scale_log));
+ pix_scales.y = exp2(ceil(pix_scale_log));
+ /* Compute alpha thresholds at our two noise scales. */
+ vec2 alpha;
+ alpha.x = hash3d(floor(pix_scales.x * P));
+ alpha.y = hash3d(floor(pix_scales.y * P));
+ /* Factor to interpolate lerp with. */
+ float fac = fract(log2(pix_scale));
+ /* Interpolate alpha threshold from noise at two scales. */
+ float x = mix(alpha.x, alpha.y, fac);
+ /* Pass into CDF to compute uniformly distrib threshold. */
+ float a = min(fac, 1.0 - fac);
+ float one_a = 1.0 - a;
+ float denom = 1.0 / (2 * a * one_a);
+ float one_x = (1 - x);
+ vec3 cases = vec3((x * x) * denom, (x - 0.5 * a) / one_a, 1.0 - (one_x * one_x * denom));
+ /* Find our final, uniformly distributed alpha threshold. */
+ float threshold = (x < one_a) ? ((x < a) ? cases.x : cases.y) : cases.z;
+ /* Jitter the threshold for TAA accumulation. */
+ threshold = fract(threshold + hash_offset);
+ /* Avoids threshold == 0. */
+ threshold = clamp(threshold, 1.0e-6, 1.0);
+ return threshold;
+}
+
+void main(void)
+{
+#ifdef MAT_TRANSPARENT
+ init_globals();
+
+ nodetree_surface();
+
+ // float noise_offset = sampling_rng_1D_get(sampling_buf, SAMPLING_TRANSPARENCY);
+ float noise_offset = 0.5;
+ float random_threshold = hashed_alpha_threshold(1.0, noise_offset, g_data.P);
+
+ float transparency = avg(g_transmittance);
+ if (transparency > random_threshold) {
+ discard;
+ }
+#endif
+}
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_forward_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_forward_frag.glsl
new file mode 100644
index 00000000000..d83802c73f3
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_forward_frag.glsl
@@ -0,0 +1,36 @@
+
+/**
+ * Forward lighting evaluation: Lighting is evaluated during the geometry rasterization.
+ *
+ * This is used by alpha blended materials and materials using Shader to RGB nodes.
+ **/
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_nodetree_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_surf_lib.glsl)
+
+void main(void)
+{
+ init_globals();
+
+ fragment_displacement();
+
+ nodetree_surface();
+
+ g_holdout = saturate(g_holdout);
+
+ out_radiance.rgb = g_emission;
+ out_radiance.rgb += g_diffuse_data.color * saturate(g_diffuse_data.N.z);
+ out_radiance.rgb += g_reflection_data.color * saturate(g_reflection_data.N.z);
+ out_radiance.a = 0.0;
+
+ out_radiance.rgb *= 1.0 - g_holdout;
+
+ out_transmittance.rgb = g_transmittance;
+ out_transmittance.a = saturate(avg(g_transmittance));
+
+ /* Test */
+ out_transmittance.a = 1.0 - out_transmittance.a;
+ out_radiance.a = 1.0 - out_radiance.a;
+}
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_lib.glsl
new file mode 100644
index 00000000000..5811b935c0d
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_lib.glsl
@@ -0,0 +1,106 @@
+
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(gpu_shader_codegen_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_nodetree_lib.glsl)
+
+#if defined(USE_BARYCENTRICS) && defined(GPU_FRAGMENT_SHADER) && defined(MAT_GEOM_MESH)
+vec3 barycentric_distances_get()
+{
+ /* NOTE: No need to undo perspective divide since it has not been applied. */
+ vec3 pos0 = (ProjectionMatrixInverse * gpu_position_at_vertex(0)).xyz;
+ vec3 pos1 = (ProjectionMatrixInverse * gpu_position_at_vertex(1)).xyz;
+ vec3 pos2 = (ProjectionMatrixInverse * gpu_position_at_vertex(2)).xyz;
+ vec3 edge21 = pos2 - pos1;
+ vec3 edge10 = pos1 - pos0;
+ vec3 edge02 = pos0 - pos2;
+ vec3 d21 = safe_normalize(edge21);
+ vec3 d10 = safe_normalize(edge10);
+ vec3 d02 = safe_normalize(edge02);
+ vec3 dists;
+ float d = dot(d21, edge02);
+ dists.x = sqrt(dot(edge02, edge02) - d * d);
+ d = dot(d02, edge10);
+ dists.y = sqrt(dot(edge10, edge10) - d * d);
+ d = dot(d10, edge21);
+ dists.z = sqrt(dot(edge21, edge21) - d * d);
+ return dists.xyz;
+}
+#endif
+
+void init_globals_mesh(void)
+{
+#if defined(USE_BARYCENTRICS) && defined(GPU_FRAGMENT_SHADER) && defined(MAT_GEOM_MESH)
+ g_data.barycentric_coords = gpu_BaryCoord.xy;
+ g_data.barycentric_dists = barycentric_distances_get();
+#endif
+}
+
+void init_globals_curves(void)
+{
+#if 0 /* TODO */
+ /* Shade as a cylinder. */
+ float cos_theta = interp.curves_time_width / interp.curves_thickness;
+ float sin_theta = sqrt(max(0.0, 1.0 - cos_theta * cos_theta));
+ g_data.N = normalize(interp.N * sin_theta + interp.curves_binormal * cos_theta);
+
+ g_data.is_strand = true;
+ g_data.hair_time = interp.curves_time;
+ g_data.hair_thickness = interp.curves_thickness;
+ g_data.hair_strand_id = interp.curves_strand_id;
+# if defined(USE_BARYCENTRICS) && defined(GPU_FRAGMENT_SHADER) && defined(MAT_GEOM_CURVES)
+ g_data.barycentric_coords = hair_resolve_barycentric(interp.barycentric_coords);
+# endif
+#endif
+}
+
+void init_globals_gpencil(void)
+{
+ /* Undo backface flip as the gpencil normal is already pointing towards the camera. */
+ g_data.N = interp.N;
+}
+
+void init_globals(void)
+{
+ /* Default values. */
+ g_data.P = interp.P;
+ g_data.N = safe_normalize(interp.N);
+ g_data.Ng = g_data.N;
+ g_data.is_strand = false;
+ g_data.hair_time = 0.0;
+ g_data.hair_thickness = 0.0;
+ g_data.hair_strand_id = 0;
+ g_data.ray_type = RAY_TYPE_CAMERA; /* TODO */
+ g_data.ray_depth = 0.0;
+ g_data.ray_length = distance(g_data.P, cameraPos);
+ g_data.barycentric_coords = vec2(0.0);
+ g_data.barycentric_dists = vec3(0.0);
+
+#ifdef GPU_FRAGMENT_SHADER
+ g_data.N = (FrontFacing) ? g_data.N : -g_data.N;
+ g_data.Ng = safe_normalize(cross(dFdx(g_data.P), dFdy(g_data.P)));
+#endif
+
+#if defined(MAT_GEOM_MESH)
+ init_globals_mesh();
+#elif defined(MAT_GEOM_CURVES)
+ init_globals_curves();
+#elif defined(MAT_GEOM_GPENCIL)
+ init_globals_gpencil();
+#endif
+}
+
+/* Avoid some compiler issue with non set interface parameters. */
+void init_interface()
+{
+#ifdef GPU_VERTEX_SHADER
+ interp.P = vec3(0.0);
+ interp.N = vec3(0.0);
+ interp.barycentric_coords = vec2(0.0);
+ interp.curves_binormal = vec3(0.0);
+ interp.curves_time = 0.0;
+ interp.curves_time_width = 0.0;
+ interp.curves_thickness = 0.0;
+ interp.curves_strand_id = 0;
+ drw_ResourceID_iface.resource_index = resource_id;
+#endif
+}
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_world_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_world_frag.glsl
new file mode 100644
index 00000000000..9901596623d
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_world_frag.glsl
@@ -0,0 +1,29 @@
+
+/**
+ * Background used to shade the world.
+ *
+ * Outputs shading parameter per pixel using a set of randomized BSDFs.
+ **/
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_attributes_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_surf_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_nodetree_lib.glsl)
+
+void main(void)
+{
+ init_globals();
+ /* View position is passed to keep accuracy. */
+ g_data.N = normal_view_to_world(viewCameraVec(interp.P));
+ g_data.Ng = g_data.N;
+ g_data.P = -g_data.N + cameraPos;
+ attrib_load();
+
+ nodetree_surface();
+
+ g_holdout = saturate(g_holdout);
+
+ out_background.rgb = safe_color(g_emission) * (1.0 - g_holdout);
+ out_background.a = saturate(avg(g_transmittance)) * g_holdout;
+}
diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh
new file mode 100644
index 00000000000..13269d833e8
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh
@@ -0,0 +1,176 @@
+
+#include "gpu_shader_create_info.hh"
+
+/* -------------------------------------------------------------------- */
+/** \name Common
+ * \{ */
+
+/* TODO(@fclem): This is a bit out of place at the moment. */
+GPU_SHADER_CREATE_INFO(eevee_shared)
+ .typedef_source("eevee_defines.hh")
+ .typedef_source("eevee_shader_shared.hh");
+
+GPU_SHADER_CREATE_INFO(eevee_sampling_data)
+ .additional_info("eevee_shared")
+ .uniform_buf(14, "SamplingData", "sampling_buf");
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Surface Mesh Type
+ * \{ */
+
+GPU_SHADER_CREATE_INFO(eevee_geom_mesh)
+ .define("MAT_GEOM_MESH")
+ .vertex_in(0, Type::VEC3, "pos")
+ .vertex_in(1, Type::VEC3, "nor")
+ .vertex_source("eevee_geom_mesh_vert.glsl")
+ .additional_info("draw_mesh", "draw_resource_id_varying", "draw_resource_handle");
+
+GPU_SHADER_CREATE_INFO(eevee_geom_gpencil)
+ .define("MAT_GEOM_GPENCIL")
+ .vertex_source("eevee_geom_gpencil_vert.glsl")
+ .additional_info("draw_gpencil", "draw_resource_id_varying", "draw_resource_handle");
+
+GPU_SHADER_CREATE_INFO(eevee_geom_curves)
+ .define("MAT_GEOM_CURVES")
+ .vertex_source("eevee_geom_curves_vert.glsl")
+ .additional_info("draw_hair", "draw_resource_id_varying", "draw_resource_handle");
+
+GPU_SHADER_CREATE_INFO(eevee_geom_world)
+ .define("MAT_GEOM_WORLD")
+ .builtins(BuiltinBits::VERTEX_ID)
+ .vertex_source("eevee_geom_world_vert.glsl")
+ .additional_info("draw_modelmat", "draw_resource_id_varying", "draw_resource_handle");
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Surface
+ * \{ */
+
+GPU_SHADER_INTERFACE_INFO(eevee_surf_iface, "interp")
+ .smooth(Type::VEC3, "P")
+ .smooth(Type::VEC3, "N")
+ .smooth(Type::VEC2, "barycentric_coords")
+ .smooth(Type::VEC3, "curves_binormal")
+ .smooth(Type::FLOAT, "curves_time")
+ .smooth(Type::FLOAT, "curves_time_width")
+ .smooth(Type::FLOAT, "curves_thickness")
+ .flat(Type::INT, "curves_strand_id");
+
+#define image_out(slot, qualifier, format, name) \
+ image(slot, format, qualifier, ImageType::FLOAT_2D, name, Frequency::PASS)
+
+GPU_SHADER_CREATE_INFO(eevee_surf_deferred)
+ .vertex_out(eevee_surf_iface)
+ /* Note: This removes the possibility of using gl_FragDepth. */
+ // .early_fragment_test(true)
+ /* Direct output. */
+ .fragment_out(0, Type::VEC4, "out_radiance", DualBlend::SRC_0)
+ .fragment_out(0, Type::VEC4, "out_transmittance", DualBlend::SRC_1)
+ /* Gbuffer. */
+ // .image_out(0, Qualifier::WRITE, GPU_R11F_G11F_B10F, "gbuff_transmit_color")
+ // .image_out(1, Qualifier::WRITE, GPU_R11F_G11F_B10F, "gbuff_transmit_data")
+ // .image_out(2, Qualifier::WRITE, GPU_RGBA16F, "gbuff_transmit_normal")
+ // .image_out(3, Qualifier::WRITE, GPU_R11F_G11F_B10F, "gbuff_reflection_color")
+ // .image_out(4, Qualifier::WRITE, GPU_RGBA16F, "gbuff_reflection_normal")
+ // .image_out(5, Qualifier::WRITE, GPU_R11F_G11F_B10F, "gbuff_emission")
+ /* Renderpasses. */
+ // .image_out(6, Qualifier::READ_WRITE, GPU_RGBA16F, "rpass_volume_light")
+ /* TODO: AOVs maybe? */
+ .fragment_source("eevee_surf_deferred_frag.glsl")
+ // .additional_info("eevee_sampling_data", "eevee_utility_texture")
+ ;
+
+#undef image_out
+
+GPU_SHADER_CREATE_INFO(eevee_surf_forward)
+ .auto_resource_location(true)
+ .vertex_out(eevee_surf_iface)
+ .fragment_out(0, Type::VEC4, "out_radiance", DualBlend::SRC_0)
+ .fragment_out(0, Type::VEC4, "out_transmittance", DualBlend::SRC_1)
+ .fragment_source("eevee_surf_forward_frag.glsl")
+ // .additional_info("eevee_sampling_data",
+ // "eevee_lightprobe_data",
+ /* Optionnally added depending on the material. */
+ // "eevee_raytrace_data",
+ // "eevee_transmittance_data",
+ // "eevee_utility_texture",
+ // "eevee_light_data",
+ // "eevee_shadow_data"
+ // )
+ ;
+
+GPU_SHADER_CREATE_INFO(eevee_surf_depth)
+ .vertex_out(eevee_surf_iface)
+ .fragment_source("eevee_surf_depth_frag.glsl")
+ // .additional_info("eevee_sampling_data", "eevee_utility_texture")
+ ;
+
+GPU_SHADER_CREATE_INFO(eevee_surf_world)
+ .vertex_out(eevee_surf_iface)
+ .fragment_out(0, Type::VEC4, "out_background")
+ .fragment_source("eevee_surf_world_frag.glsl")
+ // .additional_info("eevee_utility_texture")
+ ;
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Volume
+ * \{ */
+
+#if 0 /* TODO */
+GPU_SHADER_INTERFACE_INFO(eevee_volume_iface, "interp")
+ .smooth(Type::VEC3, "P_start")
+ .smooth(Type::VEC3, "P_end");
+
+GPU_SHADER_CREATE_INFO(eevee_volume_deferred)
+ .sampler(0, ImageType::DEPTH_2D, "depth_max_tx")
+ .vertex_in(0, Type::VEC3, "pos")
+ .vertex_out(eevee_volume_iface)
+ .fragment_out(0, Type::UVEC4, "out_volume_data")
+ .fragment_out(1, Type::VEC4, "out_transparency_data")
+ .additional_info("eevee_shared")
+ .vertex_source("eevee_volume_vert.glsl")
+ .fragment_source("eevee_volume_deferred_frag.glsl")
+ .additional_info("draw_fullscreen");
+#endif
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Test shaders
+ *
+ * Variations that are only there to test shaders at compile time.
+ * \{ */
+
+#ifdef DEBUG
+
+/* Stub functions defined by the material evaluation. */
+GPU_SHADER_CREATE_INFO(eevee_material_stub).define("EEVEE_MATERIAL_STUBS");
+
+# define EEVEE_MAT_FINAL_VARIATION(name, ...) \
+ GPU_SHADER_CREATE_INFO(name) \
+ .additional_info(__VA_ARGS__) \
+ .auto_resource_location(true) \
+ .do_static_compilation(true);
+
+# define EEVEE_MAT_GEOM_VARIATIONS(prefix, ...) \
+ EEVEE_MAT_FINAL_VARIATION(prefix##_world, "eevee_geom_world", __VA_ARGS__) \
+ EEVEE_MAT_FINAL_VARIATION(prefix##_gpencil, "eevee_geom_gpencil", __VA_ARGS__) \
+ EEVEE_MAT_FINAL_VARIATION(prefix##_hair, "eevee_geom_curves", __VA_ARGS__) \
+ EEVEE_MAT_FINAL_VARIATION(prefix##_mesh, "eevee_geom_mesh", __VA_ARGS__)
+
+# define EEVEE_MAT_PIPE_VARIATIONS(name, ...) \
+ EEVEE_MAT_GEOM_VARIATIONS(name##_world, "eevee_surf_world", __VA_ARGS__) \
+ EEVEE_MAT_GEOM_VARIATIONS(name##_depth, "eevee_surf_depth", __VA_ARGS__) \
+ EEVEE_MAT_GEOM_VARIATIONS(name##_deferred, "eevee_surf_deferred", __VA_ARGS__) \
+ EEVEE_MAT_GEOM_VARIATIONS(name##_forward, "eevee_surf_forward", __VA_ARGS__)
+
+EEVEE_MAT_PIPE_VARIATIONS(eevee_surface, "eevee_material_stub")
+
+#endif
+
+/** \} */