Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Per pixel lighting support in Metal #1551

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ enum plUVWSrcModifiers: uint32_t

using namespace metal;

constant const bool perPixelLighting [[ function_constant(FunctionConstantPerPixelLighting) ]];
constant const bool perVertexLighting = !perPixelLighting;

constant const uint8_t sourceType1 [[ function_constant(FunctionConstantSources + 0) ]];
constant const uint8_t sourceType2 [[ function_constant(FunctionConstantSources + 1) ]];
constant const uint8_t sourceType3 [[ function_constant(FunctionConstantSources + 2) ]];
Expand Down Expand Up @@ -100,7 +103,7 @@ constant const uint32_t miscFlags8 [[ function_constant(FunctionConstantLayerFla
constant const uint8_t sourceTypes[MAX_BLEND_PASSES] = { sourceType1, sourceType2, sourceType3, sourceType4, sourceType5, sourceType6, sourceType7, sourceType8};
constant const uint32_t blendModes[MAX_BLEND_PASSES] = { blendModes1, blendModes2, blendModes3, blendModes4, blendModes5, blendModes6, blendModes7, blendModes8};
constant const uint32_t miscFlags[MAX_BLEND_PASSES] = { miscFlags1, miscFlags2, miscFlags3, miscFlags4, miscFlags5, miscFlags6, miscFlags7, miscFlags8};
constant const uint8_t passCount = (sourceType1 > 0) + (sourceType2 > 0) + (sourceType3 > 0) + (sourceType4 > 0) + (sourceType5 > 0) + (sourceType6 > 0) + (sourceType7 > 0) + (sourceType8 > 0);
constant const uint8_t passCount = (sourceType1 > 0) + (sourceType2 > 0) + (sourceType3 > 0) + (sourceType4 > 0) + (sourceType5 > 0) + (sourceType6 > 0) + (sourceType7 > 0) + (sourceType8 > 0);

constant const bool has2DTexture1 = (sourceType1 == PassTypeTexture && hasLayer1);
constant const bool has2DTexture2 = (sourceType2 == PassTypeTexture && hasLayer2);
Expand Down Expand Up @@ -153,16 +156,18 @@ struct FragmentShaderArguments

typedef struct
{
float4 position [[position]];
float3 texCoord1 [[function_constant(hasLayer1)]];
float3 texCoord2 [[function_constant(hasLayer2)]];
float3 texCoord3 [[function_constant(hasLayer3)]];
float3 texCoord4 [[function_constant(hasLayer4)]];
float3 texCoord5 [[function_constant(hasLayer5)]];
float3 texCoord6 [[function_constant(hasLayer6)]];
float3 texCoord7 [[function_constant(hasLayer7)]];
float3 texCoord8 [[function_constant(hasLayer8)]];
half4 vtxColor [[ centroid_perspective ]];
float4 position [[ position ]];
float4 worldPos [[ function_constant(perPixelLighting) ]];
float3 normal [[ function_constant(perPixelLighting) ]];
float3 texCoord1 [[ function_constant(hasLayer1) ]];
float3 texCoord2 [[ function_constant(hasLayer2) ]];
float3 texCoord3 [[ function_constant(hasLayer3) ]];
float3 texCoord4 [[ function_constant(hasLayer4) ]];
float3 texCoord5 [[ function_constant(hasLayer5) ]];
float3 texCoord6 [[ function_constant(hasLayer6) ]];
float3 texCoord7 [[ function_constant(hasLayer7) ]];
float3 texCoord8 [[ function_constant(hasLayer8) ]];
half4 vtxColor [[ centroid_perspective ]];
half4 fogColor;
} ColorInOut;

Expand All @@ -172,31 +177,20 @@ typedef struct
float4 position [[position, invariant]];
float3 texCoord1;
} ShadowCasterInOut;

vertex ColorInOut pipelineVertexShader(Vertex in [[stage_in]],
constant VertexUniforms & uniforms [[ buffer( VertexShaderArgumentFixedFunctionUniforms) ]],
constant plMetalLights & lights [[ buffer(VertexShaderArgumentLights) ]],
constant float4x4 & blendMatrix1 [[ buffer(VertexShaderArgumentBlendMatrix1), function_constant(temp_hasOnlyWeight1) ]])

half4 calcLitMaterialColor(constant plMetalLights & lights,
const half4 materialColor,
constant plMaterialLightingDescriptor & materialLighting,
const float4 position,
const float3 normal)
{
ColorInOut out;
// we should have been able to swizzle, but it didn't work in Xcode beta? Try again later.
const half4 inColor = half4(in.color.b, in.color.g, in.color.r, in.color.a) / half4(255.f);

const half3 MAmbient = mix(inColor.rgb, uniforms.ambientCol, uniforms.ambientSrc);
const half4 MDiffuse = mix(inColor, uniforms.diffuseCol, uniforms.diffuseSrc);
const half3 MEmissive = mix(inColor.rgb, uniforms.emissiveCol, uniforms.emissiveSrc);

half3 LAmbient = half3(0.h, 0.h, 0.h);
half3 LDiffuse = half3(0.h, 0.h, 0.h);

const float3 Ndirection = normalize(float4(in.normal, 0.f) * uniforms.localToWorldMatrix).xyz;

float4 position = float4(in.position, 1.f) * uniforms.localToWorldMatrix;
if (temp_hasOnlyWeight1) {
const float4 position2 = float4(in.position, 1.f) * blendMatrix1;
position = (in.weight1 * position) + ((1.f - in.weight1) * position2);
}


const half3 MAmbient = mix(materialColor.rgb, materialLighting.ambientCol, materialLighting.ambientSrc);
const half4 MDiffuse = mix(materialColor, materialLighting.diffuseCol, materialLighting.diffuseSrc);
const half3 MEmissive = mix(materialColor.rgb, materialLighting.emissiveCol, materialLighting.emissiveSrc);

for (size_t i = 0; i < lights.count; i++) {
constant const plMetalShaderLightSource *lightSource = &lights.lampSources[i];
if (lightSource->scale == 0.0h)
Expand All @@ -212,6 +206,11 @@ vertex ColorInOut pipelineVertexShader(Vertex in [[stage_in]],
// Omni Light in all directions
const float3 v2l = lightSource->position.xyz - position.xyz;
const float distance = length(v2l);

if (distance > lightSource->range) {
continue;
}

direction.xyz = normalize(v2l);

direction.w = 1.f / (lightSource->constAtten + lightSource->linAtten * distance + lightSource->quadAtten * pow(distance, 2.f));
Expand All @@ -231,16 +230,42 @@ vertex ColorInOut pipelineVertexShader(Vertex in [[stage_in]],
}

LAmbient.rgb = LAmbient.rgb + half3(direction.w * (lightSource->ambient.rgb * lightSource->scale));
const float3 dotResult = dot(Ndirection, direction.xyz);
const float3 dotResult = dot(normal, direction.xyz);
LDiffuse.rgb = LDiffuse.rgb + MDiffuse.rgb * (lightSource->diffuse.rgb * lightSource->scale) * half3(max(0.f, dotResult) * direction.w);
}

const half3 ambient = (MAmbient.rgb) * clamp(uniforms.globalAmb.rgb + LAmbient.rgb, 0.h, 1.h);
const half3 ambient = (MAmbient.rgb) * clamp(materialLighting.globalAmb.rgb + LAmbient.rgb, 0.h, 1.h);
const half3 diffuse = clamp(LDiffuse.rgb, 0.h, 1.h);
const half4 material = half4(clamp(ambient + diffuse + MEmissive.rgb, 0.h, 1.h),
abs(uniforms.invVtxAlpha - MDiffuse.a));
return clamp(half4(ambient + diffuse + MEmissive.rgb, abs(materialLighting.invertAlpha - MDiffuse.a)), 0.h, 1.h);
}

vertex ColorInOut pipelineVertexShader(Vertex in [[stage_in]],
constant VertexUniforms & uniforms [[ buffer(VertexShaderArgumentFixedFunctionUniforms) ]],
constant plMaterialLightingDescriptor & materialLighting [[ buffer(VertexShaderArgumentMaterialLighting), function_constant(perVertexLighting) ]],
constant plMetalLights & lights [[ buffer(VertexShaderArgumentLights), function_constant(perVertexLighting) ]],
constant float4x4 & blendMatrix1 [[ buffer(VertexShaderArgumentBlendMatrix1), function_constant(temp_hasOnlyWeight1) ]])
{
ColorInOut out;
const half4 inColor = half4(in.color.b, in.color.g, in.color.r, in.color.a) / half4(255.f);

const float3 Ndirection = normalize(float4(in.normal, 0.f) * uniforms.localToWorldMatrix).xyz;

out.vtxColor = half4(material.rgb, abs(uniforms.invVtxAlpha - MDiffuse.a));
float4 position = float4(in.position, 1.f) * uniforms.localToWorldMatrix;
if (temp_hasOnlyWeight1) {
const float4 position2 = float4(in.position, 1.f) * blendMatrix1;
position = (in.weight1 * position) + ((1.f - in.weight1) * position2);
}

if (perPixelLighting) {
// send the world pos on to the pixel shader for lighting
out.worldPos = position;
out.normal = Ndirection;

out.vtxColor = inColor;
} else {
out.vtxColor = calcLitMaterialColor(lights, inColor, materialLighting, position, Ndirection);
}

const float4 vCamPosition = position * uniforms.worldToCameraMatrix;

// Fog
Expand Down Expand Up @@ -415,9 +440,12 @@ half4 FragmentShaderArguments::sampleLayer(const size_t index, const half4 verte
}

fragment half4 pipelineFragmentShader(ColorInOut in [[stage_in]],
const FragmentShaderArguments fragmentShaderArgs)
const FragmentShaderArguments fragmentShaderArgs,
constant plMetalLights & lights [[ buffer(FragmentShaderArgumentLights), function_constant(perPixelLighting) ]],
constant plMaterialLightingDescriptor & materialLighting [[ buffer(FragmentShaderArgumentMaterialLighting), function_constant(perPixelLighting) ]])
{
half4 currentColor = in.vtxColor;
const half4 lightingContributionColor = perPixelLighting ? calcLitMaterialColor(lights, in.vtxColor, materialLighting, in.worldPos, in.normal) : in.vtxColor;
half4 currentColor = lightingContributionColor;

/*
SPECIAL PLASMA RULE:
Expand Down Expand Up @@ -445,7 +473,7 @@ fragment half4 pipelineFragmentShader(ColorInOut in [[stage_in]],
}
}

currentColor = half4(in.vtxColor.rgb, 1.0h) * currentColor;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be a good time to ask about this one. After blending the layers - the shader again multiplies by the vertex +lighting color. This was carried over from the original GL shaders. I know if this isn't here the output is wrong. But I'm not sure why.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skimming through the code, my guess is this:
currentColor starts out set to the lighting colour with no material values, but then on the first layer is overwritten to the layer colours of the first layer (in blendFirst) with subsequent layers blending into that. Since we've lost the lighting colours at that point, we need to multiply them back in.

currentColor = lightingContributionColor * currentColor;
}

currentColor.rgb = mix(in.fogColor.rgb, currentColor.rgb, (float)clamp(in.fogColor.a, 0.0h, 1.0h));
Expand Down Expand Up @@ -612,14 +640,16 @@ fragment half4 shadowFragmentShader(ShadowCasterInOut in [[stage_in]])

vertex ColorInOut shadowCastVertexShader(Vertex in [[ stage_in ]],
constant VertexUniforms & uniforms [[ buffer( VertexShaderArgumentFixedFunctionUniforms) ]],
constant plMaterialLightingDescriptor & materialLighting [[ buffer( VertexShaderArgumentMaterialLighting) ]],
constant plShadowState & shadowState [[ buffer(VertexShaderArgumentShadowState) ]])
{
ColorInOut out;

float4 position = (float4(in.position, 1.f) * uniforms.localToWorldMatrix);
const float3 Ndirection = normalize(float4(in.normal, 0.f) * uniforms.localToWorldMatrix).xyz;
// Shadow casting uses the diffuse material color to control opacity
const half4 MDiffuse = uniforms.diffuseCol;
// FIXME: Should this be something more specific
const half4 MDiffuse = materialLighting.diffuseCol;

//w is attenation
float4 direction;
Expand Down
63 changes: 47 additions & 16 deletions Sources/Plasma/FeatureLib/pfMetalPipeline/ShaderSrc/ShaderTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,30 +54,33 @@ typedef __attribute__((__ext_vector_type__(3))) half half3;
typedef __attribute__((__ext_vector_type__(4))) half half4;
#endif

enum plMetalVertexShaderArgument
enum plMetalShaderArgument
{
/// Material State
VertexShaderArgumentFixedFunctionUniforms = 2,
/// Uniform table for Plasma dynamic shaders
VertexShaderArgumentMaterialShaderUniforms = 3,
/// Light Table
VertexShaderArgumentLights = 4,
/// Material properties for vertex lighting
VertexShaderArgumentMaterialLighting = 5,
/// Blend matrix for GPU side animation blending
VertexShaderArgumentBlendMatrix1 = 6,
/// Describes the state of a shadow caster for shadow cast shader
VertexShaderArgumentShadowState = 9
};

enum plMetalFragmentShaderArgumentIndex
{
VertexShaderArgumentShadowState = 9,

/// Texture is a legacy argument for the simpler plate shader
FragmentShaderArgumentTexture = 1,
/// Fragment uniforms
FragmentShaderArgumentShadowCastUniforms = 4,
/// Legacy argument buffer
FragmentShaderArgumentUniforms = 5,
/// Layer index of alpha for shadow fragment shader
FragmentShaderArgumentShadowCastAlphaSrc = 8
FragmentShaderArgumentShadowCastAlphaSrc = 8,
/// Light Table
FragmentShaderArgumentLights = 10,
/// Material properties for vertex lighting
FragmentShaderArgumentMaterialLighting = 11
};

enum plMetalVertexAttribute
Expand Down Expand Up @@ -110,6 +113,8 @@ enum plMetalFunctionConstant
FunctionConstantLayerFlags = 18,
/// Numbrer of weights in the FVF vertex layout.
FunctionConstantNumWeights = 26,
/// Per pixel lighting enable flag
FunctionConstantPerPixelLighting = 27,
};

enum plMetalLayerPassType: uint8_t
Expand Down Expand Up @@ -150,6 +155,7 @@ struct plMetalShaderLightSource
half4 specular;
simd::float3 direction;
simd::float4 spotProps; // (falloff, theta, phi)
__fp16 range;
__fp16 constAtten;
__fp16 linAtten;
__fp16 quadAtten;
Expand All @@ -168,15 +174,8 @@ struct UVOutDescriptor
static_assert(std::is_trivial_v<UVOutDescriptor>, "UVOutDescriptor must be a trivial type!");
#endif

struct VertexUniforms
struct plMaterialLightingDescriptor
{
// transformation
matrix_float4x4 projectionMatrix;
matrix_float4x4 localToWorldMatrix;
matrix_float4x4 cameraToWorldMatrix;
matrix_float4x4 worldToCameraMatrix;

// lighting
half4 globalAmb;
half3 ambientCol;
uint8_t ambientSrc;
Expand All @@ -186,7 +185,24 @@ struct VertexUniforms
uint8_t emissiveSrc;
half3 specularCol;
uint8_t specularSrc;
bool invVtxAlpha;

bool invertAlpha;

#ifndef __METAL_VERSION__
bool operator==(const plMaterialLightingDescriptor& rhs) const
{
return memcmp(this, &rhs, sizeof(plMaterialLightingDescriptor)) == 0;
}
#endif
};

struct VertexUniforms
{
// transformation
matrix_float4x4 projectionMatrix;
matrix_float4x4 localToWorldMatrix;
matrix_float4x4 cameraToWorldMatrix;
matrix_float4x4 worldToCameraMatrix;

uint8_t fogExponential;
simd::float2 fogValues;
Expand All @@ -197,6 +213,13 @@ struct VertexUniforms
float3 sampleLocation(size_t index, thread float3 *texCoords, const float4 normal, const float4 camPosition) constant;
half4 calcFog(float4 camPosition) constant;
#endif

#ifndef __METAL_VERSION__
bool operator==(const VertexUniforms& rhs) const
{
return memcmp(this, &rhs, sizeof(VertexUniforms)) == 0;
}
#endif
};
#ifndef __METAL_VERSION__
static_assert(std::is_trivial_v<VertexUniforms>, "VertexUniforms must be a trivial type!");
Expand All @@ -208,6 +231,14 @@ struct plMetalLights
{
uint8_t count;
plMetalShaderLightSource lampSources[kMetalMaxLightCount];

#ifndef __METAL_VERSION__
bool operator==(const plMetalLights& rhs) const
{
size_t lightSize = offsetof(plMetalLights, lampSources) + (sizeof(plMetalShaderLightSource) * count);
return rhs.count == count && memcmp(&rhs, this, lightSize ) == 0;
}
#endif
};
#ifndef __METAL_VERSION__
static_assert(std::is_trivial_v<plMetalLights>, "plMetalLights must be a trivial type!");
Expand Down
Loading