rendering: unify builtin forward and depth-style shaders

This commit is contained in:
2026-04-07 03:35:06 +08:00
parent 503ffbc4ff
commit 5f9f3386ab
27 changed files with 1135 additions and 1151 deletions

View File

@@ -1,20 +0,0 @@
// XC_BUILTIN_DEPTH_ONLY_OPENGL_PS
#version 430
layout(binding = 0) uniform sampler2D uBaseColorTexture;
layout(std140, binding = 1) uniform MaterialConstants {
vec4 gBaseColorFactor;
vec4 gAlphaCutoffParams;
};
in vec2 vTexCoord;
void main() {
#ifdef XC_ALPHA_TEST
vec4 baseColor = texture(uBaseColorTexture, vTexCoord) * gBaseColorFactor;
if (baseColor.a < gAlphaCutoffParams.x) {
discard;
}
#endif
}

View File

@@ -1,21 +0,0 @@
// XC_BUILTIN_DEPTH_ONLY_VULKAN_PS
#version 450
layout(set = 2, binding = 0) uniform texture2D uBaseColorTexture;
layout(set = 3, binding = 0) uniform sampler uLinearSampler;
layout(set = 1, binding = 0, std140) uniform MaterialConstants {
vec4 gBaseColorFactor;
vec4 gAlphaCutoffParams;
};
layout(location = 0) in vec2 vTexCoord;
void main() {
#ifdef XC_ALPHA_TEST
vec4 baseColor = texture(sampler2D(uBaseColorTexture, uLinearSampler), vTexCoord) * gBaseColorFactor;
if (baseColor.a < gAlphaCutoffParams.x) {
discard;
}
#endif
}

View File

@@ -1,20 +0,0 @@
// XC_BUILTIN_DEPTH_ONLY_D3D12_PS
Texture2D gBaseColorTexture : register(t0);
SamplerState gLinearSampler : register(s0);
cbuffer MaterialConstants : register(b1) {
float4 gBaseColorFactor;
float4 gAlphaCutoffParams;
};
struct PSInput {
float4 position : SV_POSITION;
float2 texcoord : TEXCOORD0;
};
void MainPS(PSInput input) {
#ifdef XC_ALPHA_TEST
float4 baseColor = gBaseColorTexture.Sample(gLinearSampler, input.texcoord) * gBaseColorFactor;
clip(baseColor.a - gAlphaCutoffParams.x);
#endif
}

View File

@@ -6,26 +6,67 @@ Shader "Builtin Depth Only"
_Cutoff ("Alpha Cutoff", Range) = 0.5 [Semantic(AlphaCutoff)]
_MainTex ("Base Map", 2D) = "white" [Semantic(BaseColorTexture)]
}
HLSLINCLUDE
cbuffer PerObjectConstants : register(b0)
{
float4x4 gProjectionMatrix;
float4x4 gViewMatrix;
float4x4 gModelMatrix;
};
cbuffer MaterialConstants : register(b1)
{
float4 gBaseColorFactor;
float4 gAlphaCutoffParams;
};
Texture2D BaseColorTexture : register(t0);
SamplerState LinearClampSampler : register(s0);
struct VSInput
{
float3 position : POSITION;
float3 normal : NORMAL;
float2 texcoord : TEXCOORD0;
};
struct PSInput
{
float4 position : SV_POSITION;
float2 texcoord : TEXCOORD0;
};
PSInput MainVS(VSInput input)
{
PSInput output;
const float4 positionWS = mul(gModelMatrix, float4(input.position, 1.0f));
const float4 positionVS = mul(gViewMatrix, positionWS);
output.position = mul(gProjectionMatrix, positionVS);
output.texcoord = input.texcoord;
return output;
}
float MainPS(PSInput input) : SV_Depth
{
#ifdef XC_ALPHA_TEST
const float4 baseColor =
BaseColorTexture.Sample(LinearClampSampler, input.texcoord) * gBaseColorFactor;
clip(baseColor.a - gAlphaCutoffParams.x);
#endif
return input.position.z;
}
ENDHLSL
SubShader
{
Pass
{
Name "DepthOnly"
Tags { "LightMode" = "DepthOnly" }
Resources
{
PerObjectConstants (ConstantBuffer, 0, 0) [Semantic(PerObject)]
MaterialConstants (ConstantBuffer, 1, 0) [Semantic(Material)]
BaseColorTexture (Texture2D, 2, 0) [Semantic(BaseColorTexture)]
LinearClampSampler (Sampler, 3, 0) [Semantic(LinearClampSampler)]
}
HLSLPROGRAM
#pragma target 4.5
#pragma vertex MainVS
#pragma fragment MainPS
#pragma shader_feature_local _ XC_ALPHA_TEST
#pragma backend D3D12 HLSL "depth-only.vs.hlsl" "depth-only.ps.hlsl" vs_5_0 ps_5_0
#pragma backend OpenGL GLSL "depth-only.vert.glsl" "depth-only.frag.glsl"
#pragma backend Vulkan GLSL "depth-only.vert.vk.glsl" "depth-only.frag.vk.glsl"
ENDHLSL
}
}

View File

@@ -1,19 +0,0 @@
// XC_BUILTIN_DEPTH_ONLY_OPENGL_VS
#version 430
layout(location = 0) in vec3 aPosition;
layout(location = 2) in vec2 aTexCoord;
layout(std140, binding = 0) uniform PerObjectConstants {
mat4 gProjectionMatrix;
mat4 gViewMatrix;
mat4 gModelMatrix;
};
out vec2 vTexCoord;
void main() {
vec4 positionWS = gModelMatrix * vec4(aPosition, 1.0);
vec4 positionVS = gViewMatrix * positionWS;
gl_Position = gProjectionMatrix * positionVS;
vTexCoord = aTexCoord;
}

View File

@@ -1,19 +0,0 @@
// XC_BUILTIN_DEPTH_ONLY_VULKAN_VS
#version 450
layout(location = 0) in vec3 aPosition;
layout(location = 2) in vec2 aTexCoord;
layout(set = 0, binding = 0, std140) uniform PerObjectConstants {
mat4 gProjectionMatrix;
mat4 gViewMatrix;
mat4 gModelMatrix;
};
layout(location = 0) out vec2 vTexCoord;
void main() {
vec4 positionWS = gModelMatrix * vec4(aPosition, 1.0);
vec4 positionVS = gViewMatrix * positionWS;
gl_Position = gProjectionMatrix * positionVS;
vTexCoord = aTexCoord;
}

View File

@@ -1,25 +0,0 @@
// XC_BUILTIN_DEPTH_ONLY_D3D12_VS
cbuffer PerObjectConstants : register(b0) {
float4x4 gProjectionMatrix;
float4x4 gViewMatrix;
float4x4 gModelMatrix;
};
struct VSInput {
float3 position : POSITION;
float2 texcoord : TEXCOORD0;
};
struct PSInput {
float4 position : SV_POSITION;
float2 texcoord : TEXCOORD0;
};
PSInput MainVS(VSInput input) {
PSInput output;
float4 positionWS = mul(gModelMatrix, float4(input.position, 1.0));
float4 positionVS = mul(gViewMatrix, positionWS);
output.position = mul(gProjectionMatrix, positionVS);
output.texcoord = input.texcoord;
return output;
}

View File

@@ -1,174 +0,0 @@
// XC_BUILTIN_FORWARD_LIT_OPENGL_PS
#version 430
layout(binding = 0) uniform sampler2D uBaseColorTexture;
layout(binding = 1) uniform sampler2D uShadowMapTexture;
layout(std140, binding = 0) uniform PerObjectConstants {
mat4 gProjectionMatrix;
mat4 gViewMatrix;
mat4 gModelMatrix;
mat4 gNormalMatrix;
};
const int XC_MAX_ADDITIONAL_LIGHTS = 8;
struct AdditionalLightData {
vec4 colorAndIntensity;
vec4 positionAndRange;
vec4 directionAndType;
vec4 spotAnglesAndFlags;
};
layout(std140, binding = 1) uniform LightingConstants {
vec4 gMainLightDirectionAndIntensity;
vec4 gMainLightColorAndFlags;
vec4 gLightingParams;
AdditionalLightData gAdditionalLights[XC_MAX_ADDITIONAL_LIGHTS];
};
layout(std140, binding = 2) uniform MaterialConstants {
vec4 gBaseColorFactor;
vec4 gAlphaCutoffParams;
};
layout(std140, binding = 3) uniform ShadowReceiverConstants {
mat4 gWorldToShadowMatrix;
vec4 gShadowBiasAndTexelSize;
vec4 gShadowOptions;
};
in vec3 vNormalWS;
in vec2 vTexCoord;
in vec3 vPositionWS;
layout(location = 0) out vec4 fragColor;
float ComputeShadowAttenuation(vec3 positionWS) {
#ifndef XC_MAIN_LIGHT_SHADOWS
return 1.0;
#else
if (gShadowOptions.x < 0.5) {
return 1.0;
}
vec4 shadowClip = gWorldToShadowMatrix * vec4(positionWS, 1.0);
if (shadowClip.w <= 0.0) {
return 1.0;
}
vec3 shadowNdc = shadowClip.xyz / shadowClip.w;
vec2 shadowUv = vec2(
shadowNdc.x * 0.5 + 0.5,
shadowNdc.y * 0.5 + 0.5);
if (shadowUv.x < 0.0 || shadowUv.x > 1.0 ||
shadowUv.y < 0.0 || shadowUv.y > 1.0 ||
shadowNdc.z < -1.0 || shadowNdc.z > 1.0) {
return 1.0;
}
float shadowDepth = texture(uShadowMapTexture, shadowUv).r;
float receiverDepth = shadowNdc.z * 0.5 + 0.5 - gShadowBiasAndTexelSize.x;
float shadowStrength = clamp(gShadowBiasAndTexelSize.w, 0.0, 1.0);
return receiverDepth <= shadowDepth ? 1.0 : (1.0 - shadowStrength);
#endif
}
float ComputeRangeAttenuation(float distanceSq, float range) {
if (range <= 0.0) {
return 0.0;
}
float clampedRange = max(range, 0.0001);
float rangeSq = clampedRange * clampedRange;
if (distanceSq >= rangeSq) {
return 0.0;
}
float distance = sqrt(max(distanceSq, 0.0));
float normalized = clamp(1.0 - distance / clampedRange, 0.0, 1.0);
return normalized * normalized;
}
float ComputeSpotAttenuation(AdditionalLightData light, vec3 directionToLightWS) {
float cosOuter = light.spotAnglesAndFlags.x;
float cosInner = light.spotAnglesAndFlags.y;
vec3 spotAxisToLightWS = normalize(light.directionAndType.xyz);
float cosTheta = dot(spotAxisToLightWS, directionToLightWS);
return clamp((cosTheta - cosOuter) / max(cosInner - cosOuter, 1e-4), 0.0, 1.0);
}
vec3 EvaluateAdditionalLight(AdditionalLightData light, vec3 normalWS, vec3 positionWS) {
float lightType = light.directionAndType.w;
vec3 lightColor = light.colorAndIntensity.rgb;
float lightIntensity = light.colorAndIntensity.w;
vec3 directionToLightWS = vec3(0.0);
float attenuation = 1.0;
if (lightType < 0.5) {
directionToLightWS = normalize(light.directionAndType.xyz);
} else {
vec3 lightVectorWS = light.positionAndRange.xyz - positionWS;
float distanceSq = dot(lightVectorWS, lightVectorWS);
if (distanceSq <= 1e-6) {
return vec3(0.0);
}
directionToLightWS = normalize(lightVectorWS);
attenuation = ComputeRangeAttenuation(distanceSq, light.positionAndRange.w);
if (attenuation <= 0.0) {
return vec3(0.0);
}
if (lightType > 1.5) {
attenuation *= ComputeSpotAttenuation(light, directionToLightWS);
if (attenuation <= 0.0) {
return vec3(0.0);
}
}
}
float diffuse = max(dot(normalWS, directionToLightWS), 0.0);
if (diffuse <= 0.0) {
return vec3(0.0);
}
return lightColor * (diffuse * lightIntensity * attenuation);
}
void main() {
vec4 baseColor = texture(uBaseColorTexture, vTexCoord) * gBaseColorFactor;
#ifdef XC_ALPHA_TEST
if (baseColor.a < gAlphaCutoffParams.x) {
discard;
}
#endif
int additionalLightCount = min(int(gLightingParams.x + 0.5), XC_MAX_ADDITIONAL_LIGHTS);
if (gMainLightColorAndFlags.w < 0.5 && additionalLightCount == 0) {
fragColor = baseColor;
return;
}
vec3 normalWS = normalize(vNormalWS);
vec3 lighting = gLightingParams.yyy;
if (gMainLightColorAndFlags.w >= 0.5) {
vec3 directionToLightWS = normalize(gMainLightDirectionAndIntensity.xyz);
float diffuse = max(dot(normalWS, directionToLightWS), 0.0);
float shadowAttenuation = diffuse > 0.0
? ComputeShadowAttenuation(vPositionWS)
: 1.0;
lighting +=
gMainLightColorAndFlags.rgb * (diffuse * gMainLightDirectionAndIntensity.w * shadowAttenuation);
}
for (int lightIndex = 0; lightIndex < XC_MAX_ADDITIONAL_LIGHTS; ++lightIndex) {
if (lightIndex >= additionalLightCount) {
break;
}
lighting += EvaluateAdditionalLight(gAdditionalLights[lightIndex], normalWS, vPositionWS);
}
fragColor = vec4(baseColor.rgb * lighting, baseColor.a);
}

View File

@@ -1,176 +0,0 @@
// XC_BUILTIN_FORWARD_LIT_VULKAN_PS
#version 450
layout(set = 4, binding = 0) uniform texture2D uBaseColorTexture;
layout(set = 5, binding = 0) uniform sampler uLinearSampler;
layout(set = 6, binding = 0) uniform texture2D uShadowMapTexture;
layout(set = 7, binding = 0) uniform sampler uShadowMapSampler;
layout(set = 0, binding = 0, std140) uniform PerObjectConstants {
mat4 gProjectionMatrix;
mat4 gViewMatrix;
mat4 gModelMatrix;
mat4 gNormalMatrix;
};
const int XC_MAX_ADDITIONAL_LIGHTS = 8;
struct AdditionalLightData {
vec4 colorAndIntensity;
vec4 positionAndRange;
vec4 directionAndType;
vec4 spotAnglesAndFlags;
};
layout(set = 1, binding = 0, std140) uniform LightingConstants {
vec4 gMainLightDirectionAndIntensity;
vec4 gMainLightColorAndFlags;
vec4 gLightingParams;
AdditionalLightData gAdditionalLights[XC_MAX_ADDITIONAL_LIGHTS];
};
layout(set = 2, binding = 0, std140) uniform MaterialConstants {
vec4 gBaseColorFactor;
vec4 gAlphaCutoffParams;
};
layout(set = 3, binding = 0, std140) uniform ShadowReceiverConstants {
mat4 gWorldToShadowMatrix;
vec4 gShadowBiasAndTexelSize;
vec4 gShadowOptions;
};
layout(location = 0) in vec3 vNormalWS;
layout(location = 1) in vec2 vTexCoord;
layout(location = 2) in vec3 vPositionWS;
layout(location = 0) out vec4 fragColor;
float ComputeShadowAttenuation(vec3 positionWS) {
#ifndef XC_MAIN_LIGHT_SHADOWS
return 1.0;
#else
if (gShadowOptions.x < 0.5) {
return 1.0;
}
vec4 shadowClip = gWorldToShadowMatrix * vec4(positionWS, 1.0);
if (shadowClip.w <= 0.0) {
return 1.0;
}
vec3 shadowNdc = shadowClip.xyz / shadowClip.w;
vec2 shadowUv = vec2(
shadowNdc.x * 0.5 + 0.5,
shadowNdc.y * -0.5 + 0.5);
if (shadowUv.x < 0.0 || shadowUv.x > 1.0 ||
shadowUv.y < 0.0 || shadowUv.y > 1.0 ||
shadowNdc.z < 0.0 || shadowNdc.z > 1.0) {
return 1.0;
}
float shadowDepth = texture(sampler2D(uShadowMapTexture, uShadowMapSampler), shadowUv).r;
float receiverDepth = shadowNdc.z - gShadowBiasAndTexelSize.x;
float shadowStrength = clamp(gShadowBiasAndTexelSize.w, 0.0, 1.0);
return receiverDepth <= shadowDepth ? 1.0 : (1.0 - shadowStrength);
#endif
}
float ComputeRangeAttenuation(float distanceSq, float range) {
if (range <= 0.0) {
return 0.0;
}
float clampedRange = max(range, 0.0001);
float rangeSq = clampedRange * clampedRange;
if (distanceSq >= rangeSq) {
return 0.0;
}
float distance = sqrt(max(distanceSq, 0.0));
float normalized = clamp(1.0 - distance / clampedRange, 0.0, 1.0);
return normalized * normalized;
}
float ComputeSpotAttenuation(AdditionalLightData light, vec3 directionToLightWS) {
float cosOuter = light.spotAnglesAndFlags.x;
float cosInner = light.spotAnglesAndFlags.y;
vec3 spotAxisToLightWS = normalize(light.directionAndType.xyz);
float cosTheta = dot(spotAxisToLightWS, directionToLightWS);
return clamp((cosTheta - cosOuter) / max(cosInner - cosOuter, 1e-4), 0.0, 1.0);
}
vec3 EvaluateAdditionalLight(AdditionalLightData light, vec3 normalWS, vec3 positionWS) {
float lightType = light.directionAndType.w;
vec3 lightColor = light.colorAndIntensity.rgb;
float lightIntensity = light.colorAndIntensity.w;
vec3 directionToLightWS = vec3(0.0);
float attenuation = 1.0;
if (lightType < 0.5) {
directionToLightWS = normalize(light.directionAndType.xyz);
} else {
vec3 lightVectorWS = light.positionAndRange.xyz - positionWS;
float distanceSq = dot(lightVectorWS, lightVectorWS);
if (distanceSq <= 1e-6) {
return vec3(0.0);
}
directionToLightWS = normalize(lightVectorWS);
attenuation = ComputeRangeAttenuation(distanceSq, light.positionAndRange.w);
if (attenuation <= 0.0) {
return vec3(0.0);
}
if (lightType > 1.5) {
attenuation *= ComputeSpotAttenuation(light, directionToLightWS);
if (attenuation <= 0.0) {
return vec3(0.0);
}
}
}
float diffuse = max(dot(normalWS, directionToLightWS), 0.0);
if (diffuse <= 0.0) {
return vec3(0.0);
}
return lightColor * (diffuse * lightIntensity * attenuation);
}
void main() {
vec4 baseColor = texture(sampler2D(uBaseColorTexture, uLinearSampler), vTexCoord) * gBaseColorFactor;
#ifdef XC_ALPHA_TEST
if (baseColor.a < gAlphaCutoffParams.x) {
discard;
}
#endif
int additionalLightCount = min(int(gLightingParams.x + 0.5), XC_MAX_ADDITIONAL_LIGHTS);
if (gMainLightColorAndFlags.w < 0.5 && additionalLightCount == 0) {
fragColor = baseColor;
return;
}
vec3 normalWS = normalize(vNormalWS);
vec3 lighting = gLightingParams.yyy;
if (gMainLightColorAndFlags.w >= 0.5) {
vec3 directionToLightWS = normalize(gMainLightDirectionAndIntensity.xyz);
float diffuse = max(dot(normalWS, directionToLightWS), 0.0);
float shadowAttenuation = diffuse > 0.0
? ComputeShadowAttenuation(vPositionWS)
: 1.0;
lighting +=
gMainLightColorAndFlags.rgb * (diffuse * gMainLightDirectionAndIntensity.w * shadowAttenuation);
}
for (int lightIndex = 0; lightIndex < XC_MAX_ADDITIONAL_LIGHTS; ++lightIndex) {
if (lightIndex >= additionalLightCount) {
break;
}
lighting += EvaluateAdditionalLight(gAdditionalLights[lightIndex], normalWS, vPositionWS);
}
fragColor = vec4(baseColor.rgb * lighting, baseColor.a);
}

View File

@@ -1,174 +0,0 @@
// XC_BUILTIN_FORWARD_LIT_D3D12_PS
Texture2D gBaseColorTexture : register(t0);
SamplerState gLinearSampler : register(s0);
Texture2D gShadowMapTexture : register(t1);
SamplerState gShadowMapSampler : register(s1);
cbuffer PerObjectConstants : register(b0) {
float4x4 gProjectionMatrix;
float4x4 gViewMatrix;
float4x4 gModelMatrix;
float4x4 gNormalMatrix;
};
static const int XC_MAX_ADDITIONAL_LIGHTS = 8;
struct AdditionalLightData {
float4 colorAndIntensity;
float4 positionAndRange;
float4 directionAndType;
float4 spotAnglesAndFlags;
};
cbuffer LightingConstants : register(b1) {
float4 gMainLightDirectionAndIntensity;
float4 gMainLightColorAndFlags;
float4 gLightingParams;
AdditionalLightData gAdditionalLights[XC_MAX_ADDITIONAL_LIGHTS];
};
cbuffer MaterialConstants : register(b2) {
float4 gBaseColorFactor;
float4 gAlphaCutoffParams;
};
cbuffer ShadowReceiverConstants : register(b3) {
float4x4 gWorldToShadowMatrix;
float4 gShadowBiasAndTexelSize;
float4 gShadowOptions;
};
struct PSInput {
float4 position : SV_POSITION;
float3 normalWS : TEXCOORD0;
float2 texcoord : TEXCOORD1;
float3 positionWS : TEXCOORD2;
};
float ComputeShadowAttenuation(float3 positionWS) {
#ifndef XC_MAIN_LIGHT_SHADOWS
return 1.0f;
#else
if (gShadowOptions.x < 0.5f) {
return 1.0f;
}
float4 shadowClip = mul(gWorldToShadowMatrix, float4(positionWS, 1.0f));
if (shadowClip.w <= 0.0f) {
return 1.0f;
}
float3 shadowNdc = shadowClip.xyz / shadowClip.w;
float2 shadowUv = float2(
shadowNdc.x * 0.5f + 0.5f,
shadowNdc.y * -0.5f + 0.5f);
if (shadowUv.x < 0.0f || shadowUv.x > 1.0f ||
shadowUv.y < 0.0f || shadowUv.y > 1.0f ||
shadowNdc.z < 0.0f || shadowNdc.z > 1.0f) {
return 1.0f;
}
const float shadowDepth = gShadowMapTexture.Sample(gShadowMapSampler, shadowUv).r;
const float receiverDepth = shadowNdc.z - gShadowBiasAndTexelSize.x;
const float shadowStrength = saturate(gShadowBiasAndTexelSize.w);
return receiverDepth <= shadowDepth ? 1.0f : (1.0f - shadowStrength);
#endif
}
float ComputeRangeAttenuation(float distanceSq, float range) {
if (range <= 0.0f) {
return 0.0f;
}
const float clampedRange = max(range, 0.0001f);
const float rangeSq = clampedRange * clampedRange;
if (distanceSq >= rangeSq) {
return 0.0f;
}
const float distance = sqrt(max(distanceSq, 0.0f));
const float normalized = saturate(1.0f - distance / clampedRange);
return normalized * normalized;
}
float ComputeSpotAttenuation(AdditionalLightData light, float3 directionToLightWS) {
const float cosOuter = light.spotAnglesAndFlags.x;
const float cosInner = light.spotAnglesAndFlags.y;
const float3 spotAxisToLightWS = normalize(light.directionAndType.xyz);
const float cosTheta = dot(spotAxisToLightWS, directionToLightWS);
return saturate((cosTheta - cosOuter) / max(cosInner - cosOuter, 1e-4f));
}
float3 EvaluateAdditionalLight(AdditionalLightData light, float3 normalWS, float3 positionWS) {
const float lightType = light.directionAndType.w;
const float3 lightColor = light.colorAndIntensity.rgb;
const float lightIntensity = light.colorAndIntensity.w;
float3 directionToLightWS = float3(0.0f, 0.0f, 0.0f);
float attenuation = 1.0f;
if (lightType < 0.5f) {
directionToLightWS = normalize(light.directionAndType.xyz);
} else {
const float3 lightVectorWS = light.positionAndRange.xyz - positionWS;
const float distanceSq = dot(lightVectorWS, lightVectorWS);
if (distanceSq <= 1e-6f) {
return 0.0f.xxx;
}
directionToLightWS = lightVectorWS * rsqrt(distanceSq);
attenuation = ComputeRangeAttenuation(distanceSq, light.positionAndRange.w);
if (attenuation <= 0.0f) {
return 0.0f.xxx;
}
if (lightType > 1.5f) {
attenuation *= ComputeSpotAttenuation(light, directionToLightWS);
if (attenuation <= 0.0f) {
return 0.0f.xxx;
}
}
}
const float diffuse = saturate(dot(normalWS, directionToLightWS));
if (diffuse <= 0.0f) {
return 0.0f.xxx;
}
return lightColor * (diffuse * lightIntensity * attenuation);
}
float4 MainPS(PSInput input) : SV_TARGET {
float4 baseColor = gBaseColorTexture.Sample(gLinearSampler, input.texcoord) * gBaseColorFactor;
#ifdef XC_ALPHA_TEST
clip(baseColor.a - gAlphaCutoffParams.x);
#endif
const int additionalLightCount = min((int)gLightingParams.x, XC_MAX_ADDITIONAL_LIGHTS);
if (gMainLightColorAndFlags.a < 0.5f && additionalLightCount == 0) {
return baseColor;
}
float3 normalWS = normalize(input.normalWS);
float3 lighting = gLightingParams.yyy;
if (gMainLightColorAndFlags.a >= 0.5f) {
float3 directionToLightWS = normalize(gMainLightDirectionAndIntensity.xyz);
float diffuse = saturate(dot(normalWS, directionToLightWS));
float shadowAttenuation = diffuse > 0.0f
? ComputeShadowAttenuation(input.positionWS)
: 1.0f;
lighting +=
gMainLightColorAndFlags.rgb * (diffuse * gMainLightDirectionAndIntensity.w * shadowAttenuation);
}
[unroll]
for (int lightIndex = 0; lightIndex < XC_MAX_ADDITIONAL_LIGHTS; ++lightIndex) {
if (lightIndex >= additionalLightCount) {
break;
}
lighting += EvaluateAdditionalLight(gAdditionalLights[lightIndex], normalWS, input.positionWS);
}
return float4(baseColor.rgb * lighting, baseColor.a);
}

View File

@@ -6,31 +6,240 @@ Shader "Builtin Forward Lit"
_Cutoff ("Alpha Cutoff", Range) = 0.5 [Semantic(AlphaCutoff)]
_MainTex ("Base Map", 2D) = "white" [Semantic(BaseColorTexture)]
}
HLSLINCLUDE
cbuffer PerObjectConstants : register(b0)
{
float4x4 gProjectionMatrix;
float4x4 gViewMatrix;
float4x4 gModelMatrix;
float4x4 gNormalMatrix;
};
static const int XC_MAX_ADDITIONAL_LIGHTS = 8;
struct AdditionalLightData
{
float4 colorAndIntensity;
float4 positionAndRange;
float4 directionAndType;
float4 spotAnglesAndFlags;
};
cbuffer LightingConstants : register(b1)
{
float4 gMainLightDirectionAndIntensity;
float4 gMainLightColorAndFlags;
float4 gLightingParams;
AdditionalLightData gAdditionalLights[XC_MAX_ADDITIONAL_LIGHTS];
};
cbuffer MaterialConstants : register(b2)
{
float4 gBaseColorFactor;
float4 gAlphaCutoffParams;
};
cbuffer ShadowReceiverConstants : register(b3)
{
float4x4 gWorldToShadowMatrix;
float4 gShadowBiasAndTexelSize;
float4 gShadowOptions;
};
Texture2D BaseColorTexture : register(t0);
SamplerState LinearClampSampler : register(s0);
Texture2D ShadowMapTexture : register(t1);
SamplerState ShadowMapSampler : register(s1);
struct VSInput
{
float3 position : POSITION;
float3 normal : NORMAL;
float2 texcoord : TEXCOORD0;
};
struct PSInput
{
float4 position : SV_POSITION;
float3 normalWS : TEXCOORD0;
float2 texcoord : TEXCOORD1;
float3 positionWS : TEXCOORD2;
};
PSInput MainVS(VSInput input)
{
PSInput output;
const float4 positionWS = mul(gModelMatrix, float4(input.position, 1.0f));
const float4 positionVS = mul(gViewMatrix, positionWS);
output.position = mul(gProjectionMatrix, positionVS);
output.normalWS = mul((float3x3)gNormalMatrix, input.normal);
output.texcoord = input.texcoord;
output.positionWS = positionWS.xyz;
return output;
}
float ComputeShadowAttenuation(float3 positionWS)
{
#ifndef XC_MAIN_LIGHT_SHADOWS
return 1.0f;
#else
if (gShadowOptions.x < 0.5f) {
return 1.0f;
}
const float4 shadowClip = mul(gWorldToShadowMatrix, float4(positionWS, 1.0f));
if (shadowClip.w <= 0.0f) {
return 1.0f;
}
const float3 shadowNdc = shadowClip.xyz / shadowClip.w;
#if UNITY_UV_STARTS_AT_TOP
const float shadowUvY = shadowNdc.y * -0.5f + 0.5f;
#else
const float shadowUvY = shadowNdc.y * 0.5f + 0.5f;
#endif
const float2 shadowUv = float2(shadowNdc.x * 0.5f + 0.5f, shadowUvY);
#if UNITY_NEAR_CLIP_VALUE < 0
if (shadowUv.x < 0.0f || shadowUv.x > 1.0f ||
shadowUv.y < 0.0f || shadowUv.y > 1.0f ||
shadowNdc.z < -1.0f || shadowNdc.z > 1.0f) {
return 1.0f;
}
const float receiverDepth = shadowNdc.z * 0.5f + 0.5f - gShadowBiasAndTexelSize.x;
#else
if (shadowUv.x < 0.0f || shadowUv.x > 1.0f ||
shadowUv.y < 0.0f || shadowUv.y > 1.0f ||
shadowNdc.z < 0.0f || shadowNdc.z > 1.0f) {
return 1.0f;
}
const float receiverDepth = shadowNdc.z - gShadowBiasAndTexelSize.x;
#endif
const float shadowDepth = ShadowMapTexture.Sample(ShadowMapSampler, shadowUv).r;
const float shadowStrength = saturate(gShadowBiasAndTexelSize.w);
return receiverDepth <= shadowDepth ? 1.0f : (1.0f - shadowStrength);
#endif
}
float ComputeRangeAttenuation(float distanceSq, float range)
{
if (range <= 0.0f) {
return 0.0f;
}
const float clampedRange = max(range, 0.0001f);
const float rangeSq = clampedRange * clampedRange;
if (distanceSq >= rangeSq) {
return 0.0f;
}
const float distance = sqrt(max(distanceSq, 0.0f));
const float normalized = saturate(1.0f - distance / clampedRange);
return normalized * normalized;
}
float ComputeSpotAttenuation(AdditionalLightData light, float3 directionToLightWS)
{
const float cosOuter = light.spotAnglesAndFlags.x;
const float cosInner = light.spotAnglesAndFlags.y;
const float3 spotAxisToLightWS = normalize(light.directionAndType.xyz);
const float cosTheta = dot(spotAxisToLightWS, directionToLightWS);
return saturate((cosTheta - cosOuter) / max(cosInner - cosOuter, 1e-4f));
}
float3 EvaluateAdditionalLight(AdditionalLightData light, float3 normalWS, float3 positionWS)
{
const float lightType = light.directionAndType.w;
const float3 lightColor = light.colorAndIntensity.rgb;
const float lightIntensity = light.colorAndIntensity.w;
float3 directionToLightWS = float3(0.0f, 0.0f, 0.0f);
float attenuation = 1.0f;
if (lightType < 0.5f) {
directionToLightWS = normalize(light.directionAndType.xyz);
} else {
const float3 lightVectorWS = light.positionAndRange.xyz - positionWS;
const float distanceSq = dot(lightVectorWS, lightVectorWS);
if (distanceSq <= 1e-6f) {
return 0.0f.xxx;
}
directionToLightWS = lightVectorWS * rsqrt(distanceSq);
attenuation = ComputeRangeAttenuation(distanceSq, light.positionAndRange.w);
if (attenuation <= 0.0f) {
return 0.0f.xxx;
}
if (lightType > 1.5f) {
attenuation *= ComputeSpotAttenuation(light, directionToLightWS);
if (attenuation <= 0.0f) {
return 0.0f.xxx;
}
}
}
const float diffuse = saturate(dot(normalWS, directionToLightWS));
if (diffuse <= 0.0f) {
return 0.0f.xxx;
}
return lightColor * (diffuse * lightIntensity * attenuation);
}
float4 MainPS(PSInput input) : SV_TARGET
{
float4 baseColor = BaseColorTexture.Sample(LinearClampSampler, input.texcoord) * gBaseColorFactor;
#ifdef XC_ALPHA_TEST
clip(baseColor.a - gAlphaCutoffParams.x);
#endif
const int additionalLightCount = min((int)gLightingParams.x, XC_MAX_ADDITIONAL_LIGHTS);
if (gMainLightColorAndFlags.a < 0.5f && additionalLightCount == 0) {
return baseColor;
}
const float3 normalWS = normalize(input.normalWS);
float3 lighting = gLightingParams.yyy;
if (gMainLightColorAndFlags.a >= 0.5f) {
const float3 directionToLightWS = normalize(gMainLightDirectionAndIntensity.xyz);
const float diffuse = saturate(dot(normalWS, directionToLightWS));
const float shadowAttenuation =
diffuse > 0.0f ? ComputeShadowAttenuation(input.positionWS) : 1.0f;
lighting +=
gMainLightColorAndFlags.rgb *
(diffuse * gMainLightDirectionAndIntensity.w * shadowAttenuation);
}
[unroll]
for (int lightIndex = 0; lightIndex < XC_MAX_ADDITIONAL_LIGHTS; ++lightIndex) {
if (lightIndex >= additionalLightCount) {
break;
}
lighting += EvaluateAdditionalLight(gAdditionalLights[lightIndex], normalWS, input.positionWS);
}
return float4(baseColor.rgb * lighting, baseColor.a);
}
ENDHLSL
SubShader
{
Cull Back
ZWrite On
ZTest LEqual
Pass
{
Name "ForwardLit"
Tags { "LightMode" = "ForwardBase" }
Resources
{
PerObjectConstants (ConstantBuffer, 0, 0) [Semantic(PerObject)]
LightingConstants (ConstantBuffer, 1, 0) [Semantic(Lighting)]
MaterialConstants (ConstantBuffer, 2, 0) [Semantic(Material)]
ShadowReceiverConstants (ConstantBuffer, 3, 0) [Semantic(ShadowReceiver)]
BaseColorTexture (Texture2D, 4, 0) [Semantic(BaseColorTexture)]
LinearClampSampler (Sampler, 5, 0) [Semantic(LinearClampSampler)]
ShadowMapTexture (Texture2D, 6, 0) [Semantic(ShadowMapTexture)]
ShadowMapSampler (Sampler, 7, 0) [Semantic(ShadowMapSampler)]
}
HLSLPROGRAM
#pragma target 4.5
#pragma vertex MainVS
#pragma fragment MainPS
#pragma multi_compile _ XC_MAIN_LIGHT_SHADOWS
#pragma shader_feature_local _ XC_ALPHA_TEST
#pragma backend D3D12 HLSL "forward-lit.vs.hlsl" "forward-lit.ps.hlsl" vs_5_0 ps_5_0
#pragma backend OpenGL GLSL "forward-lit.vert.glsl" "forward-lit.frag.glsl"
#pragma backend Vulkan GLSL "forward-lit.vert.vk.glsl" "forward-lit.frag.vk.glsl"
ENDHLSL
}
}

View File

@@ -1,25 +0,0 @@
// XC_BUILTIN_FORWARD_LIT_OPENGL_VS
#version 430
layout(location = 0) in vec3 aPosition;
layout(location = 1) in vec3 aNormal;
layout(location = 2) in vec2 aTexCoord;
layout(std140, binding = 0) uniform PerObjectConstants {
mat4 gProjectionMatrix;
mat4 gViewMatrix;
mat4 gModelMatrix;
mat4 gNormalMatrix;
};
out vec3 vNormalWS;
out vec2 vTexCoord;
out vec3 vPositionWS;
void main() {
vec4 positionWS = gModelMatrix * vec4(aPosition, 1.0);
vec4 positionVS = gViewMatrix * positionWS;
gl_Position = gProjectionMatrix * positionVS;
vNormalWS = mat3(gNormalMatrix) * aNormal;
vTexCoord = aTexCoord;
vPositionWS = positionWS.xyz;
}

View File

@@ -1,25 +0,0 @@
// XC_BUILTIN_FORWARD_LIT_VULKAN_VS
#version 450
layout(location = 0) in vec3 aPosition;
layout(location = 1) in vec3 aNormal;
layout(location = 2) in vec2 aTexCoord;
layout(set = 0, binding = 0, std140) uniform PerObjectConstants {
mat4 gProjectionMatrix;
mat4 gViewMatrix;
mat4 gModelMatrix;
mat4 gNormalMatrix;
};
layout(location = 0) out vec3 vNormalWS;
layout(location = 1) out vec2 vTexCoord;
layout(location = 2) out vec3 vPositionWS;
void main() {
vec4 positionWS = gModelMatrix * vec4(aPosition, 1.0);
vec4 positionVS = gViewMatrix * positionWS;
gl_Position = gProjectionMatrix * positionVS;
vNormalWS = mat3(gNormalMatrix) * aNormal;
vTexCoord = aTexCoord;
vPositionWS = positionWS.xyz;
}

View File

@@ -1,31 +0,0 @@
// XC_BUILTIN_FORWARD_LIT_D3D12_VS
cbuffer PerObjectConstants : register(b0) {
float4x4 gProjectionMatrix;
float4x4 gViewMatrix;
float4x4 gModelMatrix;
float4x4 gNormalMatrix;
};
struct VSInput {
float3 position : POSITION;
float3 normal : NORMAL;
float2 texcoord : TEXCOORD0;
};
struct PSInput {
float4 position : SV_POSITION;
float3 normalWS : TEXCOORD0;
float2 texcoord : TEXCOORD1;
float3 positionWS : TEXCOORD2;
};
PSInput MainVS(VSInput input) {
PSInput output;
float4 positionWS = mul(gModelMatrix, float4(input.position, 1.0f));
float4 positionVS = mul(gViewMatrix, positionWS);
output.position = mul(gProjectionMatrix, positionVS);
output.normalWS = mul((float3x3)gNormalMatrix, input.normal);
output.texcoord = input.texcoord;
output.positionWS = positionWS.xyz;
return output;
}

View File

@@ -1,21 +0,0 @@
// XC_BUILTIN_SHADOW_CASTER_OPENGL_PS
#version 430
layout(binding = 0) uniform sampler2D uBaseColorTexture;
layout(std140, binding = 1) uniform MaterialConstants {
vec4 gBaseColorFactor;
vec4 gAlphaCutoffParams;
};
in vec2 vTexCoord;
void main() {
#ifdef XC_ALPHA_TEST
vec4 baseColor = texture(uBaseColorTexture, vTexCoord) * gBaseColorFactor;
if (baseColor.a < gAlphaCutoffParams.x) {
discard;
}
#endif
gl_FragDepth = gl_FragCoord.z;
}

View File

@@ -1,22 +0,0 @@
// XC_BUILTIN_SHADOW_CASTER_VULKAN_PS
#version 450
layout(set = 2, binding = 0) uniform texture2D uBaseColorTexture;
layout(set = 3, binding = 0) uniform sampler uLinearSampler;
layout(set = 1, binding = 0, std140) uniform MaterialConstants {
vec4 gBaseColorFactor;
vec4 gAlphaCutoffParams;
};
layout(location = 0) in vec2 vTexCoord;
void main() {
#ifdef XC_ALPHA_TEST
vec4 baseColor = texture(sampler2D(uBaseColorTexture, uLinearSampler), vTexCoord) * gBaseColorFactor;
if (baseColor.a < gAlphaCutoffParams.x) {
discard;
}
#endif
gl_FragDepth = gl_FragCoord.z;
}

View File

@@ -1,21 +0,0 @@
// XC_BUILTIN_SHADOW_CASTER_D3D12_PS
Texture2D gBaseColorTexture : register(t0);
SamplerState gLinearSampler : register(s0);
cbuffer MaterialConstants : register(b1) {
float4 gBaseColorFactor;
float4 gAlphaCutoffParams;
};
struct PSInput {
float4 position : SV_POSITION;
float2 texcoord : TEXCOORD0;
};
float MainPS(PSInput input) : SV_Depth {
#ifdef XC_ALPHA_TEST
float4 baseColor = gBaseColorTexture.Sample(gLinearSampler, input.texcoord) * gBaseColorFactor;
clip(baseColor.a - gAlphaCutoffParams.x);
#endif
return input.position.z;
}

View File

@@ -6,26 +6,67 @@ Shader "Builtin Shadow Caster"
_Cutoff ("Alpha Cutoff", Range) = 0.5 [Semantic(AlphaCutoff)]
_MainTex ("Base Map", 2D) = "white" [Semantic(BaseColorTexture)]
}
HLSLINCLUDE
cbuffer PerObjectConstants : register(b0)
{
float4x4 gProjectionMatrix;
float4x4 gViewMatrix;
float4x4 gModelMatrix;
};
cbuffer MaterialConstants : register(b1)
{
float4 gBaseColorFactor;
float4 gAlphaCutoffParams;
};
Texture2D BaseColorTexture : register(t0);
SamplerState LinearClampSampler : register(s0);
struct VSInput
{
float3 position : POSITION;
float3 normal : NORMAL;
float2 texcoord : TEXCOORD0;
};
struct PSInput
{
float4 position : SV_POSITION;
float2 texcoord : TEXCOORD0;
};
PSInput MainVS(VSInput input)
{
PSInput output;
const float4 positionWS = mul(gModelMatrix, float4(input.position, 1.0f));
const float4 positionVS = mul(gViewMatrix, positionWS);
output.position = mul(gProjectionMatrix, positionVS);
output.texcoord = input.texcoord;
return output;
}
float MainPS(PSInput input) : SV_Depth
{
#ifdef XC_ALPHA_TEST
const float4 baseColor =
BaseColorTexture.Sample(LinearClampSampler, input.texcoord) * gBaseColorFactor;
clip(baseColor.a - gAlphaCutoffParams.x);
#endif
return input.position.z;
}
ENDHLSL
SubShader
{
Pass
{
Name "ShadowCaster"
Tags { "LightMode" = "ShadowCaster" }
Resources
{
PerObjectConstants (ConstantBuffer, 0, 0) [Semantic(PerObject)]
MaterialConstants (ConstantBuffer, 1, 0) [Semantic(Material)]
BaseColorTexture (Texture2D, 2, 0) [Semantic(BaseColorTexture)]
LinearClampSampler (Sampler, 3, 0) [Semantic(LinearClampSampler)]
}
HLSLPROGRAM
#pragma target 4.5
#pragma vertex MainVS
#pragma fragment MainPS
#pragma shader_feature_local _ XC_ALPHA_TEST
#pragma backend D3D12 HLSL "shadow-caster.vs.hlsl" "shadow-caster.ps.hlsl" vs_5_0 ps_5_0
#pragma backend OpenGL GLSL "shadow-caster.vert.glsl" "shadow-caster.frag.glsl"
#pragma backend Vulkan GLSL "shadow-caster.vert.vk.glsl" "shadow-caster.frag.vk.glsl"
ENDHLSL
}
}

View File

@@ -1,19 +0,0 @@
// XC_BUILTIN_SHADOW_CASTER_OPENGL_VS
#version 430
layout(location = 0) in vec3 aPosition;
layout(location = 2) in vec2 aTexCoord;
layout(std140, binding = 0) uniform PerObjectConstants {
mat4 gProjectionMatrix;
mat4 gViewMatrix;
mat4 gModelMatrix;
};
out vec2 vTexCoord;
void main() {
vec4 positionWS = gModelMatrix * vec4(aPosition, 1.0);
vec4 positionVS = gViewMatrix * positionWS;
gl_Position = gProjectionMatrix * positionVS;
vTexCoord = aTexCoord;
}

View File

@@ -1,19 +0,0 @@
// XC_BUILTIN_SHADOW_CASTER_VULKAN_VS
#version 450
layout(location = 0) in vec3 aPosition;
layout(location = 2) in vec2 aTexCoord;
layout(set = 0, binding = 0, std140) uniform PerObjectConstants {
mat4 gProjectionMatrix;
mat4 gViewMatrix;
mat4 gModelMatrix;
};
layout(location = 0) out vec2 vTexCoord;
void main() {
vec4 positionWS = gModelMatrix * vec4(aPosition, 1.0);
vec4 positionVS = gViewMatrix * positionWS;
gl_Position = gProjectionMatrix * positionVS;
vTexCoord = aTexCoord;
}

View File

@@ -1,25 +0,0 @@
// XC_BUILTIN_SHADOW_CASTER_D3D12_VS
cbuffer PerObjectConstants : register(b0) {
float4x4 gProjectionMatrix;
float4x4 gViewMatrix;
float4x4 gModelMatrix;
};
struct VSInput {
float3 position : POSITION;
float2 texcoord : TEXCOORD0;
};
struct PSInput {
float4 position : SV_POSITION;
float2 texcoord : TEXCOORD0;
};
PSInput MainVS(VSInput input) {
PSInput output;
float4 positionWS = mul(gModelMatrix, float4(input.position, 1.0f));
float4 positionVS = mul(gViewMatrix, positionWS);
output.position = mul(gProjectionMatrix, positionVS);
output.texcoord = input.texcoord;
return output;
}