Instantly share code, notes, and snippets.
Forked from j-j-m/commonprofile.metal
Created
September 27, 2019 14:32
-
Star
(0)
0
You must be signed in to star a gist -
Fork
(0)
0
You must be signed in to fork a gist
-
Save earltedly/6fbd5ea30dd3e2f1f92dfba77119df55 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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//////////////////////////////////////////////// | |
// 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); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment