Skip to content

Instantly share code, notes, and snippets.

@j-j-m
Created May 8, 2017 00:20
Show Gist options
  • Save j-j-m/4ebd20e1fdabdfb0f2c2ce86cbeaba45 to your computer and use it in GitHub Desktop.
Save j-j-m/4ebd20e1fdabdfb0f2c2ce86cbeaba45 to your computer and use it in GitHub Desktop.
Can't access GL Uniforms in Metal shader modifier? Apple docs for SCNShadable written in terms of GL? Pulling your hair out?... this will help.
////////////////////////////////////////////////
// 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)
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;
// new in macOS 10.12 and iOS 10
float environmentIntensity;
float4x4 inverseProjectionTransform;
float4x4 inverseViewProjectionTransform;
};
// 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
//
// Tool function
namespace scn {
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 void generate_basis(float3 inR, thread float3 *outS, thread float3 *outT)
{
float3 dir = abs(inR.z) < 0.999 ? float3(0, 0, 1) : float3(1, 0, 0);
*outS = normalize(cross(dir, inR));
*outT = cross(inR, *outS);
}
// MARK: Drawing quads
struct draw_quad_io_t {
float4 position [[ position ]];
float2 uv;
};
}
inline float4 texture2DProj(texture2d<float> tex, sampler smp, float4 uv)
{
return tex.sample(smp, uv.xy / uv.w);
}
inline float shadow2DProj(depth2d<float> tex, float4 uv)
{
constexpr sampler linear_sampler(filter::linear, mip_filter::none, compare_func::greater_equal);
//constexpr sampler linear_sampler(filter::linear, mip_filter::none, compare_func::none);
float3 uvp = uv.xyz / uv.w;
return tex.sample_compare(linear_sampler, uvp.xy, uvp.z);
}
// 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_BOUNDINGBOX
float2x3 boundingBox;
#endif
#ifdef USE_WORLDBOUNDINGBOX
float2x3 worldBoundingBox;
#endif
#ifdef USE_NODE_OPACITY
float nodeOpacity;
#endif
#ifdef USE_DOUBLE_SIDED
float orientationPreserved;
#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)]];
float3 normal [[attribute(SCNVertexSemanticNormal)]];
float4 tangent [[attribute(SCNVertexSemanticTangent)]];
float4 color [[attribute(SCNVertexSemanticColor)]];
float4 skinningWeights [[attribute(SCNVertexSemanticBoneWeights)]];
uint4 skinningJoints [[attribute(SCNVertexSemanticBoneIndices)]];
float2 texcoord0 [[attribute(SCNVertexSemanticTexcoord0)]];
float2 texcoord1 [[attribute(SCNVertexSemanticTexcoord1)]];
float2 texcoord2 [[attribute(SCNVertexSemanticTexcoord2)]];
float2 texcoord3 [[attribute(SCNVertexSemanticTexcoord3)]];
float2 texcoord4 [[attribute(SCNVertexSemanticTexcoord4)]];
float2 texcoord5 [[attribute(SCNVertexSemanticTexcoord5)]];
float2 texcoord6 [[attribute(SCNVertexSemanticTexcoord6)]];
float2 texcoord7 [[attribute(SCNVertexSemanticTexcoord7)]];
} 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
#endif
#if defined(USE_POSITION) && (USE_POSITION == 2)
float3 position;
#endif
#if defined(USE_NORMAL) && (USE_NORMAL == 2)
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_NODE_OPACITY
float nodeOpacity;
#endif
#ifdef USE_DOUBLE_SIDED
float orientationPreserved;
#endif
#ifdef USE_TEXCOORD
#endif
} commonprofile_io;
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 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; // Metalness 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
#ifdef USE_SURFACE_EXTRA_DECL
#endif
};
struct SCNShaderLightingContribution {
float3 ambient;
float3 diffuse;
float3 specular;
float3 modulate;
};
// Structure to gather property of a light, packed to give access in a light shader modifier
struct SCNShaderLight {
float4 intensity; // lowp, light intensity
float3 direction; // mediump, vector from the point toward the light
float _att;
float3 _spotDirection; // lowp, vector from the point to the light for point and spot, dist attenuations
float _distance; // mediump, distance from the point to the light (same coord. than range)
};
#ifdef USE_PBR
inline SCNPBRSurface SCNShaderSurfaceToSCNPBRSurface(SCNShaderSurface surface)
{
SCNPBRSurface s;
s.n = surface.normal;
s.v = surface.view;
s.albedo = surface.diffuse.xyz;
#ifdef USE_EMISSION
s.emission = surface.emission.xyz;
#else
s.emission = float3(0.);
#endif
s.metalness = surface.metalness;
s.roughness = surface.roughness;
s.ao = surface.ambientOcclusion;
return s;
}
static float4 scn_pbr_combine(SCNPBRSurface pbr_surface,
SCNShaderLightingContribution lighting,
texture2d<float, access::sample> specularDFG,
texturecube<float, access::sample> specularLD,
#ifdef USE_PROBES_LIGHTING
#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
#else
texturecube<float, access::sample> irradiance,
#endif
constant SCNSceneBuffer& scn_frame)
{
#ifdef USE_PROBES_LIGHTING
float3 pbr_color = scn_pbr_color_IBL(pbr_surface, specularDFG, specularLD, shCoefficients, scn_frame.viewToCubeTransform, scn_frame.environmentIntensity);
#else
float3 pbr_color = scn_pbr_color_IBL(pbr_surface, specularDFG, specularLD, irradiance, scn_frame.viewToCubeTransform, scn_frame.environmentIntensity);
#endif
float4 color;
color.rgb = (lighting.ambient * pbr_surface.ao + lighting.diffuse) * pbr_surface.albedo.rgb + lighting.specular + pbr_color;
#if defined(USE_EMISSION) && !defined(USE_EMISSION_AS_SELFILLUMINATION)
color.rgb += pbr_surface.emission.rgb;
#endif
return color;
}
static void scn_pbr_lightingContribution(SCNShaderSurface surface,
SCNShaderLight light,
constant SCNSceneBuffer& scn_frame,
thread float3& lightingContributionDiffuse,
thread float3& lightingContributionSpecular)
{
SCNPBRSurface pbr_surface = SCNShaderSurfaceToSCNPBRSurface(surface);
float3 diffuseOut, specularOut;
scn_pbr_lightingContribution_pointLight(light.direction, pbr_surface.n, pbr_surface.v, pbr_surface.albedo, pbr_surface.metalness, pbr_surface.roughness, diffuseOut, specularOut);
float3 lightFactor = light.intensity.rgb * light._att;
lightingContributionDiffuse += diffuseOut * lightFactor;
lightingContributionSpecular += specularOut * lightFactor;
}
#else // ifdef USE_PBR
inline float4 illuminate(SCNShaderSurface surface, SCNShaderLightingContribution lighting)
{
float4 color = {0.,0.,0., surface.diffuse.a};
float3 D = lighting.diffuse;
#if defined(USE_AMBIENT_LIGHTING) && (defined(LOCK_AMBIENT_WITH_DIFFUSE) || defined(USE_AMBIENT_AS_AMBIENTOCCLUSION))
D += lighting.ambient * surface.ambientOcclusion;
#endif
#ifdef USE_EMISSION_AS_SELFILLUMINATION
D += surface.emission.rgb;
#endif
// Do we want to clamp there ????
color.rgb = surface.diffuse.rgb * D;
#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)
color.rgb += S;
#endif
#if defined(USE_AMBIENT) && !defined(USE_AMBIENT_AS_AMBIENTOCCLUSION)
color.rgb += surface.ambient.rgb * lighting.ambient;
#endif
#if defined(USE_EMISSION) && !defined(USE_EMISSION_AS_SELFILLUMINATION)
color.rgb += surface.emission.rgb;
#endif
#ifdef USE_MULTIPLY
color.rgb *= surface.multiply.rgb;
#endif
#ifdef USE_MODULATE
color.rgb *= lighting.modulate;
#endif
return color;
}
#endif
struct commonprofile_lights {
#ifdef USE_LIGHTING
float4 color0;
float4 position0;
#endif
};
struct SCNShaderGeometry
{
float4 position;
float3 normal;
float4 tangent;
float4 color;
float2 texcoords[8]; // MAX_UV
};
struct commonprofile_uniforms {
float4 diffuseColor;
float4 specularColor;
float4 ambientColor;
float4 emissionColor;
float4 reflectiveColor;
float4 multiplyColor;
float4 transparentColor;
float metalness;
float roughness;
float diffuseIntensity;
float specularIntensity;
float normalIntensity;
float ambientIntensity;
float emissionIntensity;
float reflectiveIntensity;
float multiplyIntensity;
float transparentIntensity;
float metalnessIntensity;
float roughnessIntensity;
float materialShininess;
float selfIlluminationOcclusion;
float transparency;
float3 fresnel; // x: ((n1-n2)/(n1+n2))^2 y:1-x z:exponent
#ifdef TEXTURE_TRANSFORM_COUNT
float4x4 textureTransforms[TEXTURE_TRANSFORM_COUNT];
#endif
#if defined(USE_REFLECTIVE_CUBEMAP)
// float4x4 u_viewToCubeWorld;
#endif
};
// Shader modifiers declaration (only enabled if one modifier is present)
#ifdef USE_SHADER_MODIFIERS
// initial geometry is [-1,1] in XY plane (so z is always 0)
struct scn_floor_t {
float3 u_floorNormal;
float4 u_floorTangent;
float3 u_floorCenter;
float2 u_floorExtent;
};
#endif
// Vertex shader function
vertex commonprofile_io commonprofile_vert(scn_vertex_t in [[ stage_in ]],
constant SCNSceneBuffer& scn_frame [[buffer(0)]],
#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)]]
, uint instanceID [[ instance_id ]]
#else
constant commonprofile_node& scn_node [[buffer(1)]]
#endif
#ifdef USE_PER_VERTEX_LIGHTING
, constant commonprofile_lights& scn_lights [[buffer(2)]]
#endif
// used for texture transform and materialShininess in case of perVertexLighting
, constant commonprofile_uniforms& scn_commonprofile [[buffer(3)]]
#ifdef USE_VERTEX_EXTRA_ARGUMENTS
, constant scn_floor_t& scn_fg [[buffer(4)]]
#endif
)
{
#ifdef USE_INSTANCING
device commonprofile_node& scn_node = scn_nodeInstances[instanceID];
#endif
SCNShaderGeometry _geometry;
// OPTIM in could be already float4?
_geometry.position = float4(in.position, 1.0);
#ifdef USE_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
#ifdef USE_SKINNING
#if 0 // Alternate Skinning method, linear combining the joint matrices. Not fully tested yet
{
float4 joint0 = 0.;
float4 joint1 = 0.;
float4 joint2 = 0.;
uint4 idx3 = in.skinningJoints * 3;
for (int i = 0; i < MAX_BONE_INFLUENCES; ++i) {
int idx = idx3[i];
#if MAX_BONE_INFLUENCES == 1
float boneWeight = 1.;
#else
float boneWeight = in.skinningWeights[i];
#endif
joint0 += boneWeight * scn_node.skinningJointMatrices[idx];
joint1 += boneWeight * scn_node.skinningJointMatrices[idx+1];
joint2 += boneWeight * scn_node.skinningJointMatrices[idx+2];
}
float4x4 jointMatrix = float4x4(joint0, joint1, joint2, float4(0., 0., 0., 1.));
_geometry.position.xyz = (_geometry.position * jointMatrix).xyz;
#ifdef USE_NORMAL
_geometry.normal = _geometry.normal * scn::mat3(jointMatrix);
#endif
#if defined(USE_TANGENT) || defined(USE_BITANGENT)
_geometry.tangent.xyz = _geometry.tangent.xyz * scn::mat3(jointMatrix);
#endif
}
#else
{
float3 pos = 0.0;
#ifdef USE_NORMAL
float3 nrm = 0.0;
#endif
#if defined(USE_TANGENT) || defined(USE_BITANGENT)
float3 tgt = 0.0;
#endif
for (int i = 0; i < MAX_BONE_INFLUENCES; ++i) {
#if MAX_BONE_INFLUENCES == 1
float weight = 1.0;
#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;
#ifdef USE_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;
#ifdef USE_NORMAL
_geometry.normal = nrm;
#endif
#if defined(USE_TANGENT) || defined(USE_BITANGENT)
_geometry.tangent.xyz = tgt;
#endif
}
#endif
#endif // USE_SKINNING
#ifdef USE_GEOMETRY_MODIFIER
// DoGeometryModifier START
float3 u_floorNormal = scn_fg.u_floorNormal;
float4 u_floorTangent = scn_fg.u_floorTangent;
float3 u_floorCenter = scn_fg.u_floorCenter;
float2 u_floorExtent = scn_fg.u_floorExtent;
float3 floorBitangent = normalize(cross(u_floorTangent.xyz, u_floorNormal));
_geometry.position.xyz = u_floorCenter.xyz + u_floorExtent.x * (_geometry.position.x * u_floorTangent.xyz) + u_floorExtent.y * (_geometry.position.y * floorBitangent);
_geometry.normal = u_floorNormal;
_geometry.tangent = u_floorTangent;
// we could check if the texCoord are really needed with ifdef USE_xxxx_MAP , or, better, work only on texcoordN [0..1]
float2 tc;
if (u_floorNormal.y != 0.)
tc = _geometry.position.xz * 0.01;
else if (u_floorNormal.z != 0.)
tc = _geometry.position.xy * 0.01;
else
tc = _geometry.position.yz * 0.01;
for (int i = 0; i < kSCNTexcoordCount; ++i)
_geometry.texcoords[i] = tc;
// DoGeometryModifier END
#endif
// Transform the geometry elements in view space
#if defined(USE_POSITION) || defined(USE_NORMAL) || defined(USE_TANGENT) || defined(USE_BITANGENT) || defined(USE_INSTANCING)
SCNShaderSurface _surface;
#endif
#if defined(USE_POSITION) || defined(USE_INSTANCING)
_surface.position = (scn_node.modelViewTransform * _geometry.position).xyz;
#endif
#ifdef USE_NORMAL
_surface.normal = normalize(scn::mat3(scn_node.normalTransform) * _geometry.normal);
#endif
#if defined(USE_TANGENT) || defined(USE_BITANGENT)
_surface.tangent = normalize(scn::mat3(scn_node.normalTransform) * _geometry.tangent.xyz);
_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
commonprofile_io out;
#ifdef USE_PER_VERTEX_LIGHTING
// Lighting
SCNShaderLightingContribution _lightingContribution;
_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)
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
#ifdef USE_TEXCOORD
#endif
#if defined(USE_POSITION) || defined(USE_INSTANCING)
out.fragmentPosition = scn_frame.projectionTransform * float4(_surface.position, 1.);
#elif defined(USE_MODELVIEWPROJECTIONTRANSFORM) // this means that the geometry are still in model space : we can transform it directly to NDC space
out.fragmentPosition = scn_node.modelViewProjectionTransform * _geometry.position;
#endif
#ifdef USE_NODE_OPACITY
out.nodeOpacity = scn_node.nodeOpacity;
#endif
#ifdef USE_DOUBLE_SIDED
out.orientationPreserved = scn_node.orientationPreserved;
#endif
#ifdef USE_POINT_RENDERING
out.fragmentSize = 1.;
#endif
return out;
}
struct SCNOutput
{
float4 color;
};
// Fragment shader function
fragment half4 commonprofile_frag(commonprofile_io in [[stage_in]],
constant commonprofile_uniforms& scn_commonprofile [[buffer(0)]],
constant SCNSceneBuffer& scn_frame [[buffer(1)]]
#ifdef USE_PER_PIXEL_LIGHTING
, constant commonprofile_lights& scn_lights [[buffer(2)]]
#endif
#ifdef USE_EMISSION_MAP
, texture2d<float> u_emissionTexture [[texture(0)]]
, sampler u_emissionTextureSampler [[sampler(0)]]
#endif
#ifdef USE_AMBIENT_MAP
, texture2d<float> u_ambientTexture [[texture(1)]]
, sampler u_ambientTextureSampler [[sampler(1)]]
#endif
#ifdef USE_DIFFUSE_MAP
, texture2d<float> u_diffuseTexture [[texture(2)]]
, sampler u_diffuseTextureSampler [[sampler(2)]]
#endif
#ifdef USE_SPECULAR_MAP
, texture2d<float> u_specularTexture [[texture(3)]]
, sampler u_specularTextureSampler [[sampler(3)]]
#endif
#ifdef USE_REFLECTIVE_MAP
, texture2d<float> u_reflectiveTexture [[texture(4)]]
, sampler u_reflectiveTextureSampler [[sampler(4)]]
#elif defined(USE_REFLECTIVE_CUBEMAP)
, texturecube<float> u_reflectiveTexture [[texture(4)]]
, sampler u_reflectiveTextureSampler [[sampler(4)]]
#endif
#ifdef USE_TRANSPARENT_MAP
, texture2d<float> u_transparentTexture [[texture(5)]]
, sampler u_transparentTextureSampler [[sampler(5)]]
#endif
#ifdef USE_MULTIPLY_MAP
, texture2d<float> u_multiplyTexture [[texture(6)]]
, sampler u_multiplyTextureSampler [[sampler(6)]]
#endif
#ifdef USE_NORMAL_MAP
, texture2d<float> u_normalTexture [[texture(7)]]
, sampler u_normalTextureSampler [[sampler(7)]]
#endif
#ifdef USE_PBR
#ifdef USE_METALNESS_MAP
, texture2d<float> u_metalnessTexture [[texture(3)]]
, sampler u_metalnessTextureSampler [[sampler(3)]]
#endif
#ifdef USE_ROUGHNESS_MAP
, texture2d<float> u_roughnessTexture [[texture(4)]]
, sampler u_roughnessTextureSampler [[sampler(4)]]
#endif
, texturecube<float> u_irradianceTexture [[texture(8)]]
, texturecube<float> u_radianceTexture [[texture(9)]]
, texture2d<float> u_specularDFGTexture [[texture(10)]]
#endif
#ifdef USE_SSAO
, texture2d<float> u_ssaoTexture [[texture(11)]]
#endif
, constant commonprofile_node& scn_node [[buffer(3)]]
#ifdef USE_FRAGMENT_EXTRA_ARGUMENTS
#endif
#if defined(USE_DOUBLE_SIDED)
, bool isFrontFacing [[front_facing]]
#endif
)
{
SCNShaderSurface _surface;
#ifdef USE_TEXCOORD
#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., _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
#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);
#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.,0.,0.,1.);
#endif
#if defined(USE_DIFFUSE) && defined(USE_VERTEX_COLOR)
_surface.diffuse *= in.vertexColor;
#endif
#ifdef USE_SPECULAR_MAP
_surface.specular = u_specularTexture.sample(u_specularTextureSampler, _surface.specularTexcoord);
#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.);
#endif
#ifdef USE_EMISSION_MAP
_surface.emission = u_emissionTexture.sample(u_emissionTextureSampler, _surface.emissionTexcoord);
#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_MULTIPLY_MAP
_surface.multiply = u_multiplyTexture.sample(u_multiplyTextureSampler, _surface.multiplyTexcoord);
#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);
#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.);
#endif
#ifdef USE_METALNESS_MAP
_surface.metalness = u_metalnessTexture.sample(u_metalnessTextureSampler, _surface.metalnessTexcoord).r;
#ifdef USE_METALNESS_INTENSITY
_surface.metalness *= scn_commonprofile.metalnessIntensity;
#endif
#elif defined(USE_METALNESS_COLOR)
_surface.metalness = scn_commonprofile.metalness;
#else
_surface.metalness = 0;
#endif
#ifdef USE_ROUGHNESS_MAP
_surface.roughness = u_roughnessTexture.sample(u_roughnessTextureSampler, _surface.roughnessTexcoord).r;
#ifdef USE_ROUGHNESS_INTENSITY
_surface.roughness *= scn_commonprofile.roughnessIntensity;
#endif
#elif defined(USE_ROUGHNESS_COLOR)
_surface.roughness = scn_commonprofile.roughness;
#else
_surface.roughness = 0;
#endif
#if (defined USE_NORMAL) && (USE_NORMAL == 2)
#ifdef USE_DOUBLE_SIDED
_surface.geometryNormal = normalize(in.normal.xyz) * in.orientationPreserved * ((float(isFrontFacing) * 2.0) - 1.0);
#else
_surface.geometryNormal = normalize(in.normal.xyz);
#endif
_surface.normal = _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_POSITION) && (USE_POSITION == 2)
_surface.position = in.position;
#endif
#if (defined USE_VIEW) && (USE_VIEW == 2)
_surface.view = normalize(-in.position);
#endif
#ifdef USE_NORMAL_MAP
float3x3 ts2vs = float3x3(_surface.tangent, _surface.bitangent, _surface.normal);
_surface._normalTS = u_normalTexture.sample(u_normalTextureSampler, _surface.normalTexcoord).rgb * 2. - 1.;
// _surface.normal.z = 1. - sqrt(_surface.normal.x * _surface.normal.x + _surface.normal.y * _surface.normal.y);
#ifdef USE_NORMAL_INTENSITY
_surface._normalTS = mix(float3(0., 0., 1.), _surface._normalTS, scn_commonprofile.normalIntensity);
#endif
// transform the normal in view space
_surface.normal.rgb = normalize(ts2vs * _surface._normalTS);
#else
_surface._normalTS = float3(0.);
#endif
#ifdef USE_REFLECTIVE_MAP
float3 refl = reflect( -_surface.view, _surface.normal );
float m = 2.0 * sqrt( refl.x*refl.x + refl.y*refl.y + (refl.z+1.0)*(refl.z+1.0));
_surface.reflective = u_reflectiveTexture.sample(u_reflectiveTextureSampler, float2(float2(refl.x,-refl.y) / m) + 0.5);
#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.0 - saturate(dot(_surface.view, _surface.normal)), scn_commonprofile.fresnel.z);
_surface.reflective *= _surface.fresnel;
#endif
#ifdef USE_SHININESS
_surface.shininess = scn_commonprofile.materialShininess;
#endif
#ifdef USE_SURFACE_MODIFIER
// DoSurfaceModifier START
// DoSurfaceModifier END
#endif
// Lighting
SCNShaderLightingContribution _lightingContribution = {0};
// Lighting
#ifdef USE_AMBIENT_LIGHTING
_lightingContribution.ambient = scn_frame.ambientLightingColor.rgb;
#endif
#ifdef USE_LIGHTING
#ifdef USE_PER_PIXEL_LIGHTING
_lightingContribution.diffuse = float3(0.);
#ifdef USE_MODULATE
_lightingContribution.modulate = float3(1.);
#endif
#ifdef USE_SPECULAR
_lightingContribution.specular = float3(0.);
#endif
{
SCNShaderLight _light;
_light.intensity = scn_lights.color0;
_light.direction = normalize(scn_lights.position0.xyz - _surface.position);
_light._att = 1.;
_light.intensity.rgb *= _light._att * max(0.f, dot(_surface.normal, _light.direction));
_lightingContribution.diffuse += _light.intensity.rgb;
}
#else // USE_PER_PIXEL_LIGHTING
_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 // USE_SPECULAR
#endif // AVOID_OVERLIGHTING
#else // USE_LIGHTING
_lightingContribution.diffuse = float3(1.);
#endif // USE_LIGHTING
// Combine
SCNOutput _output;
#ifdef USE_PBR
SCNPBRSurface pbr_surface = SCNShaderSurfaceToSCNPBRSurface(_surface);
pbr_surface.selfIlluminationOcclusion = scn_commonprofile.selfIlluminationOcclusion;
#ifdef USE_PROBES_LIGHTING
_output.color = scn_pbr_combine(pbr_surface, _lightingContribution, u_specularDFGTexture, u_radianceTexture, scn_node.shCoefficients, scn_frame);
#else
_output.color = scn_pbr_combine(pbr_surface, _lightingContribution, u_specularDFGTexture, u_radianceTexture, u_irradianceTexture, scn_frame);
#endif
_output.color.a = _surface.diffuse.a;
#else
_output.color = illuminate(_surface, _lightingContribution);
#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
#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.212671) + (_surface.transparent.g * 0.715160) + (_surface.transparent.b * 0.072169);
_output.color *= (float4(1.) - _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
#ifdef USE_FRAGMENT_MODIFIER
// DoFragmentModifier START
// DoFragmentModifier END
#endif
//#ifdef USE_SSAO
// _output.color.rgb = float3(_surface.ambientOcclusion);
//#endif
#ifdef USE_DISCARD
if (_output.color.a == 0.) // we could set a different limit here
discard_fragment();
#endif
return half4(_output.color);
}
@drewolbrich
Copy link

Thank you. This is extraordinarily helpful.

@drewolbrich
Copy link

A more recent version of this (November 2019) with updates: https://gist.github.com/warrenm/794e459e429daa8c75b5f17c000600cf

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