Init commit
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
#include "include/shadow_mainlit.hlsli"
|
||||
|
||||
// cbuffer CBScene : register(b0)
|
||||
// {
|
||||
// row_major float4x4 view;
|
||||
// row_major float4x4 proj;
|
||||
// float4 cameraPos;
|
||||
// float4 lightDirection;
|
||||
// float4 cameraColor;
|
||||
// }
|
||||
|
||||
cbuffer CBPerMat : register(b1){
|
||||
float4 cbColor;
|
||||
}
|
||||
|
||||
// cbuffer CBPerObj : register(b2)
|
||||
// {
|
||||
// row_major float4x4 world;
|
||||
// }
|
||||
|
||||
struct VSInput
|
||||
{
|
||||
float3 position : POSITION;
|
||||
float3 normal : NORMAL;
|
||||
// float3 tangent : TANGENT;
|
||||
float2 texcoord0 : TEXCOORD0;
|
||||
// float2 texcoord1 : TEXCOORD1;
|
||||
// float4 color : COLOR0;
|
||||
// int4 boneIndex : BLENDINDICES0;
|
||||
// float4 boneWeight : BLENDWEIGHT0;
|
||||
};
|
||||
|
||||
struct PSInput
|
||||
{
|
||||
float4 pos : SV_POSITION;
|
||||
float3 worldPos : MPROP0;
|
||||
float4 shadowPos : MPROP1;
|
||||
float2 uv : MPROP2;
|
||||
float3 worldNrm : MPROP3;
|
||||
};
|
||||
|
||||
PSInput VSMain(VSInput i)
|
||||
{
|
||||
PSInput o;
|
||||
|
||||
float4 pos = float4(i.position, 1.0f);
|
||||
pos = mul(pos, cbWorld);
|
||||
o.worldPos = pos.xyz;
|
||||
o.shadowPos = mul(pos, cbLightViewProjection);
|
||||
pos = mul(pos, cbView);
|
||||
pos = mul(pos, cbProj);
|
||||
o.pos = pos;
|
||||
|
||||
o.uv = i.texcoord0;
|
||||
|
||||
float4 _normal = float4(i.normal, 0.0f);
|
||||
_normal = mul(_normal, cbWorld);
|
||||
o.worldNrm = _normal.xyz;
|
||||
|
||||
return o;
|
||||
}
|
||||
|
||||
half4 PSMain(PSInput i) : SV_TARGET
|
||||
{
|
||||
half3 color = half3(0.8f, 0.8f, 0.9f);
|
||||
|
||||
i.worldNrm = normalize(i.worldNrm);
|
||||
float intlit = saturate(-dot(i.worldNrm, cbMainLightDirection.xyz));
|
||||
intlit *= CalculateMainLightShadow(i.shadowPos, i.worldNrm);
|
||||
|
||||
intlit = 0.5f * (intlit + 1.0f);
|
||||
|
||||
|
||||
half3 col = color * intlit;
|
||||
|
||||
return half4(col.xyz, 1.0f);
|
||||
}
|
||||
@@ -0,0 +1,259 @@
|
||||
// [新增] 运行时 IBL 烘焙 Shader。
|
||||
// 使用全屏三角形逐面写入 Cube RTV,不需要额外顶点缓冲。
|
||||
|
||||
#pragma pack_matrix(row_major)
|
||||
|
||||
static const float IBL_PI = 3.14159265359f;
|
||||
static const float IBL_EPSILON = 1.0e-5f;
|
||||
|
||||
cbuffer CBBake : register(b0) {
|
||||
// x=roughness, y=源 Cube 尺寸, z=源 Cube 最大 mip, w=保留
|
||||
float4 gBakeFloatParams;
|
||||
// x=目标 face, y=采样数, z/w=保留
|
||||
uint4 gBakeUIntParams;
|
||||
};
|
||||
|
||||
TextureCube<float4> gSourceEnvironment : register(t0);
|
||||
SamplerState gSourceSampler : register(s0);
|
||||
|
||||
struct VSOutput {
|
||||
float4 positionCS : SV_POSITION;
|
||||
float2 uv : TEXCOORD0;
|
||||
};
|
||||
|
||||
VSOutput VSFullscreen(uint vertexId : SV_VertexID) {
|
||||
VSOutput output;
|
||||
|
||||
// 一个三角形覆盖整个目标视口。
|
||||
const float2 positions[3] = {
|
||||
float2(-1.0f, -1.0f),
|
||||
float2(-1.0f, 3.0f),
|
||||
float2( 3.0f, -1.0f)
|
||||
};
|
||||
|
||||
const float2 position = positions[vertexId];
|
||||
output.positionCS = float4(position, 0.0f, 1.0f);
|
||||
output.uv = float2(position.x * 0.5f + 0.5f,
|
||||
0.5f - position.y * 0.5f);
|
||||
return output;
|
||||
}
|
||||
|
||||
// [新增] 把目标 Cube 某个面的 UV 映射为世界方向。
|
||||
// face 顺序必须与 D3D11:+X、-X、+Y、-Y、+Z、-Z 完全一致。
|
||||
float3 CubeDirection(uint faceIndex, float2 uv) {
|
||||
const float2 ndc = float2(uv.x * 2.0f - 1.0f,
|
||||
1.0f - uv.y * 2.0f);
|
||||
|
||||
float3 direction = float3(ndc.x, ndc.y, 1.0f);
|
||||
switch (faceIndex) {
|
||||
case 0u: direction = float3( 1.0f, ndc.y, -ndc.x); break; // +X
|
||||
case 1u: direction = float3(-1.0f, ndc.y, ndc.x); break; // -X
|
||||
case 2u: direction = float3( ndc.x, 1.0f, -ndc.y); break; // +Y
|
||||
case 3u: direction = float3( ndc.x,-1.0f, ndc.y); break; // -Y
|
||||
case 4u: direction = float3( ndc.x, ndc.y, 1.0f); break; // +Z
|
||||
case 5u: direction = float3(-ndc.x, ndc.y, -1.0f); break; // -Z
|
||||
default: break;
|
||||
}
|
||||
return normalize(direction);
|
||||
}
|
||||
|
||||
float RadicalInverseVdC(uint bits) {
|
||||
bits = (bits << 16u) | (bits >> 16u);
|
||||
bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
|
||||
bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
|
||||
bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
|
||||
bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
|
||||
return float(bits) * 2.3283064365386963e-10f;
|
||||
}
|
||||
|
||||
float2 Hammersley(uint index, uint sampleCount) {
|
||||
return float2(float(index) / float(sampleCount), RadicalInverseVdC(index));
|
||||
}
|
||||
|
||||
void BuildBasis(float3 normal, out float3 tangent, out float3 bitangent) {
|
||||
const float3 up = abs(normal.z) < 0.999f
|
||||
? float3(0.0f, 0.0f, 1.0f)
|
||||
: float3(1.0f, 0.0f, 0.0f);
|
||||
tangent = normalize(cross(up, normal));
|
||||
bitangent = cross(normal, tangent);
|
||||
}
|
||||
|
||||
float3 CosineSampleHemisphere(float2 xi, float3 normal) {
|
||||
const float radius = sqrt(xi.y);
|
||||
const float phi = 2.0f * IBL_PI * xi.x;
|
||||
|
||||
float sinePhi;
|
||||
float cosinePhi;
|
||||
sincos(phi, sinePhi, cosinePhi);
|
||||
|
||||
const float3 localDirection = float3(
|
||||
radius * cosinePhi,
|
||||
radius * sinePhi,
|
||||
sqrt(saturate(1.0f - xi.y)));
|
||||
|
||||
float3 tangent;
|
||||
float3 bitangent;
|
||||
BuildBasis(normal, tangent, bitangent);
|
||||
return normalize(
|
||||
tangent * localDirection.x +
|
||||
bitangent * localDirection.y +
|
||||
normal * localDirection.z);
|
||||
}
|
||||
|
||||
float3 ImportanceSampleGGX(float2 xi, float3 normal, float roughness) {
|
||||
const float alpha = max(roughness * roughness, 0.001f);
|
||||
const float alpha2 = alpha * alpha;
|
||||
|
||||
const float phi = 2.0f * IBL_PI * xi.x;
|
||||
const float cosTheta = sqrt(
|
||||
saturate((1.0f - xi.y) /
|
||||
max(1.0f + (alpha2 - 1.0f) * xi.y, IBL_EPSILON)));
|
||||
const float sinTheta = sqrt(saturate(1.0f - cosTheta * cosTheta));
|
||||
|
||||
float sinePhi;
|
||||
float cosinePhi;
|
||||
sincos(phi, sinePhi, cosinePhi);
|
||||
|
||||
const float3 halfVectorTangent = float3(
|
||||
cosinePhi * sinTheta,
|
||||
sinePhi * sinTheta,
|
||||
cosTheta);
|
||||
|
||||
float3 tangent;
|
||||
float3 bitangent;
|
||||
BuildBasis(normal, tangent, bitangent);
|
||||
return normalize(
|
||||
tangent * halfVectorTangent.x +
|
||||
bitangent * halfVectorTangent.y +
|
||||
normal * halfVectorTangent.z);
|
||||
}
|
||||
|
||||
float DistributionGGXForPdf(float nDotH, float roughness) {
|
||||
const float alpha = max(roughness * roughness, 0.001f);
|
||||
const float alpha2 = alpha * alpha;
|
||||
const float denominator = nDotH * nDotH * (alpha2 - 1.0f) + 1.0f;
|
||||
return alpha2 / max(IBL_PI * denominator * denominator, IBL_EPSILON);
|
||||
}
|
||||
|
||||
float4 PSIrradiance(VSOutput input) : SV_TARGET {
|
||||
const float3 normal = CubeDirection(gBakeUIntParams.x, input.uv);
|
||||
const uint sampleCount = max(gBakeUIntParams.y, 1u);
|
||||
|
||||
float3 irradiance = float3(0.0f, 0.0f, 0.0f);
|
||||
[loop]
|
||||
for (uint sampleIndex = 0u; sampleIndex < sampleCount; ++sampleIndex) {
|
||||
const float2 xi = Hammersley(sampleIndex, sampleCount);
|
||||
const float3 lightDirection = CosineSampleHemisphere(xi, normal);
|
||||
irradiance += gSourceEnvironment.SampleLevel(
|
||||
gSourceSampler, lightDirection, 0.0f).rgb;
|
||||
}
|
||||
|
||||
// 这里直接保存 Lambert 卷积后的漫反射环境项 E/PI。
|
||||
// 因此运行时只需乘 baseColor,不再额外除以 PI。
|
||||
irradiance /= float(sampleCount);
|
||||
return float4(irradiance, 1.0f);
|
||||
}
|
||||
|
||||
float4 PSPrefilter(VSOutput input) : SV_TARGET {
|
||||
const float3 normal = CubeDirection(gBakeUIntParams.x, input.uv);
|
||||
const float roughness = saturate(gBakeFloatParams.x);
|
||||
|
||||
// roughness=0 的 mip0 直接保留原环境,避免 GGX 极窄分布的数值误差。
|
||||
if (roughness <= 0.001f) {
|
||||
return float4(gSourceEnvironment.SampleLevel(
|
||||
gSourceSampler, normal, 0.0f).rgb, 1.0f);
|
||||
}
|
||||
|
||||
const uint sampleCount = max(gBakeUIntParams.y, 1u);
|
||||
const float sourceSize = max(gBakeFloatParams.y, 1.0f);
|
||||
const float sourceMaxMip = max(gBakeFloatParams.z, 0.0f);
|
||||
|
||||
const float3 viewDirection = normal;
|
||||
float3 prefilteredColor = float3(0.0f, 0.0f, 0.0f);
|
||||
float totalWeight = 0.0f;
|
||||
|
||||
const float texelSolidAngle =
|
||||
4.0f * IBL_PI / (6.0f * sourceSize * sourceSize);
|
||||
|
||||
[loop]
|
||||
for (uint sampleIndex = 0u; sampleIndex < sampleCount; ++sampleIndex) {
|
||||
const float2 xi = Hammersley(sampleIndex, sampleCount);
|
||||
const float3 halfVector = ImportanceSampleGGX(xi, normal, roughness);
|
||||
const float3 lightDirection = normalize(
|
||||
2.0f * dot(viewDirection, halfVector) * halfVector - viewDirection);
|
||||
|
||||
const float nDotL = saturate(dot(normal, lightDirection));
|
||||
if (nDotL > 0.0f) {
|
||||
const float nDotH = saturate(dot(normal, halfVector));
|
||||
const float hDotV = saturate(dot(halfVector, viewDirection));
|
||||
const float distribution = DistributionGGXForPdf(nDotH, roughness);
|
||||
const float pdf = max(
|
||||
distribution * nDotH / max(4.0f * hDotV, IBL_EPSILON),
|
||||
IBL_EPSILON);
|
||||
|
||||
const float sampleSolidAngle =
|
||||
1.0f / (float(sampleCount) * pdf + IBL_EPSILON);
|
||||
const float mipLevel = clamp(
|
||||
0.5f * log2(max(sampleSolidAngle / texelSolidAngle, IBL_EPSILON)),
|
||||
0.0f, sourceMaxMip);
|
||||
|
||||
prefilteredColor += gSourceEnvironment.SampleLevel(
|
||||
gSourceSampler, lightDirection, mipLevel).rgb * nDotL;
|
||||
totalWeight += nDotL;
|
||||
}
|
||||
}
|
||||
|
||||
prefilteredColor /= max(totalWeight, IBL_EPSILON);
|
||||
return float4(prefilteredColor, 1.0f);
|
||||
}
|
||||
|
||||
float GeometrySchlickGGXIBL(float nDotX, float roughness) {
|
||||
const float k = (roughness * roughness) * 0.5f;
|
||||
return nDotX / max(nDotX * (1.0f - k) + k, IBL_EPSILON);
|
||||
}
|
||||
|
||||
float GeometrySmithIBL(float nDotV, float nDotL, float roughness) {
|
||||
return GeometrySchlickGGXIBL(nDotV, roughness) *
|
||||
GeometrySchlickGGXIBL(nDotL, roughness);
|
||||
}
|
||||
|
||||
float2 IntegrateBrdf(float nDotV, float roughness, uint sampleCount) {
|
||||
const float3 normal = float3(0.0f, 0.0f, 1.0f);
|
||||
const float3 viewDirection = float3(
|
||||
sqrt(saturate(1.0f - nDotV * nDotV)), 0.0f, nDotV);
|
||||
|
||||
float scale = 0.0f;
|
||||
float bias = 0.0f;
|
||||
|
||||
[loop]
|
||||
for (uint sampleIndex = 0u; sampleIndex < sampleCount; ++sampleIndex) {
|
||||
const float2 xi = Hammersley(sampleIndex, sampleCount);
|
||||
const float3 halfVector = ImportanceSampleGGX(xi, normal, roughness);
|
||||
const float3 lightDirection = normalize(
|
||||
2.0f * dot(viewDirection, halfVector) * halfVector - viewDirection);
|
||||
|
||||
const float nDotL = saturate(lightDirection.z);
|
||||
const float nDotH = saturate(halfVector.z);
|
||||
const float vDotH = saturate(dot(viewDirection, halfVector));
|
||||
|
||||
if (nDotL > 0.0f) {
|
||||
const float geometry = GeometrySmithIBL(nDotV, nDotL, roughness);
|
||||
const float geometryVisibility =
|
||||
geometry * vDotH /
|
||||
max(nDotH * nDotV, IBL_EPSILON);
|
||||
const float fresnel = pow(1.0f - vDotH, 5.0f);
|
||||
|
||||
scale += (1.0f - fresnel) * geometryVisibility;
|
||||
bias += fresnel * geometryVisibility;
|
||||
}
|
||||
}
|
||||
|
||||
return float2(scale, bias) / float(sampleCount);
|
||||
}
|
||||
|
||||
float2 PSBrdfLut(VSOutput input) : SV_TARGET {
|
||||
const float nDotV = clamp(input.uv.x, 0.001f, 1.0f);
|
||||
const float roughness = clamp(input.uv.y, 0.001f, 1.0f);
|
||||
const uint sampleCount = max(gBakeUIntParams.y, 1u);
|
||||
return IntegrateBrdf(nDotV, roughness, sampleCount);
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
#ifndef RENDER_IBL_HLSLI
|
||||
#define RENDER_IBL_HLSLI
|
||||
|
||||
// 必须与 IBLResources in ibl_resources.h.的定义顺序相同
|
||||
TextureCube<float4> gIblIrradianceMap : register(t9);
|
||||
TextureCube<float4> gIblPrefilteredMap : register(t10);
|
||||
Texture2D<float4> gIblBrdfLut : register(t11);
|
||||
SamplerState gIblSampler : register(s3);
|
||||
|
||||
cbuffer CBIBL : register(b4) {
|
||||
// x=intensity, y=prefilter max mip, z=Y rotation in radians, w=enabled
|
||||
float4 gIblParams;
|
||||
};
|
||||
|
||||
float3 IBL_RotateAroundY(float3 direction, float radians){
|
||||
float sineValue;
|
||||
float cosineValue;
|
||||
sincos(radians, sineValue, cosineValue);
|
||||
|
||||
return float3( cosineValue * direction.x + sineValue * direction.z,
|
||||
direction.y,
|
||||
-sineValue * direction.x + cosineValue * direction.z);
|
||||
}
|
||||
|
||||
float3 IBL_FresnelSchlickRoughness(float cosTheta, float3 f0, float roughness)
|
||||
{
|
||||
const float oneMinusRoughness = 1.0f - roughness;
|
||||
const float3 grazing = max(float3(oneMinusRoughness, oneMinusRoughness,
|
||||
oneMinusRoughness), f0);
|
||||
const float factor = pow(1.0f - saturate(cosTheta), 5.0f);
|
||||
return f0 + (grazing - f0) * factor;
|
||||
}
|
||||
|
||||
// V 点从表面到相机。 N 是世界空间着色法线。返回的值是线性 HDR 辐射度,必须在色调映射之前添加。
|
||||
float3 EvaluateIBL(float3 N, float3 V, float3 baseColor,
|
||||
float metallic, float roughness, float ambientOcclusion)
|
||||
{
|
||||
float3 finalRet = float3(0.0f,0.0f,0.0f);
|
||||
if (gIblParams.w < 0.5f){
|
||||
finalRet=float3(0.0f, 0.0f, 0.0f);
|
||||
} else {
|
||||
N = normalize(N);
|
||||
V = normalize(V);
|
||||
metallic = saturate(metallic);
|
||||
roughness = clamp(roughness, 0.045f, 1.0f);
|
||||
ambientOcclusion = saturate(ambientOcclusion);
|
||||
|
||||
const float nDotV = saturate(dot(N, V));
|
||||
const float3 dielectricF0 = float3(0.04f, 0.04f, 0.04f);
|
||||
const float3 f0 = lerp(dielectricF0, baseColor, metallic);
|
||||
const float3 F = IBL_FresnelSchlickRoughness(nDotV, f0, roughness);
|
||||
const float3 kD = (float3(1.0f, 1.0f, 1.0f) - F) * (1.0f - metallic);
|
||||
|
||||
const float3 sampleN = IBL_RotateAroundY(N, gIblParams.z);
|
||||
const float3 irradiance = gIblIrradianceMap.SampleLevel(gIblSampler, sampleN, 0.0f).rgb;
|
||||
const float3 diffuse = irradiance * baseColor;
|
||||
|
||||
const float3 reflection = reflect(-V, N);
|
||||
const float3 sampleR = IBL_RotateAroundY(reflection, gIblParams.z);
|
||||
const float lod = roughness * max(gIblParams.y, 0.0f);
|
||||
const float3 prefiltered = gIblPrefilteredMap.SampleLevel(gIblSampler, sampleR, lod).rgb;
|
||||
|
||||
const float2 brdf = gIblBrdfLut.SampleLevel(gIblSampler, float2(nDotV, roughness), 0.0f).rg;
|
||||
const float3 specular = prefiltered * (F * brdf.x + brdf.y);
|
||||
|
||||
finalRet = (kD * diffuse + specular) * ambientOcclusion * gIblParams.x;
|
||||
}
|
||||
|
||||
return finalRet;
|
||||
}
|
||||
|
||||
#endif // RENDER_IBL_HLSLI
|
||||
@@ -0,0 +1,64 @@
|
||||
#ifndef PBR_BRDF_HLSLI
|
||||
#define PBR_BRDF_HLSLI
|
||||
|
||||
// [PBR-ADD: NEW FILE]
|
||||
static const float PBR_PI = 3.14159265359f;
|
||||
static const float PBR_EPSILON = 1.0e-5f;
|
||||
|
||||
float3 SafeNormalize(float3 value, float3 fallbackValue) {
|
||||
const float lengthSquared = dot(value, value);
|
||||
return lengthSquared > PBR_EPSILON ?
|
||||
value * rsqrt(lengthSquared) : fallbackValue;
|
||||
}
|
||||
|
||||
float3 BuildFallbackTangent(float3 normal) {
|
||||
const float3 referenceAxis = abs(normal.y) < 0.999f ?
|
||||
float3(0.0f, 1.0f, 0.0f) : float3(1.0f, 0.0f, 0.0f);
|
||||
return SafeNormalize(cross(referenceAxis, normal), float3(1.0f, 0.0f, 0.0f));
|
||||
}
|
||||
|
||||
float3 FresnelSchlick(float cosTheta, float3 f0) {
|
||||
const float x = 1.0f - saturate(cosTheta);
|
||||
const float x2 = x * x;
|
||||
const float x5 = x2 * x2 * x;
|
||||
return f0 + (1.0f - f0) * x5;
|
||||
}
|
||||
|
||||
float DistributionGGX(float3 n, float3 h, float roughness) {
|
||||
const float alpha = roughness * roughness;
|
||||
const float alpha2 = alpha * alpha;
|
||||
const float nDotH = saturate(dot(n, h));
|
||||
const float nDotH2 = nDotH * nDotH;
|
||||
const float denominator = nDotH2 * (alpha2 - 1.0f) + 1.0f;
|
||||
return alpha2 / max(PBR_PI * denominator * denominator, PBR_EPSILON);
|
||||
}
|
||||
|
||||
float GeometrySchlickGGX(float nDotX, float roughness) {
|
||||
// UE4 常用的直接光 Schlick-GGX k。
|
||||
const float r = roughness + 1.0f;
|
||||
const float k = (r * r) / 8.0f;
|
||||
return nDotX / max(nDotX * (1.0f - k) + k, PBR_EPSILON);
|
||||
}
|
||||
|
||||
float GeometrySmith(float3 n, float3 v, float3 l, float roughness) {
|
||||
const float nDotV = saturate(dot(n, v));
|
||||
const float nDotL = saturate(dot(n, l));
|
||||
return GeometrySchlickGGX(nDotV, roughness) * GeometrySchlickGGX(nDotL, roughness);
|
||||
}
|
||||
|
||||
float3 DecodeNormalMap(float3 encodedNormal, float normalScale) {
|
||||
float3 normalTS = encodedNormal * 2.0f - 1.0f;
|
||||
normalTS.xy *= normalScale;
|
||||
return normalize(normalTS);
|
||||
}
|
||||
|
||||
float3 ToneMapACES(float3 color) {
|
||||
const float a = 2.51f;
|
||||
const float b = 0.03f;
|
||||
const float c = 2.43f;
|
||||
const float d = 0.59f;
|
||||
const float e = 0.14f;
|
||||
return saturate((color * (a * color + b)) / max(color * (c * color + d) + e, PBR_EPSILON));
|
||||
}
|
||||
|
||||
#endif // PBR_BRDF_HLSLI
|
||||
@@ -0,0 +1,26 @@
|
||||
#ifndef INCLUDE_SHADOW_COMMON
|
||||
#define INCLUDE_SHADOW_COMMON
|
||||
|
||||
cbuffer CBScene : register(b0)
|
||||
{
|
||||
row_major float4x4 cbView;
|
||||
row_major float4x4 cbProj;
|
||||
float4 cbCameraPos;
|
||||
float4 cbMainLightDirection;
|
||||
float4 cbCameraColor;
|
||||
}
|
||||
|
||||
cbuffer CBPerObj : register(b2) {
|
||||
row_major float4x4 cbWorld;
|
||||
};
|
||||
|
||||
cbuffer CBShadow : register(b3) {
|
||||
row_major float4x4 cbLightViewProjection;
|
||||
|
||||
// xy:阴影贴图 texel UV 尺寸
|
||||
// z :最小深度偏移 w :斜率深度偏移
|
||||
float4 cbShadowParams;
|
||||
};
|
||||
|
||||
|
||||
#endif //INCLUDE_SHADOW_COMMON
|
||||
@@ -0,0 +1,58 @@
|
||||
#ifndef INCLUDE_SHADOW_MAINLIT
|
||||
#define INCLUDE_SHADOW_MAINLIT
|
||||
|
||||
#include "include/shadow_common.hlsli"
|
||||
|
||||
Texture2D<float> gShadowMap : register(t8);
|
||||
SamplerComparisonState gShadowSampler : register(s2);
|
||||
|
||||
float CalculateMainLightShadow(float4 shadowPosition,float3 worldNormal)
|
||||
{
|
||||
// 默认完全受光。
|
||||
float shadowVisibility = 1.0f;
|
||||
|
||||
// 只处理位于光源相机前方的像素。
|
||||
if (shadowPosition.w > 0.0f)
|
||||
{
|
||||
float3 projected = shadowPosition.xyz / shadowPosition.w;
|
||||
|
||||
float2 shadowUV = projected.xy * float2(0.5f, -0.5f) + 0.5f;
|
||||
|
||||
float currentDepth = projected.z;
|
||||
|
||||
bool insideShadowMap = all(shadowUV >= 0.0f) && all(shadowUV <= 1.0f) &&
|
||||
currentDepth > 0.0f && currentDepth < 1.0f;
|
||||
|
||||
if (insideShadowMap)
|
||||
{
|
||||
// cbMainLightDirection 是光线传播方向,
|
||||
// 因此表面指向光源的方向需要取反。
|
||||
float3 lightToSurface = normalize(-cbMainLightDirection.xyz);
|
||||
|
||||
float normalDotLight = saturate(dot(normalize(worldNormal), lightToSurface));
|
||||
|
||||
float bias = max(cbShadowParams.z, cbShadowParams.w * (1.0f - normalDotLight));
|
||||
|
||||
float shadowSum = 0.0f;
|
||||
|
||||
[unroll] for (int y = -1; y <= 1; ++y)
|
||||
{
|
||||
[unroll] for (int x = -1; x <= 1; ++x)
|
||||
{
|
||||
float2 offset = float2((float)x, (float)y) * cbShadowParams.xy;
|
||||
|
||||
shadowSum += gShadowMap.SampleCmpLevelZero(gShadowSampler,
|
||||
shadowUV + offset,
|
||||
currentDepth - bias);
|
||||
}
|
||||
}
|
||||
|
||||
shadowVisibility = shadowSum / 9.0f;
|
||||
}
|
||||
}
|
||||
|
||||
return shadowVisibility;
|
||||
}
|
||||
|
||||
|
||||
#endif //INCLUDE_SHADOW_MAINLIT
|
||||
@@ -0,0 +1,202 @@
|
||||
// 约定:row_major 内存布局
|
||||
// mul(rowVector, matrix)
|
||||
// CPU 侧矩阵不转置
|
||||
|
||||
#pragma pack_matrix(row_major)
|
||||
#include "include/pbr_brdf.hlsli"
|
||||
|
||||
cbuffer CBScene : register(b0) {
|
||||
row_major float4x4 gView;
|
||||
row_major float4x4 gProjection;
|
||||
float4 gCameraPosition;
|
||||
float4 gLightDirection; // xyz: 光线从光源射向场景的方向
|
||||
float4 gLightColorIntensity; // rgb: 线性颜色, w: 强度
|
||||
float4 gAmbientColorIntensity; // rgb: 线性颜色, w: 强度;IBL 前的临时环境项
|
||||
float4 gRenderParams; // x: exposure,其余保留
|
||||
};
|
||||
|
||||
cbuffer CBPerMat : register(b1) {
|
||||
float4 gBaseColorFactor = float4 (1.0f,1.0f,1.0f,1.0f);
|
||||
float4 gEmissiveFactor = float4 (1.0f,1.0f,1.0f,1.0f); // xyz 有效
|
||||
float4 gPbrParams = float4 (1.0f,1.0f,1.0f,1.0f); // x metallic, y roughness, z normalScale, w AO strength
|
||||
};
|
||||
|
||||
cbuffer CBPerObj : register(b2) {
|
||||
row_major float4x4 gWorld;
|
||||
row_major float4x4 gWorldInvTranspose;
|
||||
};
|
||||
|
||||
cbuffer CBShadow : register(b3) {
|
||||
row_major float4x4 gLightViewProjection;
|
||||
float4 gShadowParams; // xy texelSize, z minBias, w slopeBias
|
||||
};
|
||||
|
||||
Texture2D gBaseColorTex : register(t0); // sRGB SRV
|
||||
Texture2D gNormalTex : register(t1); // Linear SRV
|
||||
Texture2D gOrmTex : register(t2); // Linear SRV: R=AO, G=Roughness, B=Metallic
|
||||
Texture2D gEmissiveTex : register(t3); // sRGB SRV
|
||||
Texture2D<float> gShadowMap : register(t8);
|
||||
|
||||
SamplerState gMaterialSampler : register(s0);
|
||||
SamplerComparisonState gShadowSampler : register(s2);
|
||||
|
||||
struct VSInput {
|
||||
float3 positionOS : POSITION;
|
||||
float3 normalOS : NORMAL;
|
||||
float3 tangentOS : TANGENT;
|
||||
float2 uv0 : TEXCOORD0;
|
||||
};
|
||||
|
||||
struct VSOutput {
|
||||
float4 positionCS : SV_POSITION;
|
||||
float3 positionWS : POSITION1;
|
||||
float3 normalWS : NORMAL0;
|
||||
float3 tangentWS : TANGENT0;
|
||||
float2 uv0 : TEXCOORD0;
|
||||
float4 shadowPosition : TEXCOORD1;
|
||||
};
|
||||
|
||||
VSOutput VSMain(VSInput input) {
|
||||
VSOutput output;
|
||||
|
||||
const float4 positionWS = mul(float4(input.positionOS, 1.0f), gWorld);
|
||||
const float4 positionVS = mul(positionWS, gView);
|
||||
|
||||
output.positionCS = mul(positionVS, gProjection);
|
||||
output.positionWS = positionWS.xyz;
|
||||
|
||||
// 法线必须乘 world 的逆转置矩阵。
|
||||
const float3 normalWS = SafeNormalize(
|
||||
mul(float4(input.normalOS, 0.0f), gWorldInvTranspose).xyz,
|
||||
float3(0.0f, 1.0f, 0.0f));
|
||||
|
||||
// 切线按普通方向乘 world,再对法线做 Gram-Schmidt 正交化。
|
||||
float3 tangentWS = mul(float4(input.tangentOS, 0.0f), gWorld).xyz;
|
||||
tangentWS -= normalWS * dot(normalWS, tangentWS);
|
||||
tangentWS = SafeNormalize(tangentWS, BuildFallbackTangent(normalWS));
|
||||
|
||||
output.normalWS = normalWS;
|
||||
output.tangentWS = tangentWS;
|
||||
output.uv0 = input.uv0;
|
||||
//沿法线外扩后,获取shadowPos
|
||||
float4 posWS_bigger =float4( (positionWS.xyz + normalWS.xyz * 0.1f),1.0f);
|
||||
// output.shadowPosition = mul(positionWS, gLightViewProjection);
|
||||
output.shadowPosition = mul(posWS_bigger, gLightViewProjection);
|
||||
return output;
|
||||
}
|
||||
|
||||
float SampleDirectionalShadow(float4 shadowPosition, float3 n, float3 l) {
|
||||
float finalRet=1.0f;
|
||||
if (shadowPosition.w > 0.0f){
|
||||
const float3 ndc = shadowPosition.xyz / shadowPosition.w;
|
||||
const float2 uv = float2(ndc.x * 0.5f + 0.5f, -ndc.y * 0.5f + 0.5f);
|
||||
if (!any(uv < 0.0f) && !any(uv > 1.0f) &&! (ndc.z <= 0.0f) && !(ndc.z >= 1.0f)) {
|
||||
const float bias = max(
|
||||
gShadowParams.z,
|
||||
gShadowParams.w * (1.0f - saturate(dot(n, l))));
|
||||
|
||||
float visibility = 0.0f;
|
||||
[unroll]
|
||||
for (int y = -1; y <= 1; ++y) {
|
||||
[unroll]
|
||||
for (int x = -1; x <= 1; ++x) {
|
||||
const float2 offset = float2(x, y) * gShadowParams.xy;
|
||||
visibility += gShadowMap.SampleCmpLevelZero(
|
||||
gShadowSampler, uv + offset, ndc.z - bias);
|
||||
}
|
||||
}
|
||||
finalRet = visibility/9.0f;
|
||||
}
|
||||
}
|
||||
return finalRet;
|
||||
|
||||
/* 编译器 warn -> error 多return路径
|
||||
if (shadowPosition.w <= 0.0f) return 1.0f;
|
||||
|
||||
const float3 ndc = shadowPosition.xyz / shadowPosition.w;
|
||||
const float2 uv = float2(ndc.x * 0.5f + 0.5f, -ndc.y * 0.5f + 0.5f);
|
||||
|
||||
if (any(uv < 0.0f) || any(uv > 1.0f) || ndc.z <= 0.0f || ndc.z >= 1.0f) {
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
const float bias = max(
|
||||
gShadowParams.z,
|
||||
gShadowParams.w * (1.0f - saturate(dot(n, l))));
|
||||
|
||||
float visibility = 0.0f;
|
||||
[unroll]
|
||||
for (int y = -1; y <= 1; ++y) {
|
||||
[unroll]
|
||||
for (int x = -1; x <= 1; ++x) {
|
||||
const float2 offset = float2(x, y) * gShadowParams.xy;
|
||||
visibility += gShadowMap.SampleCmpLevelZero(
|
||||
gShadowSampler, uv + offset, ndc.z - bias);
|
||||
}
|
||||
}
|
||||
return visibility / 9.0f;
|
||||
*/
|
||||
}
|
||||
|
||||
float4 PSMain(VSOutput input) : SV_TARGET {
|
||||
const float4 baseSample = gBaseColorTex.Sample(gMaterialSampler, input.uv0);
|
||||
const float4 baseColor = baseSample * gBaseColorFactor;
|
||||
|
||||
const float3 orm = gOrmTex.Sample(gMaterialSampler, input.uv0).rgb;
|
||||
const float ao = lerp(1.0f, orm.r, saturate(gPbrParams.w));
|
||||
const float roughness = clamp(orm.g * gPbrParams.y, 0.045f, 1.0f);
|
||||
const float metallic = saturate(orm.b * gPbrParams.x);
|
||||
|
||||
const float3 normalTS = DecodeNormalMap(gNormalTex.Sample(gMaterialSampler, input.uv0).xyz, gPbrParams.z);
|
||||
|
||||
const float3 geometricNormal = SafeNormalize(input.normalWS, float3(0.0f, 1.0f, 0.0f));
|
||||
const float3 tangent = SafeNormalize(input.tangentWS, BuildFallbackTangent(geometricNormal));
|
||||
|
||||
// 当前 VertexData 只有 float3 tangent,暂按 handedness=+1 构造 B。
|
||||
// 生产版本应把 tangent 改为 float4,并乘 tangent.w 处理镜像 UV。
|
||||
const float3 bitangent = SafeNormalize( cross(geometricNormal, tangent),
|
||||
cross(geometricNormal, BuildFallbackTangent(geometricNormal)));
|
||||
const float3x3 tangentToWorld = float3x3(tangent, bitangent, geometricNormal);
|
||||
const float3 n = SafeNormalize(mul(normalTS, tangentToWorld), geometricNormal);
|
||||
|
||||
const float3 v = SafeNormalize(gCameraPosition.xyz - input.positionWS, geometricNormal);
|
||||
// Scene.lightDirection 表示光线传播方向,因此表面指向光源的 L 取反。
|
||||
const float3 l = SafeNormalize(-gLightDirection.xyz, float3(0.0f, 1.0f, 0.0f));
|
||||
const float3 h = SafeNormalize(v + l, n);
|
||||
|
||||
const float nDotL = saturate(dot(n, l));
|
||||
const float nDotV = saturate(dot(n, v));
|
||||
const float hDotV = saturate(dot(h, v));
|
||||
|
||||
const float3 f0 = lerp(float3(0.04f, 0.04f, 0.04f), baseColor.rgb, metallic);
|
||||
const float d = DistributionGGX(n, h, roughness);
|
||||
const float g = GeometrySmith(n, v, l, roughness);
|
||||
const float3 f = FresnelSchlick(hDotV, f0);
|
||||
|
||||
const float3 specular = (d * g * f) / max(4.0f * nDotV * nDotL, PBR_EPSILON);
|
||||
|
||||
const float3 kS = f;
|
||||
const float3 kD = (1.0f - kS) * (1.0f - metallic);
|
||||
const float3 diffuse = kD * baseColor.rgb / PBR_PI;
|
||||
|
||||
const float visibility = SampleDirectionalShadow(input.shadowPosition, n, l);
|
||||
const float3 radiance = gLightColorIntensity.rgb * gLightColorIntensity.w;
|
||||
const float3 directLighting = (diffuse + specular) * radiance * nDotL * visibility;
|
||||
|
||||
// [PBR-TEMP] 尚未接入 IBL 的低成本环境近似。
|
||||
const float3 ambientBase = (baseColor.rgb * (1.0f - metallic) + f0 * 0.25f) * ao;
|
||||
const float3 ambientLighting = ambientBase * gAmbientColorIntensity.rgb * gAmbientColorIntensity.w;
|
||||
|
||||
const float3 emissive = gEmissiveTex.Sample(gMaterialSampler, input.uv0).rgb * gEmissiveFactor.rgb;
|
||||
|
||||
float3 color = directLighting + ambientLighting + emissive;
|
||||
color *= max(gRenderParams.x, 0.0f);
|
||||
color = ToneMapACES(color);
|
||||
|
||||
// Graphic 当前使用 UNORM back buffer,因此在 PS 中完成线性 -> 显示编码。
|
||||
color = pow(max(color, 0.0f), 1.0f / 2.2f);
|
||||
return float4(color, baseColor.a);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,224 @@
|
||||
// 约定:row_major 内存布局
|
||||
// mul(rowVector, matrix)
|
||||
// CPU 侧矩阵不转置
|
||||
|
||||
#pragma pack_matrix(row_major)
|
||||
#include "include/pbr_brdf.hlsli"
|
||||
// [新增] 全局 IBL 资源:b4、t9~t11、s3。
|
||||
#include "include/ibl.hlsli"
|
||||
|
||||
cbuffer CBScene : register(b0)
|
||||
{
|
||||
row_major float4x4 gView;
|
||||
row_major float4x4 gProjection;
|
||||
float4 gCameraPosition;
|
||||
float4 gLightDirection; // xyz: 光线从光源射向场景的方向
|
||||
float4 gLightColorIntensity; // rgb: 线性颜色, w: 强度
|
||||
float4 gAmbientColorIntensity; // rgb: 线性颜色, w: 强度;IBL 前的临时环境项
|
||||
float4 gRenderParams; // x: exposure,其余保留
|
||||
};
|
||||
|
||||
cbuffer CBPerMat : register(b1)
|
||||
{
|
||||
float4 gBaseColorFactor = float4(0.1f, 0.1f, 0.1f, 1.0f);
|
||||
float4 gEmissiveFactor = float4(1.0f, 1.0f, 1.0f, 1.0f); // xyz 有效
|
||||
float4 gPbrParams = float4(1.0f, 1.0f, 1.0f, 1.0f); // x metallic, y roughness, z normalScale, w AO strength
|
||||
};
|
||||
|
||||
cbuffer CBPerObj : register(b2)
|
||||
{
|
||||
row_major float4x4 gWorld;
|
||||
row_major float4x4 gWorldInvTranspose;
|
||||
};
|
||||
|
||||
cbuffer CBShadow : register(b3)
|
||||
{
|
||||
row_major float4x4 gLightViewProjection;
|
||||
float4 gShadowParams; // xy texelSize, z minBias, w slopeBias
|
||||
};
|
||||
|
||||
Texture2D gBaseColorTex : register(t0); // sRGB SRV
|
||||
Texture2D gNormalTex : register(t1); // Linear SRV
|
||||
Texture2D gOrmTex : register(t2); // Linear SRV: R=AO, G=Roughness, B=Metallic
|
||||
Texture2D gEmissiveTex : register(t3); // sRGB SRV
|
||||
Texture2D<float> gShadowMap : register(t8);
|
||||
|
||||
SamplerState gMaterialSampler : register(s0);
|
||||
SamplerComparisonState gShadowSampler : register(s2);
|
||||
|
||||
struct VSInput
|
||||
{
|
||||
float3 positionOS : POSITION;
|
||||
float3 normalOS : NORMAL;
|
||||
float3 tangentOS : TANGENT;
|
||||
float2 uv0 : TEXCOORD0;
|
||||
};
|
||||
|
||||
struct VSOutput
|
||||
{
|
||||
float4 positionCS : SV_POSITION;
|
||||
float3 positionWS : POSITION1;
|
||||
float3 normalWS : NORMAL0;
|
||||
float3 tangentWS : TANGENT0;
|
||||
float2 uv0 : TEXCOORD0;
|
||||
float4 shadowPosition : TEXCOORD1;
|
||||
};
|
||||
|
||||
VSOutput VSMain(VSInput input)
|
||||
{
|
||||
VSOutput output;
|
||||
|
||||
const float4 positionWS = mul(float4(input.positionOS, 1.0f), gWorld);
|
||||
const float4 positionVS = mul(positionWS, gView);
|
||||
|
||||
output.positionCS = mul(positionVS, gProjection);
|
||||
output.positionWS = positionWS.xyz;
|
||||
|
||||
// 法线必须乘 world 的逆转置矩阵
|
||||
const float3 normalWS = SafeNormalize(
|
||||
mul(float4(input.normalOS, 0.0f), gWorldInvTranspose).xyz,
|
||||
float3(0.0f, 1.0f, 0.0f));
|
||||
|
||||
// 切线按普通方向乘 world,再对法线做 Gram-Schmidt 正交化。
|
||||
float3 tangentWS = mul(float4(input.tangentOS, 0.0f), gWorld).xyz;
|
||||
tangentWS -= normalWS * dot(normalWS, tangentWS);
|
||||
tangentWS = SafeNormalize(tangentWS, BuildFallbackTangent(normalWS));
|
||||
|
||||
output.normalWS = normalWS;
|
||||
output.tangentWS = tangentWS;
|
||||
output.uv0 = input.uv0;
|
||||
// 沿法线外扩后,获取shadowPos
|
||||
float4 posWS_bigger = float4((positionWS.xyz + normalWS.xyz * 0.1f), 1.0f);
|
||||
// output.shadowPosition = mul(positionWS, gLightViewProjection);
|
||||
output.shadowPosition = mul(posWS_bigger, gLightViewProjection);
|
||||
// output.shadowPosition = mul(positionWS, gLightViewProjection);
|
||||
return output;
|
||||
}
|
||||
|
||||
float SampleDirectionalShadow(float4 shadowPosition, float3 n, float3 l)
|
||||
{
|
||||
float finalRet = 1.0f;
|
||||
if (shadowPosition.w > 0.0f)
|
||||
{
|
||||
const float3 ndc = shadowPosition.xyz / shadowPosition.w;
|
||||
const float2 uv = float2(ndc.x * 0.5f + 0.5f, -ndc.y * 0.5f + 0.5f);
|
||||
if (!any(uv < 0.0f) && !any(uv > 1.0f) && !(ndc.z <= 0.0f) && !(ndc.z >= 1.0f))
|
||||
{
|
||||
const float bias = max(
|
||||
gShadowParams.z,
|
||||
gShadowParams.w * (1.0f - saturate(dot(n, l))));
|
||||
|
||||
float visibility = 0.0f;
|
||||
[unroll]
|
||||
for (int y = -1; y <= 1; ++y)
|
||||
{
|
||||
[unroll]
|
||||
for (int x = -1; x <= 1; ++x)
|
||||
{
|
||||
const float2 offset = float2(x, y) * gShadowParams.xy;
|
||||
visibility += gShadowMap.SampleCmpLevelZero(gShadowSampler, uv + offset, ndc.z - bias);
|
||||
}
|
||||
}
|
||||
finalRet = visibility / 9.0f;
|
||||
}
|
||||
}
|
||||
return finalRet;
|
||||
|
||||
/* 编译器 warn -> error 多return路径
|
||||
if (shadowPosition.w <= 0.0f) return 1.0f;
|
||||
|
||||
const float3 ndc = shadowPosition.xyz / shadowPosition.w;
|
||||
const float2 uv = float2(ndc.x * 0.5f + 0.5f, -ndc.y * 0.5f + 0.5f);
|
||||
|
||||
if (any(uv < 0.0f) || any(uv > 1.0f) || ndc.z <= 0.0f || ndc.z >= 1.0f) {
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
const float bias = max(
|
||||
gShadowParams.z,
|
||||
gShadowParams.w * (1.0f - saturate(dot(n, l))));
|
||||
|
||||
float visibility = 0.0f;
|
||||
[unroll]
|
||||
for (int y = -1; y <= 1; ++y) {
|
||||
[unroll]
|
||||
for (int x = -1; x <= 1; ++x) {
|
||||
const float2 offset = float2(x, y) * gShadowParams.xy;
|
||||
visibility += gShadowMap.SampleCmpLevelZero(
|
||||
gShadowSampler, uv + offset, ndc.z - bias);
|
||||
}
|
||||
}
|
||||
return visibility / 9.0f;
|
||||
*/
|
||||
}
|
||||
|
||||
float4 PSMain(VSOutput input) : SV_TARGET
|
||||
{
|
||||
const float4 baseSample = gBaseColorTex.Sample(gMaterialSampler, input.uv0);
|
||||
const float4 baseColor = baseSample * gBaseColorFactor;
|
||||
|
||||
const float3 orm = gOrmTex.Sample(gMaterialSampler, input.uv0).rgb;
|
||||
const float ao = lerp(1.0f, orm.r, saturate(gPbrParams.w));
|
||||
const float roughness = clamp(orm.g * gPbrParams.y, 0.045f, 1.0f);
|
||||
const float metallic = saturate(orm.b * gPbrParams.x);
|
||||
|
||||
const float3 normalTS = DecodeNormalMap(gNormalTex.Sample(gMaterialSampler, input.uv0).xyz, gPbrParams.z);
|
||||
|
||||
const float3 geometricNormal = SafeNormalize(input.normalWS, float3(0.0f, 1.0f, 0.0f));
|
||||
const float3 tangent = SafeNormalize(input.tangentWS, BuildFallbackTangent(geometricNormal));
|
||||
|
||||
// 当前 VertexData 只有 float3 tangent,暂按 handedness=+1 构造 B。
|
||||
// 生产版本应把 tangent 改为 float4,并乘 tangent.w 处理镜像 UV。
|
||||
const float3 bitangent = SafeNormalize(cross(geometricNormal, tangent),
|
||||
cross(geometricNormal, BuildFallbackTangent(geometricNormal)));
|
||||
const float3x3 tangentToWorld = float3x3(tangent, bitangent, geometricNormal);
|
||||
const float3 n = SafeNormalize(mul(normalTS, tangentToWorld), geometricNormal);
|
||||
|
||||
const float3 v = SafeNormalize(gCameraPosition.xyz - input.positionWS, geometricNormal);
|
||||
// Scene.lightDirection 表示光线传播方向,因此表面指向光源的 L 取反。
|
||||
const float3 l = SafeNormalize(-gLightDirection.xyz, float3(0.0f, 1.0f, 0.0f));
|
||||
const float3 h = SafeNormalize(v + l, n);
|
||||
|
||||
const float nDotL = saturate(dot(n, l));
|
||||
const float nDotV = saturate(dot(n, v));
|
||||
const float hDotV = saturate(dot(h, v));
|
||||
|
||||
const float3 f0 = lerp(float3(0.04f, 0.04f, 0.04f), baseColor.rgb, metallic);
|
||||
const float d = DistributionGGX(n, h, roughness);
|
||||
const float g = GeometrySmith(n, v, l, roughness);
|
||||
const float3 f = FresnelSchlick(hDotV, f0);
|
||||
|
||||
const float3 specular = (d * g * f) / max(4.0f * nDotV * nDotL, PBR_EPSILON);
|
||||
|
||||
const float3 kS = f;
|
||||
const float3 kD = (1.0f - kS) * (1.0f - metallic);
|
||||
const float3 diffuse = kD * baseColor.rgb / PBR_PI;
|
||||
|
||||
const float visibility = SampleDirectionalShadow(input.shadowPosition, n, l);
|
||||
const float3 radiance = gLightColorIntensity.rgb * gLightColorIntensity.w;
|
||||
const float3 directLighting = (diffuse + specular) * radiance * nDotL * visibility;
|
||||
|
||||
// [修改] IBL 准备完成时使用 irradiance + GGX prefilter + BRDF LUT。
|
||||
// 若 C:/temp 六面图尚未成功导入,则保留原来的低成本环境项作为回退。
|
||||
float3 ambientLighting = float3(0.0f, 0.0f, 0.0f);
|
||||
if (gIblParams.w >= 0.5f)
|
||||
{
|
||||
ambientLighting = EvaluateIBL(n, v, baseColor.rgb, metallic, roughness, ao);
|
||||
}
|
||||
else
|
||||
{
|
||||
const float3 ambientBase = (baseColor.rgb * (1.0f - metallic) + f0 * 0.25f) * ao;
|
||||
ambientLighting = ambientBase * gAmbientColorIntensity.rgb * gAmbientColorIntensity.w;
|
||||
}
|
||||
|
||||
const float3 emissive = gEmissiveTex.Sample(gMaterialSampler, input.uv0).rgb * gEmissiveFactor.rgb;
|
||||
|
||||
float3 color = directLighting + ambientLighting + emissive;
|
||||
color *= max(gRenderParams.x, 0.0f);
|
||||
color = ToneMapACES(color);
|
||||
|
||||
// Graphic 当前使用 UNORM back buffer,因此在 PS 中完成线性 -> 显示编码。
|
||||
color = pow(max(color, 0.0f), 1.0f / 2.2f);
|
||||
return float4(color, baseColor.a);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma pack_matrix(row_major)
|
||||
|
||||
cbuffer CBPerObj : register(b2) {
|
||||
row_major float4x4 gWorld;
|
||||
row_major float4x4 gWorldInvTranspose; // 深度阶段不用,但保持 CPU 布局契约一致
|
||||
};
|
||||
|
||||
cbuffer CBShadow : register(b3) {
|
||||
row_major float4x4 gLightViewProjection;
|
||||
float4 gShadowParams;
|
||||
};
|
||||
|
||||
struct VSInput {
|
||||
float3 positionOS : POSITION;
|
||||
};
|
||||
|
||||
float4 VSMain(VSInput input) : SV_POSITION {
|
||||
const float4 positionWS = mul(float4(input.positionOS, 1.0f), gWorld);
|
||||
return mul(positionWS, gLightViewProjection);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#include "include/shadow_common.hlsli"
|
||||
|
||||
struct VSInput {
|
||||
float3 position : POSITION;
|
||||
};
|
||||
|
||||
float4 VSMain(VSInput input) : SV_POSITION {
|
||||
const float4 worldPosition = mul(float4(input.position, 1.0f), cbWorld);
|
||||
|
||||
return mul( worldPosition, cbLightViewProjection);
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
// [新增] 相机背景天空盒。
|
||||
// CPU 不创建天空盒网格;VS 使用 SV_VertexID 读取内置 36 个顶点。
|
||||
|
||||
#pragma pack_matrix(row_major)
|
||||
#include "include/pbr_brdf.hlsli"
|
||||
|
||||
cbuffer CBScene : register(b0) {
|
||||
row_major float4x4 gView;
|
||||
row_major float4x4 gProjection;
|
||||
float4 gCameraPosition;
|
||||
float4 gLightDirection;
|
||||
float4 gLightColorIntensity;
|
||||
float4 gAmbientColorIntensity;
|
||||
float4 gRenderParams; // x=exposure
|
||||
};
|
||||
|
||||
cbuffer CBIBL : register(b4) {
|
||||
// x=intensity, y=prefilter max mip, z=Y rotation, w=enabled
|
||||
float4 gIblParams;
|
||||
};
|
||||
|
||||
TextureCube<float4> gSkyboxCube : register(t12);
|
||||
SamplerState gSkyboxSampler : register(s4);
|
||||
|
||||
static const float3 gCubeVertices[36] = {
|
||||
// -Z
|
||||
float3(-1.0f, -1.0f, -1.0f), float3(-1.0f, 1.0f, -1.0f), float3( 1.0f, 1.0f, -1.0f),
|
||||
float3(-1.0f, -1.0f, -1.0f), float3( 1.0f, 1.0f, -1.0f), float3( 1.0f, -1.0f, -1.0f),
|
||||
// +Z
|
||||
float3(-1.0f, -1.0f, 1.0f), float3( 1.0f, 1.0f, 1.0f), float3(-1.0f, 1.0f, 1.0f),
|
||||
float3(-1.0f, -1.0f, 1.0f), float3( 1.0f, -1.0f, 1.0f), float3( 1.0f, 1.0f, 1.0f),
|
||||
// -X
|
||||
float3(-1.0f, -1.0f, -1.0f), float3(-1.0f, -1.0f, 1.0f), float3(-1.0f, 1.0f, 1.0f),
|
||||
float3(-1.0f, -1.0f, -1.0f), float3(-1.0f, 1.0f, 1.0f), float3(-1.0f, 1.0f, -1.0f),
|
||||
// +X
|
||||
float3( 1.0f, -1.0f, -1.0f), float3( 1.0f, 1.0f, 1.0f), float3( 1.0f, -1.0f, 1.0f),
|
||||
float3( 1.0f, -1.0f, -1.0f), float3( 1.0f, 1.0f, -1.0f), float3( 1.0f, 1.0f, 1.0f),
|
||||
// -Y
|
||||
float3(-1.0f, -1.0f, -1.0f), float3( 1.0f, -1.0f, 1.0f), float3(-1.0f, -1.0f, 1.0f),
|
||||
float3(-1.0f, -1.0f, -1.0f), float3( 1.0f, -1.0f, -1.0f), float3( 1.0f, -1.0f, 1.0f),
|
||||
// +Y
|
||||
float3(-1.0f, 1.0f, -1.0f), float3(-1.0f, 1.0f, 1.0f), float3( 1.0f, 1.0f, 1.0f),
|
||||
float3(-1.0f, 1.0f, -1.0f), float3( 1.0f, 1.0f, 1.0f), float3( 1.0f, 1.0f, -1.0f)
|
||||
};
|
||||
|
||||
struct VSOutput {
|
||||
float4 positionCS : SV_POSITION;
|
||||
float3 directionWS : TEXCOORD0;
|
||||
};
|
||||
|
||||
VSOutput VSMain(uint vertexId : SV_VertexID) {
|
||||
VSOutput output;
|
||||
|
||||
const float3 positionWS = gCubeVertices[vertexId];
|
||||
|
||||
// [修改] w=0 只应用相机旋转,不应用相机平移;天空盒因此始终以相机为中心。
|
||||
const float3 positionVS = mul(float4(positionWS, 0.0f), gView).xyz;
|
||||
float4 positionCS = mul(float4(positionVS, 1.0f), gProjection);
|
||||
|
||||
// [新增] 固定到远平面;配合 LESS_EQUAL 和深度不写入,仅覆盖背景像素。
|
||||
positionCS.z = positionCS.w;
|
||||
|
||||
output.positionCS = positionCS;
|
||||
output.directionWS = positionWS;
|
||||
return output;
|
||||
}
|
||||
|
||||
float3 RotateAroundY(float3 direction, float radians) {
|
||||
float sineValue;
|
||||
float cosineValue;
|
||||
sincos(radians, sineValue, cosineValue);
|
||||
|
||||
return float3(
|
||||
cosineValue * direction.x + sineValue * direction.z,
|
||||
direction.y,
|
||||
-sineValue * direction.x + cosineValue * direction.z);
|
||||
}
|
||||
|
||||
float4 PSMain(VSOutput input) : SV_TARGET {
|
||||
const float3 sampleDirection = RotateAroundY(
|
||||
normalize(input.directionWS), gIblParams.z);
|
||||
|
||||
float3 color = gSkyboxCube.SampleLevel(
|
||||
gSkyboxSampler, sampleDirection, 0.0f).rgb;
|
||||
// [修改] enabled 只控制物体 IBL;天空背景在 Cube 已加载时始终显示。
|
||||
color *= max(gIblParams.x, 0.0f);
|
||||
|
||||
// 与 PBR 物体使用相同曝光、色调映射和显示编码。
|
||||
color *= max(gRenderParams.x, 0.0f);
|
||||
color = ToneMapACES(color);
|
||||
color = pow(max(color, 0.0f), 1.0f / 2.2f);
|
||||
return float4(color, 1.0f);
|
||||
}
|
||||
Reference in New Issue
Block a user