rendering: unify builtin forward and depth-style shaders
This commit is contained in:
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <d3d12.h>
|
||||
#include <d3dcompiler.h>
|
||||
#include <dxgi1_4.h>
|
||||
#include <wrl/client.h>
|
||||
#include <string>
|
||||
@@ -22,6 +23,12 @@ public:
|
||||
|
||||
bool CompileFromFile(const wchar_t* filePath, const char* entryPoint, const char* target) override;
|
||||
bool Compile(const void* sourceData, size_t sourceSize, const char* entryPoint, const char* target) override;
|
||||
bool Compile(
|
||||
const void* sourceData,
|
||||
size_t sourceSize,
|
||||
const D3D_SHADER_MACRO* macros,
|
||||
const char* entryPoint,
|
||||
const char* target);
|
||||
void Shutdown() override;
|
||||
|
||||
const D3D12_SHADER_BYTECODE GetD3D12Bytecode() const;
|
||||
@@ -48,4 +55,4 @@ private:
|
||||
};
|
||||
|
||||
} // namespace RHI
|
||||
} // namespace XCEngine
|
||||
} // namespace XCEngine
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <stdio.h>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#ifdef _DEBUG
|
||||
#include <dxgidebug.h>
|
||||
@@ -51,7 +52,36 @@ bool CompileD3D12Shader(const ShaderCompileDesc& desc, D3D12Shader& shader) {
|
||||
const char* profilePtr = profile.empty() ? nullptr : profile.c_str();
|
||||
|
||||
if (!desc.source.empty()) {
|
||||
return shader.Compile(desc.source.data(), desc.source.size(), entryPointPtr, profilePtr);
|
||||
std::vector<std::string> macroNames;
|
||||
std::vector<std::string> macroDefinitions;
|
||||
std::vector<D3D_SHADER_MACRO> macroTable;
|
||||
if (!desc.macros.empty()) {
|
||||
macroNames.reserve(desc.macros.size());
|
||||
macroDefinitions.reserve(desc.macros.size());
|
||||
macroTable.reserve(desc.macros.size() + 1u);
|
||||
for (const ShaderCompileMacro& macro : desc.macros) {
|
||||
macroNames.push_back(NarrowAscii(macro.name));
|
||||
macroDefinitions.push_back(NarrowAscii(macro.definition));
|
||||
}
|
||||
|
||||
for (size_t macroIndex = 0; macroIndex < desc.macros.size(); ++macroIndex) {
|
||||
D3D_SHADER_MACRO d3dMacro = {};
|
||||
d3dMacro.Name = macroNames[macroIndex].c_str();
|
||||
d3dMacro.Definition = macroDefinitions[macroIndex].empty()
|
||||
? "1"
|
||||
: macroDefinitions[macroIndex].c_str();
|
||||
macroTable.push_back(d3dMacro);
|
||||
}
|
||||
macroTable.push_back({ nullptr, nullptr });
|
||||
}
|
||||
|
||||
const D3D_SHADER_MACRO* macroPtr = macroTable.empty() ? nullptr : macroTable.data();
|
||||
return shader.Compile(
|
||||
desc.source.data(),
|
||||
desc.source.size(),
|
||||
macroPtr,
|
||||
entryPointPtr,
|
||||
profilePtr);
|
||||
}
|
||||
|
||||
if (!desc.fileName.empty()) {
|
||||
|
||||
@@ -44,8 +44,26 @@ bool D3D12Shader::CompileFromFile(const wchar_t* filePath, const char* entryPoin
|
||||
}
|
||||
|
||||
bool D3D12Shader::Compile(const void* sourceData, size_t sourceSize, const char* entryPoint, const char* target) {
|
||||
HRESULT hResult = D3DCompile(sourceData, sourceSize, nullptr, nullptr, nullptr, entryPoint, target,
|
||||
D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION, 0, &m_bytecode, &m_error);
|
||||
return Compile(sourceData, sourceSize, nullptr, entryPoint, target);
|
||||
}
|
||||
|
||||
bool D3D12Shader::Compile(const void* sourceData,
|
||||
size_t sourceSize,
|
||||
const D3D_SHADER_MACRO* macros,
|
||||
const char* entryPoint,
|
||||
const char* target) {
|
||||
HRESULT hResult = D3DCompile(
|
||||
sourceData,
|
||||
sourceSize,
|
||||
nullptr,
|
||||
macros,
|
||||
nullptr,
|
||||
entryPoint,
|
||||
target,
|
||||
D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION,
|
||||
0,
|
||||
&m_bytecode,
|
||||
&m_error);
|
||||
|
||||
if (FAILED(hResult)) {
|
||||
if (m_error) {
|
||||
|
||||
@@ -29,7 +29,10 @@
|
||||
#include <cctype>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <regex>
|
||||
#include <sstream>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
typedef const char* (WINAPI* PFNWGLGETEXTENSIONSSTRINGARBPROC)(HDC hdc);
|
||||
@@ -166,6 +169,167 @@ bool LoadShaderSourceText(const ShaderCompileDesc& desc, std::string& outSourceT
|
||||
return file.read(outSourceText.data(), size).good();
|
||||
}
|
||||
|
||||
bool IsHlslShaderCompileDesc(const ShaderCompileDesc& desc) {
|
||||
if (desc.sourceLanguage == ShaderLanguage::HLSL) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (desc.fileName.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string extension = std::filesystem::path(desc.fileName).extension().string();
|
||||
std::transform(extension.begin(), extension.end(), extension.begin(), [](unsigned char ch) {
|
||||
return static_cast<char>(std::tolower(ch));
|
||||
});
|
||||
return extension == ".hlsl";
|
||||
}
|
||||
|
||||
void CollectHlslTextureRegisterBindings(
|
||||
const std::string& sourceText,
|
||||
std::unordered_map<std::string, GLint>& outTextureUnits,
|
||||
std::unordered_set<std::string>& outSamplerNames) {
|
||||
static const std::regex kTexturePattern(
|
||||
R"(((?:Texture2D|TextureCube)\s+([A-Za-z_][A-Za-z0-9_]*)\s*:\s*register\s*\(\s*t([0-9]+)))",
|
||||
std::regex::ECMAScript);
|
||||
static const std::regex kSamplerPattern(
|
||||
R"(((?:SamplerState|SamplerComparisonState)\s+([A-Za-z_][A-Za-z0-9_]*)\s*:\s*register\s*\(\s*s([0-9]+)))",
|
||||
std::regex::ECMAScript);
|
||||
|
||||
for (std::sregex_iterator it(sourceText.begin(), sourceText.end(), kTexturePattern), end; it != end; ++it) {
|
||||
const std::smatch& match = *it;
|
||||
if (match.size() < 4) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
outTextureUnits[match[2].str()] = static_cast<GLint>(std::stoi(match[3].str()));
|
||||
} catch (...) {
|
||||
}
|
||||
}
|
||||
|
||||
for (std::sregex_iterator it(sourceText.begin(), sourceText.end(), kSamplerPattern), end; it != end; ++it) {
|
||||
const std::smatch& match = *it;
|
||||
if (match.size() < 4) {
|
||||
continue;
|
||||
}
|
||||
|
||||
outSamplerNames.insert(match[2].str());
|
||||
}
|
||||
}
|
||||
|
||||
bool TryResolveCombinedSamplerTextureUnit(
|
||||
const std::string& uniformName,
|
||||
const std::unordered_map<std::string, GLint>& textureUnits,
|
||||
const std::unordered_set<std::string>& samplerNames,
|
||||
GLint& outTextureUnit) {
|
||||
static const std::string kCombinedPrefix = "SPIRV_Cross_Combined";
|
||||
if (uniformName.rfind(kCombinedPrefix, 0) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string combinedName = uniformName.substr(kCombinedPrefix.size());
|
||||
const size_t arraySuffixPos = combinedName.find('[');
|
||||
if (arraySuffixPos != std::string::npos) {
|
||||
combinedName = combinedName.substr(0, arraySuffixPos);
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::string, GLint>> candidates(textureUnits.begin(), textureUnits.end());
|
||||
std::sort(
|
||||
candidates.begin(),
|
||||
candidates.end(),
|
||||
[](const auto& left, const auto& right) {
|
||||
return left.first.size() > right.first.size();
|
||||
});
|
||||
|
||||
for (const auto& [textureName, textureUnit] : candidates) {
|
||||
if (combinedName.rfind(textureName, 0) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::string samplerName = combinedName.substr(textureName.size());
|
||||
if (!samplerName.empty() &&
|
||||
(samplerNames.empty() || samplerNames.find(samplerName) != samplerNames.end())) {
|
||||
outTextureUnit = textureUnit;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ConfigureOpenGLCombinedSamplerUniforms(
|
||||
GLuint program,
|
||||
const ShaderCompileDesc* shaderDescs,
|
||||
size_t shaderDescCount) {
|
||||
if (program == 0 || shaderDescs == nullptr || shaderDescCount == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, GLint> textureUnits;
|
||||
std::unordered_set<std::string> samplerNames;
|
||||
for (size_t shaderIndex = 0; shaderIndex < shaderDescCount; ++shaderIndex) {
|
||||
const ShaderCompileDesc& shaderDesc = shaderDescs[shaderIndex];
|
||||
if (!IsHlslShaderCompileDesc(shaderDesc)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string sourceText;
|
||||
if (!LoadShaderSourceText(shaderDesc, sourceText) || sourceText.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
CollectHlslTextureRegisterBindings(sourceText, textureUnits, samplerNames);
|
||||
}
|
||||
|
||||
if (textureUnits.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
GLint activeUniformCount = 0;
|
||||
glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &activeUniformCount);
|
||||
if (activeUniformCount <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
GLint previousProgram = 0;
|
||||
glGetIntegerv(GL_CURRENT_PROGRAM, &previousProgram);
|
||||
glUseProgram(program);
|
||||
|
||||
for (GLint uniformIndex = 0; uniformIndex < activeUniformCount; ++uniformIndex) {
|
||||
GLsizei nameLength = 0;
|
||||
GLint arraySize = 0;
|
||||
GLenum uniformType = 0;
|
||||
char nameBuffer[256] = {};
|
||||
glGetActiveUniform(
|
||||
program,
|
||||
static_cast<GLuint>(uniformIndex),
|
||||
static_cast<GLsizei>(std::size(nameBuffer)),
|
||||
&nameLength,
|
||||
&arraySize,
|
||||
&uniformType,
|
||||
nameBuffer);
|
||||
if (nameLength <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
(void)arraySize;
|
||||
(void)uniformType;
|
||||
|
||||
GLint textureUnit = -1;
|
||||
if (!TryResolveCombinedSamplerTextureUnit(nameBuffer, textureUnits, samplerNames, textureUnit)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const GLint location = glGetUniformLocation(program, nameBuffer);
|
||||
if (location >= 0) {
|
||||
glUniform1i(location, textureUnit);
|
||||
}
|
||||
}
|
||||
|
||||
glUseProgram(static_cast<GLuint>(previousProgram));
|
||||
}
|
||||
|
||||
bool TryResolveShaderTypeFromTarget(const std::wstring& profile, ShaderType& type) {
|
||||
const std::string asciiProfile = NarrowAscii(profile);
|
||||
if (asciiProfile.find("vs_") != std::string::npos || asciiProfile == "vs") {
|
||||
@@ -536,6 +700,8 @@ bool BuildOpenGLProgram(const ShaderCompileDesc* shaderDescs,
|
||||
return false;
|
||||
}
|
||||
|
||||
ConfigureOpenGLCombinedSamplerUniforms(program, shaderDescs, shaderDescCount);
|
||||
|
||||
outProgram = program;
|
||||
return true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user