Skip to content

Instantly share code, notes, and snippets.

@ctreffs
Forked from warrenm/CommonProfile.metal
Created October 15, 2020 17:41
Show Gist options
  • Save ctreffs/0e18ae6d0b0a6eeff0842c18679de133 to your computer and use it in GitHub Desktop.
Save ctreffs/0e18ae6d0b0a6eeff0842c18679de133 to your computer and use it in GitHub Desktop.
SceneKit's CommonProfile Shader v2 (macOS 10.15)
////////////////////////////////////////////////
// CommonProfile Shader v2
#import <metal_stdlib>
using namespace metal;
#ifndef __SCNMetalDefines__
#define __SCNMetalDefines__
enum {
SCNVertexSemanticPosition,
SCNVertexSemanticNormal,
SCNVertexSemanticTangent,
SCNVertexSemanticColor,
SCNVertexSemanticBoneIndices,
SCNVertexSemanticBoneWeights,
SCNVertexSemanticTexcoord0,
SCNVertexSemanticTexcoord1,
SCNVertexSemanticTexcoord2,
SCNVertexSemanticTexcoord3,
SCNVertexSemanticTexcoord4,
SCNVertexSemanticTexcoord5,
SCNVertexSemanticTexcoord6,
SCNVertexSemanticTexcoord7
};
// This structure hold all the informations that are constant through a render pass
// In a shader modifier, it is given both in vertex and fragment stage through an argument named "scn_frame".
struct SCNSceneBuffer {
float4x4 viewTransform;
float4x4 inverseViewTransform; // transform from view space to world space
float4x4 projectionTransform;
float4x4 viewProjectionTransform;
float4x4 viewToCubeTransform; // transform from view space to cube texture space (canonical Y Up space)
float4x4 lastFrameViewProjectionTransform;
float4 ambientLightingColor;
float4 fogColor;
float3 fogParameters; // x:-1/(end-start) y:1-start*x z:exp
float2 inverseResolution;
float time;
float sinTime;
float cosTime;
float random01;
float motionBlurIntensity;
// new in macOS 10.12 and iOS 10
float environmentIntensity;
float4x4 inverseProjectionTransform;
float4x4 inverseViewProjectionTransform;
// new in macOS 10.13 and iOS 11
float2 nearFar; // x: near, y: far
float4 viewportSize; // xy:size, zw:origin
// new in macOS 10.14 and iOS 12
float4x4 inverseTransposeViewTransform;
// internal, DO NOT USE
float4 clusterScale; // w contains z bias
};
// In custom shaders or in shader modifiers, you also have access to node relative information.
// This is done using an argument named "scn_node", which must be a struct with only the necessary fields
// among the following list:
//
// float4x4 modelTransform;
// float4x4 inverseModelTransform;
// float4x4 modelViewTransform;
// float4x4 inverseModelViewTransform;
// float4x4 normalTransform; // This is the inverseTransposeModelViewTransform, need for normal transformation
// float4x4 modelViewProjectionTransform;
// float4x4 inverseModelViewProjectionTransform;
// float2x3 boundingBox;
// float2x3 worldBoundingBox;
#endif /* defined(__SCNMetalDefines__) */
//
// Utility
//
// Helper for compute kernels
#if defined(__METAL_IOS__)
#define RETURN_IF_OUTSIDE(dst) if ((index.x >= dst.get_width()) || (index.y >= dst.get_height())) return;
#define RETURN_IF_OUTSIDE3D(dst) if ((index.x >= dst.get_width()) || (index.y >= dst.get_height()) || (index.z >= dst.get_depth())) return;
#elif defined(__METAL_MACOS__) // On macOS, we use dispatchThread with won't execute on out of texture pixels
#define RETURN_IF_OUTSIDE(dst)
#define RETURN_IF_OUTSIDE3D(dst)
#endif
// Tool function
namespace scn {
// MARK: - Matrix/Vector utils
static inline float4 reduce_op(float4 d0, float4 d1)
{
d0.x = min(d0.x, d1.x);
d0.y = max(d0.y, d1.y);
d0.z += d1.z;
d0.w += d1.w;
return d0;
}
inline float vector_reduce_min(float4 v)
{
float2 min_lh = min(v.xy, v.zw);
return min(min_lh.x, min_lh.y);
}
inline float vector_reduce_max(float4 v)
{
float2 max_lh = max(v.xy, v.zw);
return max(max_lh.x, max_lh.y);
}
inline int vector_reduce_add(int4 v)
{
int2 add_lh = v.xy + v.zw;
return add_lh.x + add_lh.y;
}
inline float3x3 mat3(float4x4 mat4)
{
return float3x3(mat4[0].xyz, mat4[1].xyz, mat4[2].xyz);
}
inline float3 mat4_mult_float3_normalized(float4x4 matrix, float3 src)
{
float3 dst = src.xxx * matrix[0].xyz;
dst += src.yyy * matrix[1].xyz;
dst += src.zzz * matrix[2].xyz;
return normalize(dst);
}
inline float3 mat4_mult_float3(float4x4 matrix, float3 src)
{
float3 dst = src.xxx * matrix[0].xyz;
dst += src.yyy * matrix[1].xyz;
dst += src.zzz * matrix[2].xyz;
return dst;
}
inline float3 matrix_rotate(float4x4 mat, float3 dir)
{
return dir.xxx * mat[0].xyz +
dir.yyy * mat[1].xyz +
dir.zzz * mat[2].xyz;
}
inline float4 matrix_transform(float4x4 mat, float3 pos)
{
return pos.xxxx * mat[0] +
pos.yyyy * mat[1] +
pos.zzzz * mat[2] +
mat[3];
}
inline float3 quaternion_rotate_vector(float4 q, float3 v)
{
float3 t = 2.f * cross(q.xyz, v);
return v + q.w * t + cross(q.xyz, t);
}
// This seems unneeded with float. Maybe half ?
template <class T>
inline vec<T, 3> robust_normalize(vec<T, 3> v)
{
vec<T, 3> zero = 0.;
return all(v == zero) ? zero : normalize(v);
}
template <class T>
inline void generate_basis(vec<T, 3> inR, thread vec<T, 3> *outS, thread vec<T, 3> *outT)
{
// from http://marc-b-reynolds.github.io/quaternions/2016/07/06/Orthonormal.html
T x = -inR.x;
T y = inR.y;
T z = inR.z;
T sz = copysign(T(1.), z);
T a = y / (abs(z) + T(1.));
T b = y * a;
T c = x * a;
*outS = (vec<T, 3>){ z + sz * b, sz * c, x };
*outT = (vec<T, 3>){ c, T(1.) - b, -sz * y };
}
// MARK: - Blending operators
inline float3 blend_add(float3 base, float3 blend)
{
return min(base + blend, 1.0);
}
inline float3 blend_lighten(float3 base, float3 blend)
{
return max(blend, base);
}
inline float3 blend_screen(float3 base, float3 blend)
{
return (1.0 - ((1.0 - base) * (1.0 - blend)));
}
// MARK: - Math
inline half sq(half f) {
return f * f;
}
inline float sq(float f) {
return f * f;
}
inline float2 sincos(float angle) {
float cs;
float sn = ::sincos(angle, cs);
return float2(sn, cs);
}
// max error ~ 9.10-3
inline float acos_fast(float f) {
float x = abs(f);
float res = -0.156583f * x + M_PI_2_F;
res *= sqrt(1.0f - x);
return (f >= 0.f) ? res : M_PI_F - res;
}
inline float asin_fast(float f)
{
return M_PI_2_F - acos_fast(f);
}
// From Michal Drobot
inline float atan_fast(float inX)
{
float x = inX;
return x*(-0.1784f * abs(x) - 0.0663f * x * x + 1.0301f);
}
inline float atan2_fast(float y, float x)
{
float sx = x > 0.f ? -1.f : 1.f;
float abs_y = abs(y) + 1e-10f; // epsilon to prevent 0/0 condition
float r = (x + abs_y*sx) / (abs_y - x*sx);
float angle = sx * M_PI_4_F + M_PI_2_F;
angle += (0.1963f * r * r - 0.9817f) * r;
return y > 0.f ? angle : -angle;
}
// phi/theta are in the [0..1] range
template <class T>
inline vec<T, 3> cartesian_from_spherical(vec<T, 2> uv)
{
// do not use sinpi() waiting for
// <rdar://problem/28486742> sinpi(x) is 3x slower than sin(x * PI) on N71
T cos_phi;
T phi = uv.x * 2.0f * M_PI_F;
T sin_phi = ::sincos(phi, cos_phi);
T cos_theta;
T theta = uv.y * M_PI_F;
T sin_theta = ::sincos(theta, cos_theta);
return vec<T, 3>(cos_phi * sin_theta,
cos_theta,
-sin_phi * sin_theta);
}
inline float2 spherical_from_cartesian(float3 dir)
{
return float2( atan2(-dir.z, dir.x) * (0.5f * M_1_PI_F), acos(dir.y) * M_1_PI_F);
}
inline half2 spherical_from_cartesian(half3 dir)
{
return half2(atan2(-dir.z, dir.x) * 0.5h, acos(dir.y)) * M_1_PI_H;
}
inline float2 spherical_from_cartesian_fast(float3 dir)
{
return float2( atan2_fast(-dir.z, dir.x) * (0.5f * M_1_PI_F), acos_fast(dir.y) * M_1_PI_F);
}
inline half2 spherical_from_cartesian_fast(half3 dir)
{
return half2( atan2_fast(-dir.z, dir.x) * 0.5h, acos_fast(dir.y)) * M_1_PI_H;
}
#define dual_contract_factor 1.0
template <class T>
inline vec<T, 2> dual_paraboloid_from_cartesian(vec<T, 3> dir)
{
dir.xy /= abs(dir.z) + 1.0;
// dir.xy /= dual_contract_factor;
dir.y = 0.5 - dir.y * 0.5;
T s = sign(dir.z) * 0.25;
dir.x = s * (dir.x - 1.0) + 0.5;
return dir.xy;
}
// uv [0..1]
template <class T>
inline vec<T, 3> cartesian_from_dual_paraboloid(vec<T, 2> uv)
{
// put uv in [-1..1] for each side
T zside = 0.5 * sign(0.5 - uv.x);
uv.x = 1.0 - abs(4.0 * uv.x - 2.0); // [-1..1|1..-1]
uv.y = 1.0 - uv.y * 2.0;
T z = length_squared(uv); // * T(dual_contract_factor);
z = (1.0 - z) * zside;
return vec<T, 3>(uv.x, uv.y, z);
}
inline float reduce_min(float3 v) {
return min(v.x, min(v.y, v.z));
}
inline float reduce_min(float4 v) {
return min(min(v.x, v.y), min(v.z, v.w));
}
inline float reduce_max(float3 v) {
return max(v.x, max(v.y, v.z));
}
inline float reduce_max(float4 v) {
return max(max(v.x, v.y), max(v.z, v.w));
}
inline float3 randomSphereDir(float2 rnd)
{
float s = rnd.x * M_PI_F * 2.f;
float t = rnd.y * 2.f - 1.f;
return float3(sin(s), cos(s), t) / sqrt(1.f + t * t);
}
// from Sledgehammer slides
template <class T>
inline T interleaved_gradient_noise(vec<T, 2> pos)
{
vec<T, 3> magic( 0.06711056f, 0.00583715f, 52.9829189f );
return fract( magic.z * fract( dot( pos, magic.xy ) ) );
}
inline float3 hemisphere_reflect(float3 v, float3 nrm)
{
return v * sign(dot(v, nrm));
}
inline float3 randomHemisphereDir(float3 dir, float2 rnd)
{
return hemisphere_reflect(randomSphereDir( rnd ), dir);
}
inline void orthogonal_basis(float3 n, thread float3& xp, thread float3& yp)
{
// method 2a variant
float sz = n.z >= 0.f ? 1.f : -1.f;
float a = n.y / (1.f + abs(n.z));
float b = n.y * a;
float c = -n.x * a;
xp = float3(n.z + sz * b, sz * c, -n.x);
yp = float3(c, 1.f - b, -sz * n.y);
}
template <class U>
inline float2 normalized_coordinate(ushort2 index, U texture)
{
return float2(float(index.x) / float(texture.get_width() - 1),
float(index.y) / float(texture.get_height() - 1));
}
template <class U>
inline float2 normalized_coordinate(uint2 index, U texture)
{
return float2(float(index.x) / float(texture.get_width() - 1),
float(index.y) / float(texture.get_height() - 1));
}
template <class U>
inline half2 normalized_coordinate_half(uint2 index, U texture)
{
return half2(half(index.x) / half(texture.get_width() - 1),
half(index.y) / half(texture.get_height() - 1));
}
// MARK: Working with cube textures
template <class T>
inline vec<T, 3> cubemap_dir_from_sampleCoord(uint face, vec<T, 2> sampleCoord) // sampleCoord in [-1, 1]
{
switch(face) {
case 0: // +X
return vec<T, 3>( 1.0, -sampleCoord.y, -sampleCoord.x);
case 1: // -X
return vec<T, 3>(-1.0, -sampleCoord.y, sampleCoord.x);
case 2: // +Y
return vec<T, 3>(sampleCoord.x, 1.0, sampleCoord.y);
case 3: // -Y
return vec<T, 3>(sampleCoord.x, -1.0, -sampleCoord.y);
case 4: // +Z
return vec<T, 3>( sampleCoord.x, -sampleCoord.y, 1.0);
default: // -Z
return vec<T, 3>(-sampleCoord.x, -sampleCoord.y, -1.0);
}
}
// convert form [0..1] to [-1..1]
template <class T>
inline T signed_unit(T uv) {
return uv * 2.0 - 1.0;
}
// convert form [-1..1] to [0..1]
template <class T>
inline T unsigned_unit(T uv) {
return uv * 0.5 + 0.5;
}
template <class T>
inline vec<T, 3> cubemap_dir_from_uv(uint face, vec<T, 2> uv) // uv in [0, 1]
{
return cubemap_dir_from_sampleCoord(face, signed_unit(uv));
}
template <class T>
inline vec<T, 3> cubemap_dir_from_uv_unit(uint face, vec<T, 2> uv) // uv in [0, 1]
{
return normalize(cubemap_dir_from_uv(face, uv));
}
// MARK: - SIMD Extensions
inline vector_float2 barycentric_mix(vector_float2 __x, vector_float2 __y, vector_float2 __z, vector_float3 __t) { return __t.x * __x + __t.y * __y + __t.z * __z; }
inline vector_float3 barycentric_mix(vector_float3 __x, vector_float3 __y, vector_float3 __z, vector_float3 __t) { return __t.x * __x + __t.y * __y + __t.z * __z; }
inline vector_float4 barycentric_mix(vector_float4 __x, vector_float4 __y, vector_float4 __z, vector_float3 __t) { return __t.x * __x + __t.y * __y + __t.z * __z; }
static inline float rect(float2 lt, float2 rb, float2 uv)
{
float2 borders = step(lt, uv) * step(uv, rb);
return borders.x * borders.y;
}
inline half4 debugColorForCascade(int cascade)
{
switch (cascade) {
case 0:
return half4(1.h, 0.h, 0.h, 1.h);
case 1:
return half4(0.9, 0.5, 0., 1.);
case 2:
return half4(1., 1., 0., 1.);
case 3:
return half4(0., 1., 0., 1.);
default:
return half4(0., 0., 0., 1.);
}
}
inline half3 debugColorForFace(int count)
{
switch (count) {
case 0: return half3(1.0h, 0.1h, 0.1h);
case 1: return half3(0.1h, 1.0h, 1.0h);
case 2: return half3(0.1h, 1.0h, 0.1h);
case 3: return half3(1.0h, 0.1h, 1.0h);
case 4: return half3(0.1h, 0.1h, 1.0h);
default: return half3(1.0h, 1.0h, 0.1h);
}
}
inline half4 debugColorForCount(int count)
{
switch (count) {
case 0: return half4(0.0h, 0.0h, 0.0h, 1.h);
case 1: return half4(0.0h, 0.0h, 0.4h, 1.h);
case 2: return half4(0.0h, 0.0h, 0.9h, 1.h);
case 3: return half4(0.0h, 0.4h, 0.7h, 1.h);
case 4: return half4(0.0h, 0.9h, 0.4h, 1.h);
case 5: return half4(0.0h, 0.9h, 0.0h, 1.h);
case 6: return half4(0.4h, 0.7h, 0.0h, 1.h);
case 7: return half4(0.9h, 0.7h, 0.0h, 1.h);
default: return half4(1., 0., 0., 1.);
}
}
inline float grid(float2 lt, float2 rb, float2 gridSize, float thickness, float2 uv)
{
float insideRect = rect(lt, rb + thickness, uv);
float2 gt = thickness * gridSize;
float2 lines = step(abs(lt - fract(uv * gridSize)), gt);
return insideRect * (lines.x + lines.y);
}
inline float checkerboard(float2 gridSize, float2 uv)
{
float2 check = floor(uv * gridSize);
return step(fmod(check.x + check.y, 2.f), 0.f);
}
// MARK: - Colors
inline float luminance(float3 color)
{
// `color` assumed to be in the linear sRGB color space
// https://en.wikipedia.org/wiki/Relative_luminance
return color.r * 0.212671 + color.g * 0.715160 + color.b * 0.072169;
}
inline float srgb_to_linear(float c)
{
return (c <= 0.04045f) ? c / 12.92f : powr((c + 0.055f) / 1.055f, 2.4f);
}
inline half srgb_to_linear_fast(half c)
{
return powr(c, 2.2h);
}
inline half3 srgb_to_linear_fast(half3 c)
{
return powr(c, 2.2h);
}
inline half srgb_to_linear(half c)
{
// return (c <= 0.04045h) ? c / 12.92h : powr((c + 0.055h) / 1.055h, 2.4h);
return (c <= 0.04045h) ? (c * 0.0773993808h) : powr(0.9478672986h * c + 0.05213270142h, 2.4h);
}
inline float3 srgb_to_linear(float3 c)
{
return float3(srgb_to_linear(c.x), srgb_to_linear(c.y), srgb_to_linear(c.z));
}
inline float linear_to_srgb(float c)
{
return (c < 0.0031308f) ? (12.92f * c) : (1.055f * powr(c, 1.f/2.4f) - 0.055f);
}
inline float3 linear_to_srgb(float3 v) { // we do not saturate since linear extended values can be fed in
return float3(linear_to_srgb(v.x), linear_to_srgb(v.y), linear_to_srgb(v.z));
}
}
// MARK: GL helpers
template <typename T>
inline T dFdx(T v) {
return dfdx(v);
}
// Y is up in GL and down in Metal
template <typename T>
inline T dFdy(T v) {
return -dfdy(v);
}
// MARK: -
inline float4 texture2DProj(texture2d<float> tex, sampler smp, float4 uv)
{
return tex.sample(smp, uv.xy / uv.w);
}
inline half4 texture2DProj(texture2d<half> tex, sampler smp, float4 uv)
{
return tex.sample(smp, uv.xy / uv.w);
}
static constexpr sampler scn_shadow_sampler_rev_z = sampler(coord::normalized, filter::linear, mip_filter::none, address::clamp_to_zero, compare_func::less_equal);
static constexpr sampler scn_shadow_sampler_ord_z = sampler(coord::normalized, filter::linear, mip_filter::none, address::clamp_to_edge, compare_func::greater_equal);
#if defined(USE_REVERSE_Z) && USE_REVERSE_Z
static constexpr sampler scn_shadow_sampler = scn_shadow_sampler_rev_z;
#else
static constexpr sampler scn_shadow_sampler = scn_shadow_sampler_ord_z;
#endif
inline float shadow2D(sampler shadow_sampler, depth2d<float> tex, float3 uv)
{
return tex.sample_compare(shadow_sampler, uv.xy, uv.z);
}
inline float shadow2DProj(sampler shadow_sampler, depth2d<float> tex, float4 uv)
{
float3 uvp = uv.xyz / uv.w;
return tex.sample_compare(shadow_sampler, uvp.xy, uvp.z);
}
inline float shadow2DArray(sampler shadow_sampler, depth2d_array<float> tex, float3 uv, uint slice)
{
return tex.sample_compare(shadow_sampler, uv.xy, slice, uv.z);
}
inline float shadow2DArrayProj(sampler shadow_sampler, depth2d_array<float> tex, float4 uv, uint slice)
{
float3 uvp = uv.xyz / uv.w;
return tex.sample_compare(shadow_sampler, uvp.xy, slice, uvp.z);
}
// MARK Shadow
inline float4 transformViewPosInShadowSpace(float3 pos, float4x4 shadowMatrix, bool reverseZ)
{
//project into light space
float4 lightScreen = shadowMatrix * float4(pos, 1.f);
// ensure receiver after the shadow projection box are not in shadow (when no caster == 1. instead of infinite)
// TODO : this is awkward : we maybe should rework the comparison order to have something more natural
if (!reverseZ) {
lightScreen.z = min(lightScreen.z, 0.9999f * lightScreen.w);
} else {
if (lightScreen.z <= 0.0) { // out of clip space will always be < of min shadowmap value (0), so considered in shadow. Artificially setting it to larger value than 1 will make the test fail = no shadow.
lightScreen.z = 2.0;
}
}
return lightScreen;
}
inline float ComputeShadow(sampler shadow_sampler, float3 worldPos, float4x4 shadowMatrix, depth2d<float> shadowMap, bool reverseZ)
{
float4 lightScreen = transformViewPosInShadowSpace(worldPos, shadowMatrix, reverseZ);
float shadow = shadow2DProj(shadow_sampler, shadowMap, lightScreen);
// Is this useful ?
shadow *= step(0., lightScreen.w);
return shadow;
}
inline float ComputeSoftShadowGrid(sampler shadow_sampler, float3 worldPos, float4x4 shadowMatrix, depth2d<float> shadowMap, int sampleCount, bool reverseZ)
{
float4 lightScreen = transformViewPosInShadowSpace(worldPos, shadowMatrix, reverseZ);
// if sampleCount is known compileTime, this get rid of the shadowKernel binding
float shadow;
if (sampleCount <= 1) {
shadow = shadow2DProj(shadow_sampler, shadowMap, lightScreen);
} else {
float3 uvp = lightScreen.xyz / lightScreen.w;
uvp.z += reverseZ ? 0.005f : -0.005f; // TODO: get rid of hardcoded bias...
float2 texelSize = 2.f / float2(shadowMap.get_width(), shadowMap.get_height());
float2 origin = uvp.xy - (sampleCount * 0.5f) * texelSize;
// penumbra
if (sampleCount <= 4) { // offset are limited to [-7..8]
half totalAccum = 0.h;
for (int y = 0; y < sampleCount; ++y) {
for (int x = 0; x < sampleCount; ++x) {
totalAccum += half(shadowMap.sample_compare(shadow_sampler, origin, uvp.z, 2 * int2(x,y)));
}
}
shadow = totalAccum / half(sampleCount * sampleCount);
} else {
float totalAccum = 0.f;
for (int y = 0; y < sampleCount; ++y) {
for (int x = 0; x < sampleCount; ++x) {
float2 samplePos = origin + texelSize * float2(x, y);
totalAccum += shadowMap.sample_compare(shadow_sampler, samplePos, uvp.z);
}
}
shadow = totalAccum / float(sampleCount * sampleCount);
}
}
// Is this useful ?
shadow *= step(0., lightScreen.w);
return shadow;
}
inline float ComputeSoftShadow(sampler shadow_sampler, float3 worldPos, float4x4 shadowMatrix, depth2d<float> shadowMap, constant float4* shadowKernel, int sampleCount, float shadowRadius, bool reverseZ)
{
float4 lightScreen = transformViewPosInShadowSpace(worldPos, shadowMatrix, reverseZ);
// if sampleCount is known compileTime, this get rid of the shadowKernel binding
float shadow;
if (sampleCount <= 1) {
shadow = shadow2DProj(shadow_sampler, shadowMap, lightScreen);
} else {
//*
// penumbra
float3 center_uv = lightScreen.xyz / lightScreen.w;
float3 scale_uv = float3(shadowRadius, shadowRadius, reverseZ ? shadowRadius * center_uv.z : shadowRadius / lightScreen.w );
//smooth all samples
float totalAccum = 0.0;
for (int i = 0; i < sampleCount; i++) {
totalAccum += shadow2D(shadow_sampler, shadowMap, center_uv + shadowKernel[i].xyz * scale_uv);
}
/*/ This version could introduce more shadow acne
float3 uvp = lightScreen.xyz / lightScreen.w;
float2 texelSize = shadowRadius;
//smooth all samples
float totalAccum = 0.0;
for (int i=0; i < sampleCount; i++){
float2 samplePos = uvp.xy + texelSize * shadowKernel[i].xy;
totalAccum += shadowMap.sample_compare(shadow_sampler, samplePos, uvp.z);
}
//*/
shadow = totalAccum / float(sampleCount);
}
// Is this useful ?
shadow *= step(0., lightScreen.w);
return shadow;
}
inline float ComputeCascadeBlendAmount(float3 shadowPos, bool cascadeBlending)
{
const float cascadeBlendingFactor = 0.1f; // No need to configure that
float3 cascadePos = abs(shadowPos.xyz * 2.f - 1.f);
if (cascadeBlending) {
#if 0
const float edge = 1.f - cascadeBlendingFactor;
// could also do a smoothstep
cascadePos = 1.f - saturate((cascadePos - edge) / cascadeBlendingFactor);
return cascadePos.x * cascadePos.y * cascadePos.z; //min(o.x, o.y);
#else
// OPTIM use reduce_max
float distToEdge = 1.0f - max(max(cascadePos.x, cascadePos.y), cascadePos.z);
return smoothstep(0.0f, cascadeBlendingFactor, distToEdge);
#endif
} else {
return step(cascadePos.x, 1.f) * step(cascadePos.y, 1.f) * step(cascadePos.z, 1.f);
}
}
inline float4 SampleShadowCascade(sampler shadow_sampler, depth2d_array<float> shadowMaps, float3 shadowPosition, uint cascadeIndex, constant float4* shadowKernel, int sampleCount, float shadowRadius)
{
// cascade debug + grid
float2 gridSize = float2(shadowMaps.get_width(), shadowMaps.get_height()) / 32;
float gd = scn::checkerboard(shadowPosition.xy, gridSize);
float3 gridCol = mix(float3(scn::debugColorForCascade(cascadeIndex).rgb), float3(0.f), float3(gd > 0.f));
float shadow = 0.f;
if (sampleCount > 1) {
// penumbra : sum all samples
for (int i = 0; i < sampleCount; ++i) {
shadow += shadow2DArray(shadow_sampler, shadowMaps, shadowKernel[i].xyz * shadowRadius + shadowPosition, cascadeIndex);
}
shadow /= float(sampleCount);
} else {
// OPTIM : do not use proj version since cascade are never projective
shadow = shadow2DArray(shadow_sampler, shadowMaps, shadowPosition, cascadeIndex);
}
return float4(gridCol, shadow);
}
inline float4 ComputeCascadedShadow(sampler shadow_sampler, float3 viewPos, float4x4 shadowMatrix, constant float4 *cascadeScale, constant float4 *cascadeBias, int cascadeCount, depth2d_array<float> shadowMaps, bool enableCascadeBlending, constant float4* shadowKernel, int sampleCount, float shadowRadius)
{
float4 shadow = 0.f;
float opacitySum = 1.f;
// get the position in light space
float3 pos_ls = (shadowMatrix * float4(viewPos, 1.f)).xyz;
for (int c = 0; c < cascadeCount; ++c) {
float3 pos_cs = pos_ls * cascadeScale[c].xyz + cascadeBias[c].xyz;
// we multiply the radius by the scale factor of the cascade
float cascadeRadius = shadowRadius * cascadeScale[c].x;
float opacity = ComputeCascadeBlendAmount(pos_cs, enableCascadeBlending);
if (opacity > 0.f) { // this cascade should be considered
float alpha = opacity * opacitySum;
shadow += SampleShadowCascade(shadow_sampler, shadowMaps, pos_cs, c, shadowKernel, sampleCount, cascadeRadius) * alpha;
opacitySum -= alpha;
}
if (opacitySum <= 0.f) // fully opaque shadow (no more blending needed) -> bail out
break;
}
return shadow;
}
namespace NAMESPACE_HASH {
// Macro for layered rendering & shadermodifier
#ifdef USE_LAYERED_RENDERING
#define texture2d_layer texture2d_array
#define sampleLayer(a,b) sample(a,b,in.sliceIndex)
#else
#define texture2d_layer texture2d
#define sampleLayer(a,b) sample(a,b)
#endif
#if defined(HAS_NORMAL) || defined(USE_OPENSUBDIV)
#define HAS_OR_GENERATES_NORMAL 1
#endif
#ifdef C3D_USE_TEXTURE_FOR_LIGHT_INDICES
#define LightIndex(lid) u_lightIndicesTexture.read((ushort)lid).x
#else
#define LightIndex(lid) u_lightIndicesBuffer[lid]
#endif
// Inputs
typedef struct {
#ifdef USE_MODELTRANSFORM
float4x4 modelTransform;
#endif
#ifdef USE_INVERSEMODELTRANSFORM
float4x4 inverseModelTransform;
#endif
#ifdef USE_MODELVIEWTRANSFORM
float4x4 modelViewTransform;
#endif
#ifdef USE_INVERSEMODELVIEWTRANSFORM
float4x4 inverseModelViewTransform;
#endif
#ifdef USE_NORMALTRANSFORM
float4x4 normalTransform;
#endif
#ifdef USE_MODELVIEWPROJECTIONTRANSFORM
float4x4 modelViewProjectionTransform;
#endif
#ifdef USE_INVERSEMODELVIEWPROJECTIONTRANSFORM
float4x4 inverseModelViewProjectionTransform;
#endif
#ifdef USE_MOTIONBLUR
float4x4 lastFrameModelTransform;
float motionBlurIntensity;
#endif
#ifdef USE_BOUNDINGBOX
float2x3 boundingBox;
#endif
#ifdef USE_WORLDBOUNDINGBOX
float2x3 worldBoundingBox;
#endif
#ifdef USE_NODE_OPACITY
float nodeOpacity;
#endif
#if defined(USE_PROBES_LIGHTING) && (USE_PROBES_LIGHTING == 2)
sh2_coefficients shCoefficients;
#elif defined(USE_PROBES_LIGHTING) && (USE_PROBES_LIGHTING == 3)
sh3_coefficients shCoefficients;
#endif
#ifdef USE_SKINNING // need to be last since we may cut the buffer size based on the real bone number
float4 skinningJointMatrices[765]; // Consider having a separate buffer ?
#endif
} commonprofile_node;
typedef struct {
float3 position [[attribute(SCNVertexSemanticPosition)]];
#ifdef HAS_NORMAL
float3 normal [[attribute(SCNVertexSemanticNormal)]];
#endif
#ifdef USE_TANGENT
float4 tangent [[attribute(SCNVertexSemanticTangent)]];
#endif
#ifdef USE_VERTEX_COLOR
float4 color [[attribute(SCNVertexSemanticColor)]];
#endif
#ifdef USE_SKINNING
float4 skinningWeights [[attribute(SCNVertexSemanticBoneWeights)]];
uint4 skinningJoints [[attribute(SCNVertexSemanticBoneIndices)]];
#endif
#if defined(NEED_IN_TEXCOORD0) || defined(DEBUG_PIXEL)
float2 texcoord0 [[attribute(SCNVertexSemanticTexcoord0)]];
#endif
#ifdef NEED_IN_TEXCOORD1
float2 texcoord1 [[attribute(SCNVertexSemanticTexcoord1)]];
#endif
#ifdef NEED_IN_TEXCOORD2
float2 texcoord2 [[attribute(SCNVertexSemanticTexcoord2)]];
#endif
#ifdef NEED_IN_TEXCOORD3
float2 texcoord3 [[attribute(SCNVertexSemanticTexcoord3)]];
#endif
#ifdef NEED_IN_TEXCOORD4
float2 texcoord4 [[attribute(SCNVertexSemanticTexcoord4)]];
#endif
#ifdef NEED_IN_TEXCOORD5
float2 texcoord5 [[attribute(SCNVertexSemanticTexcoord5)]];
#endif
#ifdef NEED_IN_TEXCOORD6
float2 texcoord6 [[attribute(SCNVertexSemanticTexcoord6)]];
#endif
#ifdef NEED_IN_TEXCOORD7
float2 texcoord7 [[attribute(SCNVertexSemanticTexcoord7)]];
#endif
} scn_vertex_t; // __attribute__((scn_per_frame));
typedef struct {
float4 fragmentPosition [[position]]; // The window relative coordinate (x, y, z, 1/w) values for the fragment
#ifdef USE_POINT_RENDERING
float fragmentSize [[point_size]];
#endif
#ifdef USE_VERTEX_COLOR
float4 vertexColor;
#endif
#ifdef USE_PER_VERTEX_LIGHTING
float3 diffuse;
#ifdef USE_SPECULAR
float3 specular;
#endif
#ifdef USE_CLEARCOAT
float clearCoat;
#endif
#ifdef USE_CLEARCOATROUGHNESS
float clearCoatRoughness;
#endif
#ifdef USE_CLEARCOATNORMAL
float clearCoatNormal;
#endif
#endif
#if defined(USE_POSITION) && (USE_POSITION == 2)
float3 position;
#endif
#if defined(USE_NORMAL) && (USE_NORMAL == 2) && defined(HAS_OR_GENERATES_NORMAL)
float3 normal;
#endif
#if defined(USE_TANGENT) && (USE_TANGENT == 2)
float3 tangent;
#endif
#if defined(USE_BITANGENT) && (USE_BITANGENT == 2)
float3 bitangent;
#endif
#ifdef USE_DISPLACEMENT_MAP
float2 displacementTexcoord; // Displacement texture coordinates
#endif
#ifdef USE_CLEARCOAT_MAP
float2 clearCoatTexcoord; // ClearCoat texture coordinates
#endif
#ifdef USE_CLEARCOATROUGHNESS_MAP
float2 clearCoatRoughnessTexcoord; // ClearCoatRoughness texture coordinates
#endif
#ifdef USE_CLEARCOATNORMAL_MAP
float2 clearCoatNormalTexcoord; // ClearCoatNormal texture coordinates
#endif
#ifdef USE_NODE_OPACITY
float nodeOpacity;
#endif
#ifdef USE_TEXCOORD
float2 texcoord0;
#endif
#ifdef USE_EXTRA_VARYINGS
#endif
#ifdef USE_MOTIONBLUR
float3 mv_fragment;
float3 mv_lastFragment;
#endif
#ifdef USE_OUTLINE
float outlineHash [[ flat ]];
#endif
#ifdef USE_LAYERED_RENDERING
uint sliceIndex [[render_target_array_index]];
#endif
#ifdef USE_MULTIPLE_VIEWPORTS_RENDERING
uint sliceIndex [[viewport_array_index]];
#endif
#if DEBUG_PIXEL
float2 uv0;
#endif
} commonprofile_io;
// Shader modifiers declaration (only enabled if one modifier is present)
#ifdef USE_SHADER_MODIFIERS
#endif
// This may rely on shader modifiers declaration
#define USE_QUAT_FOR_IES 1
// 256 bytes
struct scn_light
{
float4 color; // color.rgb + shadowColor.a 16
float3 pos; // 16 (12)
float3 dir; // 16 (12)
float shadowRadius; // 4
uint8_t lightType; // 1
uint8_t attenuationType; // 1
uint8_t shadowSampleCount; // 1
// 55 but in reality 64 for alignment reasons
union {
struct {
float4 cascadeScale[4]; // max cascade count
float4 cascadeBias[4];
} directional; // 128
struct {
float4 attenuationFactors; // scale/bias/power/invSquareRadius 16
float3 shadowScaleBias; // xy: scale/bias for z_lin -> z_ndc, z: depth bias
} omni;
struct {
float4 _attenuationFactors; // need to match omni, always use omni.attenuationFactors
float2 scaleBias; // scale/bias to compute spot falloff
} spot;
struct {
float4 _attenuationFactors; // need to match omni, always use omni.attenuationFactors
float2 scaleBias; // scale/bias to compute ies LUT
#if USE_QUAT_FOR_IES
float4 light_from_view_quat; // OPTIM: this could be a simple quaternion
#else
float4x4 light_from_view; // OPTIM: this could be a simple quaternion
#endif
} ies;
union {
struct {
float2 halfExtents;
float doubleSided;
} rectangle;
struct {
uint32_t vertexCount;
float doubleSided;
} polygon;
struct {
float halfLength;
} line;
struct {
float2 halfExtents;
float doubleSided;
} ellipse;
struct {
float3 halfExtents;
} ellipsoid;
} area;
struct {
float3 offset;
float4 halfExtents; // w: contains the blending distance
float3 parallaxCenter;
float3 parallaxExtents;
int32_t index; // index of the probe in the probe array (do not use uint8_t because of compiler crash)
int32_t parallaxCorrection; // do not use bool (compiler crash)
} probe;
} parameters; // 128
float4x4 shadowMatrix; // 64
};
#if defined(__METAL_VERSION__) && __METAL_VERSION__ >= 120
#define ambientOcclusionTexcoord ambientTexcoord
struct SCNShaderSurface {
float3 view; // Direction from the point on the surface toward the camera (V)
float3 position; // Position of the fragment
float3 normal; // Normal of the fragment (N)
float3 geometryNormal; // Normal of the fragment - not taking into account normal map
float2 normalTexcoord; // Normal texture coordinates
float3 tangent; // Tangent of the fragment
float3 bitangent; // Bitangent of the fragment
float4 ambient; // Ambient property of the fragment
float2 ambientTexcoord; // Ambient texture coordinates
float4 diffuse; // Diffuse property of the fragment. Alpha contains the opacity.
float2 diffuseTexcoord; // Diffuse texture coordinates
float4 specular; // Specular property of the fragment
float2 specularTexcoord; // Specular texture coordinates
float4 emission; // Emission property of the fragment
float2 emissionTexcoord; // Emission texture coordinates
float4 selfIllumination; // selfIllumination property of the fragment
float2 selfIlluminationTexcoord; // selfIllumination texture coordinates
float4 multiply; // Multiply property of the fragment
float2 multiplyTexcoord; // Multiply texture coordinates
float4 transparent; // Transparent property of the fragment
float2 transparentTexcoord; // Transparent texture coordinates
float4 reflective; // Reflective property of the fragment
float metalness; // Metalness
float2 metalnessTexcoord; // Metalness texture coordinates
float roughness; // Roughness
float2 roughnessTexcoord; // Roughness texture coordinates
float clearCoat; // ClearCoat property of the fragment
float2 clearCoatTexcoord; // ClearCoat texture coordinates
float clearCoatRoughness; // ClearCoatRoughness property of the fragment
float2 clearCoatRoughnessTexcoord;// ClearCoatRoughness texture coordinates
float3 clearCoatNormal; // ClearCoatNormal property of the fragment
float2 clearCoatNormalTexcoord;// ClearCoatNormal texture coordinates
float shininess; // Shininess property of the fragment.
float fresnel; // Fresnel property of the fragment.
float ambientOcclusion; // Ambient occlusion term of the fragment
float3 _normalTS; // UNDOCUMENTED in tangent space
float3 _clearCoatNormalTS; // UNDOCUMENTED in tangent space
#ifdef USE_SURFACE_EXTRA_DECL
#endif
};
// Structure to gather property of a light, packed to give access in a light shader modifier
// This must be kept intact for back compatibility in lighting modifiers
struct SCNShaderLight {
float4 intensity;
float3 direction;
float _att;
float3 _spotDirection;
float _distance;
};
enum SCNLightingModel
{
SCNLightingModelConstant,
SCNLightingModelLambert,
SCNLightingModelPhong,
SCNLightingModelBlinn,
SCNLightingModelNone,
SCNLightingModelPhysicallyBased,
SCNLightingModelShadowOnly,
SCNLightingModelCustom // 6 implicit when using a lighting shader modifier
};
enum C3DLightAttenuationType
{
kC3DLightAttenuationTypeNone,
kC3DLightAttenuationTypeConstant,
kC3DLightAttenuationTypeLinear,
kC3DLightAttenuationTypeQuadratic,
kC3DLightAttenuationTypeExponent,
kC3DLightAttenuationTypePhysicallyBased,
};
#define PROBES_NORMALIZATION 0
#define PROBES_OUTER_BLENDING 1
struct SCNShaderLightingContribution
{
float3 ambient;
float3 diffuse;
float3 specular;
float3 modulate;
#ifdef USE_SHADOWONLY
float shadowFactor;
#endif
#if PROBES_NORMALIZATION
float4 probesWeightedSum; // rgb: sum a:normalization factor
#else
float probeRadianceRemainingFactor;
#endif
thread SCNShaderSurface& surface;
#ifdef USE_PER_VERTEX_LIGHTING
commonprofile_io out;
#else
commonprofile_io in;
#endif
#if USE_REVERSE_Z
constant static constexpr bool reverseZ = true;
#else
constant static constexpr bool reverseZ = false;
#endif
#ifdef USE_PBR
static constexpr sampler linearSampler = sampler(filter::linear, mip_filter::linear);
float selfIlluminationOcclusion;
float3 reflectance;
float3 probeReflectance;
float NoV;
float NoVClearCoat;
float3 probeReflectanceClearCoat;
#endif
SCNShaderLightingContribution(thread SCNShaderSurface& iSurface, commonprofile_io io):surface(iSurface)
#ifdef USE_PER_VERTEX_LIGHTING
,out(io)
#else
,in(io)
#endif
{
ambient = 0.f;
diffuse = 0.f;
specular = 0.f;
#ifdef USE_SHADOWONLY
shadowFactor = 1.f;
#endif
#if PROBES_NORMALIZATION
#if PROBES_OUTER_BLENDING
probesWeightedSum = float4(0.f);
#else
probesWeightedSum = float4(0.f, 0.f, 0.f, 0.000001f); // avoid divide by 0 with an epsilon
#endif
#else
probeRadianceRemainingFactor = 1.f;
#endif
#ifdef USE_MODULATE
modulate = 1.f;
#else
modulate = 0.f;
#endif
}
#ifdef USE_PBR
void prepareForPBR(texture2d<float, access::sample> specularDFG, float occ)
{
selfIlluminationOcclusion = occ;
float3 n = surface.normal;
float3 v = surface.view;
reflectance = mix(PBR_F0_NON_METALLIC, surface.diffuse.rgb, surface.metalness);
NoV = saturate(dot(n, v));
float2 DFG = specularDFG.sample(linearSampler, float2(NoV, surface.roughness)).rg;
probeReflectance = reflectance * DFG.r + DFG.g;
}
void prepareForPBRClearCoat(texture2d<float, access::sample> specularDFG)
{
float3 n = surface.clearCoatNormal;
float3 v = surface.view;
NoVClearCoat = saturate(dot(n, v));
float2 DFG = specularDFG.sample(linearSampler, float2(NoVClearCoat, surface.clearCoatRoughness)).rg;
probeReflectanceClearCoat = 0.04 * DFG.r + DFG.g;
}
#endif
#ifdef USE_LIGHT_MODIFIER
#endif
float4 debug_pixel(float2 fragmentPosition)
{
const int width = 64;
switch (int(fragmentPosition.x + fragmentPosition.y ) / width) {
case 0: return float4(surface.view, 1.f);
case 1: return float4(surface.position, 1.f);
case 2: return float4(surface.normal, 1.f);
case 3: return float4(surface.geometryNormal, 1.f);
case 4: return float4(float3(surface.ambientOcclusion), 1.f);
case 5: return surface.diffuse;
case 6: return float4(float3(surface.metalness), 1.f);
case 7: return float4(float3(surface.roughness), 1.f);
case 8: return float4(ambient, 1.f);
case 9: return float4(diffuse, 1.f);
default: return float4(specular, 1.f);
}
}
// tool functions, could be external
static inline float3 lambert_diffuse(float3 l, float3 n, float3 color, float intensity) {
return color * (intensity * saturate(dot(n, l)));
}
void lambert(float3 l, float3 color, float intensity)
{
diffuse += lambert_diffuse(l, surface.normal, color, intensity);
}
void blinn(float3 l, float3 color, float intensity)
{
float3 D = lambert_diffuse(l, surface.normal, color, intensity);
diffuse += D;
float3 h = normalize(l + surface.view);
specular += powr(saturate(dot(surface.normal, h)), surface.shininess) * D;
}
void phong(float3 l, float3 color, float intensity)
{
float3 D = lambert_diffuse(l, surface.normal, color, intensity);
diffuse += D;
float3 r = reflect(-l, surface.normal);
specular += powr(saturate(dot(r, surface.view)), surface.shininess) * D;
}
#ifdef USE_PBR
void pbr(float3 l, float3 color, float intensity)
{
float3 n = surface.normal;
float3 v = surface.view;
float3 h = normalize(l + v);
float NoL = saturate(dot(n, l));
float NoH = saturate(dot(n, h));
float LoH = saturate(dot(l, h));
// for analytical lights, perfect mirror will not exhibit any specular
// so clamp the value to an arbitrary low values
// https://google.github.io/filament/Filament.md.html
// value chosen such that (MIN_ROUGHNESS^4) > 0 in fp16 (i.e. 2^(-14/4), slightly rounded up)
float roughness = max(surface.roughness, 0.089f);
float alpha = roughness * roughness; // perceptually-linear roughness
float D = scn_brdf_D(alpha, NoH);
float3 F = scn_brdf_F_opt(reflectance, LoH);
float Vis = scn_brdf_V(alpha, NoL, NoV);
// keep the scalar separated
diffuse += color * (NoL * M_1_PI_F * intensity);
specular += color * F * ( NoL * D * Vis * intensity);
#ifdef USE_CLEARCOAT
n = surface.clearCoatNormal;
roughness = max(surface.clearCoatRoughness, 0.089f);
alpha = roughness * roughness; // perceptually-linear roughness
//we should recompute NoH, because the coat normal is not necesseraly the base normal
//-≥ either the coatNormal, or the geometry normal
float NoH_coat = saturate(dot(n, h));
float NoL_coat = saturate(dot(n, l));
D = scn_brdf_D(alpha, NoH_coat);
F = scn_brdf_F_opt(0.04, LoH) * surface.clearCoat;
Vis = scn_brdf_V(alpha, NoL_coat, saturate(dot(n,v)));
float attenuation = 1.0 - F.r;
specular *= (attenuation * attenuation);
specular += color * F * ( NoL_coat * D * Vis * intensity);
#endif
}
#endif
void custom(float3 _l, float3 _color, float _intensity)
{
#ifdef USE_LIGHT_MODIFIER
thread SCNShaderLightingContribution &_lightingContribution = *this;
thread SCNShaderSurface& _surface = surface;
SCNShaderLight _light = {.direction = _l, .intensity = float4(_color, 1.f), ._att = _intensity };
// DoLightModifier START
// DoLightModifier END
#endif
}
void shade(float3 l, float3 color, float intensity)
{
#ifdef LIGHTING_MODEL
switch (LIGHTING_MODEL) {
#ifdef USE_SHADOWONLY
case SCNLightingModelShadowOnly: shadowFactor *= intensity; break;
#endif
case SCNLightingModelLambert: lambert(l, color, intensity); break;
case SCNLightingModelBlinn: blinn(l, color, intensity); break;
case SCNLightingModelPhong: phong(l, color, intensity); break;
#ifdef USE_PBR
case SCNLightingModelPhysicallyBased: pbr(l, color, intensity); break;
#endif
case SCNLightingModelCustom: custom(l, color, intensity); break;
default: break; // static_assert(0, "should not go there");
}
#endif
}
// this implementation seems more correct as it never goes larger than when near (dist<1)
// https://imdoingitwrong.wordpress.com/2011/01/31/light-attenuation/
// cutoff must be computed with Eq(1) with lightAttenuationEnd and stored in the light struct
float pbr_dist_attenuation_alternate(float3 l, float cutoff) {
// float radius = 0.01f; // consider point lights as 1cm
float radius = 0.1f; // consider point lights as 10cm
float factor = 1.f / (1.f + length(l)/radius);
float attenuation = saturate(factor * factor); // Eq(1)
return saturate((attenuation - cutoff) / (1.f - cutoff));
}
float pbr_dist_attenuation(float3 l, float inv_square_radius) {
float sqr_dist = length_squared(l);
float atten = 1.f / max(sqr_dist, 0.0001f);
// smoothing factor to avoid hard clip of the lighting
float factor = saturate(1.f - scn::sq(sqr_dist * inv_square_radius));
return atten * factor * factor;
}
float non_pbr_dist_attenuation(float3 l, float4 att)
{
return powr(saturate(length(l) * att.x + att.y), att.z);
}
float dist_attenuation(float3 unnormalized_l, scn_light light)
{
#ifdef USE_PBR
return 1000.f * pbr_dist_attenuation(unnormalized_l, light.parameters.omni.attenuationFactors.w);
// This alternate model seems to better fit V-Ray render.... To confirm
// float intensity = 1000.f * pbr_dist_attenuation_alternate(unnormalized_l, 0.f);
#else
return non_pbr_dist_attenuation(unnormalized_l, light.parameters.omni.attenuationFactors);
#endif
}
float spot_attenuation(float3 l, scn_light light)
{
// only support linear attenuation (spotExponent is SPI)
return saturate(dot(l, light.dir) * light.parameters.spot.scaleBias.x + light.parameters.spot.scaleBias.y);
}
void shade_modulate(float3 l, float4 color, float intensity)
{
constexpr half3 white = half3(1.h);
// color.a contains the gobo slot intensity -> used to fade it
modulate *= float3(mix(white, half3(color.rgb), half(color.a * intensity)));
}
float3 gobo(float3 pos, scn_light light, texture2d<half> goboTexture, sampler goboSampler)
{
half3 g = texture2DProj(goboTexture, goboSampler, (light.shadowMatrix * float4(pos, 1.f))).rgb;
return light.color.rgb * float3(mix(1.h, g, half(light.color.a)));
}
float shadow(float3 pos, scn_light light, depth2d<float> shadowMap)
{
float shadow = ComputeShadow(scn_shadow_sampler, pos, light.shadowMatrix, shadowMap, reverseZ);
return 1.f - shadow * light.color.a; // shadow color
}
// this versions takes the sample count from the light. This forces dynamic loops, not a good idea on iOS
float shadow(float3 pos, scn_light light, depth2d<float> shadowMap, constant float4* shadowKernel)
{
float shadow = ComputeSoftShadow(scn_shadow_sampler, pos, light.shadowMatrix, shadowMap, shadowKernel, light.shadowSampleCount, light.shadowRadius, reverseZ);
return 1.f - shadow * light.color.a; // shadow color
}
float shadow(float3 pos, scn_light light, depth2d<float> shadowMap, constant float4* shadowKernel, int shadowSampleCount)
{
float shadow = ComputeSoftShadow(scn_shadow_sampler, pos, light.shadowMatrix, shadowMap, shadowKernel, shadowSampleCount, light.shadowRadius, reverseZ);
return 1.f - shadow * light.color.a; // shadow color
}
float shadow(float3 pos, scn_light light, depth2d<float> shadowMap, int shadowSampleCount)
{
float shadow = ComputeSoftShadowGrid(scn_shadow_sampler, pos, light.shadowMatrix, shadowMap, shadowSampleCount, reverseZ);
return 1.f - shadow * light.color.a; // shadow color
}
float shadow_omni(float3 pos_vs, float3 nrm_vs, scn_light light, depthcube<float> shadowMap, constant float4* shadowKernel, int sampleCount)
{
// else use hemispheric sampling
#define USE_TANGENT_SAMPLING 0
float2 scaleBias = light.parameters.omni.shadowScaleBias.xy;
float depthBias = light.parameters.omni.shadowScaleBias.z;
// offset/bias for shadow acne
pos_vs += nrm_vs * depthBias;
// transform pos from view space to light space
float3 pos_ls = (light.shadowMatrix * float4(pos_vs, 1.f)).xyz;
// compute z_lin for sample cube face (symetric so +x == -x, etc)
float z_lin = scn::reduce_max(abs(pos_ls));
// if we want to clip the shadows to the far planes of the cube...
// if (z_lin > zFar)
// return 1.f;
// transform linear_z to depthbuffer_z
float z_ndc = (z_lin * scaleBias.x + scaleBias.y) / z_lin - depthBias;
// if sampleCount is known compileTime, this get rid of the shadowKernel binding
float shadow;
if (sampleCount <= 1) {
shadow = shadowMap.sample_compare(scn_shadow_sampler, pos_ls.xyz, z_ndc);
} else {
// penumbra
float filteringSizeFactor = light.shadowRadius;
#if USE_TANGENT_SAMPLING
float3 tgt_x, tgt_y;
scn::orthogonal_basis(pos_ls, tgt_x, tgt_y);
#else
float3 nrm_ls = (light.shadowMatrix * float4(nrm_vs, 0.f)).xyz;
#endif
//smooth all samples
float totalAccum = 0.0;
for(int i=0; i < sampleCount; i++){
#if USE_TANGENT_SAMPLING
float2 scale = shadowKernel[i].xy * filteringSizeFactor * 2.f;
float3 smp_ls = pos_ls.xyz + tgt_x * scale.x + tgt_y * scale.y;
#else
float3 smp_ls = pos_ls.xyz + scn::randomHemisphereDir(nrm_ls, shadowKernel[i].xy) * filteringSizeFactor;
#endif
// Do we want to compare with reference depth or smp depth?
// z_lin = scn::reduce_max(abs(smp_ls));
// z_ndc = (z_lin * scaleBias.x + scaleBias.y) / z_lin;
totalAccum += shadowMap.sample_compare(scn_shadow_sampler, smp_ls, z_ndc);
}
shadow = totalAccum / float(sampleCount);
}
return 1.f - shadow * light.color.a; // shadow color
}
float shadow(float3 pos, constant scn_light& light, depth2d_array<float> shadowMaps, int cascadeCount, bool blendCascade, constant float4* shadowKernel, int sampleCount)
{
float shadow = ComputeCascadedShadow(scn_shadow_sampler, pos, light.shadowMatrix, light.parameters.directional.cascadeScale, light.parameters.directional.cascadeBias, cascadeCount, shadowMaps, blendCascade, shadowKernel, sampleCount, light.shadowRadius).a;
return 1.f - shadow * light.color.a; // shadow color
}
// MARK: Directional
void add_directional(scn_light light)
{
#ifdef USE_PBR
float intensity = M_PI_F;
#else
float intensity = 1.f;
#endif
shade(light.dir, light.color.rgb, intensity);
}
// gobo
void add_directional(scn_light light, texture2d<half> goboTexture, sampler goboSampler, bool modulated)
{
#ifdef USE_PBR
float intensity = M_PI_F;
#else
float intensity = 1.f;
#endif
light.color.rgb = gobo(surface.position, light, goboTexture, goboSampler);
if (modulated) {
shade_modulate(light.dir, light.color, 1.f);
} else {
shade(light.dir, light.color.rgb, intensity);
}
}
// support simple shadows (no soft, no cascade)
void add_directional(scn_light light, depth2d<float> shadowMap)
{
#ifdef USE_PBR
float intensity = M_PI_F;
#else
float intensity = 1.f;
#endif
intensity *= shadow(surface.position, light, shadowMap);
shade(light.dir, light.color.rgb, intensity);
}
// support soft shadows (non cascaded, dynamic sample count)
void add_directional(scn_light light, depth2d<float> shadowMap, constant float4* shadowKernel)
{
#ifdef USE_PBR
float intensity = M_PI_F;
#else
float intensity = 1.f;
#endif
intensity *= shadow(surface.position, light, shadowMap, shadowKernel);
shade(light.dir, light.color.rgb, intensity);
}
void add_directional(scn_light light, depth2d<float> shadowMap, constant float4* shadowKernel, int sampleCount)
{
#ifdef USE_PBR
float intensity = M_PI_F;
#else
float intensity = 1.f;
#endif
intensity *= shadow(surface.position, light, shadowMap, shadowKernel, sampleCount);
shade(light.dir, light.color.rgb, intensity);
}
// regular grid PCF
void add_directional(scn_light light, depth2d<float> shadowMap, int sampleCount)
{
#ifdef USE_PBR
float intensity = M_PI_F;
#else
float intensity = 1.f;
#endif
intensity *= shadow(surface.position, light, shadowMap, sampleCount);
shade(light.dir, light.color.rgb, intensity);
}
// version supporting cascade shadows
void add_directional(constant scn_light& light, depth2d_array<float> shadowMaps, int cascadeCount, bool blendCascade, constant float4* shadowKernel, int sampleCount, bool debugCascades)
{
#ifdef USE_PBR
float intensity = M_PI_F;
#else
float intensity = 1.f;
#endif
if (debugCascades) {
float4 shadowDebug = ComputeCascadedShadow(scn_shadow_sampler, surface.position, light.shadowMatrix, light.parameters.directional.cascadeScale, light.parameters.directional.cascadeBias, cascadeCount, shadowMaps, blendCascade, shadowKernel, sampleCount, light.shadowRadius);
intensity *= (1.f - shadowDebug.a);
shade(light.dir, light.color.rgb, intensity);
diffuse.rgb = mix(diffuse.rgb, shadowDebug.rgb, light.color.a);
} else {
intensity *= shadow(surface.position, light, shadowMaps, cascadeCount, blendCascade, shadowKernel, sampleCount);
shade(light.dir, light.color.rgb, intensity);
}
}
// MARK: Omni
void add_omni(scn_light light)
{
float3 unnormalized_l = light.pos - surface.position;
float3 l = normalize(unnormalized_l);
shade(l, light.color.rgb, dist_attenuation(unnormalized_l, light));
}
void add_omni(scn_light light, depthcube<float> shadowMap, constant float4* shadowKernel, int sampleCount)
{
float3 unnormalized_l = light.pos - surface.position;
float3 l = normalize(unnormalized_l);
float intensity = dist_attenuation(unnormalized_l, light);
intensity *= shadow_omni(surface.position, surface.normal, light, shadowMap, shadowKernel, sampleCount);
shade(l, light.color.rgb, intensity);
}
void add_local_omni(scn_light light)
{
float3 unnormalized_l = light.pos - surface.position;
float3 l = normalize(unnormalized_l);
shade(l, light.color.rgb, dist_attenuation(unnormalized_l, light));
}
// MARK: Spot
void add_spot(scn_light light)
{
float3 unnormalized_l = light.pos - surface.position;
float3 l = normalize(unnormalized_l);
float intensity = dist_attenuation(unnormalized_l, light);
intensity *= spot_attenuation(l, light);
shade(l, light.color.rgb, intensity);
}
void add_spot(scn_light light, texture2d<half> goboTexture, sampler goboSampler, bool modulated)
{
float3 unnormalized_l = light.pos - surface.position;
float3 l = normalize(unnormalized_l);
float intensity = dist_attenuation(unnormalized_l, light);
intensity *= spot_attenuation(l, light);
light.color.rgb = gobo(surface.position, light, goboTexture, goboSampler);
if (modulated) {
shade_modulate(l, light.color, intensity);
} else {
shade(l, light.color.rgb, intensity);
}
}
void add_local_spot(scn_light light)
{
float3 unnormalized_l = light.pos - surface.position;
float3 l = normalize(unnormalized_l);
float intensity = dist_attenuation(unnormalized_l, light);
intensity *= spot_attenuation(l, light);
shade(l, light.color.rgb, intensity);
}
// support simple shadows (non cascaded)
void add_spot(scn_light light, depth2d<float> shadowMap, constant float4* shadowKernel, int sampleCount)
{
float3 unnormalized_l = light.pos - surface.position;
float3 l = normalize(unnormalized_l);
float intensity = dist_attenuation(unnormalized_l, light);
intensity *= spot_attenuation(l, light);
intensity *= shadow(surface.position, light, shadowMap, shadowKernel, sampleCount);
shade(l, light.color.rgb, intensity);
}
// MARK: Probe
#ifdef USE_PBR
// MARK: Radiance
#ifdef C3D_SUPPORT_CUBE_ARRAY
void add_local_probe(scn_light light, texturecube_array<half> probeTextureArray)
#else
void add_local_probe(scn_light light, texture2d_array<half> probeTextureArray)
#endif
{
#if !PROBES_NORMALIZATION
if (probeRadianceRemainingFactor <= 0.f)
return;
#endif
bool parallaxCorrection = light.parameters.probe.parallaxCorrection;
int probeIndex = light.parameters.probe.index;
float3 probeExtents = light.parameters.probe.halfExtents.xyz;
float blendDist = light.parameters.probe.halfExtents.w;
float3 probeOffset = light.parameters.probe.offset;
float3 parallaxExtents = light.parameters.probe.parallaxExtents;
float3 parallaxCenter = light.parameters.probe.parallaxCenter;
float3 n = surface.normal;
float3 v = surface.view;
float3 r = reflect(-v, n); // mirror vector (view vector around normal)
float3 specDir = scn::mat4_mult_float3(light.shadowMatrix, r);
// TODO blend weight ? accumulate in alpha and normalize ?
float3 pos_ls = (light.shadowMatrix * float4(surface.position, 1.f)).xyz;
// OPTIM: we should be able to multiply by the extents in CPU in matrix and use 1 here...
float3 d = abs(pos_ls) - probeExtents;
#if PROBES_OUTER_BLENDING
if (any(d > blendDist))
#else
if (any(d > 0.f))
#endif
{
return;
}
#if PROBES_NORMALIZATION
// inside ' | ' outside
// nd 1 1 0.5 0 0 (per component)
#if PROBES_OUTER_BLENDING
float3 nd = saturate(-(d / blendDist) * 0.5f + 0.5f);
#else
float3 nd = saturate(-(d / blendDist));
#endif
float probeFactor = (nd.x * nd.y * nd.z) * light.color.r;
#else
// signed distance in the probe box
float sd = min(max(d.x,max(d.y,d.z)),0.0) + length(max(d,0.0));
#if PROBES_OUTER_BLENDING
float probeFactor = saturate(1.f - sd / blendDist);
#else
float probeFactor = saturate(-sd / blendDist);
#endif
// inside | ' outside
// sd -x 0 b +x
// fc 1 1 0 0
probeFactor *= probeRadianceRemainingFactor * light.color.r; // Do we really need this or 1.f is enough (we need this for global probe anyway...)
#endif
if (parallaxCorrection /* && all(d < 0.f) */) {
// OPTIM: we should be able to multiply by the extents in CPU and use 1 here...
float3 pos_off = pos_ls + parallaxCenter;
float3 t1 = ( parallaxExtents - pos_off) / specDir;
float3 t2 = (-parallaxExtents - pos_off) / specDir;
float3 tmax = max(max(0, t1), t2); // max(0, ..) to avoid correction when outside the box (in the blending zone)
float t = min(tmax.x, min(tmax.y, tmax.z));
// Use Distance in WS directly to recover intersection
float3 hit_ls = pos_ls + specDir * t;
specDir = hit_ls - probeOffset;
}
float mipd = float(probeTextureArray.get_num_mip_levels()) - 1.f;
const float intensity = surface.ambientOcclusion * probeFactor;
float mips = surface.roughness * mipd;
#ifdef C3D_SUPPORT_CUBE_ARRAY
float3 LD = float3(probeTextureArray.sample(linearSampler, specDir, probeIndex, level(mips)).rgb);
#else
float2 specUV = scn::dual_paraboloid_from_cartesian(normalize(specDir));
float3 LD = float3(probeTextureArray.sample(linearSampler, specUV, probeIndex, level(mips)).rgb);
#endif
/* Debug blending with primary colors
switch (probeIndex) {
case 1: LD = float3(1.f, 0.f, 0.f); break;
case 2: LD = float3(0.f, 1.f, 0.f); break;
case 3: LD = float3(0.f, 0.f, 1.f); break;
default: LD = float3(1.f, 1.f, 1.f); break;
}*/
// radiance
#if PROBES_NORMALIZATION
probesWeightedSum += float4(LD * intensity * probeReflectance, probeFactor);
#else
probeRadianceRemainingFactor = saturate(probeRadianceRemainingFactor - probeFactor);
specular += LD * intensity * probeReflectance;
#endif
#ifdef USE_CLEARCOAT
n = surface.clearCoatNormal;
r = reflect(-v, n);
specDir = scn::mat4_mult_float3(light.shadowMatrix, r);
if (parallaxCorrection /* && all(d < 0.f) */) {
float3 pos_off = pos_ls + parallaxCenter;
// OPTIM: we should be able to multiply by the extents in CPU and use 1 here...
float3 t1 = ( parallaxExtents - pos_off) / specDir;
float3 t2 = (-parallaxExtents - pos_off) / specDir;
float3 tmax = max(max(0, t1), t2); // max(0, ..) to avoid correction when outside the box (in the blending zone)
float t = min(tmax.x, min(tmax.y, tmax.z));
// Use Distance in WS directly to recover intersection
float3 hit_ls = pos_ls + specDir * t;
specDir = hit_ls - probeOffset;
}
mips = surface.clearCoatRoughness * mipd;
#ifdef C3D_SUPPORT_CUBE_ARRAY
LD = float3(probeTextureArray.sample(linearSampler, specDir, probeIndex, level(mips)).rgb);
#else
specUV = scn::dual_paraboloid_from_cartesian(normalize(specDir));
LD = float3(probeTextureArray.sample(linearSampler, specUV, probeIndex, level(mips)).rgb);
#endif
#if PROBES_NORMALIZATION
probesWeightedSum += float4(LD * intensity * probeReflectance, probeFactor) * surface.clearCoat;
#else
specular += LD * intensity * probeReflectance * surface.clearCoat;
#endif
#endif
}
void add_global_probe(float4x4 localDirToWorldCubemapDir, float environmentIntensity,
#ifdef C3D_SUPPORT_CUBE_ARRAY
texturecube_array<half> probeTextureArray
#else
texture2d_array<half> probeTextureArray
#endif
)
{
float3 n = surface.normal;
float3 v = surface.view;
float3 r = reflect(-v, n); // mirror vector (view vector around normal)
float3 specDir = scn::mat4_mult_float3(localDirToWorldCubemapDir, r);
float mipd = float(probeTextureArray.get_num_mip_levels()) - 1.f;
const float intensity = surface.ambientOcclusion * environmentIntensity;
float mips = surface.roughness * mipd;
#ifdef C3D_SUPPORT_CUBE_ARRAY
float3 LD = float3(probeTextureArray.sample(linearSampler, specDir, 0, level(mips)).rgb);
#else
float2 specUV = scn::dual_paraboloid_from_cartesian(normalize(specDir));
float3 LD = float3(probeTextureArray.sample(linearSampler, specUV, 0, level(mips)).rgb);
#endif
// radiance
specular += LD * intensity * probeReflectance;
}
void add_global_probe(texturecube<float, access::sample> specularLD,
float4x4 localDirToWorldCubemapDir,
float environmentIntensity)
{
float3 n = surface.normal;
float3 v = surface.view;
float3 r = reflect(-v, n); // mirror vector (view vector around normal)
float roughness = surface.roughness;
float roughness2= roughness * roughness;
#if 1
float smoothness = 1.0f - roughness2;
float specularLerpFactor = (1. - smoothness * (sqrt(smoothness) + roughness2));
// This does have an effect on smooth object : seems buggy (plus costly)
//float NoV = 1.f - saturate(dot(n, v));
//specularLerpFactor = saturate(specularLerpFactor + 2.f*NoV*NoV*NoV);
float3 specularDominantNDirection = mix(r, n, specularLerpFactor); // no need to normalize as we fetch in a cubemap
#else
float3 specularDominantNDirection = r;
#endif
// Specular
float mipLevel = roughness * float(specularLD.get_num_mip_levels() - 1);
#if 0 // Seamless cubemap filtering
float3 dirAbs = abs(specularDominantNDirection);
float dirNormInf = max(dirAbs.x, max(dirAbs.y, dirAbs.z));
float scale = 1.0f - exp2(mipLevel) / float(specularLD.get_width());
if (dirAbs.x != dirNormInf) specularDominantNDirection.x *= scale;
if (dirAbs.y != dirNormInf) specularDominantNDirection.y *= scale;
if (dirAbs.z != dirNormInf) specularDominantNDirection.z *= scale;
#endif
float3 LD = specularLD.sample(linearSampler, scn::mat4_mult_float3(localDirToWorldCubemapDir, specularDominantNDirection), level(mipLevel)).rgb;
#if 1 // Specular occlusion - not physically correct
float specularOcclusion = saturate(pow(NoV + surface.ambientOcclusion, exp2(-16.0f * roughness - 1.0f)) - 1.0f + surface.ambientOcclusion);
LD *= specularOcclusion;
#endif
// effectiveAlbedo is multiplied in combine
specular += LD * (surface.ambientOcclusion * environmentIntensity) * probeReflectance;
}
void add_global_probeClearCoat(texturecube<float, access::sample> specularLD,
float4x4 localDirToWorldCubemapDir,
float environmentIntensity)
{
float3 n = surface.clearCoatNormal;
float3 v = surface.view;
float3 r = reflect(-v, n); // mirror vector (view vector around normal)
float roughness = surface.clearCoatRoughness;
// Specular
float mipLevel = roughness * float(specularLD.get_num_mip_levels() - 1);
float3 LD = specularLD.sample(linearSampler, scn::mat4_mult_float3(localDirToWorldCubemapDir, r), level(mipLevel)).rgb;
LD *= surface.ambientOcclusion;
//energy preservation
float Fc = scn_brdf_F_opt(0.04f, NoVClearCoat).r * surface.clearCoat;
float attenuation = 1.0f - Fc;
specular *= (attenuation * attenuation);
// diffuse *= attenuation; //-> darken diffuse when clearCoatRoughness is high
specular += LD * environmentIntensity * probeReflectanceClearCoat * surface.clearCoat;
}
// MARK: Irradiance
void add_irradiance_from_selfIllum()
{
float selfIlluminationAO = saturate(mix(1.f, surface.ambientOcclusion, selfIlluminationOcclusion));
float3 irradiance = selfIlluminationAO * surface.selfIllumination.rgb;
diffuse += irradiance;
}
void add_global_irradiance_from_sh(float4x4 localDirToWorldCubemapDir,
#if defined(USE_PROBES_LIGHTING) && (USE_PROBES_LIGHTING == 2)
sh2_coefficients shCoefficients)
#else
sh3_coefficients shCoefficients)
#endif
{
float3 n_sh_space = scn::mat4_mult_float3(localDirToWorldCubemapDir, surface.normal);
float3 irradiance = shEvalDirection(float4(n_sh_space, 1.), shCoefficients);
diffuse += surface.ambientOcclusion * irradiance;
}
void add_global_irradiance_probe(texturecube<float, access::sample> irradianceTexture,
float4x4 localDirToWorldCubemapDir,
float environmentIntensity)
{
float3 n_cube_space = scn::mat4_mult_float3(localDirToWorldCubemapDir, surface.normal);
float3 irradiance = irradianceTexture.sample(linearSampler, n_cube_space).rgb;
diffuse += (surface.ambientOcclusion * environmentIntensity) * irradiance;
}
#endif // USE_PBR
// MARK: IES
static constexpr sampler iesSampler = sampler(filter::linear, mip_filter::none, address::clamp_to_edge);
float ies_attenuation(float3 l, scn_light light, texture2d<half> iesTexture)
{
#if USE_QUAT_FOR_IES
float3 v = scn::quaternion_rotate_vector(light.parameters.ies.light_from_view_quat, -l);
#else
float3 v = scn::matrix_rotate(light.parameters.ies.light_from_view, -l);
#endif
float phi = (v.z * light.parameters.ies.scaleBias.x + light.parameters.ies.scaleBias.y);
float theta = atan2(v.y, v.x) * 0.5f * M_1_PI_F;
return iesTexture.sample(iesSampler, float2(phi, abs(theta))).r;
}
void add_ies(scn_light light, texture2d<half> iesTexture)
{
float3 unnormalized_l = light.pos - surface.position;
float3 l = normalize(unnormalized_l);
float intensity = dist_attenuation(unnormalized_l, light);
intensity *= ies_attenuation(l, light, iesTexture);
shade(l, light.color.rgb, intensity);
}
void add_ies(scn_light light, texture2d<half> iesTexture, depth2d<float> shadowMap, constant float4* shadowKernel, int sampleCount)
{
float3 unnormalized_l = light.pos - surface.position;
float3 l = normalize(unnormalized_l);
float intensity = dist_attenuation(unnormalized_l, light);
intensity *= ies_attenuation(l, light, iesTexture);
intensity *= shadow(surface.position, light, shadowMap, shadowKernel, sampleCount);
shade(l, light.color.rgb, intensity);
}
// MARK: Area
void add_area_rectangle(scn_light light, texture2d_array<float> bakedDataTexture)
{
#ifdef USE_PBR
float3 v = surface.view;
float3 n = surface.normal;
float3 p = surface.position;
// construct orthonormal basis around N
float3 tangent = normalize(v - n * dot(v, n));
float3 bitangent = cross(n, tangent);
float3x3 shadingSpaceTransform = transpose(float3x3(tangent, n, bitangent));
float3 lightCenter = light.shadowMatrix[3].xyz;
// check if surface is visible from light
float sidedness = dot(light.dir, lightCenter - p);
if (light.parameters.area.rectangle.doubleSided == false && sidedness <= 0.f)
return;
float3 lightRight = light.shadowMatrix[0].xyz * light.parameters.area.rectangle.halfExtents.x * sign(sidedness);
float3 lightTop = light.shadowMatrix[1].xyz * light.parameters.area.rectangle.halfExtents.y;
float4x3 cornerDirections = float4x3((lightCenter + lightRight + lightTop) - p,
(lightCenter + lightRight - lightTop) - p,
(lightCenter - lightRight - lightTop) - p,
(lightCenter - lightRight + lightTop) - p);
cornerDirections[0] = shadingSpaceTransform * cornerDirections[0];
cornerDirections[1] = shadingSpaceTransform * cornerDirections[1];
cornerDirections[2] = shadingSpaceTransform * cornerDirections[2];
cornerDirections[3] = shadingSpaceTransform * cornerDirections[3];
float diffuseAmount = pbr_area_light_eval_rectangle(cornerDirections);
float brdfNorm = 1.f;
float3x3 inverseLTCMatrix = scn_sample_area_light_precomputed_data(v, n, surface.roughness, &brdfNorm, bakedDataTexture);
cornerDirections[0] = inverseLTCMatrix * cornerDirections[0];
cornerDirections[1] = inverseLTCMatrix * cornerDirections[1];
cornerDirections[2] = inverseLTCMatrix * cornerDirections[2];
cornerDirections[3] = inverseLTCMatrix * cornerDirections[3];
float specularAmount = brdfNorm * pbr_area_light_eval_rectangle(cornerDirections);
float3 lightColor = light.color.rgb;
diffuse += diffuseAmount * lightColor;
specular += specularAmount * lightColor * reflectance;
#endif
}
void add_area_polygon(scn_light light, texture2d_array<float> bakedDataTexture, device packed_float2 *vertexPositions)
{
#ifdef USE_PBR
float3 v = surface.view;
float3 n = surface.normal;
float3 p = surface.position;
// construct orthonormal basis around N
float3 tangent = normalize(v - n * dot(v, n));
float3 bitangent = cross(n, tangent);
float3x3 shadingSpaceTransform = transpose(float3x3(tangent, n, bitangent));
float3 lightCenter = light.shadowMatrix[3].xyz;
// check if surface is visible from light
float sidedness = dot(light.dir, lightCenter - p);
if (light.parameters.area.polygon.doubleSided == false && sidedness <= 0.f)
return;
float3 lightRight = light.shadowMatrix[0].xyz * sign(sidedness);
float3 lightTop = light.shadowMatrix[1].xyz;
p = shadingSpaceTransform * p;
lightCenter = shadingSpaceTransform * lightCenter;
lightRight = shadingSpaceTransform * lightRight;
lightTop = shadingSpaceTransform * lightTop;
float diffuseAmount = pbr_area_light_eval_polygon(p, lightCenter, lightRight, lightTop, light.parameters.area.polygon.vertexCount, vertexPositions);
float brdfNorm = 1.f;
float3x3 inverseLTCMatrix = scn_sample_area_light_precomputed_data(v, n, surface.roughness, &brdfNorm, bakedDataTexture);
p = inverseLTCMatrix * p;
lightCenter = inverseLTCMatrix * lightCenter;
lightRight = inverseLTCMatrix * lightRight;
lightTop = inverseLTCMatrix * lightTop;
float specularAmount = brdfNorm * pbr_area_light_eval_polygon(p, lightCenter, lightRight, lightTop, light.parameters.area.polygon.vertexCount, vertexPositions);
float3 effectiveAlbedo = mix(float3(1.0), float3(0.0), surface.metalness); // 1.f and not `albedo` because `SCNShaderLightingContribution.diffuse` will be multiplied by `pbr_surface.albedo` later in `scn_pbr_combine`
float3 reflectance = mix(float3(PBR_F0_NON_METALLIC), surface.diffuse.rgb, surface.metalness);
float3 lightColor = light.color.rgb;
diffuse += diffuseAmount * lightColor * effectiveAlbedo;
specular += specularAmount * lightColor * reflectance;
#endif
}
void add_area_line(scn_light light, texture2d_array<float> bakedDataTexture)
{
#ifdef USE_PBR
float3 v = surface.view;
float3 n = surface.normal;
float3 p = surface.position;
// construct orthonormal basis around N
float3 tangent = normalize(v - n * dot(v, n));
float3 bitangent = cross(n, tangent);
float3x3 shadingSpaceTransform = transpose(float3x3(tangent, n, bitangent));
float3 lightCenter = light.shadowMatrix[3].xyz;
float3 lightRight = light.shadowMatrix[0].xyz * light.parameters.area.line.halfLength;
float2x3 cornerDirections = float2x3((lightCenter + lightRight) - p,
(lightCenter - lightRight) - p);
cornerDirections[0] = shadingSpaceTransform * cornerDirections[0];
cornerDirections[1] = shadingSpaceTransform * cornerDirections[1];
float diffuseAmount = pbr_area_light_eval_line(cornerDirections);
float brdfNorm = 1.f;
float3x3 inverseLTCMatrix = scn_sample_area_light_precomputed_data(v, n, surface.roughness, &brdfNorm, bakedDataTexture);
cornerDirections[0] = inverseLTCMatrix * cornerDirections[0];
cornerDirections[1] = inverseLTCMatrix * cornerDirections[1];
float specularAmount = brdfNorm * pbr_area_light_eval_line(cornerDirections);
float3 ortho = normalize(cross(cornerDirections[0], cornerDirections[1]));
float ltcWidthFactor = 1.0 / length(scn_ltc_matrix_invert_transpose(inverseLTCMatrix) * ortho);
specularAmount *= ltcWidthFactor;
float3 lightColor = light.color.rgb;
diffuse += diffuseAmount * lightColor;
specular += specularAmount * lightColor * reflectance;
#endif
}
void add_area_ellipse(scn_light light, texture2d_array<float> bakedDataTexture)
{
#ifdef USE_PBR
#endif
}
void add_area_ellipsoid(scn_light light, texture2d_array<float> bakedDataTexture)
{
#ifdef USE_PBR
#endif
}
};
#endif // #if defined(__METAL_VERSION__) && __METAL_VERSION__ >= 120
enum C3DColorMask {
kC3DColorMaskRed = 0x1 << 3,
kC3DColorMaskGreen = 0x1 << 2,
kC3DColorMaskBlue = 0x1 << 1,
kC3DColorMaskAlpha = 0x1 << 0
};
inline float4 colorFromMask(float4 col, int mask)
{
switch (mask) {
case kC3DColorMaskRed: return col.r;
case kC3DColorMaskRed|kC3DColorMaskGreen: return float4(col.rg, 0.f, 1.f);
case kC3DColorMaskRed|kC3DColorMaskBlue: return float4(col.rb, 0.f, 1.f);
case kC3DColorMaskRed|kC3DColorMaskAlpha: return float4(col.ra, 0.f, 1.f);
case kC3DColorMaskGreen: return col.g;
case kC3DColorMaskGreen|kC3DColorMaskBlue: return float4(col.bg, 0.f, 1.f);
case kC3DColorMaskGreen|kC3DColorMaskAlpha: return float4(col.ag, 0.f, 1.f);
case kC3DColorMaskBlue: return col.b;
case kC3DColorMaskBlue|kC3DColorMaskAlpha: return float4(col.ab, 0.f, 1.f);
case kC3DColorMaskAlpha: return col.a;
}
return col;
}
#ifndef USE_PBR
inline float3 illuminate(SCNShaderSurface surface, SCNShaderLightingContribution lighting)
{
float3 albedo = surface.diffuse.rgb * surface.ambientOcclusion;
float3 color = lighting.diffuse * albedo;
#if defined(USE_AMBIENT_LIGHTING) && (defined(LOCK_AMBIENT_WITH_DIFFUSE) || defined(USE_AMBIENT_AS_AMBIENTOCCLUSION))
color += lighting.ambient * albedo;
#endif
#ifdef USE_SELFILLUMINATION
color += surface.diffuse.rgb * surface.selfIllumination.rgb;
#endif
// Do we want to clamp there ????
#ifdef USE_SPECULAR
float3 S = lighting.specular;
#elif defined(USE_REFLECTIVE)
float3 S = float3(0.);
#endif
#ifdef USE_REFLECTIVE
S += surface.reflective.rgb * surface.ambientOcclusion;
#endif
#ifdef USE_SPECULAR
S *= surface.specular.rgb;
#endif
#if (defined(USE_SPECULAR) || defined(USE_REFLECTIVE)) && !defined(DISABLE_SPECULAR)
color += S;
#endif
#if defined(USE_AMBIENT) && !defined(USE_AMBIENT_AS_AMBIENTOCCLUSION)
color += surface.ambient.rgb * lighting.ambient;
#endif
#ifdef USE_EMISSION
color += surface.emission.rgb;
#endif
#ifdef USE_MULTIPLY
color *= surface.multiply.rgb;
#endif
#ifdef USE_MODULATE
color *= lighting.modulate;
#endif
return color;
}
#endif
struct SCNShaderGeometry
{
float4 position;
float3 normal;
float4 tangent;
float4 color;
float pointSize;
float2 texcoords[8]; // MAX_UV
};
struct commonprofile_uniforms {
// [id(0)]]
float4 diffuseColor;
float4 specularColor;
float4 ambientColor;
float4 emissionColor;
float4 selfIlluminationColor;
float4 reflectiveColor;
float4 multiplyColor;
float4 transparentColor;
float clearCoat;
float clearCoatRoughness;
float3 clearCoatNormal;
float metalness;
// [id(12)]]
float roughness;
float diffuseIntensity;
float specularIntensity;
float normalIntensity;
float ambientIntensity;
float emissionIntensity;
float selfIlluminationIntensity;
float reflectiveIntensity;
float multiplyIntensity;
float transparentIntensity;
// [id(22)]]
float metalnessIntensity;
float roughnessIntensity;
float clearCoatIntensity;
float clearCoatRoughnessIntensity;
float clearCoatNormalIntensity;
float displacementIntensity;
float materialShininess;
float selfIlluminationOcclusion;
float transparency;
float3 fresnel; // x: ((n1-n2)/(n1+n2))^2 y:1-x z:exponent
#if USE_ARGUMENT_BUFFERS
//[[id(32)]]
texture2d<float> emissionTexture;
sampler emissionSampler;
texture2d<float> ambientTexture;
sampler ambientSampler;
//[[id(32)]]
texture2d<float> diffuseTexture;
sampler diffuseSampler;
texture2d<float> specularTexture;
sampler specularSampler;
#if defined(USE_REFLECTIVE_CUBEMAP)
texturecube<float> reflectiveTexture;
#else
texture2d<float> reflectiveTexture;
#endif
sampler reflectiveSampler;
texture2d<float> transparentTexture;
sampler transparentSampler;
texture2d<float> multiplyTexture;
sampler multiplySampler;
//[[id(43)]]
texture2d<float> normalTexture;
sampler normalSampler;
texture2d<float> selfIlluminationTexture;
sampler selfIlluminationSampler;
texture2d<float> metalnessTexture;
sampler metalnessSampler;
texture2d<float> roughnessTexture;
sampler roughnessSampler;
texture2d<float> displacementTexture;
sampler displacementSampler;
//[[id(53)]]
#endif // USE_ARGUMENT_BUFFERS
#ifdef TEXTURE_TRANSFORM_COUNT
float4x4 textureTransforms[TEXTURE_TRANSFORM_COUNT];
#endif
};
#ifdef USE_OPENSUBDIV
struct osd_packed_vertex {
packed_float3 position;
#if defined(OSD_USER_VARYING_DECLARE_PACKED)
OSD_USER_VARYING_DECLARE_PACKED
#endif
};
#endif
#ifdef USE_DISPLACEMENT_MAP
static void applyDisplacement(texture2d<float> displacementTexture,
sampler displacementTextureSampler,
float2 displacementTexcoord,
thread SCNShaderGeometry& geometry,
constant commonprofile_uniforms& scn_commonprofile)
{
#ifdef USE_DISPLACEMENT_TEXTURE_COMPONENT
float altitude = colorFromMask(displacementTexture.sample(displacementTextureSampler, displacementTexcoord), USE_DISPLACEMENT_TEXTURE_COMPONENT).r;
#ifdef USE_DISPLACEMENT_INTENSITY
altitude *= scn_commonprofile.displacementIntensity;
#endif
#if defined(USE_NORMAL) && defined(HAS_OR_GENERATES_NORMAL)
float3 bitangent = geometry.tangent.w * normalize(cross(geometry.tangent.xyz, geometry.normal.xyz));
geometry.position.xyz += geometry.normal * altitude;
float3 offset = float3(1.f / displacementTexture.get_width(), 1.f / displacementTexture.get_height(), 0.f);
float3 h;
h.x = colorFromMask(displacementTexture.sample(displacementTextureSampler, displacementTexcoord), USE_DISPLACEMENT_TEXTURE_COMPONENT).r;
h.y = colorFromMask(displacementTexture.sample(displacementTextureSampler, displacementTexcoord+offset.xz), USE_DISPLACEMENT_TEXTURE_COMPONENT).r;
h.z = colorFromMask(displacementTexture.sample(displacementTextureSampler, displacementTexcoord-offset.zy), USE_DISPLACEMENT_TEXTURE_COMPONENT).r;
#ifdef USE_DISPLACEMENT_INTENSITY
h *= scn_commonprofile.displacementIntensity;
#endif
float3 n = normalize( float3( (h.x - h.y)/offset.x, 1., (h.x - h.z)/offset.y) );
geometry.normal = geometry.tangent.xyz * n.x + geometry.normal.xyz * n.y + bitangent.xyz * n.z;
geometry.tangent.xyz = normalize(cross(bitangent, geometry.normal));
#endif // USE_NORMAL
#else // USE_DISPLACEMENT_TEXTURE_COMPONENT
float3 displacement = displacementTexture.sample(displacementTextureSampler, displacementTexcoord).rgb;
#ifdef USE_DISPLACEMENT_INTENSITY
displacement *= scn_commonprofile.displacementIntensity;
#endif
#if defined(USE_NORMAL) && defined(HAS_OR_GENERATES_NORMAL)
float3 bitangent = geometry.tangent.w * normalize(cross(geometry.tangent.xyz, geometry.normal.xyz));
geometry.position.xyz += geometry.tangent.xyz * displacement.x + geometry.normal.xyz * displacement.y + bitangent.xyz * displacement.z;
float3 offset = float3(1.f / displacementTexture.get_width(), 1.f / displacementTexture.get_height(), 0.f);
float3 a = displacementTexture.sample(displacementTextureSampler, displacementTexcoord).rgb;
float3 b = displacementTexture.sample(displacementTextureSampler, displacementTexcoord+offset.xz).rgb;
float3 c = displacementTexture.sample(displacementTextureSampler, displacementTexcoord+offset.zy).rgb;
#ifdef USE_DISPLACEMENT_INTENSITY
a *= scn_commonprofile.displacementIntensity;
b *= scn_commonprofile.displacementIntensity;
c *= scn_commonprofile.displacementIntensity;
#endif
b += offset.xzz;
c -= offset.zzy;
float3 n = (normalize( cross( b-a, c-a ) ));
geometry.normal = geometry.tangent.xyz * n.x + geometry.normal.xyz * n.y + bitangent.xyz * n.z;
geometry.tangent.xyz = normalize(cross(bitangent, geometry.normal));
#endif // USE_NORMAL
#endif // USE_DISPLACEMENT_TEXTURE_COMPONENT
}
#endif // USE_DISPLACEMENT_MAP
#ifdef USE_OUTLINE
static inline float hash(float2 p)
{
const float2 kMod2 = float2(443.8975f, 397.2973f);
p = fract(p * kMod2);
p += dot(p.xy, p.yx+19.19f);
return fract(p.x * p.y);
}
#endif
} // namespace
using namespace NAMESPACE_HASH;
//
// MARK: - Vertex and post-tessellation vertex functions
//
#if defined(USE_TESSELLATION)
struct scn_patch_t {
patch_control_point<scn_vertex_t> controlPoints;
};
#endif
#if defined(USE_OPENSUBDIV)
#if OSD_IS_ADAPTIVE
[[ patch(quad, VERTEX_CONTROL_POINTS_PER_PATCH) ]]
#endif
#elif defined(USE_TESSELLATION)
[[ patch(triangle, 3) ]]
#endif
vertex commonprofile_io commonprofile_vert(
#if !defined(USE_TESSELLATION)
scn_vertex_t in [[ stage_in ]]
, uint scn_vertexID [[ vertex_id ]]
#else // USE_TESSELLATION
#ifdef USE_OPENSUBDIV
#if OSD_IS_ADAPTIVE
#if USE_STAGE_IN
PatchInput patchInput [[ stage_in ]]
#else
OsdVertexBufferSet patchInput
#endif
, float2 patchCoord [[ position_in_patch ]]
, uint patchID [[ patch_id ]]
, constant float& osdTessellationLevel [[ buffer(TESSELLATION_LEVEL_BUFFER_INDEX) ]]
#else // OSD_IS_ADAPTIVE
device unsigned const* osdIndicesBuffer [[ buffer(INDICES_BUFFER_INDEX) ]]
, device osd_packed_vertex const* osdVertexBuffer [[ buffer(VERTEX_BUFFER_INDEX) ]]
, uint vertexID [[ vertex_id ]]
#endif // OSD_IS_ADAPTIVE
#if defined(OSD_FVAR_WIDTH)
#if OSD_FVAR_USES_MULTIPLE_CHANNELS
, constant uint32_t& osdFaceVaryingChannelCount [[ buffer(OSD_FVAR_CHANNELS_CHANNEL_COUNT_INDEX) ]]
, device OsdFVarChannelDesc const* osdFaceVaryingChannelDescriptors [[ buffer(OSD_FVAR_CHANNELS_CHANNEL_DESCRIPTORS_INDEX) ]]
, constant uint32_t& osdFaceVaryingPatchArrayIndex [[ buffer(OSD_FVAR_CHANNELS_PATCH_ARRAY_INDEX_BUFFER_INDEX) ]]
, device void const* osdFaceVaryingChannelsPackedData [[ buffer(OSD_FVAR_CHANNELS_PACKED_DATA_BUFFER_INDEX) ]]
#else
, device float const* osdFaceVaryingData [[ buffer(OSD_FVAR_DATA_BUFFER_INDEX) ]]
, device int const* osdFaceVaryingIndices [[ buffer(OSD_FVAR_INDICES_BUFFER_INDEX) ]]
#if OSD_IS_ADAPTIVE
, device packed_int3 const* osdFaceVaryingPatchParams [[ buffer(OSD_FVAR_PATCHPARAM_BUFFER_INDEX) ]]
, constant packed_int4& osdFaceVaryingPatchArray [[ buffer(OSD_FVAR_PATCH_ARRAY_BUFFER_INDEX) ]]
#endif
#endif //OSD_FVAR_USES_MULTIPLE_CHANNELS
#endif //defined(OSD_FVAR_WIDTH)
#else // USE_OPENSUBDIV
scn_patch_t in [[ stage_in ]]
, float3 patchCoord [[ position_in_patch ]]
#endif // USE_OPENSUBDIV
#endif // USE_TESSELLATION
#ifdef USE_MULTIPLE_RENDERING
, device SCNSceneBuffer* scn_frames [[ buffer(0) ]]
#else
, constant SCNSceneBuffer& scn_frame [[ buffer(0) ]]
#endif
#ifdef USE_INSTANCING
// we use device here to override the 64Ko limit of constant buffers on NV hardware
, device commonprofile_node* scn_nodeInstances [[ buffer(1) ]]
#else
, constant commonprofile_node& scn_node [[ buffer(1) ]]
#endif
#ifdef USE_PER_VERTEX_LIGHTING
, constant scn_light* scn_lights [[ buffer(2) ]]
, constant float4* u_shadowKernel
, texture2d_array<float> u_areaLightBakedDataTexture
#endif
// used for texture transform and materialShininess in case of perVertexLighting
, constant commonprofile_uniforms& scn_commonprofile
, uint scn_instanceID [[ instance_id ]]
#ifdef USE_POINT_RENDERING
// x:pointSize, y:minimumScreenSize, z:maximumScreenSize
, constant float3& scn_pointSize
#endif
#ifdef USE_DISPLACEMENT_MAP
#if USE_ARGUMENT_BUFFERS
#define u_displacementTexture scn_commonprofile.displacementTexture
#define u_displacementTextureSampler scn_commonprofile.displacementSampler
#else
, texture2d<float> u_displacementTexture
, sampler u_displacementTextureSampler
#endif //USE_ARGUMENT_BUFFERS
#endif //USE_DISPLACEMENT_MAP
#ifdef USE_VERTEX_EXTRA_ARGUMENTS
#endif
)
{
commonprofile_io out;
#ifdef USE_MULTIPLE_RENDERING
out.sliceIndex = scn_instanceID % USE_MULTIPLE_RENDERING;
device SCNSceneBuffer& scn_frame = scn_frames[0];
device SCNSceneBuffer& scn_frame_slice = scn_frames[out.sliceIndex];
#ifdef USE_INSTANCING
device commonprofile_node& scn_node = scn_nodeInstances[scn_instanceID / USE_MULTIPLE_RENDERING];
#endif
#else
#ifdef USE_INSTANCING
device commonprofile_node& scn_node = scn_nodeInstances[scn_instanceID];
#endif
#endif
#ifdef USE_TESSELLATION
uint scn_vertexID; // we need scn_vertexID if a geometry modifier is used
scn_vertexID = 0;
#endif
//
// MARK: Populating the `_geometry` struct
//
SCNShaderGeometry _geometry;
#if !defined(USE_TESSELLATION)
// OPTIM in could be already float4?
_geometry.position = float4(in.position, 1.f);
#if defined(USE_NORMAL) && defined(HAS_NORMAL)
_geometry.normal = in.normal;
#endif
#if defined(USE_TANGENT) || defined(USE_BITANGENT)
_geometry.tangent = in.tangent;
#endif
#ifdef NEED_IN_TEXCOORD0
_geometry.texcoords[0] = in.texcoord0;
#endif
#ifdef NEED_IN_TEXCOORD1
_geometry.texcoords[1] = in.texcoord1;
#endif
#ifdef NEED_IN_TEXCOORD2
_geometry.texcoords[2] = in.texcoord2;
#endif
#ifdef NEED_IN_TEXCOORD3
_geometry.texcoords[3] = in.texcoord3;
#endif
#ifdef NEED_IN_TEXCOORD4
_geometry.texcoords[4] = in.texcoord4;
#endif
#ifdef NEED_IN_TEXCOORD5
_geometry.texcoords[5] = in.texcoord5;
#endif
#ifdef NEED_IN_TEXCOORD6
_geometry.texcoords[6] = in.texcoord6;
#endif
#ifdef NEED_IN_TEXCOORD7
_geometry.texcoords[7] = in.texcoord7;
#endif
#ifdef HAS_VERTEX_COLOR
_geometry.color = in.color;
#elif USE_VERTEX_COLOR
_geometry.color = float4(1.);
#endif
#else // USE_TESSELLATION
#ifdef USE_OPENSUBDIV
#if OSD_IS_ADAPTIVE
#if USE_STAGE_IN
int3 patchParam = patchInput.patchParam;
#else
int3 patchParam = patchInput.patchParamBuffer[patchID];
#endif
int refinementLevel = OsdGetPatchRefinementLevel(patchParam);
float tessellationLevel = min(osdTessellationLevel, (float)OSD_MAX_TESS_LEVEL) / exp2((float)refinementLevel - 1);
OsdPatchVertex patchVertex = OsdComputePatch(tessellationLevel, patchCoord, patchID, patchInput);
#if defined(OSD_FVAR_WIDTH)
int patchIndex = OsdGetPatchIndex(patchID);
#if OSD_FVAR_USES_MULTIPLE_CHANNELS
OsdInterpolateFaceVarings(_geometry, patchCoord.xy, patchIndex, osdFaceVaryingChannelCount, osdFaceVaryingChannelDescriptors, osdFaceVaryingPatchArrayIndex, osdFaceVaryingChannelsPackedData);
#else
OsdInterpolateFaceVarings(_geometry, patchCoord.xy, patchIndex, osdFaceVaryingIndices, osdFaceVaryingData, osdFaceVaryingPatchParams, osdFaceVaryingPatchArray);
#endif
#endif
_geometry.position = float4(patchVertex.position, 1.f);
#if defined(USE_NORMAL)
_geometry.normal = patchVertex.normal;
#endif
#if defined(USE_TANGENT) || defined(USE_BITANGENT)
_geometry.tangent = float4(patchVertex.tangent, -1.f);
//_geometry.bitangent = patchVertex.bitangent;
#endif
#if defined(NEED_IN_TEXCOORD0) && (OSD_TEXCOORD0_INTERPOLATION_MODE == OSD_PRIMVAR_INTERPOLATION_MODE_USER_VARYING)
_geometry.texcoords[0] = patchVertex.texcoord0;
#endif
#if defined(NEED_IN_TEXCOORD1) && (OSD_TEXCOORD1_INTERPOLATION_MODE == OSD_PRIMVAR_INTERPOLATION_MODE_USER_VARYING)
_geometry.texcoords[1] = patchVertex.texcoord1;
#endif
#if defined(NEED_IN_TEXCOORD2) && (OSD_TEXCOORD2_INTERPOLATION_MODE == OSD_PRIMVAR_INTERPOLATION_MODE_USER_VARYING)
_geometry.texcoords[2] = patchVertex.texcoord2;
#endif
#if defined(NEED_IN_TEXCOORD3) && (OSD_TEXCOORD3_INTERPOLATION_MODE == OSD_PRIMVAR_INTERPOLATION_MODE_USER_VARYING)
_geometry.texcoords[3] = patchVertex.texcoord3;
#endif
#if defined(NEED_IN_TEXCOORD4) && (OSD_TEXCOORD4_INTERPOLATION_MODE == OSD_PRIMVAR_INTERPOLATION_MODE_USER_VARYING)
_geometry.texcoords[4] = patchVertex.texcoord4;
#endif
#if defined(NEED_IN_TEXCOORD5) && (OSD_TEXCOORD5_INTERPOLATION_MODE == OSD_PRIMVAR_INTERPOLATION_MODE_USER_VARYING)
_geometry.texcoords[5] = patchVertex.texcoord5;
#endif
#if defined(NEED_IN_TEXCOORD6) && (OSD_TEXCOORD6_INTERPOLATION_MODE == OSD_PRIMVAR_INTERPOLATION_MODE_USER_VARYING)
_geometry.texcoords[6] = patchVertex.texcoord6;
#endif
#if defined(NEED_IN_TEXCOORD7) && (OSD_TEXCOORD7_INTERPOLATION_MODE == OSD_PRIMVAR_INTERPOLATION_MODE_USER_VARYING)
_geometry.texcoords[7] = patchVertex.texcoord7;
#endif
#if defined(HAS_VERTEX_COLOR) && (OSD_COLOR_INTERPOLATION_MODE == OSD_PRIMVAR_INTERPOLATION_MODE_USER_VARYING)
_geometry.color = patchVertex.color;
#endif
#else // OSD_IS_ADAPTIVE
#if OSD_PATCH_QUADS
const uint primitiveIndex = vertexID / 6;
#ifdef USE_NORMAL
float3 p0 = osdVertexBuffer[osdIndicesBuffer[primitiveIndex * 4 + 0]].position;
float3 p1 = osdVertexBuffer[osdIndicesBuffer[primitiveIndex * 4 + 1]].position;
float3 p2 = osdVertexBuffer[osdIndicesBuffer[primitiveIndex * 4 + 2]].position;
float3 normal = normalize(cross(p2 - p1, p0 - p1));
#endif
const uint triangleIndices[6] = { 0, 1, 2, 0, 2, 3 };
const uint quadVertexIndex = triangleIndices[vertexID % 6];
osd_packed_vertex osdVertex = osdVertexBuffer[osdIndicesBuffer[primitiveIndex * 4 + quadVertexIndex]];
#elif OSD_PATCH_TRIANGLES
const uint primitiveIndex = vertexID / 3;
#ifdef USE_NORMAL
float3 p0 = osdVertexBuffer[osdIndicesBuffer[primitiveIndex * 3 + 0]].position;
float3 p1 = osdVertexBuffer[osdIndicesBuffer[primitiveIndex * 3 + 1]].position;
float3 p2 = osdVertexBuffer[osdIndicesBuffer[primitiveIndex * 3 + 2]].position;
float3 normal = normalize(cross(p2 - p1, p0 - p1));
#endif
osd_packed_vertex osdVertex = osdVertexBuffer[osdIndicesBuffer[vertexID]];
#endif
float3 position = osdVertex.position;
#if defined(OSD_FVAR_WIDTH)
int patchIndex = OsdGetPatchIndex(primitiveIndex);
#if OSD_PATCH_QUADS
float2 quadUVs[4] = { float2(0,0), float2(1,0), float2(1,1), float2(0,1) };
#if OSD_FVAR_USES_MULTIPLE_CHANNELS
OsdInterpolateFaceVarings(_geometry, quadUVs[quadVertexIndex], patchIndex, osdFaceVaryingChannelCount, osdFaceVaryingChannelDescriptors, osdFaceVaryingPatchArrayIndex, osdFaceVaryingChannelsPackedData);
#else
OsdInterpolateFaceVarings(_geometry, quadUVs[quadVertexIndex], patchIndex, osdFaceVaryingIndices, osdFaceVaryingData);
#endif
#elif OSD_PATCH_TRIANGLES
//TODO
#endif
#endif //defined(OSD_FVAR_WIDTH)
#if defined(NEED_IN_TEXCOORD0) && (OSD_TEXCOORD0_INTERPOLATION_MODE == OSD_PRIMVAR_INTERPOLATION_MODE_USER_VARYING)
_geometry.texcoords[0] = osdVertex.texcoord0;
#endif
#if defined(NEED_IN_TEXCOORD1) && (OSD_TEXCOORD1_INTERPOLATION_MODE == OSD_PRIMVAR_INTERPOLATION_MODE_USER_VARYING)
_geometry.texcoords[1] = osdVertex.texcoord1;
#endif
#if defined(NEED_IN_TEXCOORD2) && (OSD_TEXCOORD2_INTERPOLATION_MODE == OSD_PRIMVAR_INTERPOLATION_MODE_USER_VARYING)
_geometry.texcoords[2] = osdVertex.texcoord2;
#endif
#if defined(NEED_IN_TEXCOORD3) && (OSD_TEXCOORD3_INTERPOLATION_MODE == OSD_PRIMVAR_INTERPOLATION_MODE_USER_VARYING)
_geometry.texcoords[3] = osdVertex.texcoord3;
#endif
#if defined(NEED_IN_TEXCOORD4) && (OSD_TEXCOORD4_INTERPOLATION_MODE == OSD_PRIMVAR_INTERPOLATION_MODE_USER_VARYING)
_geometry.texcoords[4] = osdVertex.texcoord4;
#endif
#if defined(NEED_IN_TEXCOORD5) && (OSD_TEXCOORD5_INTERPOLATION_MODE == OSD_PRIMVAR_INTERPOLATION_MODE_USER_VARYING)
_geometry.texcoords[5] = osdVertex.texcoord5;
#endif
#if defined(NEED_IN_TEXCOORD6) && (OSD_TEXCOORD6_INTERPOLATION_MODE == OSD_PRIMVAR_INTERPOLATION_MODE_USER_VARYING)
_geometry.texcoords[6] = osdVertex.texcoord6;
#endif
#if defined(NEED_IN_TEXCOORD7) && (OSD_TEXCOORD7_INTERPOLATION_MODE == OSD_PRIMVAR_INTERPOLATION_MODE_USER_VARYING)
_geometry.texcoords[7] = osdVertex.texcoord7;
#endif
#if defined(HAS_VERTEX_COLOR) && (OSD_COLOR_INTERPOLATION_MODE == OSD_PRIMVAR_INTERPOLATION_MODE_USER_VARYING)
_geometry.color = osdVertex.color;
#endif
_geometry.position = float4(position, 1.f);
#ifdef USE_NORMAL
_geometry.normal = normal;
#endif
#endif // OSD_IS_ADAPTIVE
#else // USE_OPENSUBDIV
//
// MARK: Geometry smooting
//
#if defined(TESSELLATION_SMOOTHING_MODE_PN_TRIANGLE) || defined(TESSELLATION_SMOOTHING_MODE_PHONG)
float3 P0 = in.controlPoints[0].position;
float3 P1 = in.controlPoints[1].position;
float3 P2 = in.controlPoints[2].position;
float3 N0 = in.controlPoints[0].normal;
float3 N1 = in.controlPoints[1].normal;
float3 N2 = in.controlPoints[2].normal;
#if defined(TESSELLATION_SMOOTHING_MODE_PN_TRIANGLE)
float3 position, normal;
scn_smooth_geometry_pn_triangle(position, normal, patchCoord, P0, P1, P2, N0, N1, N2);
#elif defined(TESSELLATION_SMOOTHING_MODE_PHONG)
float3 position, normal;
scn_smooth_geometry_phong(position, normal, patchCoord, P0, P1, P2, N0, N1, N2);
#endif
_geometry.position = float4(position, 1.f);
#ifdef USE_NORMAL
_geometry.normal = normal;
#endif
#else // GEOMETRY_SMOOTHING
// OPTIM in could be already float4?
_geometry.position = float4(scn::barycentric_mix(in.controlPoints[0].position, in.controlPoints[1].position, in.controlPoints[2].position, patchCoord), 1.f);
#if defined(USE_NORMAL) && defined(HAS_NORMAL)
_geometry.normal = normalize(scn::barycentric_mix(in.controlPoints[0].normal, in.controlPoints[1].normal, in.controlPoints[2].normal, patchCoord));
#endif
#endif // GEOMETRY_SMOOTHING
#if defined(USE_TANGENT) || defined(USE_BITANGENT)
_geometry.tangent = normalize(scn::barycentric_mix(in.controlPoints[0].tangent, in.controlPoints[1].tangent, in.controlPoints[2].tangent, patchCoord));
#endif
#ifdef NEED_IN_TEXCOORD0
_geometry.texcoords[0] = scn::barycentric_mix(in.controlPoints[0].texcoord0, in.controlPoints[1].texcoord0, in.controlPoints[2].texcoord0, patchCoord);
#endif
#ifdef NEED_IN_TEXCOORD1
_geometry.texcoords[1] = scn::barycentric_mix(in.controlPoints[0].texcoord1, in.controlPoints[1].texcoord1, in.controlPoints[2].texcoord1, patchCoord);
#endif
#ifdef NEED_IN_TEXCOORD2
_geometry.texcoords[2] = scn::barycentric_mix(in.controlPoints[0].texcoord2, in.controlPoints[1].texcoord2, in.controlPoints[2].texcoord2, patchCoord);
#endif
#ifdef NEED_IN_TEXCOORD3
_geometry.texcoords[3] = scn::barycentric_mix(in.controlPoints[0].texcoord3, in.controlPoints[1].texcoord3, in.controlPoints[2].texcoord3, patchCoord);
#endif
#ifdef NEED_IN_TEXCOORD4
_geometry.texcoords[4] = scn::barycentric_mix(in.controlPoints[0].texcoord4, in.controlPoints[1].texcoord4, in.controlPoints[2].texcoord4, patchCoord);
#endif
#ifdef NEED_IN_TEXCOORD5
_geometry.texcoords[5] = scn::barycentric_mix(in.controlPoints[0].texcoord5, in.controlPoints[1].texcoord5, in.controlPoints[2].texcoord5, patchCoord);
#endif
#ifdef NEED_IN_TEXCOORD6
_geometry.texcoords[6] = scn::barycentric_mix(in.controlPoints[0].texcoord6, in.controlPoints[1].texcoord6, in.controlPoints[2].texcoord6, patchCoord);
#endif
#ifdef NEED_IN_TEXCOORD7
_geometry.texcoords[7] = scn::barycentric_mix(in.controlPoints[0].texcoord7, in.controlPoints[1].texcoord7, in.controlPoints[2].texcoord7, patchCoord);
#endif
#ifdef HAS_VERTEX_COLOR
_geometry.color = scn::barycentric_mix(in.controlPoints[0].color, in.controlPoints[1].color, in.controlPoints[2].color, patchCoord);
#elif USE_VERTEX_COLOR
_geometry.color = float4(1.);
#endif
#endif // USE_OPENSUBDIV
#endif // USE_TESSELLATION
#ifdef USE_POINT_RENDERING
_geometry.pointSize = scn_pointSize.x;
#endif
#ifdef USE_TEXCOORD
#endif
#ifdef USE_DISPLACEMENT_MAP
applyDisplacement(u_displacementTexture, u_displacementTextureSampler, _displacementTexcoord, _geometry, scn_commonprofile);
#endif
//
// MARK: Skinning
//
#ifdef USE_SKINNING
#if !defined(USE_TESSELLATION)
{
float3 pos = 0.f;
#if defined(USE_NORMAL) && defined(HAS_NORMAL)
float3 nrm = 0.f;
#endif
#if defined(USE_TANGENT) || defined(USE_BITANGENT)
float3 tgt = 0.f;
#endif
for (int i = 0; i < MAX_BONE_INFLUENCES; ++i) {
#if MAX_BONE_INFLUENCES == 1
float weight = 1.f;
#else
float weight = in.skinningWeights[i];
if (weight <= 0.f)
continue;
#endif
int idx = int(in.skinningJoints[i]) * 3;
float4x4 jointMatrix = float4x4(scn_node.skinningJointMatrices[idx],
scn_node.skinningJointMatrices[idx+1],
scn_node.skinningJointMatrices[idx+2],
float4(0., 0., 0., 1.));
pos += (_geometry.position * jointMatrix).xyz * weight;
#if defined(USE_NORMAL) && defined(HAS_NORMAL)
nrm += _geometry.normal * scn::mat3(jointMatrix) * weight;
#endif
#if defined(USE_TANGENT) || defined(USE_BITANGENT)
tgt += _geometry.tangent.xyz * scn::mat3(jointMatrix) * weight;
#endif
}
_geometry.position.xyz = pos;
#if defined(USE_NORMAL) && defined(HAS_NORMAL)
_geometry.normal = nrm;
#endif
#if defined(USE_TANGENT) || defined(USE_BITANGENT)
_geometry.tangent.xyz = tgt;
#endif
}
#else // USE_TESSELLATION
#if !defined(USE_OPENSUBDIV)
{
float3 pos[3] = {0.f, 0.f, 0.f};
#if defined(USE_NORMAL) && defined(HAS_NORMAL)
float3 nrm[3] = {0.f, 0.f, 0.f};
#endif
#if defined(USE_TANGENT) || defined(USE_BITANGENT)
float3 tgt[3] = {0.f, 0.f, 0.f};
#endif
for (int controlPointIndex = 0; controlPointIndex < 3; ++controlPointIndex) {
for (int i = 0; i < MAX_BONE_INFLUENCES; ++i) {
#if MAX_BONE_INFLUENCES == 1
float weight = 1.f;
#else
float weight = in.controlPoints[controlPointIndex].skinningWeights[i];
if (weight <= 0.f)
continue;
#endif
int idx = int(in.controlPoints[controlPointIndex].skinningJoints[i]) * 3;
float4x4 jointMatrix = float4x4(scn_node.skinningJointMatrices[idx],
scn_node.skinningJointMatrices[idx+1],
scn_node.skinningJointMatrices[idx+2],
float4(0., 0., 0., 1.));
pos[controlPointIndex] += (_geometry.position * jointMatrix).xyz * weight;
#if defined(USE_NORMAL) && defined(HAS_NORMAL)
nrm[controlPointIndex] += _geometry.normal * scn::mat3(jointMatrix) * weight;
#endif
#if defined(USE_TANGENT) || defined(USE_BITANGENT)
tgt[controlPointIndex] += _geometry.tangent.xyz * scn::mat3(jointMatrix) * weight;
#endif
}
}
_geometry.position.xyz = scn::barycentric_mix(pos[0], pos[1], pos[2], patchCoord);
#if defined(USE_NORMAL) && defined(HAS_NORMAL)
_geometry.normal = scn::barycentric_mix(nrm[0], nrm[1], nrm[2], patchCoord);
#endif
#if defined(USE_TANGENT) || defined(USE_BITANGENT)
_geometry.tangent.xyz = scn::barycentric_mix(tgt[0], tgt[1], tgt[2], patchCoord);
#endif
}
#endif // !defined(USE_OPENSUBDIV)
#endif // USE_TESSELLATION
#endif // USE_SKINNING
#ifdef USE_DISPLACEMENT_MAP
out.displacementTexcoord = _displacementTexcoord;
#endif
//
// MARK: Geometry shader modifier
//
#ifdef USE_GEOMETRY_MODIFIER
// DoGeometryModifier START
// DoGeometryModifier END
#endif
//
// MARK: Populating the `_surface` struct
//
// Transform the geometry elements in view space
#if defined(USE_POSITION) || (defined(USE_NORMAL) && defined(HAS_OR_GENERATES_NORMAL)) || defined(USE_TANGENT) || defined(USE_BITANGENT) || defined(USE_INSTANCING)
SCNShaderSurface _surface;
#endif
#if defined(USE_POSITION) || defined(USE_INSTANCING)
#ifdef USE_MULTIPLE_RENDERING
_surface.position = (scn_frame.viewTransform * (scn_node.modelTransform * _geometry.position)).xyz;
#else
_surface.position = (scn_node.modelViewTransform * _geometry.position).xyz;
#endif
#endif
#if defined(USE_NORMAL) && defined(HAS_OR_GENERATES_NORMAL)
#ifdef USE_MULTIPLE_RENDERING
#ifdef HINT_UNIFORM_SCALE
_surface.normal = (scn_frame.viewTransform * scn_node.modelTransform * float4(_geometry.normal,0.)).xyz;
#else
_surface.normal = normalize( (scn_frame.inverseTransposeViewTransform * scn_node.modelTransform * float4(_geometry.normal,0.)).xyz );
#endif
#else
float3x3 nrmTransform = scn::mat3(scn_node.modelViewTransform);
#ifdef HINT_UNIFORM_SCALE
_surface.normal = nrmTransform * _geometry.normal;
#else
float3 invScaleSquared = 1.f / float3(length_squared(nrmTransform[0]), length_squared(nrmTransform[1]), length_squared(nrmTransform[2]));
_surface.normal = normalize(nrmTransform * (_geometry.normal * invScaleSquared));
#endif
#endif
#endif
#if defined(USE_TANGENT) || defined(USE_BITANGENT)
#ifdef USE_MULTIPLE_RENDERING
_surface.tangent = normalize( (scn_frame.viewTransform * scn_node.modelTransform * float4(_geometry.tangent.xyz, 0.f)).xyz );
#else
_surface.tangent = normalize(scn::mat3(scn_node.modelViewTransform) * _geometry.tangent.xyz);
#endif
_surface.bitangent = _geometry.tangent.w * cross(_surface.tangent, _surface.normal); // no need to renormalize since tangent and normal should be orthogonal
// old code : _surface.bitangent = normalize(cross(_surface.normal,_surface.tangent));
#endif
//if USE_VIEW is 2 we may also need to set _surface.view. todo: make USE_VIEW a mask
#ifdef USE_VIEW
_surface.view = normalize(-_surface.position);
#endif
//
// MARK: Per-vertex lighting
//
#ifdef USE_PER_VERTEX_LIGHTING
// Lighting
SCNShaderLightingContribution _lightingContribution(_surface, out);
_lightingContribution.diffuse = 0.;
#ifdef USE_SPECULAR
_lightingContribution.specular = 0.;
_surface.shininess = scn_commonprofile.materialShininess;
#endif
out.diffuse = _lightingContribution.diffuse;
#ifdef USE_SPECULAR
out.specular = _lightingContribution.specular;
#endif
#endif
#if defined(USE_POSITION) && (USE_POSITION == 2)
out.position = _surface.position;
#endif
#if defined(USE_NORMAL) && (USE_NORMAL == 2) && defined(HAS_OR_GENERATES_NORMAL)
out.normal = _surface.normal;
#endif
#if defined(USE_TANGENT) && (USE_TANGENT == 2)
out.tangent = _surface.tangent;
#endif
#if defined(USE_BITANGENT) && (USE_BITANGENT == 2)
out.bitangent = _surface.bitangent;
#endif
#ifdef USE_VERTEX_COLOR
out.vertexColor = _geometry.color;
#endif
#if DEBUG_PIXEL
out.uv0 = in.texcoord0;
#endif
#ifdef USE_TEXCOORD
out.texcoord0 = _geometry.texcoords[0].xy;
#endif
//
// MARK: Determining the fragment position
//
#if defined(USE_POSITION) || defined(USE_INSTANCING)
#ifdef USE_MULTIPLE_RENDERING
out.fragmentPosition = scn_frame_slice.viewProjectionTransform * scn_node.modelTransform * _geometry.position;
#else
out.fragmentPosition = scn_frame.projectionTransform * float4(_surface.position, 1.);
#endif
#elif defined(USE_MODELVIEWPROJECTIONTRANSFORM) // this means that the geometry are still in model space : we can transform it directly to NDC space
#ifdef USE_MULTIPLE_RENDERING
out.fragmentPosition = scn_frame_slice.viewProjectionTransform * scn_node.modelTransform * _geometry.position;
#else
out.fragmentPosition = scn_node.modelViewProjectionTransform * _geometry.position;
#endif
#endif
#ifdef USE_NODE_OPACITY
out.nodeOpacity = scn_node.nodeOpacity;
#endif
#ifdef USE_POINT_RENDERING
float screenSize = _geometry.pointSize / out.fragmentPosition.w;
out.fragmentSize = clamp(screenSize, scn_pointSize.y, scn_pointSize.z);
#endif
#ifdef USE_MOTIONBLUR
float4 lastFrameFragmentPosition = scn_frame.lastFrameViewProjectionTransform * scn_node.lastFrameModelTransform * _geometry.position;
out.mv_fragment = out.fragmentPosition.xyw;
out.mv_lastFragment = lastFrameFragmentPosition.xyw;
#endif
#ifdef USE_OUTLINE
out.outlineHash = hash(scn_node.modelTransform[3].xy)+1.f/255.f;
#endif
return out;
}
//
// MARK: - Fragment shader function
//
struct SCNOutput
{
float4 color [[ color(0) ]];
#ifdef USE_COLOR1_OUTPUT
half4 color1 [[ color(1) ]];
#endif
#ifdef USE_NORMALS_OUTPUT
half4 normals [[ color(2) ]];
#endif
#ifdef USE_MOTIONBLUR
half4 motionblur [[ color(3) ]];
#endif
#ifdef USE_REFLECTANCE_ROUGHNESS_OUTPUT
half4 reflectanceRoughnessOutput [[ color(4) ]];
#endif
#ifdef USE_RADIANCE_OUTPUT
half4 radiance [[ color(5) ]];
#endif
};
fragment SCNOutput commonprofile_frag(commonprofile_io in [[ stage_in ]]
, constant commonprofile_uniforms& scn_commonprofile [[ buffer(0) ]]
#ifdef USE_MULTIPLE_RENDERING
, device SCNSceneBuffer* scn_frames [[ buffer(1) ]]
#else
, constant SCNSceneBuffer& scn_frame [[ buffer(1) ]]
#endif
, constant commonprofile_node& scn_node [[ buffer(2) ]]
#ifdef USE_PER_PIXEL_LIGHTING
, constant scn_light* scn_lights [[ buffer(3) ]]
, constant float4* u_shadowKernel
, texture2d_array<float> u_areaLightBakedDataTexture
#ifdef C3D_SUPPORT_CUBE_ARRAY
, texturecube_array<half> u_reflectionProbeTexture
#else
, texture2d_array<half> u_reflectionProbeTexture
#endif
, texture3d<ushort> u_clusterTexture
#ifdef C3D_USE_TEXTURE_FOR_LIGHT_INDICES
, texture1d<ushort> u_lightIndicesTexture
#else
, constant C3DLightIndexType* u_lightIndicesBuffer
#endif
#endif
#if USE_ARGUMENT_BUFFERS
#define u_emissionTexture scn_commonprofile.emissionTexture
#define u_emissionTextureSampler scn_commonprofile.emissionSampler
#define u_ambientTexture scn_commonprofile.ambientTexture
#define u_ambientTextureSampler scn_commonprofile.ambientSampler
#define u_diffuseTexture scn_commonprofile.diffuseTexture
#define u_diffuseTextureSampler scn_commonprofile.diffuseSampler
#define u_specularTexture scn_commonprofile.specularTexture
#define u_specularTextureSampler scn_commonprofile.specularSampler
#define u_reflectiveTexture scn_commonprofile.reflectiveTexture
#define u_reflectiveTextureSampler scn_commonprofile.reflectiveSampler
#define u_transparentTexture scn_commonprofile.transparentTexture
#define u_transparentTextureSampler scn_commonprofile.transparentSampler
#define u_multiplyTexture scn_commonprofile.multiplyTexture
#define u_multiplyTextureSampler scn_commonprofile.multiplySampler
#define u_normalTexture scn_commonprofile.normalTexture
#define u_normalTextureSampler scn_commonprofile.normalSampler
#define u_selfIlluminationTexture scn_commonprofile.selfIlluminationTexture
#define u_selfIlluminationTextureSampler scn_commonprofile.selfIlluminationSampler
#define u_metalnessTexture scn_commonprofile.metalnessTexture
#define u_metalnessTextureSampler scn_commonprofile.metalnessSampler
#define u_roughnessTexture scn_commonprofile.roughnessTexture
#define u_roughnessTextureSampler scn_commonprofile.roughnessSampler
#else
#ifdef USE_EMISSION_MAP
, texture2d<float> u_emissionTexture
, sampler u_emissionTextureSampler
#endif
#ifdef USE_AMBIENT_MAP
, texture2d<float> u_ambientTexture
, sampler u_ambientTextureSampler
#endif
#ifdef USE_DIFFUSE_MAP
, texture2d<float> u_diffuseTexture
, sampler u_diffuseTextureSampler
#endif
#ifdef USE_SPECULAR_MAP
, texture2d<float> u_specularTexture
, sampler u_specularTextureSampler
#endif
#ifdef USE_REFLECTIVE_MAP
, texture2d<float> u_reflectiveTexture
, sampler u_reflectiveTextureSampler
#elif defined(USE_REFLECTIVE_CUBEMAP)
, texturecube<float> u_reflectiveTexture
, sampler u_reflectiveTextureSampler
#endif
#ifdef USE_TRANSPARENT_MAP
, texture2d<float> u_transparentTexture
, sampler u_transparentTextureSampler
#endif
#ifdef USE_MULTIPLY_MAP
, texture2d<float> u_multiplyTexture
, sampler u_multiplyTextureSampler
#endif
#ifdef USE_NORMAL_MAP
, texture2d<float> u_normalTexture
, sampler u_normalTextureSampler
#endif
#ifdef USE_SELFILLUMINATION_MAP
, texture2d<float> u_selfIlluminationTexture
, sampler u_selfIlluminationTextureSampler
#endif
#ifdef USE_DISPLACEMENT_MAP
, texture2d<float> u_displacementTexture
, sampler u_displacementTextureSampler
#endif
#ifdef USE_PBR
#ifdef USE_METALNESS_MAP
, texture2d<float> u_metalnessTexture
, sampler u_metalnessTextureSampler
#endif
#ifdef USE_ROUGHNESS_MAP
, texture2d<float> u_roughnessTexture
, sampler u_roughnessTextureSampler
#endif
#ifdef USE_CLEARCOAT_MAP
, texture2d<float> u_clearCoatTexture
, sampler u_clearCoatTextureSampler
#endif
#ifdef USE_CLEARCOATROUGHNESS_MAP
, texture2d<float> u_clearCoatRoughnessTexture
, sampler u_clearCoatRoughnessTextureSampler
#endif
#ifdef USE_CLEARCOATNORMAL_MAP
, texture2d<float> u_clearCoatNormalTexture
, sampler u_clearCoatNormalTextureSampler
#endif
#endif // USE_PBR
#endif // USE_ARGUMENT_BUFFERS
#ifdef USE_PBR
, texturecube<float> u_radianceTexture
, texture2d<float> u_specularDFGTexture
#if !defined(USE_SELFILLUMINATION_MAP)
, texturecube<float> u_irradianceTexture
#endif
#endif // USE_PBR
#ifdef USE_SSAO
, texture2d<float> u_ssaoTexture
#endif
#ifdef USE_FRAGMENT_EXTRA_ARGUMENTS
#endif
#if defined(USE_DOUBLE_SIDED)
, bool isFrontFacing [[front_facing]]
#endif
#ifdef USE_POINT_RENDERING
, float2 pointCoord [[point_coord]]
#endif
)
{
#ifdef USE_MULTIPLE_RENDERING
device SCNSceneBuffer& scn_frame = scn_frames[0];
device SCNSceneBuffer& scn_frame_slice = scn_frames[in.sliceIndex];
#endif
SCNOutput _output;
//
// MARK: Populating the `_surface` struct
//
SCNShaderSurface _surface;
#ifdef USE_TEXCOORD
_surface.diffuseTexcoord = in.texcoord0;
#endif
_surface.ambientOcclusion = 1.f; // default to no AO
#ifdef USE_AMBIENT_MAP
#ifdef USE_AMBIENT_AS_AMBIENTOCCLUSION
_surface.ambientOcclusion = u_ambientTexture.sample(u_ambientTextureSampler, _surface.ambientTexcoord).r;
#ifdef USE_AMBIENT_INTENSITY
_surface.ambientOcclusion = saturate(mix(1.f, _surface.ambientOcclusion, scn_commonprofile.ambientIntensity));
#endif
#else // AMBIENT_MAP
_surface.ambient = u_ambientTexture.sample(u_ambientTextureSampler, _surface.ambientTexcoord);
#ifdef USE_AMBIENT_INTENSITY
_surface.ambient *= scn_commonprofile.ambientIntensity;
#endif
#endif // USE_AMBIENT_AS_AMBIENTOCCLUSION
#if defined(USE_AMBIENT_TEXTURE_COMPONENT)
_surface.ambient = colorFromMask(_surface.ambient, USE_AMBIENT_TEXTURE_COMPONENT).r;
#endif
#elif defined(USE_AMBIENT_COLOR)
_surface.ambient = scn_commonprofile.ambientColor;
#elif defined(USE_AMBIENT)
_surface.ambient = float4(0.);
#endif
#if defined(USE_AMBIENT) && defined(USE_VERTEX_COLOR)
_surface.ambient *= in.vertexColor;
#endif
#if defined(USE_SSAO)
_surface.ambientOcclusion *= u_ssaoTexture.sample( sampler(filter::linear), in.fragmentPosition.xy * scn_frame.inverseResolution.xy ).x;
#endif
#ifdef USE_DIFFUSE_MAP
_surface.diffuse = u_diffuseTexture.sample(u_diffuseTextureSampler, _surface.diffuseTexcoord);
#if defined(USE_DIFFUSE_TEXTURE_COMPONENT)
_surface.diffuse = colorFromMask(_surface.diffuse, USE_DIFFUSE_TEXTURE_COMPONENT);
#endif
#ifdef USE_DIFFUSE_INTENSITY
_surface.diffuse.rgb *= scn_commonprofile.diffuseIntensity;
#endif
#elif defined(USE_DIFFUSE_COLOR)
_surface.diffuse = scn_commonprofile.diffuseColor;
#else
_surface.diffuse = float4(0.f,0.f,0.f,1.f);
#endif
#if defined(USE_DIFFUSE) && defined(USE_VERTEX_COLOR)
_surface.diffuse.rgb *= in.vertexColor.rgb;
_surface.diffuse *= in.vertexColor.a; // vertex color are not premultiplied to allow interpolation
#endif
#ifdef USE_SPECULAR_MAP
_surface.specular = u_specularTexture.sample(u_specularTextureSampler, _surface.specularTexcoord);
#if defined(USE_SPECULAR_TEXTURE_COMPONENT)
_surface.specular = colorFromMask(_surface.specular, USE_SPECULAR_TEXTURE_COMPONENT);
#endif
#ifdef USE_SPECULAR_INTENSITY
_surface.specular *= scn_commonprofile.specularIntensity;
#endif
#elif defined(USE_SPECULAR_COLOR)
_surface.specular = scn_commonprofile.specularColor;
#elif defined(USE_SPECULAR)
_surface.specular = float4(0.f);
#endif
#ifdef USE_CLEARCOAT_MAP
_surface.clearCoat = u_clearCoatTexture.sample(u_clearCoatTextureSampler, _surface.clearCoatTexcoord).r;
#if defined(USE_CLEARCOAT_TEXTURE_COMPONENT)
_surface.clearCoat = colorFromMask(_surface.clearCoat, USE_CLEARCOAT_TEXTURE_COMPONENT).r;
#endif
#ifdef USE_CLEARCOAT_INTENSITY
_surface.clearCoat *= scn_commonprofile.clearCoatIntensity;
#endif
#elif defined(USE_CLEARCOAT_COLOR)
_surface.clearCoat = scn_commonprofile.clearCoat;
#elif defined(USE_CLEARCOAT)
_surface.clearCoat = 0.f;
#endif
#ifdef USE_CLEARCOATROUGHNESS_MAP
#if defined(USE_CLEARCOATROUGHNESS_TEXTURE_COMPONENT)
_surface.clearCoatRoughness = colorFromMask(u_clearCoatRoughnessTexture.sample(u_clearCoatRoughnessTextureSampler, _surface.clearCoatRoughnessTexcoord), USE_CLEARCOATROUGHNESS_TEXTURE_COMPONENT).r;
#else
_surface.clearCoatRoughness = u_clearCoatRoughnessTexture.sample(u_clearCoatRoughnessTextureSampler, _surface.clearCoatRoughnessTexcoord).r;
#endif
#ifdef USE_CLEARCOATROUGHNESS_INTENSITY
_surface.clearCoatRoughness *= scn_commonprofile.clearCoatRoughnessIntensity;
#endif
#elif defined(USE_CLEARCOATROUGHNESS_COLOR)
_surface.clearCoatRoughness = scn_commonprofile.clearCoatRoughness;
#else
_surface.clearCoatRoughness = 0.03f;
#endif
#ifdef USE_EMISSION_MAP
_surface.emission = u_emissionTexture.sample(u_emissionTextureSampler, _surface.emissionTexcoord);
#if defined(USE_EMISSION_TEXTURE_COMPONENT)
_surface.emission = colorFromMask(_surface.emission, USE_EMISSION_TEXTURE_COMPONENT);
#endif
#ifdef USE_EMISSION_INTENSITY
_surface.emission *= scn_commonprofile.emissionIntensity;
#endif
#elif defined(USE_EMISSION_COLOR)
_surface.emission = scn_commonprofile.emissionColor;
#elif defined(USE_EMISSION)
_surface.emission = float4(0.);
#endif
#ifdef USE_SELFILLUMINATION_MAP
_surface.selfIllumination = u_selfIlluminationTexture.sample(u_selfIlluminationTextureSampler, _surface.selfIlluminationTexcoord);
#if defined(USE_SELFILLUMINATION_TEXTURE_COMPONENT)
_surface.selfIllumination = colorFromMask(_surface.selfIllumination, USE_SELFILLUMINATION_TEXTURE_COMPONENT);
#endif
#ifdef USE_SELFILLUMINATION_INTENSITY
_surface.selfIllumination *= scn_commonprofile.selfIlluminationIntensity;
#endif
#elif defined(USE_SELFILLUMINATION_COLOR)
_surface.selfIllumination = scn_commonprofile.selfIlluminationColor;
#elif defined(USE_SELFILLUMINATION)
_surface.selfIllumination = float4(0.);
#endif
#ifdef USE_MULTIPLY_MAP
_surface.multiply = u_multiplyTexture.sample(u_multiplyTextureSampler, _surface.multiplyTexcoord);
#if defined(USE_MULTIPLY_TEXTURE_COMPONENT)
_surface.multiply = colorFromMask(_surface.multiply, USE_MULTIPLY_TEXTURE_COMPONENT);
#endif
#ifdef USE_MULTIPLY_INTENSITY
_surface.multiply = mix(float4(1.), _surface.multiply, scn_commonprofile.multiplyIntensity);
#endif
#elif defined(USE_MULTIPLY_COLOR)
_surface.multiply = scn_commonprofile.multiplyColor;
#elif defined(USE_MULTIPLY)
_surface.multiply = float4(1.);
#endif
#ifdef USE_TRANSPARENT_MAP
_surface.transparent = u_transparentTexture.sample(u_transparentTextureSampler, _surface.transparentTexcoord);
#if defined(USE_TRANSPARENT_TEXTURE_COMPONENT)
_surface.transparent = colorFromMask(_surface.transparent, USE_TRANSPARENT_TEXTURE_COMPONENT);
#endif
#ifdef USE_TRANSPARENT_INTENSITY
_surface.transparent *= scn_commonprofile.transparentIntensity;
#endif
#elif defined(USE_TRANSPARENT_COLOR)
_surface.transparent = scn_commonprofile.transparentColor;
#elif defined(USE_TRANSPARENT)
_surface.transparent = float4(1.f);
#endif
#ifdef USE_METALNESS_MAP
#if defined(USE_METALNESS_TEXTURE_COMPONENT)
_surface.metalness = colorFromMask(u_metalnessTexture.sample(u_metalnessTextureSampler, _surface.metalnessTexcoord), USE_METALNESS_TEXTURE_COMPONENT).r;
#else
_surface.metalness = u_metalnessTexture.sample(u_metalnessTextureSampler, _surface.metalnessTexcoord).r;
#endif
#ifdef USE_METALNESS_INTENSITY
_surface.metalness *= scn_commonprofile.metalnessIntensity;
#endif
#elif defined(USE_METALNESS_COLOR)
_surface.metalness = scn_commonprofile.metalness;
#else
_surface.metalness = 0.f;
#endif
#ifdef USE_ROUGHNESS_MAP
#if defined(USE_ROUGHNESS_TEXTURE_COMPONENT)
_surface.roughness = colorFromMask(u_roughnessTexture.sample(u_roughnessTextureSampler, _surface.roughnessTexcoord), USE_ROUGHNESS_TEXTURE_COMPONENT).r;
#else
_surface.roughness = u_roughnessTexture.sample(u_roughnessTextureSampler, _surface.roughnessTexcoord).r;
#endif
#ifdef USE_ROUGHNESS_INTENSITY
_surface.roughness *= scn_commonprofile.roughnessIntensity;
#endif
#elif defined(USE_ROUGHNESS_COLOR)
_surface.roughness = scn_commonprofile.roughness;
#else
_surface.roughness = 0.f;
#endif
#if (defined USE_POSITION) && (USE_POSITION == 2)
_surface.position = in.position;
#endif
#if (defined USE_NORMAL) && (USE_NORMAL == 2)
#if defined(HAS_NORMAL) || defined(USE_OPENSUBDIV)
#ifdef USE_DOUBLE_SIDED
_surface.geometryNormal = normalize(in.normal.xyz) * (isFrontFacing ? 1.f : -1.f );
#else
_surface.geometryNormal = normalize(in.normal.xyz);
#endif
#else // need to generate the normal from the derivatives
_surface.geometryNormal = normalize( cross(dfdy( _surface.position ), dfdx( _surface.position ) ));
#endif
_surface.normal = _surface.geometryNormal;
_surface.clearCoatNormal = _surface.geometryNormal;
#endif
#if defined(USE_TANGENT) && (USE_TANGENT == 2)
_surface.tangent = in.tangent;
#endif
#if defined(USE_BITANGENT) && (USE_BITANGENT == 2)
_surface.bitangent = in.bitangent;
#endif
#if (defined USE_VIEW) && (USE_VIEW == 2)
_surface.view = normalize(-in.position);
#endif
#if defined(USE_NORMAL_MAP)
{
float3x3 ts2vs = float3x3(_surface.tangent, _surface.bitangent, _surface.normal);
#ifdef USE_NORMAL_MAP
#if defined(USE_NORMAL_TEXTURE_COMPONENT)
_surface._normalTS.xy = colorFromMask(u_normalTexture.sample(u_normalTextureSampler, _surface.normalTexcoord), USE_NORMAL_TEXTURE_COMPONENT).rg * 2.f - 1.f;
_surface._normalTS.z = sqrt(1.f - saturate(length_squared(_surface._normalTS.xy)));
#else
_surface._normalTS = u_normalTexture.sample(u_normalTextureSampler, _surface.normalTexcoord).rgb;
_surface._normalTS = _surface._normalTS * 2.f - 1.f;
#endif
#ifdef USE_NORMAL_INTENSITY
_surface._normalTS = mix(float3(0.f, 0.f, 1.f), _surface._normalTS, scn_commonprofile.normalIntensity);
#endif
#else
_surface._normalTS = float3(0.f, 0.f, 1.f);
#endif
_surface.normal.rgb = normalize(ts2vs * _surface._normalTS.xyz );
}
#else
_surface._normalTS = float3(0.f, 0.f, 1.f);
#endif
#if defined(USE_CLEARCOATNORMAL_MAP)
{
//when there is no normal map, there seems to be no tangent and it breaks the clearCoatNormal...
float3x3 ts2vs = float3x3(_surface.tangent, _surface.bitangent, _surface.geometryNormal);
#ifdef USE_CLEARCOATNORMAL_MAP
#if defined(USE_CLEARCOATNORMAL_TEXTURE_COMPONENT)
_surface._clearCoatNormalTS.xy = colorFromMask(u_clearCoatNormalTexture.sample(u_clearCoatnormalTextureSampler, _surface.clearCoatNormalTexcoord), USE_CLEARCOATNORMAL_TEXTURE_COMPONENT).rg * 2.f - 1.f;
_surface._clearCoatNormalTS.z = sqrt(1.f - saturate(length_squared(_surface._clearCoatNormalTS.xy)));
#else
_surface._clearCoatNormalTS = u_clearCoatNormalTexture.sample(u_clearCoatNormalTextureSampler, _surface.clearCoatNormalTexcoord).rgb;
_surface._clearCoatNormalTS = _surface._clearCoatNormalTS * 2.f - 1.f;
#endif
#ifdef USE_CLEARCOATNORMAL_INTENSITY
_surface._clearCoatNormalTS = mix(float3(0.f, 0.f, 1.f), _surface._clearCoatNormalTS, scn_commonprofile.clearCoatNormalIntensity);
#endif
#else
_surface._clearCoatNormalTS = float3(0.f, 0.f, 1.f);
#endif
_surface.clearCoatNormal.rgb = normalize(ts2vs * _surface._clearCoatNormalTS.xyz );
}
#else
_surface._clearCoatNormalTS = float3(0.f, 0.f, 1.f);
#endif
#ifdef USE_REFLECTIVE_MAP
float3 refl = reflect( -_surface.view, _surface.normal );
float m = 2.f * sqrt( refl.x*refl.x + refl.y*refl.y + (refl.z+1.f)*(refl.z+1.f));
_surface.reflective = u_reflectiveTexture.sample(u_reflectiveTextureSampler, float2(float2(refl.x,-refl.y) / m) + 0.5f);
#if defined(USE_REFLECTIVE_TEXTURE_COMPONENT)
_surface.reflective = colorFromMask(_surface.reflective, USE_REFLECTIVE_TEXTURE_COMPONENT).r;
#endif
#ifdef USE_REFLECTIVE_INTENSITY
_surface.reflective *= scn_commonprofile.reflectiveIntensity;
#endif
#elif defined(USE_REFLECTIVE_CUBEMAP)
float3 refl = reflect( _surface.position, _surface.normal );
_surface.reflective = u_reflectiveTexture.sample(u_reflectiveTextureSampler, scn::mat4_mult_float3(scn_frame.viewToCubeTransform, refl)); // sample the cube map in world space
#ifdef USE_REFLECTIVE_INTENSITY
_surface.reflective *= scn_commonprofile.reflectiveIntensity;
#endif
#elif defined(USE_REFLECTIVE_COLOR)
_surface.reflective = scn_commonprofile.reflectiveColor;
#elif defined(USE_REFLECTIVE)
_surface.reflective = float4(0.);
#endif
#ifdef USE_FRESNEL
_surface.fresnel = scn_commonprofile.fresnel.x + scn_commonprofile.fresnel.y * pow(1.f - saturate(dot(_surface.view, _surface.normal)), scn_commonprofile.fresnel.z);
_surface.reflective *= _surface.fresnel;
#endif
#ifdef USE_SHININESS
_surface.shininess = scn_commonprofile.materialShininess;
#endif
//
// MARK: Surface shader modifier
//
#ifdef USE_SURFACE_MODIFIER
// DoSurfaceModifier START
// DoSurfaceModifier END
#endif
//
// MARK: Lighting
//
SCNShaderLightingContribution _lightingContribution(_surface, in);
#ifdef USE_LIGHT_MODIFIER
#endif
#ifdef USE_AMBIENT_LIGHTING
_lightingContribution.ambient = scn_frame.ambientLightingColor.rgb;
#endif
#ifdef USE_LIGHTING
#ifdef USE_PER_PIXEL_LIGHTING
#ifdef USE_CLUSTERED_LIGHTING
uint3 clusterIndex;
clusterIndex.xy = uint2(in.fragmentPosition.xy * scn_frame.clusterScale.xy); // TODO Multiple rendering
clusterIndex.z = in.position.z * scn_frame.clusterScale.z + scn_frame.clusterScale.w; // scale/bias
// x:offset y:spot<<8|omni z:area?|probe w:????
ushort4 cluster_offset_count = u_clusterTexture.read(clusterIndex);
int lid = cluster_offset_count.x;
#endif
#ifdef USE_PBR
_lightingContribution.prepareForPBR(u_specularDFGTexture, scn_commonprofile.selfIlluminationOcclusion);
#ifdef USE_CLEARCOAT
_lightingContribution.prepareForPBRClearCoat(u_specularDFGTexture);
#endif
// Irradiance
#ifdef USE_SELFILLUMINATION
_lightingContribution.add_irradiance_from_selfIllum();
#else
#ifdef USE_PROBES_LIGHTING // Irradiance SH
_lightingContribution.add_global_irradiance_from_sh(scn_frame.viewToCubeTransform, scn_node.shCoefficients);
#else
_lightingContribution.add_global_irradiance_probe(u_irradianceTexture, scn_frame.viewToCubeTransform, scn_frame.environmentIntensity);
#endif
#endif
// Radiance
#ifndef DISABLE_SPECULAR
#ifdef C3D_USE_REFLECTION_PROBES
int probe_count = (cluster_offset_count.z & 0xff);
for (int i = 0 ; i < probe_count; ++i, ++lid) {
_lightingContribution.add_local_probe(scn_lights[LightIndex(lid)], u_reflectionProbeTexture);
}
#if PROBES_NORMALIZATION
#if PROBES_OUTER_BLENDING
_lightingContribution.specular += _lightingContribution.probesWeightedSum.rgb / max(1.f, _lightingContribution.probesWeightedSum.a);
#else
_lightingContribution.specular += _lightingContribution.probesWeightedSum.rgb / _lightingContribution.probesWeightedSum.a;
#endif
float globalFactor = saturate(1.f - _lightingContribution.probesWeightedSum.a);
#else
float globalFactor = _lightingContribution.probeRadianceRemainingFactor;
#endif
_lightingContribution.add_global_probe(scn_frame.viewToCubeTransform, globalFactor * scn_frame.environmentIntensity,
u_reflectionProbeTexture);
#else // Global Probe
_lightingContribution.add_global_probe(u_radianceTexture, scn_frame.viewToCubeTransform, scn_frame.environmentIntensity);
#ifdef USE_CLEARCOAT
_lightingContribution.add_global_probeClearCoat(u_radianceTexture, scn_frame.viewToCubeTransform, scn_frame.environmentIntensity);
#endif
#endif // C3D_USE_REFLECTION_PROBES
#endif // DISABLE_SPECULAR
#endif // USE_PBR
#if DEBUG_PIXEL
switch (DEBUG_PIXEL) {
case 1: _output.color = float4(_surface.normal * 0.5f + 0.5f, 1.f); break;
case 2: _output.color = float4(_surface.geometryNormal * 0.5f + 0.5f, 1.f); break;
case 3: _output.color = float4(_surface.tangent * 0.5f + 0.5f, 1.f); break;
case 4: _output.color = float4(in.uv0, 0.f, 1.f); break;
case 5: _output.color = float4(_surface.diffuse.rgb, 1.f); break;
case 6: _output.color = float4(float3(_surface.roughness), 1.f); break;
case 7: _output.color = float4(float3(_surface.metalness), 1.f); break;
case 8: _output.color = float4(float3(_surface.ambientOcclusion), 1.f); break;
default: break;
}
return _output;
#endif
_lightingContribution.add_directional(scn_lights[0]);
#ifdef USE_CLUSTERED_LIGHTING
// Omni lights
int omni_count = cluster_offset_count.y & 0xff;
for (int i = 0 ; i < omni_count; ++i, ++lid) {
_lightingContribution.add_local_omni(scn_lights[LightIndex(lid)]);
}
// Spot lights
int spot_count = (cluster_offset_count.y >> 8);
for (int i = 0 ; i < spot_count; ++i, ++lid) {
_lightingContribution.add_local_spot(scn_lights[LightIndex(lid)]);
}
#endif
#else // USE_PER_PIXEL_LIGHTING
_lightingContribution.diffuse = in.diffuse;
#ifdef USE_SPECULAR
_lightingContribution.specular = in.specular;
#endif
#endif // USE_PER_PIXEL_LIGHTING
#ifdef AVOID_OVERLIGHTING
_lightingContribution.diffuse = saturate(_lightingContribution.diffuse);
#ifdef USE_SPECULAR
_lightingContribution.specular = saturate(_lightingContribution.specular);
#endif // USE_SPECULAR
#endif // AVOID_OVERLIGHTING
#else // USE_LIGHTING
_lightingContribution.diffuse = float3(1.);
#endif // USE_LIGHTING
//
// MARK: Populating the `_output` struct
//
#ifdef USE_PBR
{ // combine IBL + lighting
float3 albedo = _surface.diffuse.rgb;
float3 diffuseAlbedo = mix(albedo, float3(0.0), _surface.metalness);
// ambient
float3 color = (_lightingContribution.ambient * _surface.ambientOcclusion) * albedo;
color += _lightingContribution.diffuse * diffuseAlbedo;
#ifndef DISABLE_SPECULAR
color += _lightingContribution.specular;
#endif
#ifdef USE_EMISSION
color += _surface.emission.rgb;
#endif
#ifdef USE_MULTIPLY
color *= _surface.multiply.rgb;
#endif
#ifdef USE_MODULATE
color *= _lightingContribution.modulate;
#endif
_output.color.rgb = color;
}
#else // USE_PBR
#ifdef USE_SHADOWONLY
_output.color.rgb = float3(0.0);
_output.color.a = 1. - _lightingContribution.shadowFactor;
#else
_output.color.rgb = illuminate(_surface, _lightingContribution);
#endif
#endif
#ifndef USE_SHADOWONLY
_output.color.a = _surface.diffuse.a;
#endif
#ifdef USE_FOG
float fogFactor = pow(clamp(length(_surface.position.xyz) * scn_frame.fogParameters.x + scn_frame.fogParameters.y, 0., scn_frame.fogColor.a), scn_frame.fogParameters.z);
_output.color.rgb = mix(_output.color.rgb, scn_frame.fogColor.rgb * _output.color.a, fogFactor);
#endif
#ifndef DIFFUSE_PREMULTIPLIED
_output.color.rgb *= _surface.diffuse.a;
#endif
//
// MARK: Opacity and transparency
//
#ifdef USE_SHADOWONLY
float transparencyFactor = 1.0;
#ifdef USE_NODE_OPACITY
transparencyFactor *= in.nodeOpacity;
#endif
_output.color.a *= transparencyFactor; // for AR compositing: alpha should be at 0 where there's no shadow
#else
#ifdef USE_TRANSPARENT // Either a map or a color
#ifdef USE_TRANSPARENCY
_surface.transparent *= scn_commonprofile.transparency;
#endif
#ifdef USE_TRANSPARENCY_RGBZERO
#ifdef USE_NODE_OPACITY
_output.color *= in.nodeOpacity;
#endif
// compute luminance
_surface.transparent.a = (_surface.transparent.r * 0.212671f) + (_surface.transparent.g * 0.715160f) + (_surface.transparent.b * 0.072169f);
_output.color *= (float4(1.f) - _surface.transparent);
#else // ALPHA_ONE
#ifdef USE_NODE_OPACITY
_output.color *= (in.nodeOpacity * _surface.transparent.a);
#else
_output.color *= _surface.transparent.a;
#endif
#endif
#else
#ifdef USE_TRANSPARENCY // TRANSPARENCY without TRANSPARENT slot (nodeOpacity + diffuse.a)
#ifdef USE_NODE_OPACITY
_output.color *= (in.nodeOpacity * scn_commonprofile.transparency);
#else
_output.color *= scn_commonprofile.transparency;
#endif // NODE_OPACITY
#endif
#endif
#endif // USE_SHADOWONLY
//
// MARK: Fragment shader modifier
//
#ifdef USE_FRAGMENT_MODIFIER
// DoFragmentModifier START
// DoFragmentModifier END
#endif
#if defined(USE_CLUSTERED_LIGHTING) && defined(DEBUG_CLUSTER_TILE)
_output.color.rgb = mix(_output.color.rgb, float3(scn::debugColorForCount(clusterIndex.z).xyz), 0.1f);
_output.color.rgb = mix(_output.color.rgb, float3(clusterIndex.x & 0x1 ^ clusterIndex.y & 0x1).xyz, 0.01f);
#endif
#ifdef DISABLE_LINEAR_RENDERING
_output.color.rgb = scn::linear_to_srgb(_output.color.rgb);
#endif
#ifdef USE_DISCARD
if (_output.color.a == 0.) // we could set a different limit here
discard_fragment();
#endif
#ifdef USE_POINT_RENDERING
if ((dfdx(pointCoord.x) < 0.5f) && (length_squared(pointCoord * 2.f - 1.f) > 1.f)) {
discard_fragment();
}
#endif
#ifdef USE_OUTLINE
_output.color.rgb = in.outlineHash;
#endif
// MRT
#ifdef USE_MOTIONBLUR
#ifdef USE_MULTIPLE_RENDERING
_output.motionblur.xy = (half2(((in.fragmentPosition.xy-scn_frame_slice.viewportSize.zw)/scn_frame_slice.viewportSize.xy)*2.f-1.f) - half2((in.velocity.xy) / in.velocity.z)) * scn_frame_slice.motionBlurIntensity;
#else
_output.motionblur.xy = half2((in.mv_fragment.xy / in.mv_fragment.z) - (in.mv_lastFragment.xy / in.mv_lastFragment.z))*half2(1.,-1.) * scn_frame.motionBlurIntensity;
#endif
_output.motionblur.z = length(_output.motionblur.xy);
_output.motionblur.w = half(-_surface.position.z);
#endif
#ifdef USE_NORMALS_OUTPUT
_output.normals = half4( half3(_surface.normal.xyz), half(_surface.roughness) );
#endif
#ifdef USE_RADIANCE_OUTPUT
_output.radiance.rgb = half3(_lightingContribution.specular.rgb);
#endif
#ifdef USE_REFLECTANCE_ROUGHNESS_OUTPUT
#ifdef USE_PBR
_output.reflectanceRoughnessOutput = half4( half3(_lightingContribution.probeReflectance), half(_surface.roughness) );
#else // SSR on non pbr material is not supported
_output.reflectanceRoughnessOutput = half4( 0.h );
#endif
#endif
_output.color = min(_output.color, float4(160.));
return _output;
}
@BlueFalconHD
Copy link

I extracted this from some XPC communications, it seems pretty similar:

////////////////////////////////////////////////
// CommonProfile Shader v2

#pragma mark - Includes begin

#import <metal_stdlib>

using namespace metal;

#ifndef __VFXMetalDefines__
#define __VFXMetalDefines__

enum {
    VFXVertexSemanticPosition,
    VFXVertexSemanticNormal,
    VFXVertexSemanticTangent,
    VFXVertexSemanticColor,
    VFXVertexSemanticBoneIndices,
    VFXVertexSemanticBoneWeights,
    VFXVertexSemanticTexcoord0,
    VFXVertexSemanticTexcoord1,
    VFXVertexSemanticTexcoord2,
    VFXVertexSemanticTexcoord3,
    VFXVertexSemanticTexcoord4,
    VFXVertexSemanticTexcoord5,
    VFXVertexSemanticTexcoord6,
    VFXVertexSemanticTexcoord7
};

enum {
    VFXGBufferAttachmentEmission,
    VFXGBufferAttachmentNormal,
    VFXGBufferAttachmentRoughnessMetalness,
    VFXGBufferAttachmentAlbedo,
    VFXGBufferAttachmentVelocity,
    VFXGBufferAttachmentClearCoat,
    VFXGBufferAttachmentSubsurface,
    VFXGBufferAttachmentTransmission
};



struct VFXWorldBuffer {
    float4x4    viewTransform;
    float4x4    inverseViewTransform; 
    float4x4    projectionTransform;
    float4x4    viewProjectionTransform;
    float4x4    viewToCubeTransform; 
    float4x4    lastFrameViewProjectionTransform;
    float4      ambientLightingColor;
    float4		fogColor;
    float3		fogParameters; 
    float2      inverseResolution;
    float       time;
    float       timeFract;
    float       deltaTime;
    float       motionBlurIntensity;

    float       environmentIntensity;
    float4x4    environmentTransform;
    float4x4    inverseProjectionTransform;
    float4x4    inverseViewProjectionTransform;

    float2      nearFar; 
    float4      viewportSize; 

    float4x4    inverseTransposeViewTransform;

    
    float4      clusterScale; 
    
    uint32_t    frame;
};















#endif 






#include <TargetConditionals.h>


#if TARGET_OS_OSX || TARGET_OS_MACCATALYST
    
    
    #define SHOULD_CHECK_IF_OUTSIDE_TEXTURE  0
    #define RETURN_IF_OUTSIDE_TEXTURE(dst)
    #define RETURN_IF_OUTSIDE_TEXTURE3D(dst)
#else
    #define SHOULD_CHECK_IF_OUTSIDE_TEXTURE  1
    #define RETURN_IF_OUTSIDE_TEXTURE(dst)   if ((index.x >= dst.get_width()) || (index.y >= dst.get_height())) return;
    #define RETURN_IF_OUTSIDE_TEXTURE3D(dst) if ((index.x >= dst.get_width()) || (index.y >= dst.get_height()) || (index.z >= dst.get_depth())) return;
#endif

#if __METAL_VERSION__ >= 220
    #define SUPPORTS_LAYERED_RENDERING           1
    #define SUPPORTS_MULTIPLE_VIEWPORT_RENDERING 1
#else
    #if TARGET_OS_OSX || TARGET_OS_MACCATALYST
        #define SUPPORTS_LAYERED_RENDERING           (__METAL_VERSION__ >= 200)
        #define SUPPORTS_MULTIPLE_VIEWPORT_RENDERING (__METAL_VERSION__ >= 200)
    #else
        #define SUPPORTS_LAYERED_RENDERING           (__METAL_VERSION__ >= 210)
        #define SUPPORTS_MULTIPLE_VIEWPORT_RENDERING (__METAL_VERSION__ >= 210)
    #endif
#endif


#define GOLDEN_RATIO 1.61803398875
#define GOLDEN_RATIO_H 1.61803398875h
#define GOLDEN_ANGLE 2.399963229728
#define GOLDEN_ANGLE_H 2.399963229728h




namespace vfx {
    
    
    static inline float4 reduce_op(float4 d0, float4 d1)
    {
        d0.x = min(d0.x, d1.x);
        d0.y = max(d0.y, d1.y);
        d0.z += d1.z;
        d0.w += d1.w;
        return d0;
    }
    
    inline float vector_reduce_min(float4 v)
    {
        float2 min_lh = min(v.xy, v.zw);
        return min(min_lh.x, min_lh.y);
    }
    
    inline float vector_reduce_max(float4 v)
    {
        float2 max_lh = max(v.xy, v.zw);
        return max(max_lh.x, max_lh.y);
    }
    
    inline int vector_reduce_add(int4 v)
    {
        int2 add_lh = v.xy + v.zw;
        return add_lh.x + add_lh.y;
    }
    
    inline float3x3 mat3(float4x4 mat4)
    {
        return float3x3(mat4[0].xyz, mat4[1].xyz, mat4[2].xyz);
    }
    
    inline float3 mat4_mult_float3_normalized(float4x4 matrix, float3 src)
    {
        float3 dst  =  src.xxx * matrix[0].xyz;
        dst         += src.yyy * matrix[1].xyz;
        dst         += src.zzz * matrix[2].xyz;
        return normalize(dst);
    }
    
    inline float3 mat4_mult_float3(float4x4 matrix, float3 src)
    {
        float3 dst  =  src.xxx * matrix[0].xyz;
        dst         += src.yyy * matrix[1].xyz;
        dst         += src.zzz * matrix[2].xyz;
        return dst;
    }

    inline float3 matrix_rotate(float4x4 mat, float3 dir)
    {
        return  dir.xxx * mat[0].xyz +
                dir.yyy * mat[1].xyz +
                dir.zzz * mat[2].xyz;
    }

    inline float4 matrix_transform(float4x4 mat, float3 pos)
    {
        return  pos.xxxx * mat[0] +
                pos.yyyy * mat[1] +
                pos.zzzz * mat[2] +
                           mat[3];
    }

    inline float3 quaternion_rotate_vector(float4 q, float3 v)
    {
        float3 t = 2.f * cross(q.xyz, v);
        return v + q.w * t + cross(q.xyz, t);
    }

    
    template <class T>
    inline vec<T, 3> robust_normalize(vec<T, 3> v)
    {
        vec<T, 3> zero = 0.;
        return all(v == zero) ? zero : normalize(v);
    }

    template <class T>
    inline void generate_basis(vec<T, 3> inR, thread vec<T, 3> *outS, thread vec<T, 3> *outT)
    {
        
        T x  = -inR.x;
        T y  = inR.y;
        T z  = inR.z;
        T sz = copysign(T(1.), z);
        T a  = y / (abs(z) + T(1.));
        T b  = y * a;
        T c  = x * a;
        *outS = (vec<T, 3>){ z + sz * b,  sz * c,       x       };
        *outT = (vec<T, 3>){ c,           T(1.) - b,    -sz * y };
    }
    
    
    
    inline float3 blend_add(float3 base, float3 blend)
    {
        return min(base + blend, 1.0);
    }
    
    inline float3 blend_lighten(float3 base, float3 blend)
    {
        return max(blend, base);
    }
    
    inline float3 blend_screen(float3 base, float3 blend)
    {
        return (1.0 - ((1.0 - base) * (1.0 - blend)));
    }

    
    
    inline half sq(half f) {
        return f * f;
    }

    inline float sq(float f) {
        return f * f;
    }
    
    inline float2 sincos(float angle) {
        float cs;
        float sn = ::sincos(angle, cs);
        return float2(sn, cs);
    }
    
    
    inline float acos_fast(float f) {
        float x = abs(f);
        float res = -0.156583f * x + M_PI_2_F;
        res *= sqrt(1.0f - x);
        return (f >= 0.f) ? res : M_PI_F - res;
    }

    inline float asin_fast(float f)
    {
        return M_PI_2_F - acos_fast(f);
    }

    
    inline float atan_fast(float inX)
    {
        float  x = inX;
        return x*(-0.1784f * abs(x) - 0.0663f * x * x + 1.0301f);
    }
    
    inline float atan2_fast(float y, float x)
    {
        float sx = x > 0.f ? -1.f : 1.f;
        float abs_y = abs(y) + 1e-10f; 
        float r = (x + abs_y*sx) / (abs_y - x*sx);
        float angle = sx * M_PI_4_F + M_PI_2_F;
        angle      += (0.1963f * r * r - 0.9817f) * r;
        return y > 0.f ? angle : -angle;
    }
    
    
    template <class T>
    inline vec<T, 3> cartesian_from_spherical(vec<T, 2> uv)
    {
        
        
        T cos_phi;
        T phi = uv.x * 2.0f * M_PI_F;
        T sin_phi = ::sincos(phi, cos_phi);
        
        T cos_theta;
        T theta     = uv.y * M_PI_F;
        T sin_theta = ::sincos(theta, cos_theta);

        return vec<T, 3>(cos_phi * sin_theta,
                         cos_theta,
                         -sin_phi * sin_theta);
    }

    inline float2 spherical_from_cartesian(float3 dir)
    {
        return float2( atan2(-dir.z, dir.x) * (0.5f * M_1_PI_F), acos(dir.y) * M_1_PI_F);
    }

    inline half2 spherical_from_cartesian(half3 dir)
    {
        return half2(atan2(-dir.z, dir.x) * 0.5h, acos(dir.y)) * M_1_PI_H;
    }

    inline float2 spherical_from_cartesian_fast(float3 dir)
    {
        return float2( atan2_fast(-dir.z, dir.x) * (0.5f * M_1_PI_F), acos_fast(dir.y) * M_1_PI_F);
    }

    inline half2 spherical_from_cartesian_fast(half3 dir)
    {
        return half2( atan2_fast(-dir.z, dir.x) * 0.5h, acos_fast(dir.y)) * M_1_PI_H;
    }

    #define dual_contract_factor  1.0

    template <class T>
    inline vec<T, 2> dual_paraboloid_from_cartesian(vec<T, 3> dir)
    {
        dir.xy /= abs(dir.z) + 1.0;

        dir.y = 0.5 - dir.y * 0.5;
        T s   = sign(dir.z) * 0.25;
        dir.x = s * (dir.x - 1.0) + 0.5;
        return dir.xy;
    }
    
    
    template <class T>
    inline vec<T, 3> cartesian_from_dual_paraboloid(vec<T, 2>  uv)
    {
        
        T zside = 0.5 * sign(0.5 - uv.x);
        uv.x = 1.0 - abs(4.0 * uv.x - 2.0); 
        uv.y   = 1.0 - uv.y * 2.0;
        T z = length_squared(uv); 
        z = (1.0 - z) * zside;
        
        return vec<T, 3>(uv.x, uv.y, z);
    }

    
    template <class T>
    inline vec<T, 2> signNotZero(vec<T, 2> v) {
        return vec<T, 2>((v.x >= 0.0) ? +1.0 : -1.0, (v.y >= 0.0) ? +1.0 : -1.0);
    }

    template <class T>
    inline vec<T, 2> octEncode(vec<T, 3> v) {
        float2 p = v.xy * (1.0 / (abs(v.x) + abs(v.y) + abs(v.z)));
        return (v.z <= 0.0) ? ((1.0 - abs(p.yx)) * signNotZero(p)) : p;
    }

    template <class T>
    inline vec<T, 3> octDecode(vec<T, 2> p) {
        float3 v = float3(p.xy, 1.0 - abs(p.x) - abs(p.y));
        if (v.z < 0) v.xy = (1.0 - abs(v.yx)) * signNotZero(v.xy);
        return normalize(v);
    }

    inline float reduce_min(float3 v) {
        return min(v.x, min(v.y, v.z));
    }
    
    inline float reduce_min(float4 v) {
        return min(min(v.x, v.y), min(v.z, v.w));
    }

    inline float reduce_max(float3 v) {
        return max(v.x, max(v.y, v.z));
    }

    inline float reduce_max(float4 v) {
        return max(max(v.x, v.y), max(v.z, v.w));
    }

    
    inline float3 randomSphereDir(float2 rnd)
    {
        float s = rnd.x * M_PI_F * 2.f;
        float t = rnd.y * 2.f - 1.f;
        return float3( float2(sin(s), cos(s)) * sqrt(1.f-t*t), t );
    }
    
    
    template <class T>
    inline T interleaved_gradient_noise(vec<T, 2> pos)
    {
        vec<T, 3> magic( 0.06711056f, 0.00583715f, 52.9829189f );
        return fract( magic.z * fract( dot( pos, magic.xy ) ) );
    }
    
    inline float3 hemisphere_reflect(float3 v, float3 nrm)
    {
        
        
        
        float k = dot(v,nrm);
        return (k>0.0) ? v : v-2.0*nrm*k;
    }

    
    inline float3 randomHemisphereDir(float3 dir, float2 rnd)
    {
        return hemisphere_reflect(randomSphereDir( rnd ), dir);
    }
    
    inline void orthogonal_basis(float3 n, thread float3& xp, thread float3& yp)
    {
        
        float sz = n.z >= 0.f ? 1.f : -1.f;
        float a  =  n.y / (1.f + abs(n.z));
        float b  =  n.y * a;
        float c  = -n.x * a;
        
        xp = float3(n.z + sz * b,   sz * c,     -n.x);
        yp = float3(c,              1.f - b,    -sz * n.y);
    }

    template <class U>
    inline float2 normalized_coordinate(ushort2 index, U texture)
    {
        uint width  = texture.get_width();
        uint height = texture.get_height();
        
        float u = width  == 1 ? 0.5f : float(index.x) / float(width - 1);
        float v = height == 1 ? 0.5f : float(index.y) / float(height - 1);
        
        return float2(u, v);
    }

    template <class U>
    inline float2 normalized_coordinate(uint2 index, U texture)
    {
        uint width  = texture.get_width();
        uint height = texture.get_height();
        
        float u = width  == 1 ? 0.5f : float(index.x) / float(width - 1);
        float v = height == 1 ? 0.5f : float(index.y) / float(height - 1);
        
        return float2(u, v);
    }

    template <class U>
    inline half2 normalized_coordinate_half(uint2 index, U texture)
    {
        uint width  = texture.get_width();
        uint height = texture.get_height();
        
        half u = width  == 1 ? 0.5h : half(index.x) / half(width - 1);
        half v = height == 1 ? 0.5h : half(index.y) / half(height - 1);
        
        return half2(u, v);
    }

    
    
    
    inline float2 R2(float2 jitt, float i) {
        const float2 a = float2(0.245122333753, 0.4301597090025);
        return fract(a * i + jitt);
    }

    

    template <class T>
    inline vec<T, 3> cubemap_dir_from_sampleCoord(uint face, vec<T, 2> sampleCoord) 
    {
        switch(face) {
            case 0: 
                return vec<T, 3>( 1.0, -sampleCoord.y, -sampleCoord.x);

            case 1: 
                return vec<T, 3>(-1.0, -sampleCoord.y,  sampleCoord.x);

            case 2: 
                return vec<T, 3>(sampleCoord.x,  1.0,  sampleCoord.y);

            case 3: 
                return vec<T, 3>(sampleCoord.x, -1.0, -sampleCoord.y);

            case 4: 
                return vec<T, 3>( sampleCoord.x, -sampleCoord.y,  1.0);

            default: 
                return vec<T, 3>(-sampleCoord.x, -sampleCoord.y, -1.0);
        }
    }

    
    template <class T>
    inline T signed_unit(T uv) {
        return uv * 2.0 - 1.0;
    }

    
    template <class T>
    inline T unsigned_unit(T uv) {
        return uv * 0.5 + 0.5;
    }

    template <class T>
    inline vec<T, 3> cubemap_dir_from_uv(uint face, vec<T, 2> uv) 
    {
        return cubemap_dir_from_sampleCoord(face, signed_unit(uv));
    }

    template <class T>
    inline vec<T, 3> cubemap_dir_from_uv_unit(uint face, vec<T, 2> uv) 
    {
        return normalize(cubemap_dir_from_uv(face, uv));
    }

    
    
    inline float2 barycentric_mix(float2 __x, float2 __y, float2 __z, float3 __t) { return __t.x * __x + __t.y * __y + __t.z * __z; }
    inline float3 barycentric_mix(float3 __x, float3 __y, float3 __z, float3 __t) { return __t.x * __x + __t.y * __y + __t.z * __z; }
    inline float4 barycentric_mix(float4 __x, float4 __y, float4 __z, float3 __t) { return __t.x * __x + __t.y * __y + __t.z * __z; }
    
    static inline float rect(float2 lt, float2 rb, float2 uv)
    {
        float2 borders = step(lt, uv) * step(uv, rb);
        return borders.x * borders.y;
    }
    
    inline half4 debugColorForCascade(int cascade)
    {
        switch (cascade) {
            case 0:
            return half4(1.h, 0.h, 0.h, 1.h);
            case 1:
            return half4(0.9, 0.5, 0., 1.);
            case 2:
            return half4(1., 1., 0., 1.);
            case 3:
            return half4(0., 1., 0., 1.);
            default:
            return half4(0., 0., 0., 1.);
        }
    }

    inline half3 debugColorForFace(int count)
    {
        switch (count) {
            case 0:  return half3(1.0h, 0.1h, 0.1h);
            case 1:  return half3(0.1h, 1.0h, 1.0h);
            case 2:  return half3(0.1h, 1.0h, 0.1h);
            case 3:  return half3(1.0h, 0.1h, 1.0h);
            case 4:  return half3(0.1h, 0.1h, 1.0h);
            default: return half3(1.0h, 1.0h, 0.1h);
        }
    }

    inline half4 debugColorForCount(int count)
    {
        switch (count) {
            case 0: return half4(0.0h, 0.0h, 0.0h, 1.h);
            case 1: return half4(0.0h, 0.0h, 0.4h, 1.h);
            case 2: return half4(0.0h, 0.0h, 0.9h, 1.h);
            case 3: return half4(0.0h, 0.4h, 0.7h, 1.h);
            case 4: return half4(0.0h, 0.9h, 0.4h, 1.h);
            case 5: return half4(0.0h, 0.9h, 0.0h, 1.h);
            case 6: return half4(0.4h, 0.7h, 0.0h, 1.h);
            case 7: return half4(0.9h, 0.7h, 0.0h, 1.h);
            default: return half4(1., 0., 0., 1.);
        }
    }

    inline float grid(float2 lt, float2 rb, float2 gridSize, float thickness, float2 uv)
    {
        float insideRect = rect(lt, rb + thickness, uv);
        float2 gt = thickness * gridSize;
        float2 lines = step(abs(lt - fract(uv * gridSize)), gt);
        return insideRect * (lines.x + lines.y);
    }

    inline float checkerboard(float2 gridSize, float2 uv)
    {
        float2 check = floor(uv * gridSize);
        return step(fmod(check.x + check.y, 2.f), 0.f);
    }

    

    inline float luminance(float3 color)
    {
        
        
        return dot(color, float3(0.212671, 0.715160, 0.072169));
    }
    inline half luminance(half3 color)
    {
        
        
        return dot(color, half3(0.212671h, 0.715160h, 0.072169h));
    }
    
    inline float srgb_to_linear(float c)
    {
        return (c <= 0.04045f) ? c / 12.92f : powr((c + 0.055f) / 1.055f, 2.4f);
    }
    
    inline half srgb_to_linear_fast(half c)
    {
        return powr(c, 2.2h);
    }
    
    inline half3 srgb_to_linear_fast(half3 c)
    {
        return powr(c, 2.2h);
    }
    
    inline half srgb_to_linear(half c)
    {
        
        return (c <= 0.04045h) ? (c * 0.0773993808h) :  powr(0.9478672986h * c + 0.05213270142h, 2.4h);
    }
    
    inline float3 srgb_to_linear(float3 c)
    {
        return float3(srgb_to_linear(c.x), srgb_to_linear(c.y), srgb_to_linear(c.z));
    }
    
    inline float linear_to_srgb(float c)
    {
        return (c < 0.0031308f) ? (12.92f * c) : (1.055f * powr(c, 1.f/2.4f) - 0.055f);
    }
    
    inline float3 linear_to_srgb(float3 v) { 
        return float3(linear_to_srgb(v.x), linear_to_srgb(v.y), linear_to_srgb(v.z));
    }
    
}



inline float4 texture2DProj(texture2d<float> tex, sampler smp, float4 uv)
{
    return tex.sample(smp, uv.xy / uv.w);
}

inline half4 texture2DProj(texture2d<half> tex, sampler smp, float4 uv)
{
    return tex.sample(smp, uv.xy / uv.w);
}

static constexpr sampler vfx_shadow_sampler_rev_z = sampler(coord::normalized, filter::linear, mip_filter::none, address::clamp_to_zero, compare_func::less_equal);

static constexpr sampler vfx_shadow_sampler = vfx_shadow_sampler_rev_z;

inline float shadow2DProj(sampler shadow_sampler, depth2d<float> tex, float4 uv, float4 tile)
{
    float3 uvp = uv.xyz / uv.w;
    uvp.xy = tile.xy + uvp.xy * tile.zw;
    return tex.sample_compare(shadow_sampler, uvp.xy, uvp.z);
}

inline float shadow2DArray(sampler shadow_sampler, depth2d_array<float> tex, float3 uv, uint slice)
{
    return tex.sample_compare(shadow_sampler, uv.xy, slice, uv.z);
}

inline float shadow2DArrayProj(sampler shadow_sampler, depth2d_array<float> tex, float4 uv, uint slice)
{
    float3 uvp = uv.xyz / uv.w;
    return tex.sample_compare(shadow_sampler, uvp.xy, slice, uvp.z);
}



inline float4 transformViewPosInShadowSpace(float3 pos, float4x4 shadowMatrix)
{
    
    float4 lightScreen =  shadowMatrix * float4(pos, 1.f);

    return lightScreen;
}

inline float ComputeCascadeBlendAmount(float3 shadowPos, bool cascadeBlending)
{
    const float cascadeBlendingFactor = 0.1f; 

    float3 cascadePos = abs(shadowPos.xyz * 2.f - 1.f);
    
    if (cascadeBlending) {
#if 0
        const float edge = 1.f - cascadeBlendingFactor;
        
        cascadePos = 1.f - saturate((cascadePos - edge) / cascadeBlendingFactor);
        return cascadePos.x * cascadePos.y * cascadePos.z; 
#else
        
        float distToEdge = 1.0f - max(max(cascadePos.x, cascadePos.y), cascadePos.z);
        return smoothstep(0.0f, cascadeBlendingFactor, distToEdge);
#endif
    } else {
        return step(cascadePos.x, 1.f) * step(cascadePos.y, 1.f) * step(cascadePos.z, 1.f);
    }
}

template <class T>
inline void applyFog(thread vec<T, 4>& color, float eye_distance, float3 fogParameters, vec<T, 4> fogColor) {
    float factor = eye_distance * fogParameters.x + fogParameters.y;
    T fogFactor = pow(clamp(T(factor), T(0), fogColor.a), T(fogParameters.z));
    color.rgb = mix(color.rgb, fogColor.rgb * color.a, fogFactor);
}



#pragma mark Pack/Unpack

inline ushort packHalf2ToUShort(half2 v) {
    v = saturate(v);
    v = round(v*255);
    ushort2 uv = ushort2(v);
    ushort res = (uv.x & 0x00ff) | ((uv.y & 0x00ff) << 8);
    return res;
}

inline half2 unpackHalf2FromUShort(ushort v) {
    half2 res;
    
    res.x = half(v & 0x00ff);
    res.y = half( (v & 0xff00) >> 8);
    
    return res/255.;
}



#ifdef USE_RE_SYSTEM_TREATMENTS
#import "vfx_re_shaders.h"
#import "vfx_re_shaders_internal.h"
#import "vfx_render_options.h"
#import "vfx_shader_api_v2.h"
#endif 

#pragma mark - Includes end

#pragma mark - Namespace Begin




inline float shadow2D(sampler shadow_sampler, depth2d<float> tex, float3 uv, float4 tile)
{
#if CFX_USE_ATLAS_FOR_SHADOW_MAP
    float2 mapSize = float2(tex.get_width(), tex.get_height());
    float2 duv = 0.5f / mapSize;
    float2 uv0 = tile.xy;
    float2 uvs = tile.zw;

    float2 b = uv0 + duv;
    float2 s = uvs - 2 * duv;

    uv.xy = uv.xy * s + b;
#endif

    return tex.sample_compare(shadow_sampler, uv.xy, uv.z);
}


inline float ComputeSoftShadowGrid(sampler shadow_sampler, float3 worldPos, float4x4 shadowMatrix, depth2d<float> shadowMap, int sampleCount, float4 tile)
{
    float4 lightScreen =  transformViewPosInShadowSpace(worldPos, shadowMatrix);
    lightScreen.xyz /= lightScreen.w;

    if (any(lightScreen.xy < 0.0 || lightScreen.xy > 1.0)) {
        return 0.0;
    }

    
    float shadow;
    if (sampleCount <= 1) {
        shadow = shadow2D(shadow_sampler, shadowMap, lightScreen.xyz, tile);
    } else {

        lightScreen.z += 0.005f; 

        float2 texelSize = 2.f / float2(shadowMap.get_width(), shadowMap.get_height());
        float2 origin    = lightScreen.xy - (sampleCount * 0.5f) * texelSize;

        
        if (sampleCount <= 4) { 
            half totalAccum = 0.h;
            for (int y = 0; y < sampleCount; ++y) {
                for (int x = 0; x < sampleCount; ++x) {
                    totalAccum  += half(shadowMap.sample_compare(shadow_sampler, origin, lightScreen.z, 2 * int2(x,y)));
                }
            }
            shadow = totalAccum / half(sampleCount * sampleCount);
        } else {
            float totalAccum = 0.f;
            for (int y = 0; y < sampleCount; ++y) {
                for (int x = 0; x < sampleCount; ++x) {
                    float2 samplePos = origin + texelSize * float2(x, y);
                    totalAccum  += shadowMap.sample_compare(shadow_sampler, samplePos, lightScreen.z);
                }
            }
            shadow = totalAccum / float(sampleCount * sampleCount);
        }
    }

    
    shadow *= step(0., lightScreen.w);

    return shadow;
}

inline float ComputeSoftShadow(sampler shadow_sampler, float3 lightScreen, depth2d<float> shadowMap, constant float4* shadowKernel, int sampleCount, float shadowRadius, float4 tile)
{
    if (any(lightScreen.xyz < 0.0 || lightScreen.xyz > 1.0)) {
        return 0.0;
    }

    
    float shadow;
    if (sampleCount <= 1) {
        shadow = shadow2D(shadow_sampler, shadowMap, lightScreen.xyz, tile);
    } else {
        

        
        float3 duvdist_dx = dfdx(lightScreen.xyz);
        float3 duvdist_dy = dfdy(lightScreen.xyz);

        
        
        
        
        
        

        float inv_det_J = 1.0 / ((duvdist_dx.x * duvdist_dy.y) - (duvdist_dx.y * duvdist_dy.x));
        float2 ddist_duv = inv_det_J * float2(duvdist_dy.y * duvdist_dx.z - duvdist_dx.y * duvdist_dy.z,
                                              duvdist_dx.x * duvdist_dy.z - duvdist_dy.x * duvdist_dx.z);

        float totalAccum = 0.0;

        for (int i = 0; i < sampleCount; i++) {
            float3 uvOffset = shadowKernel[i].xyz * shadowRadius;

            float2 uvWithOffset = lightScreen.xy + uvOffset.xy;
            if (any(uvWithOffset < 0.0 || uvWithOffset > 1.0)) {
                continue;
            }
            float dist = lightScreen.z * (1.0 + uvOffset.z) + (ddist_duv.x * uvOffset.x + ddist_duv.y * uvOffset.y);
            if (dist < 0.0) {
                continue;
            }

            totalAccum += shadow2D(shadow_sampler, shadowMap, float3(uvWithOffset, dist), tile);
        }

        shadow = totalAccum / float(sampleCount);
    }

    return shadow;
}


#if CFX_USE_ATLAS_FOR_SHADOW_MAP
inline float4 SampleShadowCascade(sampler shadow_sampler, depth2d<float> shadowMaps, float3 shadowPosition, uint cascadeIndex, constant float4* shadowKernel, int sampleCount, float shadowRadius, float4 tile)
#else
inline float4 SampleShadowCascade(sampler shadow_sampler, depth2d_array<float> shadowMaps, float3 shadowPosition, uint cascadeIndex, constant float4* shadowKernel, int sampleCount, float shadowRadius)
#endif
{
    
    float2 gridSize = float2(shadowMaps.get_width(), shadowMaps.get_height()) / 32;
    float gd = vfx::checkerboard(shadowPosition.xy, gridSize);
    float3 gridCol = mix(float3(vfx::debugColorForCascade(cascadeIndex).rgb), float3(0.f), float3(gd > 0.f));


#if CFX_USE_ATLAS_FOR_SHADOW_MAP
    float shadow = ComputeSoftShadow(shadow_sampler, shadowPosition, shadowMaps, shadowKernel, sampleCount, shadowRadius, tile);
#else
    float shadow = 0.0;
    if (sampleCount > 1) {

        
        for (int i = 0; i < sampleCount; ++i) {
            shadow += shadow2DArray(shadow_sampler, shadowMaps, shadowKernel[i].xyz * shadowRadius + shadowPosition, cascadeIndex);
        }
        shadow /= float(sampleCount);
    } else {
        
        shadow = shadow2DArray(shadow_sampler, shadowMaps, shadowPosition, cascadeIndex);
    }
#endif
    return float4(gridCol, shadow);
}

#if CFX_USE_ATLAS_FOR_SHADOW_MAP
inline float4 ComputeCascadedShadow(sampler shadow_sampler, float3 viewPos, float4x4 shadowMatrix, constant float4 *cascadeScale, constant float4 *cascadeBias, int cascadeCount, depth2d<float> shadowMaps, bool enableCascadeBlending, constant float4* shadowKernel, int sampleCount, float shadowRadius, constant float4* tiles)
#else
inline float4 ComputeCascadedShadow(sampler shadow_sampler, float3 viewPos, float4x4 shadowMatrix, constant float4 *cascadeScale, constant float4 *cascadeBias, int cascadeCount, depth2d_array<float> shadowMaps, bool enableCascadeBlending, constant float4* shadowKernel, int sampleCount, float shadowRadius)
#endif
{
    float4 shadow = 0.f;
    float shadowSum = 0.f;
    
    
    float3 pos_ls =  (shadowMatrix * float4(viewPos, 1.f)).xyz;
    for (int c = 0; c < cascadeCount; ++c) {
        
        float3 pos_cs =  pos_ls * cascadeScale[c].xyz + cascadeBias[c].xyz;

        
        float cascadeRadius = shadowRadius * cascadeScale[c].x;

        float opacity = ComputeCascadeBlendAmount(pos_cs, enableCascadeBlending);


        if (shadowSum >= 1.f && opacity <= 0.0) 
            break;                              
                                                

        if (opacity > 0.f) { 
            
#if CFX_USE_ATLAS_FOR_SHADOW_MAP
            shadow += SampleShadowCascade(shadow_sampler, shadowMaps, pos_cs, c, shadowKernel, sampleCount, cascadeRadius, tiles[c]) * opacity;
#else
            shadow += SampleShadowCascade(shadow_sampler, shadowMaps, pos_cs, c, shadowKernel, sampleCount, cascadeRadius) * opacity;
#endif
            shadowSum += opacity;
        }
    }

    if (shadowSum > 0) {
        shadow /= shadowSum;
    }
    return shadow;
}

inline float ComputeShadow(sampler shadow_sampler, float3 worldPos, float4x4 shadowMatrix, depth2d<float> shadowMap, float4 tile)
{
    float4 lightScreen =  transformViewPosInShadowSpace(worldPos, shadowMatrix);
    lightScreen.xyz /= lightScreen.w;

    if (any(lightScreen.xy < 0.0 || lightScreen.xy > 1.0)) {
        return 0.0;
    }

    float shadow = shadow2D(shadow_sampler, shadowMap, lightScreen.xyz, tile);

    
    shadow *= step(0., lightScreen.w);

    return shadow;
}






#ifdef USE_LAYERED_RENDERING
#define texture2d_layer texture2d_array
#define sampleLayer(a,b) sample(a,b,in.sliceIndex)
#else
#define texture2d_layer texture2d
#define sampleLayer(a,b) sample(a,b)
#endif

#ifdef CFX_USE_TEXTURE_FOR_LIGHT_INDICES
#define LightIndex(lid) u_lightIndicesTexture.read((ushort)lid).x
#else
#define LightIndex(lid) u_lightIndicesBuffer[lid]
#endif

#ifdef IS_BEZIER_CURVE
[[visible]] bool vfx_is_inside_bezier_curve(float2 p, device void const* curveData, device packed_float3 const* controlPoints);
[[visible]] float vfx_distance_to_bezier_curve(float2 p, thread float2& directionToCurve, float maximumDistance, device void const* curveData, device packed_float3 const* controlPoints);
#endif

#ifdef USE_GBUFFER_OUTPUT
    #undef USE_PER_VERTEX_LIGHTING
    #undef USE_PER_PIXEL_LIGHTING
    #undef USE_LIGHTING
#endif



typedef struct {

#ifdef USE_MODELTRANSFORM
    float4x4 modelTransform;
#endif
#ifdef USE_INVERSEMODELTRANSFORM
    float4x4 inverseModelTransform;
#endif
#ifdef USE_MODELVIEWTRANSFORM
    float4x4 modelViewTransform;
#endif
#ifdef USE_INVERSEMODELVIEWTRANSFORM
    float4x4 inverseModelViewTransform;
#endif
#ifdef USE_NORMALTRANSFORM
    float4x4 normalTransform;
#endif
#ifdef USE_INVERSEMODELVIEWPROJECTIONTRANSFORM
    float4x4 inverseModelViewProjectionTransform;
#endif
#ifdef USE_MODELVIEWPROJECTIONTRANSFORM
    float4x4 modelViewProjectionTransform;
#endif
#ifdef USE_LASTFRAMETRANSFORM
    float4x4 lastFrameModelTransform;
#endif
#ifdef USE_MOTIONBLUR
    float motionBlurIntensity;
#endif
#ifdef USE_BOUNDINGBOX
    float2x3 boundingBox;
#endif
#ifdef USE_WORLDBOUNDINGBOX
    float2x3 worldBoundingBox;
#endif
#ifdef USE_NODE_OPACITY
    float nodeOpacity;
#endif
#if defined(USE_PROBES_LIGHTING) && (USE_PROBES_LIGHTING == 2)
    sh2_coefficients shCoefficients;
#elif defined(USE_PROBES_LIGHTING) && (USE_PROBES_LIGHTING == 3)
    sh3_coefficients shCoefficients;
#endif
#ifdef USE_CLUSTERED_LIGHTING
    uint8_t categoryBitmask;
#endif
} commonprofile_node;

static commonprofile_node compute_view_dependant_uniforms(device commonprofile_node& vfx_node, constant VFXWorldBuffer& vfx_frame) {
    
#ifdef USE_LATE_LATCHING
#ifdef USE_MODELVIEWTRANSFORM
    vfx_node.modelViewTransform = vfx_frame.viewTransform * vfx_node.modelTransform;
#endif
#if defined(USE_INVERSEMODELVIEWTRANSFORM) || defined(USE_NORMALTRANSFORM)
    vfx_node.inverseModelViewTransform = vfx_node.inverseModelTransform * vfx_frame.inverseViewTransform;
#endif
#ifdef USE_NORMALTRANSFORM
    vfx_node.normalTransform = transpose(vfx_node.inverseModelViewTransform);
#endif
#ifdef USE_MODELVIEWPROJECTIONTRANSFORM
    vfx_node.modelViewProjectionTransform = vfx_frame.viewProjectionTransform * vfx_node.modelTransform;
#endif
#ifdef USE_INVERSEMODELVIEWPROJECTIONTRANSFORM
    vfx_node.inverseModelViewProjectionTransform = vfx_node.inverseModelTransform * vfx_frame.inverseViewProjectionTransform;
#endif
#endif 
    return vfx_node;
}

typedef struct {
    float3 position         [[attribute(VFXVertexSemanticPosition)]];
#ifdef HAS_NORMAL
    float3 normal           [[attribute(VFXVertexSemanticNormal)]];
#endif
#ifdef USE_TANGENT
    float4 tangent          [[attribute(VFXVertexSemanticTangent)]];
#endif
#ifdef USE_VERTEX_COLOR
    float4 color            [[attribute(VFXVertexSemanticColor)]];
#endif
#if defined(NEED_IN_TEXCOORD0) || defined(DEBUG_PIXEL)
    float2 texcoord0        [[attribute(VFXVertexSemanticTexcoord0)]];
#endif
#ifdef NEED_IN_TEXCOORD1
    float2 texcoord1        [[attribute(VFXVertexSemanticTexcoord1)]];
#endif
#ifdef NEED_IN_TEXCOORD2
    float2 texcoord2        [[attribute(VFXVertexSemanticTexcoord2)]];
#endif
#ifdef NEED_IN_TEXCOORD3
    float2 texcoord3        [[attribute(VFXVertexSemanticTexcoord3)]];
#endif
#ifdef NEED_IN_TEXCOORD4
    float2 texcoord4        [[attribute(VFXVertexSemanticTexcoord4)]];
#endif
#ifdef NEED_IN_TEXCOORD5
    float2 texcoord5        [[attribute(VFXVertexSemanticTexcoord5)]];
#endif
#ifdef NEED_IN_TEXCOORD6
    float2 texcoord6        [[attribute(VFXVertexSemanticTexcoord6)]];
#endif
#ifdef NEED_IN_TEXCOORD7
    float2 texcoord7        [[attribute(VFXVertexSemanticTexcoord7)]];
#endif
} vfx_vertex_t; 

typedef struct {
    float4 fragmentPosition [[position]]; 
#ifdef USE_POINT_RENDERING
    float fragmentSize [[point_size]];
#endif
#ifdef USE_VERTEX_COLOR
    float4 vertexColor;
#endif
#ifdef USE_PER_VERTEX_LIGHTING
    float3 diffuse;
#ifdef USE_SPECULAR
    float3 specular;
#endif
#ifdef USE_CLEARCOAT
    float clearCoat;
#endif
#ifdef USE_CLEARCOATROUGHNESS
    float clearCoatRoughness;
#endif
#ifdef USE_CLEARCOATNORMAL
    float clearCoatNormal;
#endif
#endif
#if defined(USE_POSITION) && (USE_POSITION == 2)
    float3 position;
#endif
#if defined(USE_NORMAL) && (USE_NORMAL == 2) && defined(HAS_OR_GENERATES_NORMAL)
    float3 normal;
#endif
#if defined(USE_TANGENT) && (USE_TANGENT == 2)
    float3 tangent;
#endif
#if defined(USE_BITANGENT) && (USE_BITANGENT == 2)
    float3 bitangent;
#endif
#ifdef USE_DISPLACEMENT_MAP
    float2 displacementTexcoord;   
#endif
#ifdef USE_CLEARCOAT_MAP
    float2 clearCoatTexcoord;   
#endif
#ifdef USE_CLEARCOATROUGHNESS_MAP
    float2 clearCoatRoughnessTexcoord;   
#endif
#ifdef USE_CLEARCOATNORMAL_MAP
    float2 clearCoatNormalTexcoord;   
#endif
#ifdef USE_NODE_OPACITY
    float nodeOpacity;
#endif
#ifdef USE_TEXCOORD
    float2 texcoord0;

#endif
    
#ifdef USE_EXTRA_VARYINGS
    
#endif
#ifdef USE_LASTFRAMETRANSFORM
    float3 mv_lastFragment;
    float3 mv_fragment;
#endif
#ifdef USE_OUTLINE
    float outlineHash [[ flat ]];
#endif
#ifdef USE_INSTANCING
    uint   instanceID [[ flat ]];
#endif
#ifndef USE_VERTEX_AMPLIFICATION 
#ifdef USE_LAYERED_RENDERING
    uint   sliceIndex [[render_target_array_index]];
#endif
#ifdef USE_MULTIPLE_VIEWPORTS_RENDERING
    uint   sliceIndex [[viewport_array_index]];
#endif
#endif
#if DEBUG_PIXEL
    float2 uv0;
#endif
#if USE_SCREEN_UV
    float2 screen_uv [[ center_no_perspective ]];
#endif
#ifdef USE_CR_WORLD_POSITION
    float3 crworldPosition;
#endif
} commonprofile_io;


#ifdef USE_SHADER_MODIFIERS
typedef struct {
    float2 blobPosition;
    float radius;
    float2 screenRatio;
    float blobDimensions;
    float blobAnimationSpeed;
    float transitionToBlob;
    float lightMode;
    float2 applePosition;
    float isHDR;
    float appleScale;
} vfx_modifiers_params_fragment_t;


inline float2x2 rotate(float v) {
    float a = cos(v);
    float b = sin(v);
    return float2x2(a,-b,b,a);
}

struct Blob {
    const float metaSmooth = 0.12;
    const float metaSize = 0.37;
    const float metaOffset = 0.18;
    const float offsetSpeed = 1.3;
    
    float smin(float a, float b, float k)
    {
        float h = max(k-abs(a-b), 0.0)/k;
        return min(a, b) - h*h*k*(1.0/4.0);
    }

    float2x2 rot;
    float4 metaPosSize[4];
    
    void init(float time) {
        rot = rotate(time*0.4*M_PI_2_F);
        
        float ct = cos(time * offsetSpeed) * metaSize * metaOffset;
        float st = sin(time * offsetSpeed) * metaSize * metaOffset;
        metaPosSize[0] = float4( st*1.1, st,  0.0, metaSize * 1.15);
        metaPosSize[1] = float4(-st*1.1, st,  0.2, metaSize * 1.15);
        metaPosSize[2] = float4(     st, ct,-0.24,        metaSize);
        metaPosSize[3] = float4(     st,-ct,  0.1,        metaSize);
    }
    
    
    float map(float2 p) {
        float2 pp = rot * p;
        float d0 = length(pp - metaPosSize[0].xy) - metaPosSize[0].w;
        float d1 = length(pp - metaPosSize[1].xy) - metaPosSize[1].w;
        float d2 = length(pp - metaPosSize[2].xy) - metaPosSize[2].w;
        float d3 = length(pp - metaPosSize[3].xy) - metaPosSize[3].w;

        float d = smin(d0, d1, metaSmooth);
        d = smin(d,d2, metaSmooth);
        d = smin(d,d3, metaSmooth);
        
        return d;
    }
    
    float3 normal(float2 p, float d) {
        float2 eps = float2(.01, 0.);
        float3 n;
        n.x = d - map(p-eps.xy);
        n.y = d - map(p-eps.yx);
        return n /= eps.x*2.;
    }
};




#endif


#define USE_QUAT_FOR_IES 1
#define USE_PBR_DOMINANT_DIRECTION 1

#define PBR_INTENSITY_FACTOR M_PI_2_F
#define BoostFactor (20.0f * PBR_INTENSITY_FACTOR)


struct vfx_light
{
    float4 color; 
    float3 pos; 
    float3 dir; 
    float shadowRadius; 
    uint8_t lightType; 
    uint8_t shadowSampleCount; 
                                
    union {
        struct {
            float4      cascadeScale[4]; 
            float4      cascadeBias[4];
        } directional; 
        struct {
            float4      attenuationFactors; 
#if CFX_USE_ATLAS_FOR_SHADOW_MAP
            float4x4    projection;
            float       depthBias;
#else
            float3      shadowScaleBias; 
#endif
        } omni;
        struct {
            float4      _attenuationFactors; 
            float2      scaleBias; 
        } spot;
        struct {
            float4      _attenuationFactors; 
            float2      scaleBias; 
#if USE_QUAT_FOR_IES
            float4      light_from_view_quat; 
#else
            float4x4    light_from_view; 
#endif
        } ies;
        union {
            struct {
                float2 halfExtents;
                float doubleSided;
            } rectangle;
            struct {
                uint32_t vertexCount;
                float doubleSided;
            } polygon;
            struct {
                float halfLength;
            } line;
            struct {
                float2 halfExtents;
                float doubleSided;
            } ellipse;
            struct {
                float3 halfExtents;
            } ellipsoid;
        } area;
        struct {
            float3  offset;
            float4  halfExtents; 
            float3  parallaxCenter;
            float3  parallaxExtents;
            int32_t index; 
            int32_t parallaxCorrection; 
        } probe;
    } parameters; 

    float4x4    shadowMatrix; 

#if CFX_USE_ATLAS_FOR_SHADOW_MAP
    float4 tiles[6]; 
#endif
	uint8_t categoryBitmask;
};

#if defined(__METAL_VERSION__) 

using namespace metal;

namespace vfx_lighting {
    static constexpr sampler linearSampler = sampler(filter::linear, mip_filter::linear);
}

struct VFXShaderSurface {
    float3 view;                
    float3 position;            
    float3 normal;              
#ifdef USE_BENTNORMALS
    float3 bentNormal;          
    float aoDirectionnal;       
#endif
    float3 geometryNormal;      
    float2 normalTexcoord;      
    float3 tangent;             
    float3 bitangent;           
    float4 diffuse;             
    float2 diffuseTexcoord;     
    float4 specular;            
    float2 specularTexcoord;    
    float4 emission;            
    float2 emissionTexcoord;    
    float4 selfIllumination;            
    float2 selfIlluminationTexcoord;    
    float4 multiply;            
    float2 multiplyTexcoord;    
    float4 transparent;         
    float2 transparentTexcoord; 
    float4 reflective;          
    float  metalness;           
    float2 metalnessTexcoord;   
    float  roughness;           
    float2 roughnessTexcoord;   
    float  rawRoughness;        
    float clearCoat;            
    float2 clearCoatTexcoord;   
    float clearCoatRoughness;   
    float2 clearCoatRoughnessTexcoord;
    float3 clearCoatNormal;     
    float2 clearCoatNormalTexcoord;
#ifdef USE_SUBSURFACE
    float subsurface;
    float2 subsurfaceTexcoord;
    float3 subsurfaceRadius;
    float2 subsurfaceRadiusTexcoord;
#endif
#ifdef USE_TRANSMISSION
    float transmission;
    float2 transmissionTexcoord;
    float3 transmissionColor;
    float2 transmissionColorTexcoord;
#endif
    float shininess;            
    float fresnel;              
    float ambientOcclusion;     
    float2 ambientOcclusionTexcoord;   
    float3 _normalTS;           
    float3 _clearCoatNormalTS;  
#ifdef USE_SURFACE_EXTRA_DECL
        float2 inRampTexcoord;
    float2 inSDFTexcoord;

#endif
};



struct VFXShaderLight {
    float4 intensity;
    float3 direction;

    float  _att;
    float3 _spotDirection;
    float  _distance;
};

enum VFXShadingModel
{
    VFXShadingModelConstant,
    VFXShadingModelPhong,
    VFXShadingModelNone,
    VFXShadingModelPhysicallyBased,
    VFXShadingModelShadowOnly,

    VFXShadingModelCustom 
};

#define PROBES_NORMALIZATION 0
#define PROBES_OUTER_BLENDING 1

struct VFXShaderLightingContribution
{
    float3 ambient;
    float3 diffuse;
    float3 specular;
    float3 modulate;

#ifdef USE_SHADOWONLY
    float shadowFactor;
#endif

#if PROBES_NORMALIZATION
    float4 probesWeightedSum; 
#else
    float  probeRadianceRemainingFactor;
#endif

    thread VFXShaderSurface& surface;

#ifdef USE_PER_VERTEX_LIGHTING
    commonprofile_io out;
#else
    commonprofile_io in;
#endif

#ifdef USE_PBR
    struct {
        float3 albedo;
        float3 envDiffuse;
        float3 envSpecular;
        float3 reflectance;
        float3 probeReflectance;
#ifndef USE_PBR_LAMBERTIAN_REFLECTION
        float2 diffuseHammonFactors;
#endif
#ifdef USE_PBR_TRANSPARENCY
        float  transparency;
#endif
        float  NoV;
        float  selfIlluminationOcclusion;
#ifdef USE_CLEARCOAT
        float  NoVClearCoat;
        float3 probeReflectanceClearCoat;
#endif
    } pbr;
#endif
    
    VFXShaderLightingContribution(thread VFXShaderSurface& iSurface, commonprofile_io io):surface(iSurface)
#ifdef USE_PER_VERTEX_LIGHTING
    ,out(io)
#else
    ,in(io)
#endif
    {
        ambient = 0.f;
        diffuse = 0.f;
        specular = 0.f;
#ifdef USE_SHADOWONLY
        shadowFactor = 1.f;
#endif

#if PROBES_NORMALIZATION
#if PROBES_OUTER_BLENDING
        probesWeightedSum = float4(0.f);
#else
        probesWeightedSum = float4(0.f, 0.f, 0.f, 0.000001f); 
#endif
#else
        probeRadianceRemainingFactor = 1.f;
#endif

#ifdef USE_MODULATE
        modulate = 1.f;
#else
        modulate = 0.f;
#endif
    }

#ifdef USE_PBR
    void prepareForPBR(texture2d<float, access::sample> specularDFGDiffuseHammonTexture, float occ)
    {
        pbr.envDiffuse = 0.f;
        pbr.envSpecular = 0.f;
        pbr.selfIlluminationOcclusion = occ;
      
        pbr.albedo = surface.diffuse.rgb;
#ifdef USE_PBR_TRANSPARENCY
  #ifdef DIFFUSE_PREMULTIPLIED
        
        pbr.transparency = 1.f;
  #else
        pbr.transparency = surface.diffuse.a;
  #endif
  #ifdef USE_TRANSPARENCY
    #ifdef USE_PER_VERTEX_LIGHTING
        pbr.transparency *= out.transparency;
    #else
        pbr.transparency *= in.transparency;
    #endif
  #endif
  #ifdef USE_TRANSPARENT
        pbr.transparency *= surface.transparent.a;
  #endif
        pbr.albedo *= pbr.transparency;
  #ifdef DIFFUSE_PREMULTIPLIED
        
        
        pbr.transparency *= surface.diffuse.a;
  #endif
#endif
        
        float3 n = surface.normal;
        float3 v = surface.view;
        pbr.NoV = abs(dot(n, v));
        
        float roughness = surface.roughness;
#ifdef USE_PBR_LAMBERTIAN_REFLECTION
        float2 specularDFG = specularDFGDiffuseHammonTexture.sample(vfx_lighting::linearSampler, float2(pbr.NoV, roughness)).rg;
#else
#ifdef USE_RE_RADIANCE_IRRADIANCE_MAP_SAMPLING
        float4 env = specularDFGDiffuseHammonTexture.sample(vfx_lighting::linearSampler, float2(roughness, 1.0f - pbr.NoV));
#else
        float4 env = specularDFGDiffuseHammonTexture.sample(vfx_lighting::linearSampler, float2(pbr.NoV, roughness));
#endif
        float2 specularDFG = env.xy;
        pbr.diffuseHammonFactors = env.zw;
#endif
        
        pbr.reflectance = mix(PBR_F0_NON_METALLIC, pbr.albedo, surface.metalness);
        pbr.probeReflectance = pbr.reflectance * specularDFG.r + specularDFG.g;
                                
#ifdef USE_CLEARCOAT
        pbr.NoVClearCoat = abs(dot(surface.clearCoatNormal, v));
        float2 DFGClearCoat = specularDFGDiffuseHammonTexture.sample(vfx_lighting::linearSampler, float2(pbr.NoVClearCoat, surface.clearCoatRoughness)).rg;
        pbr.probeReflectanceClearCoat = 0.04 * DFGClearCoat.r + DFGClearCoat.g;
#endif
    }
#endif


#ifdef USE_LIGHT_MODIFIER
    
#endif

    float4 debug_pixel(float2 fragmentPosition)
    {
        const int width = 64;
        switch (int(fragmentPosition.x + fragmentPosition.y ) / width) {
            case 0: return float4(surface.view, 1.f);
            case 1: return float4(surface.position, 1.f);
            case 2: return float4(surface.normal, 1.f);
            case 3: return float4(surface.geometryNormal, 1.f);
            case 4: return float4(float3(surface.ambientOcclusion), 1.f);
            case 5: return surface.diffuse;
            case 6: return float4(float3(surface.metalness), 1.f);
            case 7: return float4(float3(surface.roughness), 1.f);
            case 8: return float4(ambient, 1.f);
            case 9: return float4(diffuse, 1.f);
            default: return float4(specular, 1.f);
        }
    }

    

    static inline float3 lambert_diffuse(float3 l, float3 n, float3 color, float intensity) {
        return color * (intensity * saturate(dot(n, l)));
    }

    void phong(float3 l, float3 color, float intensity)
    {
        float3 D = lambert_diffuse(l, surface.normal, color, intensity);
        diffuse += D;

        float3 r = reflect(-l, surface.normal);
        specular += powr(saturate(dot(r, surface.view)), surface.shininess) * D;
    }

#ifdef USE_PBR
    void physicallyBased(float3 l, float3 color, float intensity)
    {
        float3 n         = surface.normal;
        float3 v         = surface.view;
        float  roughness = surface.roughness;
        float  alpha     = roughness * roughness;

        float3 h = normalize(l + v);

        float NoL = saturate(dot(n, l));
        float NoH = saturate(dot(n, h));
        float LoH = saturate(dot(l, h));
        
        float D   = vfx_brdf_D(alpha, NoH);
        float3 F  = vfx_brdf_F_opt(pbr.reflectance, LoH);
        float Vis = vfx_brdf_V(alpha, NoL, pbr.NoV);

        
        diffuse  += color * (NoL * M_1_PI_F * intensity);
        specular += color * F * (NoL * D * Vis * intensity);
        
        #ifdef USE_CLEARCOAT
            n = surface.clearCoatNormal;

            roughness = max(surface.clearCoatRoughness, 0.089f);
            alpha = roughness * roughness; 
        
            
            
            float NoH_coat = saturate(dot(n, h));
            float NoL_coat = saturate(dot(n, l));
            D   = vfx_brdf_D(alpha, NoH_coat);
            F   = vfx_brdf_F_opt(0.04, LoH) * surface.clearCoat;
            Vis = vfx_brdf_V(alpha, NoL_coat, saturate(dot(n,v)));
        
            float attenuation = 1.0 - F.r;
            specular *=  (attenuation * attenuation);
            specular += color * F * ( NoL_coat * D * Vis * intensity);
        #endif
    }
#endif

    void custom(float3 _l, float3 _color, float _intensity)
    {
#ifdef USE_LIGHT_MODIFIER
        thread VFXShaderLightingContribution &_lightingContribution = *this;
        thread VFXShaderSurface& _surface = surface;
        VFXShaderLight _light = {.direction = _l, .intensity = float4(_color, 1.f), ._att = _intensity };
        
        
        
#endif
    }

    void shade(float3 l, float3 color, float intensity)
    {
#ifdef LIGHTING_MODEL
        switch (LIGHTING_MODEL) {
#ifdef USE_SHADOWONLY
            case VFXShadingModelShadowOnly:      shadowFactor *= intensity; break;
#endif
            case VFXShadingModelPhong:           phong(l, color, intensity); break;
#ifdef USE_PBR
            case VFXShadingModelPhysicallyBased: physicallyBased(l, color, intensity); break;
#endif
            case VFXShadingModelCustom:          custom(l, color, intensity); break;
            default:  break; 
        }
#endif
    }

    
    
    
    float pbr_dist_attenuation_alternate(float3 l, float cutoff) {
        
        float radius = 0.1f; 
        float factor = 1.f / (1.f + length(l)/radius);
        float attenuation = saturate(factor * factor); 
        return saturate((attenuation - cutoff) / (1.f - cutoff));
    }

    float pbr_dist_attenuation(float3 l, float inv_square_radius) {
        float sqr_dist = length_squared(l);
        float atten = 1.f / max(sqr_dist, 0.0001f);

        
        float factor = saturate(1.f - vfx::sq(sqr_dist * inv_square_radius));
        return atten * factor * factor;
    }

    float non_pbr_dist_attenuation(float3 l, float4 att)
    {
        return powr(saturate(length(l) * att.x + att.y), att.z);
    }

    float dist_attenuation(float3 unnormalized_l, vfx_light light)
    {
#ifdef USE_PBR
        return pbr_dist_attenuation(unnormalized_l, light.parameters.omni.attenuationFactors.w);
        
        
#else
#ifdef USE_SHADOWONLY
        return 1.f;
#endif
        return non_pbr_dist_attenuation(unnormalized_l, light.parameters.omni.attenuationFactors);
#endif
    }

    float spot_attenuation(float3 l, vfx_light light)
    {
#ifdef USE_SHADOWONLY
        return 1.f;
#endif
        
        return saturate(dot(l, light.dir) * light.parameters.spot.scaleBias.x + light.parameters.spot.scaleBias.y);
    }

    void shade_modulate(float3 l, float4 color, float intensity)
    {
        constexpr half3 white = half3(1.h);
        
        modulate *= float3(mix(white, half3(color.rgb), half(color.a * intensity)));
    }

    float3 gobo(float3 pos, vfx_light light, texture2d<half> goboTexture, sampler goboSampler)
    {
        half3 g = texture2DProj(goboTexture, goboSampler, (light.shadowMatrix * float4(pos, 1.f))).rgb;
        return light.color.rgb * float3(mix(1.h, g, half(light.color.a)));
    }

    float shadow(float3 pos, vfx_light light, depth2d<float> shadowMap)
    {
#if CFX_USE_ATLAS_FOR_SHADOW_MAP
        float4 tile = light.tiles[0];
#else
        float4 tile = {0.0, 0.0, 1.0, 1.0};
#endif
        float shadow = ComputeShadow(vfx_shadow_sampler, pos, light.shadowMatrix, shadowMap, tile);
        return 1.f - shadow * light.color.a; 
    }

    
    float shadow(float3 pos, vfx_light light, depth2d<float> shadowMap, constant float4* shadowKernel)
    {

#if CFX_USE_ATLAS_FOR_SHADOW_MAP
        float4 tile = light.tiles[0];
#else
        float4 tile = {0.0, 0.0, 1.0, 1.0};
#endif
        float4 lightScreen = transformViewPosInShadowSpace(pos, light.shadowMatrix);
        lightScreen.xyz /= lightScreen.w;
        float shadow = ComputeSoftShadow(vfx_shadow_sampler, lightScreen.xyz, shadowMap, shadowKernel, light.shadowSampleCount, light.shadowRadius, tile);
        return 1.f - shadow * light.color.a; 
    }

    float shadow(float3 pos, vfx_light light, depth2d<float> shadowMap, constant float4* shadowKernel, int shadowSampleCount)
    {
#if CFX_USE_ATLAS_FOR_SHADOW_MAP
        float4 tile = light.tiles[0];
#else
        float4 tile = {0.0, 0.0, 1.0, 1.0};
#endif
        float4 lightScreen = transformViewPosInShadowSpace(pos, light.shadowMatrix);
        lightScreen.xyz /= lightScreen.w;
        float shadow = ComputeSoftShadow(vfx_shadow_sampler, lightScreen.xyz, shadowMap, shadowKernel, shadowSampleCount, light.shadowRadius, tile);
        return 1.f - shadow * light.color.a; 
    }

    float shadow(float3 pos, vfx_light light, depth2d<float> shadowMap, int shadowSampleCount)
    {
#if CFX_USE_ATLAS_FOR_SHADOW_MAP
        float4 tile = light.tiles[0];
#else
        float4 tile = {0.0, 0.0, 1.0, 1.0};
#endif
        float shadow = ComputeSoftShadowGrid(vfx_shadow_sampler, pos, light.shadowMatrix, shadowMap, shadowSampleCount, tile);
        return 1.f - shadow * light.color.a; 
    }


ushort getCubeFace(float3 dir)
{
    
    float3 absDir = abs(dir);
    float maxAxis = max(absDir.x, max(absDir.y, absDir.z));
    if (absDir.z == maxAxis) {
        
        return dir.z > 0.0f ? 4 : 5;
    } else if (absDir.y == maxAxis) {
        
        return dir.y > 0.0f ? 2 : 3;
    } else {
        
        
        return dir.x > 0.0f ? 0 : 1;
    }
}


float4x4 getFaceRotation(ushort face) {
    float4 xAxis(1.0, 0.0, 0.0, 0.0);
    float4 yAxis(0.0, 1.0, 0.0, 0.0);
    float4 zAxis(0.0, 0.0, 1.0, 0.0);
    float4 zero (0.0, 0.0, 0.0, 1.0);
    switch (face) {
        case 0: return float4x4(-zAxis, yAxis,  xAxis, zero); 
        case 1: return float4x4( zAxis, yAxis, -xAxis, zero); 
        case 2: return float4x4( xAxis,-zAxis,  yAxis, zero); 
        case 3: return float4x4( xAxis, zAxis, -yAxis, zero); 
        case 4: return float4x4(-xAxis, yAxis, -zAxis, zero); 
        default:  return float4x4( xAxis, yAxis,  zAxis, zero); 
    }
}

#if CFX_USE_ATLAS_FOR_SHADOW_MAP
    float shadow_omni(float3 pos_vs, float3 nrm_vs, vfx_light light, depth2d<float> shadowMap, constant float4* shadowKernel, int sampleCount)
#else
    float shadow_omni(float3 pos_vs, float3 nrm_vs, vfx_light light, depthcube<float> shadowMap, constant float4* shadowKernel, int sampleCount)
#endif
    {
        
#define USE_TANGENT_SAMPLING 0

#if CFX_USE_ATLAS_FOR_SHADOW_MAP
        float  depthBias = light.parameters.omni.depthBias;
#else
        float2 scaleBias = light.parameters.omni.shadowScaleBias.xy;
        float  depthBias = light.parameters.omni.shadowScaleBias.z;
#endif

        
        pos_vs += nrm_vs * depthBias;

        
        float4 pos_ls = (light.shadowMatrix * float4(pos_vs, 1.f));

#if !CFX_USE_ATLAS_FOR_SHADOW_MAP
        
        float z_lin = vfx::reduce_max(abs(pos_ls));

        
        
        
        
        float z_ndc = (z_lin * scaleBias.x + scaleBias.y) / z_lin - depthBias;
#endif

        
        float shadow;
        if (sampleCount <= 1) {
#if CFX_USE_ATLAS_FOR_SHADOW_MAP
            ushort face = getCubeFace(normalize(pos_ls.xyz));
            float4 tile = light.tiles[face];
            shadow = ComputeShadow(vfx_shadow_sampler, (getFaceRotation(face) * pos_ls).xyz, light.parameters.omni.projection, shadowMap, tile);
#else
            shadow = shadowMap.sample_compare(vfx_shadow_sampler, pos_ls.xyz, z_ndc);
#endif
        } else {

            
            float filteringSizeFactor = light.shadowRadius;

#if USE_TANGENT_SAMPLING
            float3 tgt_x, tgt_y;
            vfx::orthogonal_basis(pos_ls, tgt_x, tgt_y);
#else
            float3 nrm_ls = (light.shadowMatrix * float4(nrm_vs, 0.f)).xyz;
#endif

            
            float totalAccum = 0.0;
            for(int i=0; i < sampleCount; i++){

#if USE_TANGENT_SAMPLING
                float2 scale = shadowKernel[i].xy * filteringSizeFactor * 2.f;
                float3 smp_ls = pos_ls.xyz + tgt_x * scale.x + tgt_y * scale.y;
#else
                float3 smp_ls = pos_ls.xyz + vfx::randomHemisphereDir(nrm_ls, shadowKernel[i].xy * 0.5 + 0.5) * filteringSizeFactor;
#endif

                
                
                


#if CFX_USE_ATLAS_FOR_SHADOW_MAP
                ushort face = getCubeFace(normalize(smp_ls.xyz));
                float4 tile = light.tiles[face];
                totalAccum += ComputeShadow(vfx_shadow_sampler, (getFaceRotation(face) * float4(smp_ls, 1.0)).xyz, light.parameters.omni.projection, shadowMap, tile);
#else
                totalAccum += shadowMap.sample_compare(vfx_shadow_sampler, smp_ls, z_ndc);
#endif

            }
            shadow = totalAccum / float(sampleCount);
        }

        return 1.f - shadow * light.color.a; 
    }

#if CFX_USE_ATLAS_FOR_SHADOW_MAP
    float shadow(float3 pos, constant vfx_light& light, depth2d<float> shadowMaps, int cascadeCount, bool blendCascade, constant float4* shadowKernel, int sampleCount)
    {
        float shadow = ComputeCascadedShadow(vfx_shadow_sampler, pos, light.shadowMatrix, light.parameters.directional.cascadeScale, light.parameters.directional.cascadeBias, cascadeCount, shadowMaps, blendCascade, shadowKernel, sampleCount, light.shadowRadius, light.tiles).a;
        return 1.f - shadow * light.color.a; 
    }
#else
    float shadow(float3 pos, constant vfx_light& light, depth2d_array<float> shadowMaps, int cascadeCount, bool blendCascade, constant float4* shadowKernel, int sampleCount)
    {
        float shadow = ComputeCascadedShadow(vfx_shadow_sampler, pos, light.shadowMatrix, light.parameters.directional.cascadeScale, light.parameters.directional.cascadeBias, cascadeCount, shadowMaps, blendCascade, shadowKernel, sampleCount, light.shadowRadius).a;
        return 1.f - shadow * light.color.a; 
    }
#endif

    

    void add_directional(vfx_light light)
    {
#ifdef USE_PBR
        float intensity = PBR_INTENSITY_FACTOR;
#else
        float intensity = 1.0f;
#endif
        shade(light.dir, light.color.rgb, intensity);
    }

    
    void add_directional(vfx_light light, texture2d<half> goboTexture, sampler goboSampler)
    {
#ifdef USE_PBR
        float intensity = PBR_INTENSITY_FACTOR;
#else
        float intensity = 1.0f;
#endif
        light.color.rgb = gobo(surface.position, light, goboTexture, goboSampler);
        shade(light.dir, light.color.rgb, intensity);
    }

    
    void add_directional(vfx_light light, depth2d<float> shadowMap)
    {
#ifdef USE_PBR
        float intensity = PBR_INTENSITY_FACTOR;
#else
        float intensity = 1.0f;
#endif
        intensity *= shadow(surface.position, light, shadowMap);
        shade(light.dir, light.color.rgb, intensity);
    }

    
    void add_directional(vfx_light light, depth2d<float> shadowMap, constant float4* shadowKernel)
    {
#ifdef USE_PBR
        float intensity = PBR_INTENSITY_FACTOR;
#else
        float intensity = 1.0f;
#endif
        intensity *= shadow(surface.position, light, shadowMap, shadowKernel);
        shade(light.dir, light.color.rgb, intensity);
    }

    void add_directional(vfx_light light, depth2d<float> shadowMap, constant float4* shadowKernel, int sampleCount)
    {
#ifdef USE_PBR
        float intensity = PBR_INTENSITY_FACTOR;
#else
        float intensity = 1.0f;
#endif
        intensity *= shadow(surface.position, light, shadowMap, shadowKernel, sampleCount);
        shade(light.dir, light.color.rgb, intensity);
    }

    
    void add_directional(vfx_light light, depth2d<float> shadowMap, int sampleCount)
    {
#ifdef USE_PBR
        float intensity = PBR_INTENSITY_FACTOR;
#else
        float intensity = 1.0f;
#endif
        intensity *= shadow(surface.position, light, shadowMap, sampleCount);
        shade(light.dir, light.color.rgb, intensity);
    }

    

#if CFX_USE_ATLAS_FOR_SHADOW_MAP
    void add_directional(constant vfx_light& light, depth2d<float> shadowMaps, int cascadeCount, bool blendCascade, constant float4* shadowKernel, int sampleCount, bool debugCascades)
#else
    void add_directional(constant vfx_light& light, depth2d_array<float> shadowMaps, int cascadeCount, bool blendCascade, constant float4* shadowKernel, int sampleCount, bool debugCascades)
#endif
    {
#ifdef USE_PBR
        float intensity = PBR_INTENSITY_FACTOR;
#else
        float intensity = 1.0f;
#endif
        if (debugCascades) {

#if CFX_USE_ATLAS_FOR_SHADOW_MAP
            float4 shadowDebug = ComputeCascadedShadow(vfx_shadow_sampler, surface.position, light.shadowMatrix, light.parameters.directional.cascadeScale, light.parameters.directional.cascadeBias, cascadeCount, shadowMaps, blendCascade, shadowKernel, sampleCount, light.shadowRadius, light.tiles);
#else
            float4 shadowDebug = ComputeCascadedShadow(vfx_shadow_sampler, surface.position, light.shadowMatrix, light.parameters.directional.cascadeScale, light.parameters.directional.cascadeBias, cascadeCount, shadowMaps, blendCascade, shadowKernel, sampleCount, light.shadowRadius);
#endif
            intensity *= (1.f - shadowDebug.a);
            shade(light.dir, light.color.rgb, intensity);
            diffuse.rgb = mix(diffuse.rgb, shadowDebug.rgb, light.color.a);
        } else {
            intensity *= shadow(surface.position, light, shadowMaps, cascadeCount, blendCascade, shadowKernel, sampleCount);
            shade(light.dir, light.color.rgb, intensity);
        }
    }

    

    void add_omni(vfx_light light)
    {
        float3 unnormalized_l = light.pos - surface.position;
        float3 l = normalize(unnormalized_l);
        shade(l, light.color.rgb, dist_attenuation(unnormalized_l, light) * BoostFactor);
    }

#if CFX_USE_ATLAS_FOR_SHADOW_MAP
    void add_omni(vfx_light light, depth2d<float> shadowMap, constant float4* shadowKernel, int sampleCount)
#else
    void add_omni(vfx_light light, depthcube<float> shadowMap, constant float4* shadowKernel, int sampleCount)
#endif
    {
        float3 unnormalized_l = light.pos - surface.position;
        float3 l = normalize(unnormalized_l);
        float intensity = dist_attenuation(unnormalized_l, light) * BoostFactor;
        intensity *= shadow_omni(surface.position, surface.normal, light, shadowMap, shadowKernel, sampleCount);
        shade(l, light.color.rgb, intensity);
    }

    void add_local_omni(vfx_light light)
    {
        float3 unnormalized_l = light.pos - surface.position;
        float3 l = normalize(unnormalized_l);
        shade(l, light.color.rgb, dist_attenuation(unnormalized_l, light) * BoostFactor);
    }

    

    void add_spot(vfx_light light)
    {
        float3 unnormalized_l = light.pos - surface.position;
        float3 l = normalize(unnormalized_l);
        float intensity = dist_attenuation(unnormalized_l, light) * BoostFactor;
        intensity      *= spot_attenuation(l, light);
        shade(l, light.color.rgb, intensity);
    }

    void add_spot(vfx_light light, texture2d<half> goboTexture, sampler goboSampler)
    {
        float3 unnormalized_l = light.pos - surface.position;
        float3 l = normalize(unnormalized_l);
        float intensity = dist_attenuation(unnormalized_l, light) * BoostFactor;
        intensity      *= spot_attenuation(l, light);
        light.color.rgb = gobo(surface.position, light, goboTexture, goboSampler);

        
        
        
              shade(l, light.color.rgb, intensity);
        
    }

    void add_local_spot(vfx_light light)
    {
        float3 unnormalized_l = light.pos - surface.position;
        float3 l = normalize(unnormalized_l);
        float intensity = dist_attenuation(unnormalized_l, light) * BoostFactor;
        intensity      *= spot_attenuation(l, light);
        shade(l, light.color.rgb, intensity);
    }

    
    void add_spot(vfx_light light, depth2d<float> shadowMap, constant float4* shadowKernel, int sampleCount)
    {
        float3 unnormalized_l = light.pos - surface.position;
        float3 l = normalize(unnormalized_l);
        float intensity = dist_attenuation(unnormalized_l, light) * BoostFactor;;
        intensity      *= spot_attenuation(l, light);
        intensity      *= shadow(surface.position, light, shadowMap, shadowKernel, sampleCount);
        shade(l, light.color.rgb, intensity);
    }
    
    void add_spot(vfx_light light, 
                  depth2d<float> shadowMap, constant float4* shadowKernel, int sampleCount,
                  texture2d<half> goboTexture, sampler goboSampler)
    {
        float3 unnormalized_l = light.pos - surface.position;
        float3 l = normalize(unnormalized_l);
        float intensity = dist_attenuation(unnormalized_l, light);
        intensity      *= spot_attenuation(l, light);
        intensity      *= shadow(surface.position, light, shadowMap, shadowKernel, sampleCount);
        light.color.rgb = gobo(surface.position, light, goboTexture, goboSampler);
        shade(l, light.color.rgb, intensity);
    }

    

#ifdef USE_PBR

    

#ifdef CFX_SUPPORT_CUBE_ARRAY
    void add_local_probe(vfx_light light, texturecube_array<half> probeTextureArray)
#else
    void add_local_probe(vfx_light light, texture2d_array<half> probeTextureArray)
#endif
    {
#if !PROBES_NORMALIZATION
        if (probeRadianceRemainingFactor <= 0.f)
            return;
#endif

        bool parallaxCorrection = light.parameters.probe.parallaxCorrection;
        int    probeIndex       = light.parameters.probe.index;
        float3 probeExtents     = light.parameters.probe.halfExtents.xyz;
        float  blendDist        = light.parameters.probe.halfExtents.w;
        float3 probeOffset      = light.parameters.probe.offset;
        float3 parallaxExtents  = light.parameters.probe.parallaxExtents;
        float3 parallaxCenter   = light.parameters.probe.parallaxCenter;

        float3 n = surface.normal;
        float3 v = surface.view;
        float3 r = reflect(-v, n); 

        float3 specDir = vfx::mat4_mult_float3(light.shadowMatrix, r);

        
        float3 pos_ls = (light.shadowMatrix * float4(surface.position, 1.f)).xyz;

        
        float3 d = abs(pos_ls) - probeExtents;
#if PROBES_OUTER_BLENDING
        if (any(d > blendDist))
#else
        if (any(d > 0.f))
#endif
        {
            return;
        }

#if PROBES_NORMALIZATION
        
        
#if PROBES_OUTER_BLENDING
        float3 nd = saturate(-(d / blendDist) * 0.5f + 0.5f);
#else
        float3 nd = saturate(-(d / blendDist));
#endif
        float probeFactor = (nd.x * nd.y * nd.z) * light.color.r;
#else
        
        float sd = min(max(d.x,max(d.y,d.z)),0.0) + length(max(d,0.0));
#if PROBES_OUTER_BLENDING
        float probeFactor = saturate(1.f - sd / blendDist);
#else
        float probeFactor = saturate(-sd / blendDist);
#endif
        
        
        
        probeFactor *= probeRadianceRemainingFactor * light.color.r; 
#endif

        if (parallaxCorrection ) {
            
            float3 pos_off = pos_ls + parallaxCenter;
            float3 t1 = ( parallaxExtents - pos_off) / specDir;
            float3 t2 = (-parallaxExtents - pos_off) / specDir;
            float3 tmax = max(max(0, t1), t2); 
            float t = min(tmax.x, min(tmax.y, tmax.z));

            
            float3 hit_ls = pos_ls + specDir * t;
            specDir = hit_ls - probeOffset;
        }

        
        specDir.z *= -1.0;
        
        
        
        float ao = surface.ambientOcclusion;
#ifdef USE_BENTNORMALS
        ao = mix(mix(max(dot(surface.bentNormal, r), 0.), 1., ao), ao, surface.roughness*surface.roughness);
#endif
        
        float mipd = float(probeTextureArray.get_num_mip_levels()) - 1.f;
        const float intensity = ao * probeFactor;

        float mips = surface.roughness * mipd;
#ifdef CFX_SUPPORT_CUBE_ARRAY
        float3 LD = float3(probeTextureArray.sample(vfx_lighting::linearSampler, specDir, probeIndex, level(mips)).rgb);
#else
        float2 specUV = vfx::dual_paraboloid_from_cartesian(normalize(specDir));
        float3 LD = float3(probeTextureArray.sample(vfx_lighting::linearSampler, specUV, probeIndex, level(mips)).rgb);
#endif

        

        
#if PROBES_NORMALIZATION
        probesWeightedSum += float4(LD * intensity * pbr.probeReflectance, probeFactor);
#else
        probeRadianceRemainingFactor = saturate(probeRadianceRemainingFactor - probeFactor);
#ifdef DISABLE_SPECULAR
        pbr.envDiffuse += LD * intensity * pbr.probeReflectance;
#else
        pbr.envSpecular += LD * intensity * pbr.probeReflectance;
#endif
#endif
        
#ifdef USE_CLEARCOAT
        n = surface.clearCoatNormal;
        r = reflect(-v, n);
        specDir = vfx::mat4_mult_float3(light.shadowMatrix, r);
        
        specDir.z *= -1.0;
        if (parallaxCorrection ) {
            float3 pos_off = pos_ls + parallaxCenter;
            
            float3 t1 = ( parallaxExtents - pos_off) / specDir;
            float3 t2 = (-parallaxExtents - pos_off) / specDir;
            float3 tmax = max(max(0, t1), t2); 
            float t = min(tmax.x, min(tmax.y, tmax.z));
            
            
            float3 hit_ls = pos_ls + specDir * t;
            specDir = hit_ls - probeOffset;
        }
        mips = surface.clearCoatRoughness * mipd;
#ifdef CFX_SUPPORT_CUBE_ARRAY
        LD = float3(probeTextureArray.sample(vfx_lighting::linearSampler, specDir, probeIndex, level(mips)).rgb);
#else
        specUV = vfx::dual_paraboloid_from_cartesian(normalize(specDir));
        LD = float3(probeTextureArray.sample(vfx_lighting::linearSampler, specUV, probeIndex, level(mips)).rgb);
#endif
#if PROBES_NORMALIZATION
        probesWeightedSum += float4(LD * intensity * pbr.probeReflectanceClearCoat, probeFactor) * surface.clearCoat;
#else

#ifdef DISABLE_SPECULAR
        pbr.envDiffuse += LD * intensity * pbr.probeReflectanceClearCoat  * surface.clearCoat;
#else
        specular += LD * intensity * pbr.probeReflectanceClearCoat  * surface.clearCoat;
#endif
#endif
#endif
    }

    void add_global_probe(float4x4 localDirToWorldCubemapDir,
                          float environmentIntensity,
#ifdef CFX_SUPPORT_CUBE_ARRAY
                          texturecube_array<half> probeTextureArray
#else
                          texture2d_array<half> probeTextureArray
#endif
                          )
    {
        float3 n = surface.normal;
        float3 v = surface.view;
        float3 r = reflect(-v, n); 
        
        float3 specDir = vfx::mat4_mult_float3(localDirToWorldCubemapDir, r);
        float mips = surface.roughness * float(probeTextureArray.get_num_mip_levels() - 1);
        
#ifdef CFX_SUPPORT_CUBE_ARRAY
        float3 LD = float3(probeTextureArray.sample(vfx_lighting::linearSampler, specDir, 0, level(mips)).rgb);
#else
        float2 specUV = vfx::dual_paraboloid_from_cartesian(normalize(specDir));
        float3 LD = float3(probeTextureArray.sample(vfx_lighting::linearSampler, specUV, 0, level(mips)).rgb);
#endif
        
        
        specular += pbr.probeReflectance * LD * surface.ambientOcclusion * environmentIntensity;
    }

    void add_global_probe(texturecube<float, access::sample> specularLD,
                          float4x4                           localDirToWorldCubemapDir,
                          float                              environmentIntensity)
    {
        float3 n        = surface.normal;
        float3 v        = surface.view;
        float3 r        = reflect(-v, n); 
        float roughness = surface.roughness;

#if USE_PBR_DOMINANT_DIRECTION
        float alpha = roughness * roughness;
        float smoothness = 1.0f - alpha;
        
        float specularLerpFactor = (1. - smoothness * (sqrt(smoothness) + alpha));
        
        float3 specularDominantNDirection = mix(r, n, specularLerpFactor); 
#else
        float3 specularDominantNDirection = r;
#endif
        
        float ao = surface.ambientOcclusion;
        
        
        
#ifdef USE_BENTNORMALS
        ao = mix(mix(max(dot(surface.bentNormal, r), 0.), 1., ao), ao, roughness*roughness);
#endif
        
        
#ifdef USE_RE_RADIANCE_IRRADIANCE_MAP_SAMPLING
        float mipLevel = roughness * float(specularLD.get_num_mip_levels() - 1);
#else
        float mipLevel = sqrt(roughness) * float(specularLD.get_num_mip_levels() - 1);
#endif
        float3 dir = vfx::mat4_mult_float3(localDirToWorldCubemapDir, specularDominantNDirection);
        float3 LD = specularLD.sample(vfx_lighting::linearSampler, dir, level(mipLevel)).rgb;
        pbr.envSpecular += pbr.probeReflectance * LD * ao * environmentIntensity;
    }

#ifdef USE_CLEARCOAT
    void add_global_probeClearCoat(texturecube<float, access::sample> specularLD,
                          float4x4                           localDirToWorldCubemapDir,
                          float                              environmentIntensity)
    {
        float3 n = surface.clearCoatNormal;
        
        float3 v        = surface.view;
        float3 r        = reflect(-v, n); 
        float roughness = surface.clearCoatRoughness;

        
        
        float ao = surface.ambientOcclusion;
        
        
#ifdef USE_BENTNORMALS
        ao = mix(mix(max(dot(surface.bentNormal, r), 0.), 1., ao), ao, roughness*roughness);
#endif
        
        
        float mipLevel = sqrt(roughness) * float(specularLD.get_num_mip_levels() - 1);
        float3 LD = specularLD.sample(vfx_lighting::linearSampler, vfx::mat4_mult_float3(localDirToWorldCubemapDir, r), level(mipLevel)).rgb;

        
        float Fc = vfx_brdf_F_opt(0.04f, pbr.NoVClearCoat).r * surface.clearCoat;
        float attenuation = 1.0f - Fc;
        specular *= (attenuation * attenuation);
        
        specular += pbr.probeReflectanceClearCoat * LD  * surface.clearCoat * ao * environmentIntensity;
    }
#endif
    
    

    void add_irradiance_from_selfIllum()
    {
        float selfIlluminationAO = saturate(mix(1.f, surface.ambientOcclusion, pbr.selfIlluminationOcclusion));
        float3 irradiance = surface.selfIllumination.rgb;
        
        float3 diffuseAlbedo = mix(pbr.albedo, float3(0.0), surface.metalness);
#ifdef USE_PBR_LAMBERTIAN_REFLECTION
        pbr.envDiffuse += selfIlluminationAO * irradiance * diffuseAlbedo;
#else
        float3 diffuseReflectance = diffuseAlbedo * (pbr.diffuseHammonFactors.x + diffuseAlbedo * pbr.diffuseHammonFactors.y);
        pbr.envDiffuse += selfIlluminationAO * irradiance * diffuseReflectance;
#endif
    }

    void add_global_irradiance_from_sh(float4x4         localDirToWorldCubemapDir,
#if defined(USE_PROBES_LIGHTING) && (USE_PROBES_LIGHTING == 2)
                                       sh2_coefficients shCoefficients)
#else
    sh3_coefficients shCoefficients)
#endif
    {
#ifdef USE_BENTNORMALS
        float3 n = surface.bentNormal;
        float ao = surface.aoDirectionnal;
#else
        float3 n = surface.normal;
        float ao = surface.ambientOcclusion;
#endif
        float3 n_sh_space = vfx::mat4_mult_float3(localDirToWorldCubemapDir, n);
        float3 irradiance = shEvalDirection(float4(n_sh_space.xy, -n_sh_space.z, 1.), shCoefficients);
        
        float3 diffuseAlbedo = mix(pbr.albedo, float3(0.0), surface.metalness);
#ifdef USE_PBR_LAMBERTIAN_REFLECTION
        pbr.envDiffuse += ao * irradiance * diffuseAlbedo;
#else
        float3 diffuseReflectance = diffuseAlbedo * (pbr.diffuseHammonFactors.x + diffuseAlbedo * pbr.diffuseHammonFactors.y);
        pbr.envDiffuse += ao * irradiance * diffuseReflectance;
#endif
    }

    void add_global_irradiance_probe(texturecube<float, access::sample> irradianceTexture,
                                     float4x4                           localDirToWorldCubemapDir,
                                     float                              environmentIntensity)
    {
#if USE_PBR_DOMINANT_DIRECTION
#ifdef USE_BENTNORMALS
        float3 n = surface.bentNormal;
        float ao = surface.aoDirectionnal;
#else
        float3 n = surface.normal;
        float ao = surface.ambientOcclusion;
#endif
        float3 v = surface.view;
        
        
        const half a = 1.02341h * surface.roughness - 1.51174h; 
        const half b = -0.511705h * surface.roughness + 0.755868h;
        const half diffuseBendFactor = saturate((pbr.NoV * a + b) * surface.roughness);
        float3 diffuseDominantNDirection = mix(n, v, diffuseBendFactor);
#else
        float3 diffuseDominantNDirection = n;
#endif
        
        float3 n_cube_space = vfx::mat4_mult_float3(localDirToWorldCubemapDir, diffuseDominantNDirection);
        float3 irradiance = irradianceTexture.sample(vfx_lighting::linearSampler, n_cube_space).rgb;
        
        float3 diffuseAlbedo = mix(pbr.albedo, float3(0.0), surface.metalness);
        
#ifdef USE_PBR_LAMBERTIAN_REFLECTION
        pbr.envDiffuse += (ao * environmentIntensity) * irradiance * diffuseAlbedo;
#else
        float3 diffuseReflectance = diffuseAlbedo * (pbr.diffuseHammonFactors.x + diffuseAlbedo * pbr.diffuseHammonFactors.y);
        pbr.envDiffuse += (ao * environmentIntensity) * irradiance * diffuseReflectance;
#endif
    }

#endif 

    

    static constexpr sampler iesSampler = sampler(filter::linear, mip_filter::none, address::clamp_to_edge);
    
    float ies_attenuation(float3 l, vfx_light light, texture2d<half> iesTexture)
    {
#if USE_QUAT_FOR_IES
        float3 v    = vfx::quaternion_rotate_vector(light.parameters.ies.light_from_view_quat, -l);
#else
        float3 v    = vfx::matrix_rotate(light.parameters.ies.light_from_view, -l);
#endif
        float phi   = (v.z * light.parameters.ies.scaleBias.x + light.parameters.ies.scaleBias.y);
        float theta = atan2(v.y, v.x) * 0.5f * M_1_PI_F;
        return iesTexture.sample(iesSampler, float2(phi, abs(theta))).r;
    }

    void add_ies(vfx_light light, texture2d<half> iesTexture)
    {
        float3 unnormalized_l = light.pos - surface.position;
        float3 l = normalize(unnormalized_l);
        float intensity = dist_attenuation(unnormalized_l, light);
        intensity      *= ies_attenuation(l, light, iesTexture);
        shade(l, light.color.rgb, intensity);
    }

    void add_ies(vfx_light light, texture2d<half> iesTexture, depth2d<float> shadowMap, constant float4* shadowKernel, int sampleCount)
    {
        float3 unnormalized_l = light.pos - surface.position;
        float3 l = normalize(unnormalized_l);
        float intensity = dist_attenuation(unnormalized_l, light);
        intensity      *= ies_attenuation(l, light, iesTexture);
        intensity      *= shadow(surface.position, light, shadowMap, shadowKernel, sampleCount);
        shade(l, light.color.rgb, intensity);
    }

    

    void add_area_rectangle(vfx_light light, texture2d_array<float> bakedDataTexture)
    {
#ifdef USE_PBR
        float3 v = surface.view;
        float3 n = surface.normal;
        float3 p = surface.position;

        
        float3 tangent = normalize(v - n * dot(v, n));
        float3 bitangent = cross(n, tangent);
        float3x3 shadingSpaceTransform = transpose(float3x3(tangent, n, bitangent));

        float3 lightCenter = light.shadowMatrix[3].xyz;
        
        
        float sidedness = dot(light.dir, lightCenter - p);
        if (light.parameters.area.rectangle.doubleSided == false && sidedness <= 0.f)
            return;
        
        float3 lightRight = light.shadowMatrix[0].xyz * light.parameters.area.rectangle.halfExtents.x * sign(sidedness);
        float3 lightTop   = light.shadowMatrix[1].xyz * light.parameters.area.rectangle.halfExtents.y;
        
        float4x3 cornerDirections = float4x3((lightCenter + lightRight + lightTop) - p,
                                             (lightCenter + lightRight - lightTop) - p,
                                             (lightCenter - lightRight - lightTop) - p,
                                             (lightCenter - lightRight + lightTop) - p);

        cornerDirections[0] = shadingSpaceTransform * cornerDirections[0];
        cornerDirections[1] = shadingSpaceTransform * cornerDirections[1];
        cornerDirections[2] = shadingSpaceTransform * cornerDirections[2];
        cornerDirections[3] = shadingSpaceTransform * cornerDirections[3];

        float diffuseAmount = pbr_area_light_eval_rectangle(cornerDirections);

        float brdfNorm = 1.f;
        float3x3 inverseLTCMatrix = vfx_sample_area_light_precomputed_data(v, n, surface.roughness, &brdfNorm, bakedDataTexture);

        cornerDirections[0] = inverseLTCMatrix * cornerDirections[0];
        cornerDirections[1] = inverseLTCMatrix * cornerDirections[1];
        cornerDirections[2] = inverseLTCMatrix * cornerDirections[2];
        cornerDirections[3] = inverseLTCMatrix * cornerDirections[3];

        float specularAmount = brdfNorm * pbr_area_light_eval_rectangle(cornerDirections);

        float3 effectiveAlbedo = mix(float3(1.0), float3(0.0), surface.metalness); 
        
        float3 lightColor = light.color.rgb;
        diffuse  += diffuseAmount * lightColor * effectiveAlbedo;
        specular += specularAmount * lightColor * pbr.reflectance;
#endif
    }

    void add_area_polygon(vfx_light light, texture2d_array<float> bakedDataTexture, device packed_float2 *vertexPositions)
    {
#ifdef USE_PBR
        float3 v = surface.view;
        float3 n = surface.normal;
        float3 p = surface.position;

        
        float3 tangent = normalize(v - n * dot(v, n));
        float3 bitangent = cross(n, tangent);
        float3x3 shadingSpaceTransform = transpose(float3x3(tangent, n, bitangent));

        float3 lightCenter = light.shadowMatrix[3].xyz;
        
        
        float sidedness = dot(light.dir, lightCenter - p);
        if (light.parameters.area.polygon.doubleSided == false && sidedness <= 0.f)
            return;
        
        float3 lightRight = light.shadowMatrix[0].xyz * sign(sidedness);
        float3 lightTop   = light.shadowMatrix[1].xyz;

        p           = shadingSpaceTransform * p;
        lightCenter = shadingSpaceTransform * lightCenter;
        lightRight  = shadingSpaceTransform * lightRight;
        lightTop    = shadingSpaceTransform * lightTop;

        float diffuseAmount = pbr_area_light_eval_polygon(p, lightCenter, lightRight, lightTop, light.parameters.area.polygon.vertexCount, vertexPositions);

        float brdfNorm = 1.f;
        float3x3 inverseLTCMatrix = vfx_sample_area_light_precomputed_data(v, n, surface.roughness, &brdfNorm, bakedDataTexture);

        p           = inverseLTCMatrix * p;
        lightCenter = inverseLTCMatrix * lightCenter;
        lightRight  = inverseLTCMatrix * lightRight;
        lightTop    = inverseLTCMatrix * lightTop;

        float specularAmount = brdfNorm * pbr_area_light_eval_polygon(p, lightCenter, lightRight, lightTop, light.parameters.area.polygon.vertexCount, vertexPositions);
        
        float3 effectiveAlbedo = mix(float3(1.0), float3(0.0), surface.metalness); 

        float3 lightColor = light.color.rgb;
        diffuse  += diffuseAmount * lightColor * effectiveAlbedo;
        specular += specularAmount * lightColor * pbr.reflectance;
#endif
    }

    void add_area_line(vfx_light light, texture2d_array<float> bakedDataTexture)
    {
#ifdef USE_PBR
        float3 v = surface.view;
        float3 n = surface.normal;
        float3 p = surface.position;

        
        float3 tangent = normalize(v - n * dot(v, n));
        float3 bitangent = cross(n, tangent);
        float3x3 shadingSpaceTransform = transpose(float3x3(tangent, n, bitangent));

        float3 lightCenter = light.shadowMatrix[3].xyz;
        float3 lightRight  = light.shadowMatrix[0].xyz * light.parameters.area.line.halfLength;

        float2x3 cornerDirections = float2x3((lightCenter + lightRight) - p,
                                             (lightCenter - lightRight) - p);

        cornerDirections[0] = shadingSpaceTransform * cornerDirections[0];
        cornerDirections[1] = shadingSpaceTransform * cornerDirections[1];

        float diffuseAmount = pbr_area_light_eval_line(cornerDirections);

        float brdfNorm = 1.f;
        float3x3 inverseLTCMatrix = vfx_sample_area_light_precomputed_data(v, n, surface.roughness, &brdfNorm, bakedDataTexture);

        cornerDirections[0] = inverseLTCMatrix * cornerDirections[0];
        cornerDirections[1] = inverseLTCMatrix * cornerDirections[1];

        float specularAmount = brdfNorm * pbr_area_light_eval_line(cornerDirections);

        float3 ortho = normalize(cross(cornerDirections[0], cornerDirections[1]));
        float ltcWidthFactor = 1.0 / length(vfx_ltc_matrix_invert_transpose(inverseLTCMatrix) * ortho);
        specularAmount *= ltcWidthFactor;
        
        float3 effectiveAlbedo = mix(float3(1.0), float3(0.0), surface.metalness); 

        float3 lightColor = light.color.rgb;
        diffuse  += diffuseAmount * lightColor * effectiveAlbedo;
        specular += specularAmount * lightColor * pbr.reflectance;
#endif
    }

    void add_area_ellipse(vfx_light light, texture2d_array<float> bakedDataTexture)
    {
#ifdef USE_PBR
#endif
    }

    void add_area_ellipsoid(vfx_light light, texture2d_array<float> bakedDataTexture)
    {
#ifdef USE_PBR
#endif
    }
};

#endif 


enum CFXColorMask {
    kCFXColorMaskRed    = 0x1 << 3,
    kCFXColorMaskGreen  = 0x1 << 2,
    kCFXColorMaskBlue   = 0x1 << 1,
    kCFXColorMaskAlpha  = 0x1 << 0
};

inline float4 colorFromMask(float4 col, int mask)
{
    switch (mask) {

        case kCFXColorMaskRed:                      return col.r;
        case kCFXColorMaskRed|kCFXColorMaskGreen:   return float4(col.rg, 0.f, 1.f);
        case kCFXColorMaskRed|kCFXColorMaskBlue:    return float4(col.rb, 0.f, 1.f);
        case kCFXColorMaskRed|kCFXColorMaskAlpha:   return float4(col.ra, 0.f, 1.f);

        case kCFXColorMaskGreen:                    return col.g;
        case kCFXColorMaskGreen|kCFXColorMaskBlue:  return float4(col.bg, 0.f, 1.f);
        case kCFXColorMaskGreen|kCFXColorMaskAlpha: return float4(col.ag, 0.f, 1.f);

        case kCFXColorMaskBlue:     return col.b;
        case kCFXColorMaskBlue|kCFXColorMaskAlpha:  return float4(col.ab, 0.f, 1.f);

        case kCFXColorMaskAlpha:    return col.a;
    }
    return col;
}

#ifndef USE_PBR

inline float3 illuminate(VFXShaderSurface surface, VFXShaderLightingContribution lighting)
{
    float3 albedo = surface.diffuse.rgb * surface.ambientOcclusion;
    float3 color = lighting.diffuse * albedo;
#if defined(USE_AMBIENT_LIGHTING)
    color +=  lighting.ambient * albedo;
#endif
#ifdef USE_SELFILLUMINATION
    color += surface.diffuse.rgb * surface.selfIllumination.rgb;
#endif
    
    
    
#ifdef USE_SPECULAR
    float3 S = lighting.specular;
#elif defined(USE_REFLECTIVE)
    float3 S = float3(0.);
#endif
#ifdef USE_REFLECTIVE
    S += surface.reflective.rgb * surface.ambientOcclusion;
#endif
#ifdef USE_SPECULAR
    S *= surface.specular.rgb;
#endif
#if (defined(USE_SPECULAR) || defined(USE_REFLECTIVE)) && !defined(DISABLE_SPECULAR)
    color += S;
#endif
#ifdef USE_EMISSION
    color += surface.emission.rgb;
#endif
#ifdef USE_MULTIPLY
    color *= surface.multiply.rgb;
#endif
#ifdef USE_MODULATE
    color *= lighting.modulate;
#endif
    return color;
}
#endif

struct VFXShaderGeometry
{
    float4 position;
    float3 normal;
    float4 tangent;
    float4 color;
    float pointSize;
    float2 texcoords[8]; 
#ifdef USE_CLIP_DISTANCE0
    float clipDistance0;
#endif
#ifdef USE_CLIP_DISTANCE1
    float clipDistance1;
#endif
#ifdef USE_CLIP_DISTANCE2
    float clipDistance2;
#endif
#ifdef USE_CLIP_DISTANCE3
    float clipDistance3;
#endif
};

struct commonprofile_uniforms {
    
    float4 diffuseColor;
    float4 specularColor;
    float4 ambientOcclusionColor;
    float4 emissionColor;
    float4 selfIlluminationColor;
    float4 reflectiveColor;
    float4 multiplyColor;
    float4 transparentColor;
    float clearCoat;
    float clearCoatRoughness;
    float3 clearCoatNormal;
    float subsurface;
    float4 subsurfaceRadius;
    float transmission;
    float4 transmissionColor;
    float indexOfRefraction;
    float metalness;
    
    float roughness;
    float diffuseIntensity;
    float specularIntensity;
    float normalIntensity;
    float ambientOcclusionIntensity;
    float emissionIntensity;
    float selfIlluminationIntensity;
    float reflectiveIntensity;
    float multiplyIntensity;
    float transparentIntensity;
    
    float metalnessIntensity;
    float roughnessIntensity;
    float clearCoatIntensity;
    float clearCoatRoughnessIntensity;
    float clearCoatNormalIntensity;
    float subsurfaceIntensity;
    float subsurfaceRadiusIntensity;
    float transmissionIntensity;
    float transmissionColorIntensity;
    float displacementIntensity;
    float materialShininess;
    float selfIlluminationOcclusion;
    float alphaCutoff;
    float3 fresnel; 
    
#if USE_ARGUMENT_BUFFERS
    texture2d<float>    emissionTexture;
    sampler             emissionSampler;
    texture2d<float>    ambientOcclusionTexture;
    sampler             ambientOcclusionSampler;
    texture2d<float>    diffuseTexture;
    sampler             diffuseSampler;
    texture2d<float>    specularTexture;
    sampler             specularSampler;
#if defined(USE_REFLECTIVE_CUBEMAP)
    texturecube<float>  reflectiveTexture;
#else
    texture2d<float>    reflectiveTexture;
#endif
    sampler             reflectiveSampler;
    texture2d<float>    transparentTexture;
    sampler             transparentSampler;
    texture2d<float>    multiplyTexture;
    sampler             multiplySampler;
    texture2d<float>    normalTexture;
    sampler             normalSampler;
    texture2d<float>    selfIlluminationTexture;
    sampler             selfIlluminationSampler;
    texture2d<float>    metalnessTexture;
    sampler             metalnessSampler;
    texture2d<float>    roughnessTexture;
    sampler             roughnessSampler;
    texture2d<float>    displacementTexture;
    sampler             displacementSampler;
    
#endif 
#ifdef TEXTURE_TRANSFORM_COUNT
    float4x4 textureTransforms[TEXTURE_TRANSFORM_COUNT];
#endif
};

#ifdef USE_OPENSUBDIV





struct osd_packed_vertex {
    packed_float3 position;
#if defined(OSD_USER_VARYING_DECLARE_PACKED)
    OSD_USER_VARYING_DECLARE_PACKED
#endif
};

#endif


#ifdef USE_DISPLACEMENT_MAP
static void applyDisplacement(texture2d<float>                 displacementTexture,
                              sampler                          displacementTextureSampler,
                              float2                           displacementTexcoord,
                              thread VFXShaderGeometry&        geometry,
                              constant commonprofile_uniforms& vfx_commonprofile)
{
#ifdef USE_DISPLACEMENT_TEXTURE_COMPONENT
	float altitude = colorFromMask(displacementTexture.sample(displacementTextureSampler, displacementTexcoord), USE_DISPLACEMENT_TEXTURE_COMPONENT).r;
#ifdef USE_DISPLACEMENT_INTENSITY
	altitude *= vfx_commonprofile.displacementIntensity;
#endif
#if defined(USE_NORMAL) && defined(HAS_OR_GENERATES_NORMAL)
	float3 bitangent = geometry.tangent.w * normalize(cross(geometry.tangent.xyz, geometry.normal.xyz));
	geometry.position.xyz += geometry.normal * altitude;
	
	float3 offset = float3(1.f / displacementTexture.get_width(), 1.f / displacementTexture.get_height(), 0.f);
	float3 h;
	h.x = colorFromMask(displacementTexture.sample(displacementTextureSampler, displacementTexcoord), USE_DISPLACEMENT_TEXTURE_COMPONENT).r;
	h.y = colorFromMask(displacementTexture.sample(displacementTextureSampler, displacementTexcoord+offset.xz), USE_DISPLACEMENT_TEXTURE_COMPONENT).r;
	h.z = colorFromMask(displacementTexture.sample(displacementTextureSampler, displacementTexcoord-offset.zy), USE_DISPLACEMENT_TEXTURE_COMPONENT).r;
	
#ifdef USE_DISPLACEMENT_INTENSITY
	h *= vfx_commonprofile.displacementIntensity;
#endif
	
	float3 n = normalize( float3( (h.x - h.y)/offset.x, 1., (h.x - h.z)/offset.y) );
	geometry.normal = geometry.tangent.xyz * n.x + geometry.normal.xyz * n.y + bitangent.xyz * n.z;
	geometry.tangent.xyz = normalize(cross(bitangent, geometry.normal));
#endif 
#else 
	float3 displacement = displacementTexture.sample(displacementTextureSampler, displacementTexcoord).rgb;
#ifdef USE_DISPLACEMENT_INTENSITY
	displacement *= vfx_commonprofile.displacementIntensity;
#endif
#if defined(USE_NORMAL) && defined(HAS_OR_GENERATES_NORMAL)
	float3 bitangent = geometry.tangent.w * normalize(cross(geometry.tangent.xyz, geometry.normal.xyz));
	geometry.position.xyz += geometry.tangent.xyz * displacement.x + geometry.normal.xyz * displacement.y + bitangent.xyz * displacement.z;
	
	float3 offset = float3(1.f / displacementTexture.get_width(), 1.f / displacementTexture.get_height(), 0.f);
	float3 a = displacementTexture.sample(displacementTextureSampler, displacementTexcoord).rgb;
	float3 b = displacementTexture.sample(displacementTextureSampler, displacementTexcoord+offset.xz).rgb;
	float3 c = displacementTexture.sample(displacementTextureSampler, displacementTexcoord+offset.zy).rgb;
	
#ifdef USE_DISPLACEMENT_INTENSITY
	a *= vfx_commonprofile.displacementIntensity;
	b *= vfx_commonprofile.displacementIntensity;
	c *= vfx_commonprofile.displacementIntensity;
#endif
	
	b += offset.xzz;
	c -= offset.zzy;
	float3 n = (normalize( cross( b-a, c-a ) ));
	geometry.normal = geometry.tangent.xyz * n.x + geometry.normal.xyz * n.y + bitangent.xyz * n.z;
	geometry.tangent.xyz = normalize(cross(bitangent, geometry.normal));
#endif 
#endif 
}
#endif 

#ifdef USE_OUTLINE
static inline float hash(float2 p)
{
    const float2 kMod2 = float2(443.8975f, 397.2973f);
    p  = fract(p * kMod2);
    p += dot(p.xy, p.yx+19.19f);
    return fract(p.x * p.y);
}
#endif
    




#if defined(USE_TESSELLATION)
struct vfx_patch_t {
    patch_control_point<vfx_vertex_t> controlPoints;
};
#endif

#if defined(USE_OPENSUBDIV)
#if OSD_IS_ADAPTIVE
[[ patch(quad, VERTEX_CONTROL_POINTS_PER_PATCH) ]]
#endif
#elif defined(USE_TESSELLATION)
[[ patch(triangle, 3) ]]
#endif
    
    
vertex commonprofile_io commonprofile_vert(
#if !defined(USE_TESSELLATION)
                                           vfx_vertex_t                       in                               [[ stage_in ]]
                                           , uint                             vfx_vertexID                     [[ vertex_id ]]
#else 
                                           
#ifdef USE_OPENSUBDIV
#if OSD_IS_ADAPTIVE
#if USE_STAGE_IN
                                           PatchInput                         patchInput                       [[ stage_in ]]
#else
                                           OsdVertexBufferSet                 patchInput
#endif
                                           , float2                           patchCoord                       [[ position_in_patch ]]
                                           , uint                             patchID                          [[ patch_id ]]
                                           , constant float&                  osdTessellationLevel             [[ buffer(TESSELLATION_LEVEL_BUFFER_INDEX) ]]
#else 
                                           constant unsigned*                 osdIndicesBuffer                 [[ buffer(INDICES_BUFFER_INDEX) ]]
                                           , constant osd_packed_vertex*      osdVertexBuffer                  [[ buffer(VERTEX_BUFFER_INDEX) ]]
                                           , uint                             vertexID                         [[ vertex_id ]]
#endif 
#if defined(OSD_FVAR_WIDTH)
#if OSD_FVAR_USES_MULTIPLE_CHANNELS
                                           , constant uint32_t&               osdFaceVaryingChannelCount       [[ buffer(OSD_FVAR_CHANNELS_CHANNEL_COUNT_INDEX) ]]
                                           , constant OsdFVarChannelDesc*     osdFaceVaryingChannelDescriptors [[ buffer(OSD_FVAR_CHANNELS_CHANNEL_DESCRIPTORS_INDEX) ]]
                                           , constant uint32_t&               osdFaceVaryingPatchArrayIndex    [[ buffer(OSD_FVAR_CHANNELS_PATCH_ARRAY_INDEX_BUFFER_INDEX) ]]
                                           , constant void*                   osdFaceVaryingChannelsPackedData [[ buffer(OSD_FVAR_CHANNELS_PACKED_DATA_BUFFER_INDEX) ]]
#else
                                           , constant float*                  osdFaceVaryingData               [[ buffer(OSD_FVAR_DATA_BUFFER_INDEX) ]]
                                           , constant int*                    osdFaceVaryingIndices            [[ buffer(OSD_FVAR_INDICES_BUFFER_INDEX) ]]
#if OSD_IS_ADAPTIVE
                                           , constant packed_int3*            osdFaceVaryingPatchParams        [[ buffer(OSD_FVAR_PATCHPARAM_BUFFER_INDEX) ]]
                                           , constant packed_int4&            osdFaceVaryingPatchArray         [[ buffer(OSD_FVAR_PATCH_ARRAY_BUFFER_INDEX) ]]
#endif
#endif 
#endif 
#else 
                                           vfx_patch_t                        in                               [[ stage_in ]]
                                           , float3                           patchCoord                       [[ position_in_patch ]]
#endif 
#endif 
                                           
#ifdef USE_MULTIPLE_RENDERING
                                           , constant VFXWorldBuffer*         vfx_frame_multi                  [[ buffer(0) ]]
#else
                                           , constant VFXWorldBuffer&         vfx_frame                        [[ buffer(0) ]]
#endif
#if defined(USE_INSTANCING) || defined(USE_MULTIPLE_RENDERING)
                                           , device commonprofile_node*     vfx_nodes_in                     [[ buffer(1) ]]
#else
                                           , device commonprofile_node&     vfx_node_in                      [[ buffer(1) ]]
#endif
#ifdef USE_PER_VERTEX_LIGHTING
                                           , constant vfx_light*              vfx_lights                       [[ buffer(2) ]]
                                           , constant float4*                 u_shadowKernel
                                           , texture2d_array<float>           u_areaLightBakedDataTexture
#endif
                                           
                                           , constant commonprofile_uniforms& vfx_commonprofile
#ifdef USE_RE_SYSTEM_TREATMENTS
                                           , constant GlobalConstants&        u_re_globalConstants
#endif
#ifdef USE_INSTANCING
                                           , uint                             vfx_instanceID                   [[ instance_id ]]
#endif
#ifdef USE_VERTEX_AMPLIFICATION
                                           , ushort                           amplificationID                  [[ amplification_id ]]
#endif

#ifdef USE_POINT_RENDERING
                                           
                                           , constant float3&                 vfx_pointSize
#endif
#ifdef USE_DISPLACEMENT_MAP
#if USE_ARGUMENT_BUFFERS
#define u_displacementTexture           vfx_commonprofile.displacementTexture
#define u_displacementTextureSampler    vfx_commonprofile.displacementSampler
#else
                                           , texture2d<float>                 u_displacementTexture
                                           , sampler                          u_displacementTextureSampler
#endif 
#endif 
#ifdef USE_VERTEX_EXTRA_ARGUMENTS
                                           
#endif
                                           )
{
    commonprofile_io out;
    
#ifdef USE_MULTIPLE_RENDERING

#ifdef USE_VERTEX_AMPLIFICATION
    constant VFXWorldBuffer& vfx_frame = vfx_frame_multi[amplificationID];
#else
    out.instanceID = vfx_instanceID / USE_MULTIPLE_RENDERING;
    out.sliceIndex = vfx_instanceID % USE_MULTIPLE_RENDERING;
    constant VFXWorldBuffer& vfx_frame = vfx_frame_multi[out.sliceIndex];
#endif
    
#ifdef USE_INSTANCING
#ifdef USE_VERTEX_AMPLIFICATION
    device commonprofile_node& vfx_node_in = vfx_nodes_in[USE_MULTIPLE_RENDERING * vfx_instanceID + amplificationID];
#else
    
    
    device commonprofile_node& vfx_node_in = vfx_nodes_in[vfx_instanceID];
#endif
#else
#ifdef USE_VERTEX_AMPLIFICATION
    device commonprofile_node& vfx_node_in = vfx_nodes_in[amplificationID];
#else
    device commonprofile_node& vfx_node_in = vfx_nodes_in[out.sliceIndex];
#endif
#endif
    
#else 
    
#ifdef USE_INSTANCING
    out.instanceID = vfx_instanceID;
    device commonprofile_node& vfx_node_in = vfx_nodes_in[vfx_instanceID];
#endif
    
#endif 
    
    commonprofile_node vfx_node = compute_view_dependant_uniforms(vfx_node_in, vfx_frame);
    
#ifdef USE_TESSELLATION
    uint vfx_vertexID; 
    vfx_vertexID = 0;
#endif
    
    
    
    
    
    VFXShaderGeometry _geometry;
    
#if !defined(USE_TESSELLATION)
    
    
    _geometry.position = float4(in.position, 1.f);
#if defined(USE_NORMAL) && defined(HAS_NORMAL)
    _geometry.normal = in.normal;
#endif
#if defined(USE_TANGENT) || defined(USE_BITANGENT)
    _geometry.tangent = in.tangent;
#endif
#if defined(NEED_IN_TEXCOORD0) || defined(DEBUG_PIXEL)
    _geometry.texcoords[0] = in.texcoord0;
#endif
#ifdef NEED_IN_TEXCOORD1
    _geometry.texcoords[1] = in.texcoord1;
#endif
#ifdef NEED_IN_TEXCOORD2
    _geometry.texcoords[2] = in.texcoord2;
#endif
#ifdef NEED_IN_TEXCOORD3
    _geometry.texcoords[3] = in.texcoord3;
#endif
#ifdef NEED_IN_TEXCOORD4
    _geometry.texcoords[4] = in.texcoord4;
#endif
#ifdef NEED_IN_TEXCOORD5
    _geometry.texcoords[5] = in.texcoord5;
#endif
#ifdef NEED_IN_TEXCOORD6
    _geometry.texcoords[6] = in.texcoord6;
#endif
#ifdef NEED_IN_TEXCOORD7
    _geometry.texcoords[7] = in.texcoord7;
#endif
#ifdef HAS_VERTEX_COLOR
    _geometry.color = in.color;
#elif USE_VERTEX_COLOR
    _geometry.color = float4(1.);
#endif
    
#else 
    
#ifdef USE_OPENSUBDIV
#if OSD_IS_ADAPTIVE
#if USE_STAGE_IN
    int3 patchParam = patchInput.patchParam;
#else
    int3 patchParam = patchInput.patchParamBuffer[patchID];
#endif
    
    int refinementLevel = OsdGetPatchRefinementLevel(patchParam);
    float tessellationLevel = min(osdTessellationLevel, (float)OSD_MAX_TESS_LEVEL) / exp2((float)refinementLevel - 1);
    
    OsdPatchVertex patchVertex = OsdComputePatch(tessellationLevel, patchCoord, patchID, patchInput);
    
#if defined(OSD_FVAR_WIDTH)
    int patchIndex = OsdGetPatchIndex(patchID);
#if OSD_FVAR_USES_MULTIPLE_CHANNELS
    OsdInterpolateFaceVarings(_geometry, patchCoord.xy, patchIndex, osdFaceVaryingChannelCount, osdFaceVaryingChannelDescriptors, osdFaceVaryingPatchArrayIndex, osdFaceVaryingChannelsPackedData);
#else
    OsdInterpolateFaceVarings(_geometry, patchCoord.xy, patchIndex, osdFaceVaryingIndices, osdFaceVaryingData, osdFaceVaryingPatchParams, osdFaceVaryingPatchArray);
#endif
#endif
    
    _geometry.position = float4(patchVertex.position, 1.f);
    
#if defined(USE_NORMAL)
    _geometry.normal = patchVertex.normal;
#endif
#if defined(USE_TANGENT) || defined(USE_BITANGENT)
    _geometry.tangent = float4(patchVertex.tangent, -1.f);
    
#endif
#if defined(NEED_IN_TEXCOORD0) && (OSD_TEXCOORD0_INTERPOLATION_MODE == OSD_PRIMVAR_INTERPOLATION_MODE_USER_VARYING)
    _geometry.texcoords[0] = patchVertex.texcoord0;
#endif
#if defined(NEED_IN_TEXCOORD1) && (OSD_TEXCOORD1_INTERPOLATION_MODE == OSD_PRIMVAR_INTERPOLATION_MODE_USER_VARYING)
    _geometry.texcoords[1] = patchVertex.texcoord1;
#endif
#if defined(NEED_IN_TEXCOORD2) && (OSD_TEXCOORD2_INTERPOLATION_MODE == OSD_PRIMVAR_INTERPOLATION_MODE_USER_VARYING)
    _geometry.texcoords[2] = patchVertex.texcoord2;
#endif
#if defined(NEED_IN_TEXCOORD3) && (OSD_TEXCOORD3_INTERPOLATION_MODE == OSD_PRIMVAR_INTERPOLATION_MODE_USER_VARYING)
    _geometry.texcoords[3] = patchVertex.texcoord3;
#endif
#if defined(NEED_IN_TEXCOORD4) && (OSD_TEXCOORD4_INTERPOLATION_MODE == OSD_PRIMVAR_INTERPOLATION_MODE_USER_VARYING)
    _geometry.texcoords[4] = patchVertex.texcoord4;
#endif
#if defined(NEED_IN_TEXCOORD5) && (OSD_TEXCOORD5_INTERPOLATION_MODE == OSD_PRIMVAR_INTERPOLATION_MODE_USER_VARYING)
    _geometry.texcoords[5] = patchVertex.texcoord5;
#endif
#if defined(NEED_IN_TEXCOORD6) && (OSD_TEXCOORD6_INTERPOLATION_MODE == OSD_PRIMVAR_INTERPOLATION_MODE_USER_VARYING)
    _geometry.texcoords[6] = patchVertex.texcoord6;
#endif
#if defined(NEED_IN_TEXCOORD7) && (OSD_TEXCOORD7_INTERPOLATION_MODE == OSD_PRIMVAR_INTERPOLATION_MODE_USER_VARYING)
    _geometry.texcoords[7] = patchVertex.texcoord7;
#endif
#if defined(HAS_VERTEX_COLOR) && (OSD_COLOR_INTERPOLATION_MODE == OSD_PRIMVAR_INTERPOLATION_MODE_USER_VARYING)
    _geometry.color = patchVertex.color;
#endif
    
#else 
    
#if OSD_PATCH_QUADS
    const uint primitiveIndex = vertexID / 6;
#ifdef USE_NORMAL
    float3 p0 = osdVertexBuffer[osdIndicesBuffer[primitiveIndex * 4 + 0]].position;
    float3 p1 = osdVertexBuffer[osdIndicesBuffer[primitiveIndex * 4 + 1]].position;
    float3 p2 = osdVertexBuffer[osdIndicesBuffer[primitiveIndex * 4 + 2]].position;
    float3 normal = normalize(cross(p2 - p1, p0 - p1));
#endif
    const uint triangleIndices[6] = { 0, 1, 2, 0, 2, 3 };
    const uint quadVertexIndex = triangleIndices[vertexID % 6];
    osd_packed_vertex osdVertex = osdVertexBuffer[osdIndicesBuffer[primitiveIndex * 4 + quadVertexIndex]];
#elif OSD_PATCH_TRIANGLES
    const uint primitiveIndex = vertexID / 3;
#ifdef USE_NORMAL
    float3 p0 = osdVertexBuffer[osdIndicesBuffer[primitiveIndex * 3 + 0]].position;
    float3 p1 = osdVertexBuffer[osdIndicesBuffer[primitiveIndex * 3 + 1]].position;
    float3 p2 = osdVertexBuffer[osdIndicesBuffer[primitiveIndex * 3 + 2]].position;
    float3 normal = normalize(cross(p2 - p1, p0 - p1));
#endif
    osd_packed_vertex osdVertex = osdVertexBuffer[osdIndicesBuffer[vertexID]];
#endif
    
    float3 position = osdVertex.position;
    
#if defined(OSD_FVAR_WIDTH)
    int patchIndex = OsdGetPatchIndex(primitiveIndex);
#if OSD_PATCH_QUADS
    float2 quadUVs[4] = { float2(0,0), float2(1,0), float2(1,1), float2(0,1) };
#if OSD_FVAR_USES_MULTIPLE_CHANNELS
    OsdInterpolateFaceVarings(_geometry, quadUVs[quadVertexIndex], patchIndex, osdFaceVaryingChannelCount, osdFaceVaryingChannelDescriptors, osdFaceVaryingPatchArrayIndex, osdFaceVaryingChannelsPackedData);
#else
    OsdInterpolateFaceVarings(_geometry, quadUVs[quadVertexIndex], patchIndex, osdFaceVaryingIndices, osdFaceVaryingData);
#endif
#elif OSD_PATCH_TRIANGLES
    
#endif
#endif 
    
#if defined(NEED_IN_TEXCOORD0) && (OSD_TEXCOORD0_INTERPOLATION_MODE == OSD_PRIMVAR_INTERPOLATION_MODE_USER_VARYING)
    _geometry.texcoords[0] = osdVertex.texcoord0;
#endif
#if defined(NEED_IN_TEXCOORD1) && (OSD_TEXCOORD1_INTERPOLATION_MODE == OSD_PRIMVAR_INTERPOLATION_MODE_USER_VARYING)
    _geometry.texcoords[1] = osdVertex.texcoord1;
#endif
#if defined(NEED_IN_TEXCOORD2) && (OSD_TEXCOORD2_INTERPOLATION_MODE == OSD_PRIMVAR_INTERPOLATION_MODE_USER_VARYING)
    _geometry.texcoords[2] = osdVertex.texcoord2;
#endif
#if defined(NEED_IN_TEXCOORD3) && (OSD_TEXCOORD3_INTERPOLATION_MODE == OSD_PRIMVAR_INTERPOLATION_MODE_USER_VARYING)
    _geometry.texcoords[3] = osdVertex.texcoord3;
#endif
#if defined(NEED_IN_TEXCOORD4) && (OSD_TEXCOORD4_INTERPOLATION_MODE == OSD_PRIMVAR_INTERPOLATION_MODE_USER_VARYING)
    _geometry.texcoords[4] = osdVertex.texcoord4;
#endif
#if defined(NEED_IN_TEXCOORD5) && (OSD_TEXCOORD5_INTERPOLATION_MODE == OSD_PRIMVAR_INTERPOLATION_MODE_USER_VARYING)
    _geometry.texcoords[5] = osdVertex.texcoord5;
#endif
#if defined(NEED_IN_TEXCOORD6) && (OSD_TEXCOORD6_INTERPOLATION_MODE == OSD_PRIMVAR_INTERPOLATION_MODE_USER_VARYING)
    _geometry.texcoords[6] = osdVertex.texcoord6;
#endif
#if defined(NEED_IN_TEXCOORD7) && (OSD_TEXCOORD7_INTERPOLATION_MODE == OSD_PRIMVAR_INTERPOLATION_MODE_USER_VARYING)
    _geometry.texcoords[7] = osdVertex.texcoord7;
#endif
#if defined(HAS_VERTEX_COLOR) && (OSD_COLOR_INTERPOLATION_MODE == OSD_PRIMVAR_INTERPOLATION_MODE_USER_VARYING)
    _geometry.color = osdVertex.color;
#endif
    
    _geometry.position = float4(position, 1.f);
#ifdef USE_NORMAL
    _geometry.normal = normal;
#endif
    
#endif 
    
#else 
    
    
    
    
    
#if defined(TESSELLATION_SMOOTHING_MODE_PN_TRIANGLE) || defined(TESSELLATION_SMOOTHING_MODE_PHONG)
    float3 P0 = in.controlPoints[0].position;
    float3 P1 = in.controlPoints[1].position;
    float3 P2 = in.controlPoints[2].position;
    float3 N0 = in.controlPoints[0].normal;
    float3 N1 = in.controlPoints[1].normal;
    float3 N2 = in.controlPoints[2].normal;
#if defined(TESSELLATION_SMOOTHING_MODE_PN_TRIANGLE)
    float3 position, normal;
    vfx_smooth_geometry_pn_triangle(position, normal, patchCoord, P0, P1, P2, N0, N1, N2);
#elif defined(TESSELLATION_SMOOTHING_MODE_PHONG)
    float3 position, normal;
    vfx_smooth_geometry_phong(position, normal, patchCoord, P0, P1, P2, N0, N1, N2);
#endif
    _geometry.position = float4(position, 1.f);
#ifdef USE_NORMAL
    _geometry.normal = normal;
#endif
#else 
    
    _geometry.position = float4(vfx::barycentric_mix(in.controlPoints[0].position, in.controlPoints[1].position, in.controlPoints[2].position, patchCoord), 1.f);
#if defined(USE_NORMAL) && defined(HAS_NORMAL)
    _geometry.normal = normalize(vfx::barycentric_mix(in.controlPoints[0].normal, in.controlPoints[1].normal, in.controlPoints[2].normal, patchCoord));
#endif
#endif 
    
#if defined(USE_TANGENT) || defined(USE_BITANGENT)
    _geometry.tangent = normalize(vfx::barycentric_mix(in.controlPoints[0].tangent, in.controlPoints[1].tangent, in.controlPoints[2].tangent, patchCoord));
#endif
#ifdef NEED_IN_TEXCOORD0
    _geometry.texcoords[0] = vfx::barycentric_mix(in.controlPoints[0].texcoord0, in.controlPoints[1].texcoord0, in.controlPoints[2].texcoord0, patchCoord);
#endif
#ifdef NEED_IN_TEXCOORD1
    _geometry.texcoords[1] = vfx::barycentric_mix(in.controlPoints[0].texcoord1, in.controlPoints[1].texcoord1, in.controlPoints[2].texcoord1, patchCoord);
#endif
#ifdef NEED_IN_TEXCOORD2
    _geometry.texcoords[2] = vfx::barycentric_mix(in.controlPoints[0].texcoord2, in.controlPoints[1].texcoord2, in.controlPoints[2].texcoord2, patchCoord);
#endif
#ifdef NEED_IN_TEXCOORD3
    _geometry.texcoords[3] = vfx::barycentric_mix(in.controlPoints[0].texcoord3, in.controlPoints[1].texcoord3, in.controlPoints[2].texcoord3, patchCoord);
#endif
#ifdef NEED_IN_TEXCOORD4
    _geometry.texcoords[4] = vfx::barycentric_mix(in.controlPoints[0].texcoord4, in.controlPoints[1].texcoord4, in.controlPoints[2].texcoord4, patchCoord);
#endif
#ifdef NEED_IN_TEXCOORD5
    _geometry.texcoords[5] = vfx::barycentric_mix(in.controlPoints[0].texcoord5, in.controlPoints[1].texcoord5, in.controlPoints[2].texcoord5, patchCoord);
#endif
#ifdef NEED_IN_TEXCOORD6
    _geometry.texcoords[6] = vfx::barycentric_mix(in.controlPoints[0].texcoord6, in.controlPoints[1].texcoord6, in.controlPoints[2].texcoord6, patchCoord);
#endif
#ifdef NEED_IN_TEXCOORD7
    _geometry.texcoords[7] = vfx::barycentric_mix(in.controlPoints[0].texcoord7, in.controlPoints[1].texcoord7, in.controlPoints[2].texcoord7, patchCoord);
#endif
#ifdef HAS_VERTEX_COLOR
    _geometry.color = vfx::barycentric_mix(in.controlPoints[0].color, in.controlPoints[1].color, in.controlPoints[2].color, patchCoord);
#elif USE_VERTEX_COLOR
    _geometry.color = float4(1.);
#endif
    
#endif 
    
#endif 
    
#ifdef USE_POINT_RENDERING
    _geometry.pointSize = vfx_pointSize.x;
#endif
    
#ifdef USE_TEXCOORD
    
#endif
    
#ifdef USE_DISPLACEMENT_MAP
    applyDisplacement(u_displacementTexture, u_displacementTextureSampler, _displacementTexcoord, _geometry, vfx_commonprofile);
    out.displacementTexcoord = _displacementTexcoord;
#endif
    
    
    
    
    
#ifdef USE_GEOMETRY_MODIFIER
    
    
    
#endif
    
#ifdef USE_CLIP_DISTANCE0
    out.clipDistance[0] = _geometry.clipDistance0;
#endif
#ifdef USE_CLIP_DISTANCE1
    out.clipDistance[1] = _geometry.clipDistance1;
#endif
#ifdef USE_CLIP_DISTANCE2
    out.clipDistance[2] = _geometry.clipDistance2;
#endif
#ifdef USE_CLIP_DISTANCE3
    out.clipDistance[3] = _geometry.clipDistance3;
#endif
    
    
    
    
    
    
#if defined(USE_POSITION) || (defined(USE_NORMAL) && defined(HAS_OR_GENERATES_NORMAL)) || defined(USE_TANGENT) || defined(USE_BITANGENT) || defined(USE_INSTANCING)
    VFXShaderSurface _surface;
#endif
#if defined(USE_POSITION) || defined(USE_INSTANCING)
    _surface.position = (vfx_node.modelViewTransform * _geometry.position).xyz;
#endif
#if defined(USE_NORMAL) && defined(HAS_OR_GENERATES_NORMAL)
#ifdef HINT_UNIFORM_SCALE
    float3x3 nrmTransform = vfx::mat3(vfx_node.modelViewTransform);
    _surface.normal = nrmTransform * _geometry.normal;
#else
    float3x3 modelViewTransform = vfx::mat3(vfx_node.modelViewTransform);
    float3 invScaleSquared = 1.f / float3(length_squared(modelViewTransform[0]),
                                          length_squared(modelViewTransform[1]),
                                          length_squared(modelViewTransform[2]));
    _surface.normal = normalize(modelViewTransform * (_geometry.normal * invScaleSquared));
#endif
#ifdef USE_GBUFFER_OUTPUT
    _surface.normal = vfx::mat3(vfx_node.modelTransform) * _geometry.normal;
#endif
#endif
#if defined(USE_TANGENT) || defined(USE_BITANGENT)
    _surface.tangent = normalize(vfx::mat3(vfx_node.modelViewTransform) * _geometry.tangent.xyz);
    _surface.bitangent = _geometry.tangent.w * cross(_surface.tangent, _surface.normal); 
    
#endif
    
    
#ifdef USE_VIEW
    _surface.view = normalize(-_surface.position);
#endif
    
    
    
    
    
#ifdef USE_PER_VERTEX_LIGHTING
    
    VFXShaderLightingContribution _lightingContribution(_surface, out);
    _lightingContribution.diffuse = 0.;
  #ifdef USE_SPECULAR
    _lightingContribution.specular = 0.;
    _surface.shininess = vfx_commonprofile.materialShininess;
  #endif
    
    
    
    out.diffuse = _lightingContribution.diffuse;
  #ifdef USE_SPECULAR
    out.specular = _lightingContribution.specular;
  #endif
#endif
#if defined(USE_POSITION) && (USE_POSITION == 2)
    out.position = _surface.position;
#endif
#if defined(USE_NORMAL) && (USE_NORMAL == 2) && defined(HAS_OR_GENERATES_NORMAL)
    out.normal = _surface.normal;
#endif
#if defined(USE_TANGENT) && (USE_TANGENT == 2)
    out.tangent = _surface.tangent;
#endif
#if defined(USE_BITANGENT) && (USE_BITANGENT == 2)
    out.bitangent = _surface.bitangent;
#endif
#ifdef USE_VERTEX_COLOR
    out.vertexColor = _geometry.color;
#endif
    
#if DEBUG_PIXEL
    out.uv0 = _geometry.texcoords[0];
#endif

#ifdef USE_TEXCOORD
    out.texcoord0 = _geometry.texcoords[0].xy;

#endif
    
#ifdef IS_BEZIER_CURVE
    out.bezierCurveUV = in.texcoord0;
#endif
    
    
    
    
    
#if defined(USE_POSITION) || defined(USE_INSTANCING)
    out.fragmentPosition = vfx_frame.projectionTransform * float4(_surface.position, 1.);
#elif defined(USE_MODELVIEWPROJECTIONTRANSFORM) 
    out.fragmentPosition = vfx_node.modelViewProjectionTransform * _geometry.position;
#endif
    
#ifdef USE_NODE_OPACITY
    out.nodeOpacity = vfx_node.nodeOpacity;
#endif
    
#ifdef USE_POINT_RENDERING
    float screenSize = _geometry.pointSize / out.fragmentPosition.w;
    out.fragmentSize = clamp(screenSize, vfx_pointSize.y, vfx_pointSize.z);
#endif
    
#ifdef USE_LASTFRAMETRANSFORM
    float4 lastFrameFragmentPosition = vfx_frame.lastFrameViewProjectionTransform * vfx_node.lastFrameModelTransform * _geometry.position;
    out.mv_fragment = out.fragmentPosition.xyw;
    out.mv_lastFragment = lastFrameFragmentPosition.xyw;
#endif
    
#ifdef USE_OUTLINE
	out.outlineHash = hash(vfx_node.modelTransform[3].xy)+1.f/255.f;
#endif
    
#if USE_SCREEN_UV
    float2 ndc = out.fragmentPosition.xy / out.fragmentPosition.w;
    out.screen_uv = float2(ndc * float2(0.5f, -0.5f) + 0.5f);
#endif

#ifdef USE_CR_WORLD_POSITION
    float3 worldPosition = (vfx_node.modelTransform * _geometry.position).xyz;
#ifdef USE_RE_SYSTEM_TREATMENTS
    out.crworldPosition = vfx::api_v2::crws_position(worldPosition, u_re_globalConstants);
#else
    
#endif
#endif

    return out;
}





struct VFXOutput
{
    float4 color [[ color(0) ]];
#ifdef USE_COLOR1_OUTPUT
    half4 color1 [[ color(1) ]];
#endif
#ifdef USE_NORMAL_ROUGHNESS_OUTPUT
    half4 normalRoughness [[ color(2) ]];
#endif
#if defined(USE_MOTIONBLUR) && !defined(USE_GBUFFER_OUTPUT)
    half4 motionblur [[ color(3) ]];
#endif
#ifdef USE_ALBEDO_METALNESS_OUTPUT
    half4 albedoMetalnessOutput [[ color(4) ]];
#endif
#ifdef USE_RADIANCE_AO_OUTPUT
    half4 radianceAOOutput [[ color(5) ]];
#endif
#ifdef USE_GBUFFER_OUTPUT
    float4 albedo [[ color(VFXGBufferAttachmentAlbedo) ]];
    float4 normals [[ color(VFXGBufferAttachmentNormal) ]];
    float4 roughmetal [[ color(VFXGBufferAttachmentRoughnessMetalness) ]];
    
#ifdef USE_MOTIONBLUR
    float4 velocity [[ color(VFXGBufferAttachmentVelocity) ]];
#else
    float2 velocity [[ color(VFXGBufferAttachmentVelocity) ]];
#endif
    half4 clearCoat [[ color(VFXGBufferAttachmentClearCoat) ]];
    half4 subsurface [[ color(VFXGBufferAttachmentSubsurface) ]];
    ushort4 transmission [[ color(VFXGBufferAttachmentTransmission) ]];
#endif
};
    
#ifdef USE_MODIFIER_FRAMEBUFFER
struct VFXFramebuffer
{
    float4 color;
};
#endif

fragment VFXOutput commonprofile_frag(commonprofile_io                   in                          [[ stage_in  ]]
                                      , constant commonprofile_uniforms& vfx_commonprofile           [[ buffer(0) ]]
#ifdef USE_MULTIPLE_RENDERING
                                      , constant VFXWorldBuffer*         vfx_frame_multi             [[ buffer(1) ]]
#else
                                      , constant VFXWorldBuffer&         vfx_frame                   [[ buffer(1) ]]
#endif
#if defined(USE_INSTANCING) || defined(USE_MULTIPLE_RENDERING)
                                      , device commonprofile_node*     vfx_nodes_in                [[ buffer(2) ]]
#else
                                      , device commonprofile_node&     vfx_node_in                 [[ buffer(2) ]]
#endif
#ifdef USE_PER_PIXEL_LIGHTING
                                      , constant vfx_light*              vfx_lights                  [[ buffer(3) ]]
                                      , constant float4*                 u_shadowKernel
                                      , texture2d_array<float>           u_areaLightBakedDataTexture
#ifdef CFX_SUPPORT_CUBE_ARRAY
                                      , texturecube_array<half>          u_reflectionProbeTexture
#else
                                      , texture2d_array<half>            u_reflectionProbeTexture
#endif
                                      , texture3d<ushort>                u_clusterTexture
#ifdef CFX_USE_TEXTURE_FOR_LIGHT_INDICES
                                      , texture1d<ushort>                u_lightIndicesTexture
#else
                                      , constant CFXLightIndexType*      u_lightIndicesBuffer
#endif
#endif
#ifdef USE_RE_SYSTEM_TREATMENTS
                                      , constant EntityConstants&           u_re_entityConstants
                                      , constant ViewConstants&             u_re_viewConstants
                                      , constant GlobalConstants&           u_re_globalConstants
                                      , constant re_vfx_object_constants&   u_re_vfx_objectConstants
                                      , constant re_entity_argument_buffer& u_re_vfx_entityArgumentBuffer
                                      , constant re_scene_argument_buffer&  u_re_vfx_sceneArgumentBuffer
#ifdef USE_RE_SYSTEM_TREATMENTS_TIER_1_AB
                                      , constant VirtualEnvironmentProbeLighting::TextureArgumentBuffer& u_re_vfx_virtualEnvProbeTextures
#endif
#endif
#if defined(CFX_SUPPORTS_PROGRAMMABLE_BLENDING) && defined(USE_MODIFIER_FRAMEBUFFER_COLOR0)
                                      , float4                           framebufferColor0           [[ color(0) ]]
#endif
#if USE_ARGUMENT_BUFFERS

#define u_emissionTexture               vfx_commonprofile.emissionTexture
#define u_emissionTextureSampler        vfx_commonprofile.emissionSampler
#define u_ambientOcclusionTexture       vfx_commonprofile.ambientOcclusionTexture
#define u_ambientOcclusionTextureSampler vfx_commonprofile.ambientOcclusionSampler
#define u_diffuseTexture                vfx_commonprofile.diffuseTexture
#define u_diffuseTextureSampler         vfx_commonprofile.diffuseSampler
#define u_specularTexture               vfx_commonprofile.specularTexture
#define u_specularTextureSampler        vfx_commonprofile.specularSampler
#define u_reflectiveTexture             vfx_commonprofile.reflectiveTexture
#define u_reflectiveTextureSampler      vfx_commonprofile.reflectiveSampler
#define u_transparentTexture            vfx_commonprofile.transparentTexture
#define u_transparentTextureSampler     vfx_commonprofile.transparentSampler
#define u_multiplyTexture               vfx_commonprofile.multiplyTexture
#define u_multiplyTextureSampler        vfx_commonprofile.multiplySampler
#define u_normalTexture                 vfx_commonprofile.normalTexture
#define u_normalTextureSampler          vfx_commonprofile.normalSampler
#define u_selfIlluminationTexture       vfx_commonprofile.selfIlluminationTexture
#define u_selfIlluminationTextureSampler vfx_commonprofile.selfIlluminationSampler
#define u_metalnessTexture              vfx_commonprofile.metalnessTexture
#define u_metalnessTextureSampler       vfx_commonprofile.metalnessSampler
#define u_roughnessTexture              vfx_commonprofile.roughnessTexture
#define u_roughnessTextureSampler       vfx_commonprofile.roughnessSampler

#else
#ifdef USE_EMISSION_MAP
                                      , texture2d<float>              u_emissionTexture
                                      , sampler                       u_emissionTextureSampler
#endif
#ifdef USE_AMBIENTOCCLUSION_MAP
                                      , texture2d<float>              u_ambientOcclusionTexture
                                      , sampler                       u_ambientOcclusionTextureSampler
#endif
#ifdef USE_DIFFUSE_MAP
                                      , texture2d<float>              u_diffuseTexture
                                      , sampler                       u_diffuseTextureSampler
#endif
#ifdef USE_SPECULAR_MAP
                                      , texture2d<float>              u_specularTexture
                                      , sampler                       u_specularTextureSampler
#endif
#ifdef USE_REFLECTIVE_MAP
                                      , texture2d<float>              u_reflectiveTexture
                                      , sampler                       u_reflectiveTextureSampler
#elif defined(USE_REFLECTIVE_CUBEMAP)
                                      , texturecube<float>            u_reflectiveTexture
                                      , sampler                       u_reflectiveTextureSampler
#endif
#ifdef USE_TRANSPARENT_MAP
                                      , texture2d<float>              u_transparentTexture
                                      , sampler                       u_transparentTextureSampler
#endif
#ifdef USE_MULTIPLY_MAP
                                      , texture2d<float>              u_multiplyTexture
                                      , sampler                       u_multiplyTextureSampler
#endif
#ifdef USE_NORMAL_MAP
                                      , texture2d<float>              u_normalTexture
                                      , sampler                       u_normalTextureSampler
#endif
#ifdef USE_SELFILLUMINATION_MAP
                                      , texture2d<float>              u_selfIlluminationTexture
                                      , sampler                       u_selfIlluminationTextureSampler
#endif
#ifdef USE_DISPLACEMENT_MAP
                                      , texture2d<float>              u_displacementTexture
                                      , sampler                       u_displacementTextureSampler
#endif
#ifdef USE_PBR
#ifdef USE_METALNESS_MAP
                                      , texture2d<float>              u_metalnessTexture
                                      , sampler                       u_metalnessTextureSampler
#endif
#ifdef USE_ROUGHNESS_MAP
                                      , texture2d<float>              u_roughnessTexture
                                      , sampler                       u_roughnessTextureSampler
#endif
#ifdef USE_CLEARCOAT_MAP
                                      , texture2d<float>              u_clearCoatTexture
                                      , sampler                       u_clearCoatTextureSampler
#endif
#ifdef USE_CLEARCOATROUGHNESS_MAP
                                      , texture2d<float>              u_clearCoatRoughnessTexture
                                      , sampler                       u_clearCoatRoughnessTextureSampler
#endif
#ifdef USE_CLEARCOATNORMAL_MAP
                                      , texture2d<float>              u_clearCoatNormalTexture
                                      , sampler                       u_clearCoatNormalTextureSampler
#endif
#ifdef USE_SUBSURFACE_MAP
                                      , texture2d<float>              u_subsurfaceTexture
                                      , sampler                       u_subsurfaceTextureSampler
#endif
#ifdef USE_SUBSURFACERADIUS_MAP
                                      , texture2d<float>              u_subsurfaceRadiusTexture
                                      , sampler                       u_subsurfaceRadiusTextureSampler
#endif
#ifdef USE_TRANSMISSION_MAP
                                      , texture2d<float>              u_transmissionTexture
                                      , sampler                       u_transmissionTextureSampler
#endif
#ifdef USE_TRANSMISSIONCOLOR_MAP
                                      , texture2d<float>              u_transmissionColorTexture
                                      , sampler                       u_transmissionColorTextureSampler
#endif
#endif 
#endif 
#ifdef USE_PBR
                                      , texturecube<float>            u_radianceTexture
                                      , texture2d<float>              u_specularDFGDiffuseHammonTexture
#if !defined(USE_SELFILLUMINATION_MAP)
                                      , texturecube<float>            u_irradianceTexture
#endif
#endif 
#ifdef USE_SSAO
                                      , texture2d<float>              u_ssaoTexture
#endif
#ifdef USE_FRAGMENT_EXTRA_ARGUMENTS
                                      , texture2d<float> inSDF
, texture1d<float> inRamp
, constant vfx_modifiers_params_fragment_t& u_shaderModifierParametersFragment

#endif
#if defined(USE_DOUBLE_SIDED)
                                      , bool                          isFrontFacing                    [[front_facing]]
#endif
#ifdef USE_POINT_RENDERING
                                      , float2                        pointCoord                       [[point_coord]]
#endif
#ifdef USE_VERTEX_AMPLIFICATION
                                      , ushort                        amplificationID                  [[amplification_id]]
#endif
#ifdef USE_BARYCENTRIC_COORD
                                      , float3                        u_barycentricCoord                 [[barycentric_coord]]
#endif
                                      )
{
#ifdef USE_MULTIPLE_RENDERING
    
#ifdef USE_VERTEX_AMPLIFICATION
    constant VFXWorldBuffer& vfx_frame = vfx_frame_multi[amplificationID];
#else
    constant VFXWorldBuffer& vfx_frame = vfx_frame_multi[in.sliceIndex];
#endif
    
#ifdef USE_INSTANCING
#ifdef USE_VERTEX_AMPLIFICATION
    device commonprofile_node& vfx_node_in = vfx_nodes_in[USE_MULTIPLE_RENDERING * in.instanceID + amplificationID];
#else
    device commonprofile_node& vfx_node_in = vfx_nodes_in[USE_MULTIPLE_RENDERING * in.instanceID + in.sliceIndex];
#endif
#else
#ifdef USE_VERTEX_AMPLIFICATION
    device commonprofile_node& vfx_node_in = vfx_nodes_in[amplificationID];
#else
    device commonprofile_node& vfx_node_in = vfx_nodes_in[in.sliceIndex];
#endif
#endif
    
#else 
    
#ifdef USE_INSTANCING
    device commonprofile_node& vfx_node_in = vfx_nodes_in[in.instanceID];
#endif
    
#endif 
    
    commonprofile_node vfx_node = vfx_node_in;
    
#ifdef IS_BEZIER_CURVE
    int vfx_bezierCurveCoverage = 0;
    for (uint sampleID = 0; sampleID < get_num_samples(); ++sampleID) {
        float2 uv = in.bezierCurveUV.interpolate_at_sample(sampleID);
        bool insideCurve = vfx_is_inside_bezier_curve(uv, vfx_bezier_curve_data, vfx_bezier_curve_controlPoints);
        vfx_bezierCurveCoverage += insideCurve;
    }
    if (vfx_bezierCurveCoverage == 0) {
        discard_fragment();
    }
#endif
    
    VFXOutput _output;

    
    
    
    
    VFXShaderSurface _surface;
#ifdef USE_TEXCOORD
        _surface.diffuseTexcoord = in.texcoord0;
    _surface.inRampTexcoord = in.texcoord0;
    _surface.inSDFTexcoord = in.texcoord0;

#endif
    _surface.ambientOcclusion = 1.f; 

#ifdef USE_AMBIENTOCCLUSION_MAP
#if defined(USE_AMBIENTOCCLUSION_TEXTURE_COMPONENT)
    _surface.ambientOcclusion = colorFromMask(u_ambientOcclusionTexture.sample(u_ambientOcclusionTextureSampler, _surface.ambientOcclusionTexcoord), USE_AMBIENTOCCLUSION_TEXTURE_COMPONENT).r;
#else
    _surface.ambientOcclusion = u_ambientOcclusionTexture.sample(u_ambientOcclusionTextureSampler, _surface.ambientOcclusionTexcoord).r;
#endif 
#ifdef USE_AMBIENTOCCLUSION_INTENSITY
    _surface.ambientOcclusion = saturate(mix(1.f, _surface.ambientOcclusion, vfx_commonprofile.ambientOcclusionIntensity));
#endif
#endif


#if defined(USE_SSAO)
    
    
    float2 uvSSAO = in.fragmentPosition.xy * vfx_frame.inverseResolution.xy;
#if defined(USE_BENTNORMALS)
    float4 aoBent = u_ssaoTexture.sample(sampler(filter::linear), uvSSAO).rgba;
    _surface.ambientOcclusion *= aoBent.r;
    _surface.bentNormal = aoBent.gba*2.0 - 1.0;
#else
    _surface.ambientOcclusion *= u_ssaoTexture.sample( sampler(filter::linear), uvSSAO).r;
#endif
#endif
    
#ifdef USE_DIFFUSE_MAP
    _surface.diffuse = u_diffuseTexture.sample(u_diffuseTextureSampler, _surface.diffuseTexcoord);
#if defined(USE_DIFFUSE_TEXTURE_COMPONENT)
    _surface.diffuse = colorFromMask(_surface.diffuse, USE_DIFFUSE_TEXTURE_COMPONENT);
#endif
#ifdef USE_DIFFUSE_INTENSITY
    _surface.diffuse.rgb *= vfx_commonprofile.diffuseIntensity;
#endif
#elif defined(USE_DIFFUSE_COLOR)
    _surface.diffuse = vfx_commonprofile.diffuseColor;
#else
    _surface.diffuse = float4(0.f,0.f,0.f,1.f);
#endif
#if defined(USE_DIFFUSE) && defined(USE_VERTEX_COLOR)
    _surface.diffuse.rgb    *= in.vertexColor.rgb;
    _surface.diffuse        *= in.vertexColor.a; 
#endif
#ifdef USE_SPECULAR_MAP
    _surface.specular = u_specularTexture.sample(u_specularTextureSampler, _surface.specularTexcoord);
#if defined(USE_SPECULAR_TEXTURE_COMPONENT)
    _surface.specular = colorFromMask(_surface.specular, USE_SPECULAR_TEXTURE_COMPONENT);
#endif
#ifdef USE_SPECULAR_INTENSITY
    _surface.specular *= vfx_commonprofile.specularIntensity;
#endif
#elif defined(USE_SPECULAR_COLOR)
    _surface.specular = vfx_commonprofile.specularColor;
#elif defined(USE_SPECULAR)
    _surface.specular = float4(0.f);
#endif
    
#ifdef USE_CLEARCOAT_MAP
    _surface.clearCoat = u_clearCoatTexture.sample(u_clearCoatTextureSampler, _surface.clearCoatTexcoord).r;
#if defined(USE_CLEARCOAT_TEXTURE_COMPONENT)
    _surface.clearCoat = colorFromMask(_surface.clearCoat, USE_CLEARCOAT_TEXTURE_COMPONENT).r;
#endif
#ifdef USE_CLEARCOAT_INTENSITY
    _surface.clearCoat *= vfx_commonprofile.clearCoatIntensity;
#endif
#elif defined(USE_CLEARCOAT_COLOR)
    _surface.clearCoat = vfx_commonprofile.clearCoat;
#else
    _surface.clearCoat = 0.f;
#endif
    
#ifdef USE_CLEARCOATROUGHNESS_MAP
#if defined(USE_CLEARCOATROUGHNESS_TEXTURE_COMPONENT)
    _surface.clearCoatRoughness = colorFromMask(u_clearCoatRoughnessTexture.sample(u_clearCoatRoughnessTextureSampler, _surface.clearCoatRoughnessTexcoord), USE_CLEARCOATROUGHNESS_TEXTURE_COMPONENT).r;
#else
    _surface.clearCoatRoughness = u_clearCoatRoughnessTexture.sample(u_clearCoatRoughnessTextureSampler, _surface.clearCoatRoughnessTexcoord).r;
#endif
#ifdef USE_CLEARCOATROUGHNESS_INTENSITY
    _surface.clearCoatRoughness *= vfx_commonprofile.clearCoatRoughnessIntensity;
#endif
#elif defined(USE_CLEARCOATROUGHNESS_COLOR)
    _surface.clearCoatRoughness = vfx_commonprofile.clearCoatRoughness;
#else
    _surface.clearCoatRoughness = 0.03f;
#endif
    
#ifdef USE_SUBSURFACE
#ifdef USE_SUBSURFACE_MAP
    _surface.subsurface = u_subsurfaceTexture.sample(u_subsurfaceTextureSampler, _surface.subsurfaceTexcoord).r;
#if defined(USE_SUBSURFACE_TEXTURE_COMPONENT)
    _surface.subsurface = colorFromMask(_surface.subsurface, USE_SUBSURFACE_TEXTURE_COMPONENT).r;
#endif
#ifdef USE_SUBSURFACE_INTENSITY
    _surface.subsurface *= vfx_commonprofile.subsurfaceIntensity;
#endif
#elif defined(USE_SUBSURFACE_COLOR)
    _surface.subsurface = vfx_commonprofile.subsurface;
#else
    _surface.subsurface = 0.f;
#endif
    
#ifdef USE_SUBSURFACERADIUS_MAP
    _surface.subsurfaceRadius = u_subsurfaceRadiusTexture.sample(u_subsurfaceRadiusTextureSampler, _surface.subsurfaceRadiusTexcoord).rgb;
#if defined(USE_SUBSURFACERADIUS_TEXTURE_COMPONENT)
    _surface.subsurfaceRadius = colorFromMask(_surface.subsurfaceRadius, USE_SUBSURFACERADIUS_TEXTURE_COMPONENT).rgb;
#endif
#ifdef USE_SUBSURFACERADIUS_INTENSITY
    _surface.subsurfaceRadius *= vfx_commonprofile.subsurfaceRadiusIntensity;
#endif
#elif defined(USE_SUBSURFACERADIUS_COLOR)
    _surface.subsurfaceRadius = vfx_commonprofile.subsurfaceRadius.rgb;
#else
    _surface.subsurfaceRadius = float3(0.f);
#endif
#endif
    
    
#ifdef USE_TRANSMISSION
#ifdef USE_TRANSMISSION_MAP
    _surface.transmission = u_transmissionTexture.sample(u_transmissionTextureSampler, _surface.transmissionTexcoord).r;
#if defined(USE_TRANSMISSION_TEXTURE_COMPONENT)
    _surface.transmission = colorFromMask(_surface.transmission, USE_TRANSMISSION_TEXTURE_COMPONENT).r;
#endif
#ifdef USE_TRANSMISSION_INTENSITY
    _surface.transmission *= vfx_commonprofile.transmissionIntensity;
#endif
#elif defined(USE_TRANSMISSION_COLOR)
    _surface.transmission = vfx_commonprofile.transmission;
#else
    _surface.transmission = 0.f;
#endif
    
#ifdef USE_TRANSMISSIONCOLOR_MAP
    _surface.transmissionColor = u_transmissionColorTexture.sample(u_transmissionColorTextureSampler, _surface.transmissionColorTexcoord).rgb;
#if defined(USE_TRANSMISSIONCOLOR_TEXTURE_COMPONENT)
    _surface.transmissionColor = colorFromMask(_surface.transmissionColor, USE_TRANSMISSIONCOLOR_TEXTURE_COMPONENT).rgb;
#endif
#ifdef USE_TRANSMISSIONCOLOR_INTENSITY
    _surface.transmissionColor *= vfx_commonprofile.transmissionColorIntensity;
#endif
#elif defined(USE_TRANSMISSIONCOLOR_COLOR)
    _surface.transmissionColor = vfx_commonprofile.transmissionColor.rgb;
#else
    _surface.transmissionColor = float3(0.f);
#endif
#endif
    
#ifdef USE_EMISSION_MAP
    _surface.emission = u_emissionTexture.sample(u_emissionTextureSampler, _surface.emissionTexcoord);
#if defined(USE_EMISSION_TEXTURE_COMPONENT)
    _surface.emission = colorFromMask(_surface.emission, USE_EMISSION_TEXTURE_COMPONENT);
#endif
#ifdef USE_EMISSION_INTENSITY
    _surface.emission *= vfx_commonprofile.emissionIntensity;
#endif
#elif defined(USE_EMISSION_COLOR)
    _surface.emission = vfx_commonprofile.emissionColor;
#elif defined(USE_EMISSION)
    _surface.emission = float4(0.);
#endif
#ifdef USE_SELFILLUMINATION_MAP
    _surface.selfIllumination = u_selfIlluminationTexture.sample(u_selfIlluminationTextureSampler, _surface.selfIlluminationTexcoord);
#if defined(USE_SELFILLUMINATION_TEXTURE_COMPONENT)
    _surface.selfIllumination = colorFromMask(_surface.selfIllumination, USE_SELFILLUMINATION_TEXTURE_COMPONENT);
#endif
#ifdef USE_SELFILLUMINATION_INTENSITY
    _surface.selfIllumination *= vfx_commonprofile.selfIlluminationIntensity;
#endif
#elif defined(USE_SELFILLUMINATION_COLOR)
    _surface.selfIllumination = vfx_commonprofile.selfIlluminationColor;
#elif defined(USE_SELFILLUMINATION)
    _surface.selfIllumination = float4(0.);
#endif
#ifdef USE_MULTIPLY_MAP
    _surface.multiply = u_multiplyTexture.sample(u_multiplyTextureSampler, _surface.multiplyTexcoord);
#if defined(USE_MULTIPLY_TEXTURE_COMPONENT)
    _surface.multiply = colorFromMask(_surface.multiply, USE_MULTIPLY_TEXTURE_COMPONENT);
#endif
#ifdef USE_MULTIPLY_INTENSITY
    _surface.multiply = mix(float4(1.), _surface.multiply, vfx_commonprofile.multiplyIntensity);
#endif
#elif defined(USE_MULTIPLY_COLOR)
    _surface.multiply = vfx_commonprofile.multiplyColor;
#elif defined(USE_MULTIPLY)
    _surface.multiply = float4(1.);
#endif
#ifdef USE_TRANSPARENT_MAP
    _surface.transparent = u_transparentTexture.sample(u_transparentTextureSampler, _surface.transparentTexcoord);
#if defined(USE_TRANSPARENT_TEXTURE_COMPONENT)
    _surface.transparent = colorFromMask(_surface.transparent, USE_TRANSPARENT_TEXTURE_COMPONENT);
#endif
#ifdef USE_TRANSPARENT_INTENSITY
    _surface.transparent *= vfx_commonprofile.transparentIntensity;
#endif
#elif defined(USE_TRANSPARENT_COLOR)
    _surface.transparent = vfx_commonprofile.transparentColor;
#elif defined(USE_TRANSPARENT)
    _surface.transparent = float4(1.f);
#endif
    
#ifdef USE_METALNESS_MAP
#if defined(USE_METALNESS_TEXTURE_COMPONENT)
    _surface.metalness = colorFromMask(u_metalnessTexture.sample(u_metalnessTextureSampler, _surface.metalnessTexcoord), USE_METALNESS_TEXTURE_COMPONENT).r;
#else
    _surface.metalness = u_metalnessTexture.sample(u_metalnessTextureSampler, _surface.metalnessTexcoord).r;
#endif
#ifdef USE_METALNESS_INTENSITY
    _surface.metalness *= vfx_commonprofile.metalnessIntensity;
#endif
#elif defined(USE_METALNESS_COLOR)
    _surface.metalness = vfx_commonprofile.metalness;
#else
    _surface.metalness = 0.f;
#endif
    
#ifdef USE_ROUGHNESS_MAP
#if defined(USE_ROUGHNESS_TEXTURE_COMPONENT)
    _surface.roughness = colorFromMask(u_roughnessTexture.sample(u_roughnessTextureSampler, _surface.roughnessTexcoord), USE_ROUGHNESS_TEXTURE_COMPONENT).r;
#else
    _surface.roughness = u_roughnessTexture.sample(u_roughnessTextureSampler, _surface.roughnessTexcoord).r;
#endif
#ifdef USE_ROUGHNESS_INTENSITY
    _surface.roughness *= vfx_commonprofile.roughnessIntensity;
#endif
#elif defined(USE_ROUGHNESS_COLOR)
    _surface.roughness = vfx_commonprofile.roughness;
#else
    _surface.roughness = 0.f;
#endif
#if (defined USE_POSITION) && (USE_POSITION == 2)
    _surface.position = in.position;
#endif
#if (defined USE_NORMAL) && (USE_NORMAL == 2)
#if defined(HAS_NORMAL) || defined(USE_OPENSUBDIV)
#ifdef USE_DOUBLE_SIDED
    _surface.geometryNormal = normalize(in.normal.xyz) * (isFrontFacing ? 1.f : -1.f );
#else
    _surface.geometryNormal = normalize(in.normal.xyz);
#endif
#else 
    _surface.geometryNormal = normalize( cross(dfdy( _surface.position ), dfdx( _surface.position ) ));
#ifdef USE_GBUFFER_OUTPUT
    _surface.geometryNormal = vfx::mat3(vfx_frame.inverseViewTransform) * _surface.geometryNormal;
#endif
#endif
    _surface.normal = _surface.geometryNormal;
    _surface.clearCoatNormal = _surface.geometryNormal;
#ifdef USE_BENTNORMALS
    _surface.aoDirectionnal = mix(0., _surface.ambientOcclusion, dot(_surface.bentNormal, _surface.normal)*0.5+0.5);
#endif
#endif
#if defined(USE_TANGENT) && (USE_TANGENT == 2)
    _surface.tangent = in.tangent;
#endif
#if defined(USE_BITANGENT) && (USE_BITANGENT == 2)
    _surface.bitangent = in.bitangent;
#endif
#if (defined USE_VIEW) && (USE_VIEW == 2)
    _surface.view = normalize(-in.position);
    {
        
        float NoV = dot(_surface.geometryNormal, _surface.view);
        _surface.view = _surface.view + max(0.f, -2.f * NoV) * _surface.geometryNormal;         
        
    }
#endif
#if defined(USE_NORMAL_MAP)
    {
        float3x3 ts2vs = float3x3(_surface.tangent, _surface.bitangent, _surface.normal);
#ifdef USE_NORMAL_MAP
#if defined(USE_NORMAL_TEXTURE_COMPONENT)
        _surface._normalTS.xy = colorFromMask(u_normalTexture.sample(u_normalTextureSampler, _surface.normalTexcoord), USE_NORMAL_TEXTURE_COMPONENT).rg * 2.f - 1.f;
        _surface._normalTS.z = sqrt(1.f - saturate(length_squared(_surface._normalTS.xy)));
#else
        _surface._normalTS = u_normalTexture.sample(u_normalTextureSampler, _surface.normalTexcoord).rgb;
        _surface._normalTS = _surface._normalTS * 2.f - 1.f;
#endif
#ifdef USE_NORMAL_INTENSITY
        _surface._normalTS = normalize(mix(float3(0.f, 0.f, 1.f), _surface._normalTS, vfx_commonprofile.normalIntensity));
#endif
#else
        _surface._normalTS = float3(0.f, 0.f, 1.f);
#endif
        _surface.normal.rgb = normalize(ts2vs * _surface._normalTS.xyz );
    }
#else
    _surface._normalTS = float3(0.f, 0.f, 1.f);
#endif
#if defined(USE_PBR) && !defined(USE_GBUFFER_OUTPUT)
    {
        _surface.rawRoughness = _surface.roughness;
        float roughness = clamp(_surface.roughness, PBR_MIN_ROUGHNESS, 1.0);
        float alpha = vfx_filteredAlphaFromRoughness(_surface.normal, roughness);
        _surface.roughness = sqrt(alpha);
    }
#endif
#if defined(USE_CLEARCOATNORMAL_MAP)
    {
        
        float3x3 ts2vs = float3x3(_surface.tangent, _surface.bitangent, _surface.geometryNormal);
#ifdef USE_CLEARCOATNORMAL_MAP
#if defined(USE_CLEARCOATNORMAL_TEXTURE_COMPONENT)
        _surface._clearCoatNormalTS.xy = colorFromMask(u_clearCoatNormalTexture.sample(u_clearCoatnormalTextureSampler, _surface.clearCoatNormalTexcoord), USE_CLEARCOATNORMAL_TEXTURE_COMPONENT).rg * 2.f - 1.f;
        _surface._clearCoatNormalTS.z = sqrt(1.f - saturate(length_squared(_surface._clearCoatNormalTS.xy)));
#else
        _surface._clearCoatNormalTS = u_clearCoatNormalTexture.sample(u_clearCoatNormalTextureSampler, _surface.clearCoatNormalTexcoord).rgb;
        _surface._clearCoatNormalTS = _surface._clearCoatNormalTS * 2.f - 1.f;
#endif
#ifdef USE_CLEARCOATNORMAL_INTENSITY
        _surface._clearCoatNormalTS = mix(float3(0.f, 0.f, 1.f), _surface._clearCoatNormalTS, vfx_commonprofile.clearCoatNormalIntensity);
#endif
#else
        _surface._clearCoatNormalTS = float3(0.f, 0.f, 1.f);
#endif
        _surface.clearCoatNormal.rgb = normalize(ts2vs * _surface._clearCoatNormalTS.xyz );
    }
#else
    _surface._clearCoatNormalTS = float3(0.f, 0.f, 1.f);
#endif
    
#ifdef USE_REFLECTIVE_MAP
    float3 refl = reflect( -_surface.view, _surface.normal );
    float m = 2.f * sqrt( refl.x*refl.x + refl.y*refl.y + (refl.z+1.f)*(refl.z+1.f));
    _surface.reflective = u_reflectiveTexture.sample(u_reflectiveTextureSampler, float2(float2(refl.x,-refl.y) / m) + 0.5f);
#if defined(USE_REFLECTIVE_TEXTURE_COMPONENT)
    _surface.reflective = colorFromMask(_surface.reflective, USE_REFLECTIVE_TEXTURE_COMPONENT).r;
#endif
#ifdef USE_REFLECTIVE_INTENSITY
    _surface.reflective *= vfx_commonprofile.reflectiveIntensity;
#endif
#elif defined(USE_REFLECTIVE_CUBEMAP)
    float3 refl = reflect( _surface.position, _surface.normal );
    _surface.reflective = u_reflectiveTexture.sample(u_reflectiveTextureSampler, vfx::mat4_mult_float3(vfx_frame.viewToCubeTransform, refl)); 
#ifdef USE_REFLECTIVE_INTENSITY
    _surface.reflective *= vfx_commonprofile.reflectiveIntensity;
#endif
#elif defined(USE_REFLECTIVE_COLOR)
    _surface.reflective = vfx_commonprofile.reflectiveColor;
#elif defined(USE_REFLECTIVE)
    _surface.reflective = float4(0.);
#endif
#ifdef USE_FRESNEL
    _surface.fresnel = vfx_commonprofile.fresnel.x + vfx_commonprofile.fresnel.y * pow(1.f - saturate(dot(_surface.view, _surface.normal)), vfx_commonprofile.fresnel.z);
    _surface.reflective *= _surface.fresnel;
#endif
#ifdef USE_SHININESS
    _surface.shininess = vfx_commonprofile.materialShininess;
#endif
    
    
    
    
    
#ifdef USE_SURFACE_MODIFIER
    
    
    
#endif
    
    
    
    
    
    VFXShaderLightingContribution _lightingContribution(_surface, in);
#ifdef USE_LIGHT_MODIFIER
    
#endif
#ifdef USE_AMBIENT_LIGHTING
    _lightingContribution.ambient = vfx_frame.ambientLightingColor.rgb;
#endif
#ifdef USE_LIGHTING
#ifdef USE_PER_PIXEL_LIGHTING
#ifdef USE_CLUSTERED_LIGHTING
    uint3 clusterIndex;
    clusterIndex.xy = uint2(in.fragmentPosition.xy * vfx_frame.clusterScale.xy); 
    clusterIndex.z = in.position.z * vfx_frame.clusterScale.z + vfx_frame.clusterScale.w; 
    
    
    ushort4 cluster_offset_count = u_clusterTexture.read(clusterIndex);
    int lid = cluster_offset_count.x;
#endif

#ifdef USE_PBR
    _lightingContribution.prepareForPBR(u_specularDFGDiffuseHammonTexture, vfx_commonprofile.selfIlluminationOcclusion);
    
    
#ifdef USE_SELFILLUMINATION
    _lightingContribution.add_irradiance_from_selfIllum();
#else
#ifdef USE_PROBES_LIGHTING 

#ifdef USE_IBL_TRANSFORM
    _lightingContribution.add_global_irradiance_from_sh(vfx_frame.environmentTransform * vfx_frame.viewToCubeTransform, vfx_node.shCoefficients);
#else
    _lightingContribution.add_global_irradiance_from_sh(vfx_frame.viewToCubeTransform, vfx_node.shCoefficients);
#endif 

#else

#ifdef USE_IBL_TRANSFORM
    _lightingContribution.add_global_irradiance_probe(u_irradianceTexture, vfx_frame.environmentTransform * vfx_frame.viewToCubeTransform, vfx_frame.environmentIntensity);
#else
    _lightingContribution.add_global_irradiance_probe(u_irradianceTexture, vfx_frame.viewToCubeTransform, vfx_frame.environmentIntensity);
#endif 

#endif 
#endif

    
#ifdef CFX_USE_REFLECTION_PROBES
    int probe_count = (cluster_offset_count.z & 0xff);
    for (int i = 0 ; i < probe_count; ++i, ++lid) {
        if ((vfx_node.categoryBitmask & vfx_lights[LightIndex(lid)].categoryBitmask) == 0) continue;
        _lightingContribution.add_local_probe(vfx_lights[LightIndex(lid)], u_reflectionProbeTexture);
    }
#if PROBES_NORMALIZATION
    float3 probesNormalization = 0;
#if PROBES_OUTER_BLENDING
    probesNormalization = _lightingContribution.probesWeightedSum.rgb / max(1.f, _lightingContribution.probesWeightedSum.a);
#else
    probesNormalization = _lightingContribution.probesWeightedSum.rgb / _lightingContribution.probesWeightedSum.a;
#endif 
#ifdef DISABLE_SPECULAR
    _lightingContribution.pbr.envDiffuse += probesNormalization;
#else
    _lightingContribution.specular += probesNormalization;
#endif

    float globalFactor = saturate(1.f - _lightingContribution.probesWeightedSum.a);
#else
    float globalFactor = _lightingContribution.probeRadianceRemainingFactor;
#endif 

#ifndef DISABLE_SPECULAR
#ifdef USE_IBL_TRANSFORM
    _lightingContribution.add_global_probe(vfx_frame.environmentTransform * vfx_frame.viewToCubeTransform, globalFactor * vfx_frame.environmentIntensity,
                                           u_reflectionProbeTexture);
#else
    _lightingContribution.add_global_probe(vfx_frame.viewToCubeTransform, globalFactor * vfx_frame.environmentIntensity,
                                           u_reflectionProbeTexture);
#endif 
#endif 
    
#else 

#ifndef DISABLE_SPECULAR

#ifdef USE_IBL_TRANSFORM
    _lightingContribution.add_global_probe(u_radianceTexture, vfx_frame.environmentTransform * vfx_frame.viewToCubeTransform, vfx_frame.environmentIntensity);
#else
   _lightingContribution.add_global_probe(u_radianceTexture, vfx_frame.viewToCubeTransform, vfx_frame.environmentIntensity);
#endif 
    
#ifdef USE_CLEARCOAT
    
#ifdef USE_IBL_TRANSFORM
    _lightingContribution.add_global_probeClearCoat(u_radianceTexture, vfx_frame.environmentTransform * vfx_frame.viewToCubeTransform, vfx_frame.environmentIntensity);
#else
    _lightingContribution.add_global_probeClearCoat(u_radianceTexture, vfx_frame.viewToCubeTransform, vfx_frame.environmentIntensity);
#endif 
    
#endif 

    
#endif 
#endif 

#endif 
    #if DEBUG_PIXEL
        switch (DEBUG_PIXEL) {
            case 1: _output.color = float4(_surface.normal * 0.5f + 0.5f, 1.f); break;
            case 2: _output.color = float4(_surface.geometryNormal * 0.5f + 0.5f, 1.f); break;
            case 3: _output.color = float4(_surface.tangent * 0.5f + 0.5f, 1.f); break;
            case 4: _output.color = float4(in.uv0, 0.f, 1.f); break;
            case 5: _output.color = float4(_surface.diffuse.rgb, 1.f); break;
            case 6: _output.color = float4(float3(_surface.roughness), 1.f); break;
            case 7: _output.color = float4(float3(_surface.metalness), 1.f); break;
            case 8: _output.color = float4(float3(_surface.ambientOcclusion), 1.f); break;
                
            #ifdef USE_BENTNORMALS
                case 9: _output.color = float4(float3(_surface.bentNormal * 0.5f + 0.5f), 1.f); break;
            #else
                case 9: _output.color = float4(float3(_surface.normal * 0.5f + 0.5f), 1.f); break;
            #endif
            default: break;
        }
        return _output;
    #endif
    
    
    
    #ifdef USE_CLUSTERED_LIGHTING
        
        int omni_count = cluster_offset_count.y & 0xff;
        for (int i = 0 ; i < omni_count; ++i, ++lid) {
            if ((vfx_node.categoryBitmask & vfx_lights[LightIndex(lid)].categoryBitmask) == 0) continue;
            _lightingContribution.add_local_omni(vfx_lights[LightIndex(lid)]);
        }

        
        int spot_count = (cluster_offset_count.y >> 8);
        for (int i = 0 ; i < spot_count; ++i, ++lid) {
            if ((vfx_node.categoryBitmask & vfx_lights[LightIndex(lid)].categoryBitmask) == 0) continue;
            _lightingContribution.add_local_spot(vfx_lights[LightIndex(lid)]);
        }

    #endif
#else 
        _lightingContribution.diffuse = in.diffuse;
    #ifdef USE_SPECULAR
        _lightingContribution.specular = in.specular;
    #endif
#endif 
    #ifdef AVOID_OVERLIGHTING
        _lightingContribution.diffuse = saturate(_lightingContribution.diffuse);
    #ifdef USE_SPECULAR
        _lightingContribution.specular = saturate(_lightingContribution.specular);
    #endif 
    #endif 
#else 
    _lightingContribution.diffuse = float3(0.);
#endif 

    
    
    
    
#ifndef USE_GBUFFER_OUTPUT
#ifdef USE_PBR
    { 
        float3 diffuseAlbedo = mix(_lightingContribution.pbr.albedo, float3(0.0), _surface.metalness);
        
        
#ifdef USE_PBR_TRANSPARENCY
        float3 color = (_lightingContribution.ambient * _surface.ambientOcclusion) * _lightingContribution.pbr.albedo;
#else
        float3 color = (_lightingContribution.ambient * _surface.ambientOcclusion) * _surface.diffuse.rgb;
#endif
        
        color += _lightingContribution.pbr.envDiffuse;
        color += _lightingContribution.diffuse * diffuseAlbedo;
#ifndef DISABLE_SPECULAR
#ifndef DISABLE_SPECULAR_IBL
        color += _lightingContribution.pbr.envSpecular;
#endif
        color += _lightingContribution.specular;
#endif
#ifdef USE_EMISSION
        color += _surface.emission.rgb;
#endif
#ifdef USE_MULTIPLY
        color *= _surface.multiply.rgb;
#endif
#ifdef USE_MODULATE
        color *= _lightingContribution.modulate;
#endif
#ifndef USE_GBUFFER_OUTPUT
        _output.color.rgb = color;
#endif
    }
#else 

#ifdef USE_SHADOWONLY
    _output.color.rgb = float3(0.0);
    _output.color.a = 1. - _lightingContribution.shadowFactor;
#else
#ifdef USE_CONSTANT
    _output.color.rgb = _surface.diffuse.rgb;
    
#ifdef USE_EMISSION
    _output.color.rgb += _surface.emission.rgb;
#endif
#ifdef USE_MULTIPLY
    _output.color.rgb *= _surface.multiply.rgb;
#endif
    
#else
    _output.color.rgb = illuminate(_surface, _lightingContribution);
#endif
#endif 
#endif 

#ifndef USE_SHADOWONLY
  #ifdef USE_PBR_TRANSPARENCY
    _output.color.a = _lightingContribution.pbr.transparency;
  #else
    _output.color.a = _surface.diffuse.a;
  #endif
#endif

#ifdef USE_FOG
    applyFog(_output.color, length(_surface.position.xyz), vfx_frame.fogParameters, vfx_frame.fogColor);
#endif

#if !defined(DIFFUSE_PREMULTIPLIED) && !defined(USE_PBR_TRANSPARENCY)
    _output.color.rgb *= _surface.diffuse.a;
#endif
    
    
    
    
    
#ifdef USE_SHADOWONLY
    float transparencyFactor = 1.0;
  #ifdef USE_NODE_OPACITY
    transparencyFactor *= in.nodeOpacity;
  #endif
    _output.color.a *= transparencyFactor; 

#else 

#ifdef USE_TRANSPARENT 
    
#ifndef USE_PBR_TRANSPARENCY
  _output.color *= _surface.transparent.a;
#endif

#endif 
    
#ifdef USE_NODE_OPACITY
    _output.color *= in.nodeOpacity;
#endif
    
#endif 
#endif 

    
    
    
    
#ifdef USE_MODIFIER_FRAMEBUFFER
    const VFXFramebuffer _framebuffer = {
#if defined(CFX_SUPPORTS_PROGRAMMABLE_BLENDING) && defined(USE_MODIFIER_FRAMEBUFFER_COLOR0)
        .color = framebufferColor0
#else
        .color = 0.f
#endif
    };
#endif
    
#ifdef USE_FRAGMENT_MODIFIER
    
    const float2 blobPosition = u_shaderModifierParametersFragment.blobPosition;
const float radius = u_shaderModifierParametersFragment.radius;
const float2 screenRatio = u_shaderModifierParametersFragment.screenRatio;
const float blobDimensions = u_shaderModifierParametersFragment.blobDimensions;
const float blobAnimationSpeed = u_shaderModifierParametersFragment.blobAnimationSpeed;
const float transitionToBlob = u_shaderModifierParametersFragment.transitionToBlob;
const float lightMode = u_shaderModifierParametersFragment.lightMode;
const float2 applePosition = u_shaderModifierParametersFragment.applePosition;
const float isHDR = u_shaderModifierParametersFragment.isHDR;
const float appleScale = u_shaderModifierParametersFragment.appleScale;
constexpr sampler smp_repeat(filter::linear, address::repeat);
constexpr sampler smp_border_white(filter::linear, address::clamp_to_edge, border_color::opaque_white);
float time = vfx_frame.time;

float2 uv = _surface.diffuseTexcoord;
float2 effectPosition = mix(applePosition, blobPosition, transitionToBlob);
effectPosition.x *= -1.;
float2 appleScale2 = mix(appleScale, blobDimensions, transitionToBlob);
float2 UVapple = (uv - 0.5) + effectPosition ;
UVapple *= screenRatio;
UVapple /= appleScale2;
UVapple += 0.5;

half c = inSDF.sample(smp_border_white, float2(UVapple.x, UVapple.y)).x;

const float texWidth = inSDF.get_width();
const float kStep = 8./texWidth;
const float2 eps = float2(kStep, 0.);
float2 grad =  float2(inSDF.sample(smp_border_white, UVapple + eps.xy).x - inSDF.sample(smp_border_white, UVapple - eps.xy).x,
                      inSDF.sample(smp_border_white, UVapple + eps.yx).x - inSDF.sample(smp_border_white, UVapple - eps.yx).x);
grad /= (2.*kStep); // normalize

if (transitionToBlob > 0.0001)
{
    float2 uvBlob = (float2(uv.x, uv.y) - 0.5) + float2(effectPosition.x, effectPosition.y);
    uvBlob.y = -uvBlob.y;
    uvBlob *= screenRatio;
    uvBlob /= blobDimensions;
 
    Blob blob;
    blob.init(time * blobAnimationSpeed);
    float blobD = blob.map(uvBlob);
    
    c = mix(c, half(blobD), half(transitionToBlob));
    grad = mix(grad, blob.normal(uvBlob, blob.map(uvBlob)).xy, transitionToBlob);
}

float rampUV = atan2(grad.x, grad.y);
rampUV = (rampUV + M_PI_F) / (M_PI_F * 2);
rampUV += fract(time * 0.13);
float4 rampColor = inRamp.sample(smp_repeat, rampUV);
float colorMult = mix(1.1,1.1,lightMode);
rampColor.rgb = pow(rampColor.rgb * colorMult, mix(0.6,0.7,lightMode));


half outlineWidth = radius;
half smooth = 20 / texWidth;
float outline = smoothstep(outlineWidth+0.h, outlineWidth-smooth, abs(c));

outlineWidth *= 0.95;
float outline2 = 0.0 * smoothstep(outlineWidth + 0.001h, outlineWidth - 0.001h, abs(c)) * pow(smoothstep(outlineWidth - 0.05h, outlineWidth + 0.000h, abs(c)), 5.h);

float outline3 = 1 * smoothstep(outlineWidth + 0.001h, outlineWidth - 0.004h, abs(c)) * pow(smoothstep(outlineWidth - 0.14h, outlineWidth + 0.000h, abs(c)), 5.h);
outline3 = pow(outline3, 1.3);

outline2 += outline3;
float insideMult = mix(0.1, 0.3,lightMode);

outlineWidth *= 0.5;
if (lightMode < 0.5)
{
    rampColor.rgb *=  1 + 0.5 * outline2; 
    
    outline += pow(smoothstep(outlineWidth + 0.26h, outlineWidth - 0.01h, abs(c)), 3.9h) * 0.7; 
}
else
{
    rampColor.rgb *=  1 + 0.4 * outline2; 
    
    outline += pow(smoothstep(outlineWidth + 0.18h, outlineWidth - 0.01h, abs(c)), 3.5h) * 0.7; 
}


float4 out_color = float4(rampColor) *  saturate(outline);
_output.color = float4(out_color * pow(1-transitionToBlob, 2));

    
#endif
#if defined(USE_CLUSTERED_LIGHTING) && defined(DEBUG_CLUSTER_TILE)
    _output.color.rgb = mix(_output.color.rgb, float3(vfx::debugColorForCount(clusterIndex.z).xyz), 0.1f);
    _output.color.rgb = mix(_output.color.rgb, float3(clusterIndex.x & 0x1 ^ clusterIndex.y & 0x1).xyz, 0.01f);
#endif
    
#ifdef USE_ALPHA_CUTOFF
    if (_output.color.a <= vfx_commonprofile.alphaCutoff)
        discard_fragment();
#endif

#ifdef USE_POINT_RENDERING
    if ((dfdx(pointCoord.x) < 0.5f) && (length_squared(pointCoord * 2.f - 1.f) > 1.f)) {
        discard_fragment();
    }
#endif
    
    
#ifdef USE_OUTLINE
    _output.color.rgb = in.outlineHash;
#endif
    

#if defined(USE_MOTIONBLUR) && !defined(USE_GBUFFER_OUTPUT)
#ifdef USE_MULTIPLE_RENDERING
    _output.motionblur.xy = half2((in.mv_fragment.xy - vfx_frame.viewportSize.zw) / in.mv_fragment.z - (in.mv_lastFragment.xy / in.mv_lastFragment.z))*half2(1.,-1.) * vfx_frame.motionBlurIntensity;
#else
    _output.motionblur.xy = half2((in.mv_fragment.xy / in.mv_fragment.z) - (in.mv_lastFragment.xy / in.mv_lastFragment.z))*half2(1.,-1.) * vfx_frame.motionBlurIntensity;
#endif
    _output.motionblur.z = length(_output.motionblur.xy);
    _output.motionblur.w = half(-_surface.position.z);
#endif

#ifdef USE_NORMAL_ROUGHNESS_OUTPUT
#ifdef USE_PBR
    _output.normalRoughness = half4( half3(_surface.normal.xyz), half(_surface.rawRoughness) );
#else
    _output.normalRoughness = half4( half3(_surface.normal.xyz), 0.h );
#endif
#endif
                                 
#ifdef USE_ALBEDO_METALNESS_OUTPUT
#ifdef USE_PBR
    _output.albedoMetalnessOutput = half4( half3(_surface.diffuse.rgb), half(_surface.metalness) );
#else 
    _output.albedoMetalnessOutput = half4( 0.h );
#endif
#endif
    
#ifdef USE_RADIANCE_AO_OUTPUT
#ifdef USE_PBR
    _output.radianceAOOutput = half4(half3(_lightingContribution.pbr.envSpecular.rgb), half(_surface.ambientOcclusion));
#else
    _output.radianceAOOutput = half4(0.h, 0.h, 0.h, 0.h);
#endif
#endif
    
#ifdef USE_BARYCENTRIC_WIREFRAME
#ifdef USE_BARYCENTRIC_COORD
    const float3 d = 1.0f * fwidth(baryCoord);
    const float3 s = smoothstep(d * 0.25f, d * 0.75, u_barycentricCoord);
    _output.color = mix(float4(1.0), _output.color, min3(s.x, s.y, s.z));
#endif
#endif
    
#ifdef USE_GBUFFER_OUTPUT
    float opacity = _surface.diffuse.a;
#ifdef USE_NODE_OPACITY
    opacity *= in.nodeOpacity;
#endif
    
    float dither = vfx::interleaved_gradient_noise(in.fragmentPosition.xy);
    dither = fract(dither + in.fragmentPosition.z * 1.61803398875);
    dither = fract(dither + vfx_frame.frame * 1.61803398875);
    if (opacity < dither) discard_fragment();
    if (opacity < 0.01) discard_fragment();
    
    float3 emission = float3(0.);
#ifdef USE_EMISSION
    emission = _surface.emission.rgb;
#endif
    _output.albedo = float4(_surface.diffuse.rgb, opacity);
    _output.normals = float4(_surface.normal.xyz, -_surface.position.z);
    _output.roughmetal = float4(_surface.roughness, _surface.metalness, _surface.ambientOcclusion, 1.);
    _output.color = float4(emission, 1.);
    
    float2 vfx_prevUv = (in.mv_lastFragment.xy / in.mv_lastFragment.z);
    float2 vfx_uv = (in.mv_fragment.xy / in.mv_fragment.z);
    _output.velocity.xy = (vfx_prevUv - vfx_uv) * float2(.5,-.5);
    
#ifdef USE_MOTIONBLUR
    _output.velocity.z = length(_output.velocity.xy);
    _output.velocity.w = -_surface.position.z;
#endif
    ushort clearcoatIR = packHalf2ToUShort(half2(_surface.clearCoat, _surface.clearCoatRoughness));
    _output.clearCoat = half4(half3(_surface.clearCoatNormal), as_type<half>(clearcoatIR));
    
    _output.subsurface = half4(half3(_surface.subsurfaceRadius), half(_surface.subsurface));
    
    float transmissionColorLength = length(_surface.transmissionColor);
    _surface.transmissionColor /= max(1e-4, transmissionColorLength);
    ushort transmissionRG = packHalf2ToUShort(half2(_surface.transmissionColor.r, _surface.transmissionColor.g));
    ushort transmissionBW = packHalf2ToUShort(half2(_surface.transmissionColor.b, _surface.transmission));
    _output.transmission = ushort4(transmissionRG, transmissionBW,
                                 as_type<ushort>(half(transmissionColorLength)), as_type<ushort>(half(vfx_commonprofile.indexOfRefraction)));
#endif

#ifdef USE_RE_SYSTEM_TREATMENTS

#ifdef USE_MULTIPLE_RENDERING
#ifdef USE_VERTEX_AMPLIFICATION
    uint cameraIndex = amplificationID;
#else
    uint cameraIndex = in.sliceIndex;
#endif
#else
    uint cameraIndex = 0;
#endif 

    uint sampleMask = 0;

    vfx::api_v2::re_buffers buffers = vfx::api_v2::re_buffers {
        .entityConstants      = u_re_entityConstants,
        .viewConstants        = u_re_viewConstants,
        .globalConstants      = u_re_globalConstants,
        .objectConstants      = u_re_vfx_objectConstants,
        .entityArgumentBuffer = u_re_vfx_entityArgumentBuffer,
        .sceneArgumentBuffer  = u_re_vfx_sceneArgumentBuffer,
#ifdef USE_RE_SYSTEM_TREATMENTS_TIER_1_AB
        .probeTextures        = u_re_vfx_virtualEnvProbeTextures
#endif
    };

    auto params = vfx::api_v2::make_system_treatment_parameters(in.crworldPosition, in.fragmentPosition, cameraIndex, in.screen_uv);
    _output.color = float4(vfx::api_v2::apply_system_treatments(half4(_output.color), params, buffers, sampleMask));
#endif 

    return _output;
}

#pragma mark - Namespace End

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment