add Preetham sky; add point light and spot light;

This commit is contained in:
2026-03-16 01:44:23 +08:00
parent 3f56a6878d
commit 28d9a7dfb6
18 changed files with 723 additions and 129 deletions

View File

@ -75,11 +75,30 @@ const float Epsilon = 0.00001;
const int LightCount = 1;
const vec3 Fdielectric = vec3(0.04);
struct DirectionalLight
{
struct DirectionalLight {
vec3 Direction;
vec3 Radiance;
float Multiplier;
float Intensity;
bool CastShadows;
};
struct PointLight {
vec3 Position;
vec3 Radiance;
float Intensity;
float Range;
bool CastShadows;
};
struct SpotLight {
vec3 Position;
vec3 Direction;
vec3 Radiance;
float Intensity;
float Range;
float InnerConeCos;
float OuterConeCos;
bool CastShadows;
};
in VertexOutput
@ -100,6 +119,13 @@ layout(location = 1) out vec4 o_BloomColor;
uniform DirectionalLight u_DirectionalLights;
uniform vec3 u_CameraPosition;
uniform int u_PointLightCount;
uniform PointLight u_PointLights;
uniform int u_SpotLightCount;
uniform SpotLight u_SpotLights;
// PBR
uniform sampler2D u_AlbedoTexture;
uniform sampler2D u_NormalTexture;
@ -185,13 +211,104 @@ vec3 fresnelSchlickRoughness(vec3 F0, float cosTheta, float roughness)
}
// ---------- direction light ----------
vec3 ComputeDirectionalLight(DirectionalLight light, vec3 F0, PBRParameters params)
{
vec3 L = normalize(-light.Direction);
vec3 Lradiance = light.Radiance * light.Intensity;
vec3 Lh = normalize(L + params.View);
float cosLi = max(0.0, dot(params.Normal, L));
float cosLh = max(0.0, dot(params.Normal, Lh));
vec3 F = fresnelSchlick(F0, max(0.0, dot(Lh, params.View)));
float D = ndfGGX(cosLh, params.Roughness);
float G = GeometrySmith(params.Normal, params.View, L, params.Roughness);
vec3 kd = (1.0 - F) * (1.0 - params.Metalness);
vec3 diffuseBRDF = kd * params.Albedo;
vec3 specularBRDF = (F * D * G) / max(Epsilon, 4.0 * cosLi * params.NdotV);
return (diffuseBRDF + specularBRDF) * Lradiance * cosLi;
}
vec3 ComputePointLight(PointLight light, vec3 F0, PBRParameters params, vec3 worldPos)
{
vec3 lightVec = light.Position - worldPos;
float dist = length(lightVec);
if (dist > light.Range) return vec3(0.0);
vec3 L = lightVec / dist;
vec3 Lradiance = light.Radiance * light.Intensity;
// 距离衰减:通常使用平方衰减,但为避免分母为零,加一个小值
float attenuation = 1.0 / (dist * dist + 0.0001);
// 可选:范围平滑衰减
float rangeFactor = clamp(1.0 - (dist / light.Range), 0.0, 1.0);
rangeFactor = rangeFactor * rangeFactor; // 平滑
attenuation *= rangeFactor;
vec3 Lh = normalize(L + params.View);
float cosLi = max(0.0, dot(params.Normal, L));
float cosLh = max(0.0, dot(params.Normal, Lh));
vec3 F = fresnelSchlick(F0, max(0.0, dot(Lh, params.View)));
float D = ndfGGX(cosLh, params.Roughness);
float G = GeometrySmith(params.Normal, params.View, L, params.Roughness);
vec3 kd = (1.0 - F) * (1.0 - params.Metalness);
vec3 diffuseBRDF = kd * params.Albedo;
vec3 specularBRDF = (F * D * G) / max(Epsilon, 4.0 * cosLi * params.NdotV);
return (diffuseBRDF + specularBRDF) * Lradiance * cosLi * attenuation;
}
vec3 ComputeSpotLight(SpotLight light, vec3 F0, PBRParameters params, vec3 worldPos)
{
vec3 lightVec = light.Position - worldPos;
float dist = length(lightVec);
if (dist > light.Range) return vec3(0.0);
vec3 L = lightVec / dist;
vec3 Lradiance = light.Radiance * light.Intensity;
// 距离衰减
float attenuation = 1.0 / (dist * dist + 0.0001);
float rangeFactor = clamp(1.0 - (dist / light.Range), 0.0, 1.0);
rangeFactor = rangeFactor * rangeFactor;
attenuation *= rangeFactor;
// 角度衰减(聚光锥)
float cosAngle = dot(-L, normalize(light.Direction)); // 光方向指向外,所以用 -L
if (cosAngle < light.OuterConeCos) return vec3(0.0);
float angleFalloff = (cosAngle - light.OuterConeCos) / (light.InnerConeCos - light.OuterConeCos);
angleFalloff = clamp(angleFalloff, 0.0, 1.0);
attenuation *= angleFalloff;
vec3 Lh = normalize(L + params.View);
float cosLi = max(0.0, dot(params.Normal, L));
float cosLh = max(0.0, dot(params.Normal, Lh));
vec3 F = fresnelSchlick(F0, max(0.0, dot(Lh, params.View)));
float D = ndfGGX(cosLh, params.Roughness);
float G = GeometrySmith(params.Normal, params.View, L, params.Roughness);
vec3 kd = (1.0 - F) * (1.0 - params.Metalness);
vec3 diffuseBRDF = kd * params.Albedo;
vec3 specularBRDF = (F * D * G) / max(Epsilon, 4.0 * cosLi * params.NdotV);
return (diffuseBRDF + specularBRDF) * Lradiance * cosLi * attenuation;
}
vec3 Lighting(vec3 F0)
{
vec3 result = vec3(0.0);
for(int i = 0; i < LightCount; i++)
{
vec3 Li = u_DirectionalLights.Direction;
vec3 Lradiance = u_DirectionalLights.Radiance * u_DirectionalLights.Multiplier;
vec3 Lradiance = u_DirectionalLights.Radiance * u_DirectionalLights.Intensity;
vec3 Lh = normalize(Li + m_Params.View);
float cosLi = max(0.0, dot(m_Params.Normal, Li));
@ -216,9 +333,9 @@ vec3 RotateVectorAboutY(float angle, vec3 vec)
{
angle = radians(angle);
mat3 rotationMatrix = mat3(
vec3(cos(angle), 0.0, sin(angle)),
vec3(0.0, 1.0, 0.0),
vec3(-sin(angle), 0.0, cos(angle))
vec3(cos(angle), 0.0, sin(angle)),
vec3(0.0, 1.0, 0.0),
vec3(-sin(angle), 0.0, cos(angle))
);
return rotationMatrix * vec;
}
@ -232,9 +349,9 @@ vec3 IBL(vec3 F0, vec3 Lr)
int u_EnvRadianceTexLevels = textureQueryLevels(u_EnvRadianceTex);
vec3 specularIrradiance = textureLod(
u_EnvRadianceTex,
RotateVectorAboutY(u_EnvMapRotation, Lr),
m_Params.Roughness * u_EnvRadianceTexLevels
u_EnvRadianceTex,
RotateVectorAboutY(u_EnvMapRotation, Lr),
m_Params.Roughness * u_EnvRadianceTexLevels
).rgb;
vec2 specularBRDF = texture(u_BRDFLUTTexture, vec2(m_Params.NdotV, 1.0 - m_Params.Roughness)).rg;
@ -334,8 +451,15 @@ void main()
shadowFactor = 1.0 - shadow;
}
// directional light with shadow
vec3 lightContribution = u_DirectionalLights.Multiplier > 0.0 ? Lighting(F0) * shadowFactor : vec3(0.0);
vec3 lightContribution = u_DirectionalLights.Intensity > 0.0 ? Lighting(F0) * shadowFactor : vec3(0.0);
if(u_PointLightCount > 0)
lightContribution += ComputePointLight(u_PointLights, F0, m_Params, vs_Input.WorldPosition);
if(u_SpotLightCount > 0)
lightContribution += ComputeSpotLight(u_SpotLights, F0, m_Params, vs_Input.WorldPosition);
vec3 iblContribution = IBL(F0, Lr) * u_IBLContribution;

View File

@ -61,13 +61,33 @@ const float Epsilon = 0.00001;
const int LightCount = 1;
const vec3 Fdielectric = vec3(0.04);
struct DirectionalLight
{
struct DirectionalLight {
vec3 Direction;
vec3 Radiance;
float Multiplier;
float Intensity;
bool CastShadows;
};
struct PointLight {
vec3 Position;
vec3 Radiance;
float Intensity;
float Range;
bool CastShadows;
};
struct SpotLight {
vec3 Position;
vec3 Direction;
vec3 Radiance;
float Intensity;
float Range;
float InnerConeCos;
float OuterConeCos;
bool CastShadows;
};
in VertexOutput
{
vec3 WorldPosition;
@ -84,6 +104,14 @@ layout(location = 0) out vec4 color;
layout(location = 1) out vec4 o_BloomColor;
uniform DirectionalLight u_DirectionalLights;
uniform int u_PointLightCount;
uniform PointLight u_PointLights;
uniform int u_SpotLightCount;
uniform SpotLight u_SpotLights;
uniform vec3 u_CameraPosition;
// PBR
@ -171,13 +199,102 @@ vec3 fresnelSchlickRoughness(vec3 F0, float cosTheta, float roughness)
}
// ---------- direction light ----------
vec3 ComputeDirectionalLight(DirectionalLight light, vec3 F0, PBRParameters params)
{
vec3 L = normalize(-light.Direction);
vec3 Lradiance = light.Radiance * light.Intensity;
vec3 Lh = normalize(L + params.View);
float cosLi = max(0.0, dot(params.Normal, L));
float cosLh = max(0.0, dot(params.Normal, Lh));
vec3 F = fresnelSchlick(F0, max(0.0, dot(Lh, params.View)));
float D = ndfGGX(cosLh, params.Roughness);
float G = GeometrySmith(params.Normal, params.View, L, params.Roughness);
vec3 kd = (1.0 - F) * (1.0 - params.Metalness);
vec3 diffuseBRDF = kd * params.Albedo;
vec3 specularBRDF = (F * D * G) / max(Epsilon, 4.0 * cosLi * params.NdotV);
return (diffuseBRDF + specularBRDF) * Lradiance * cosLi;
}
vec3 ComputePointLight(PointLight light, vec3 F0, PBRParameters params, vec3 worldPos)
{
vec3 lightVec = light.Position - worldPos;
float dist = length(lightVec);
if (dist > light.Range) return vec3(0.0);
vec3 L = lightVec / dist;
vec3 Lradiance = light.Radiance * light.Intensity;
// 距离衰减:通常使用平方衰减,但为避免分母为零,加一个小值
float attenuation = 1.0 / (dist * dist + 0.0001);
// 可选:范围平滑衰减
float rangeFactor = clamp(1.0 - (dist / light.Range), 0.0, 1.0);
rangeFactor = rangeFactor * rangeFactor; // 平滑
attenuation *= rangeFactor;
vec3 Lh = normalize(L + params.View);
float cosLi = max(0.0, dot(params.Normal, L));
float cosLh = max(0.0, dot(params.Normal, Lh));
vec3 F = fresnelSchlick(F0, max(0.0, dot(Lh, params.View)));
float D = ndfGGX(cosLh, params.Roughness);
float G = GeometrySmith(params.Normal, params.View, L, params.Roughness);
vec3 kd = (1.0 - F) * (1.0 - params.Metalness);
vec3 diffuseBRDF = kd * params.Albedo;
vec3 specularBRDF = (F * D * G) / max(Epsilon, 4.0 * cosLi * params.NdotV);
return (diffuseBRDF + specularBRDF) * Lradiance * cosLi * attenuation;
}
vec3 ComputeSpotLight(SpotLight light, vec3 F0, PBRParameters params, vec3 worldPos)
{
vec3 lightVec = light.Position - worldPos;
float dist = length(lightVec);
if (dist > light.Range) return vec3(0.0);
vec3 L = lightVec / dist;
vec3 Lradiance = light.Radiance * light.Intensity;
// 距离衰减
float attenuation = 1.0 / (dist * dist + 0.0001);
float rangeFactor = clamp(1.0 - (dist / light.Range), 0.0, 1.0);
rangeFactor = rangeFactor * rangeFactor;
attenuation *= rangeFactor;
// 角度衰减(聚光锥)
float cosAngle = dot(-L, normalize(light.Direction)); // 光方向指向外,所以用 -L
if (cosAngle < light.OuterConeCos) return vec3(0.0);
float angleFalloff = (cosAngle - light.OuterConeCos) / (light.InnerConeCos - light.OuterConeCos);
angleFalloff = clamp(angleFalloff, 0.0, 1.0);
attenuation *= angleFalloff;
vec3 Lh = normalize(L + params.View);
float cosLi = max(0.0, dot(params.Normal, L));
float cosLh = max(0.0, dot(params.Normal, Lh));
vec3 F = fresnelSchlick(F0, max(0.0, dot(Lh, params.View)));
float D = ndfGGX(cosLh, params.Roughness);
float G = GeometrySmith(params.Normal, params.View, L, params.Roughness);
vec3 kd = (1.0 - F) * (1.0 - params.Metalness);
vec3 diffuseBRDF = kd * params.Albedo;
vec3 specularBRDF = (F * D * G) / max(Epsilon, 4.0 * cosLi * params.NdotV);
return (diffuseBRDF + specularBRDF) * Lradiance * cosLi * attenuation;
}
vec3 Lighting(vec3 F0)
{
vec3 result = vec3(0.0);
for(int i = 0; i < LightCount; i++)
{
vec3 Li = u_DirectionalLights.Direction;
vec3 Lradiance = u_DirectionalLights.Radiance * u_DirectionalLights.Multiplier;
vec3 Lradiance = u_DirectionalLights.Radiance * u_DirectionalLights.Intensity;
vec3 Lh = normalize(Li + m_Params.View);
float cosLi = max(0.0, dot(m_Params.Normal, Li));
@ -202,9 +319,9 @@ vec3 RotateVectorAboutY(float angle, vec3 vec)
{
angle = radians(angle);
mat3 rotationMatrix = mat3(
vec3(cos(angle), 0.0, sin(angle)),
vec3(0.0, 1.0, 0.0),
vec3(-sin(angle), 0.0, cos(angle))
vec3(cos(angle), 0.0, sin(angle)),
vec3(0.0, 1.0, 0.0),
vec3(-sin(angle), 0.0, cos(angle))
);
return rotationMatrix * vec;
}
@ -218,9 +335,9 @@ vec3 IBL(vec3 F0, vec3 Lr)
int u_EnvRadianceTexLevels = textureQueryLevels(u_EnvRadianceTex);
vec3 specularIrradiance = textureLod(
u_EnvRadianceTex,
RotateVectorAboutY(u_EnvMapRotation, Lr),
m_Params.Roughness * u_EnvRadianceTexLevels
u_EnvRadianceTex,
RotateVectorAboutY(u_EnvMapRotation, Lr),
m_Params.Roughness * u_EnvRadianceTexLevels
).rgb;
vec2 specularBRDF = texture(u_BRDFLUTTexture, vec2(m_Params.NdotV, 1.0 - m_Params.Roughness)).rg;
@ -314,6 +431,7 @@ void main()
vec3 F0 = mix(Fdielectric, m_Params.Albedo, m_Params.Metalness);
// Shadow
float shadowFactor = 1.0;
if (u_ShadowEnabled > 0.5) {
float shadow = calculateShadow(vs_Input.FragPosLightSpace, m_Params.Normal, u_DirectionalLights.Direction);
@ -321,7 +439,13 @@ void main()
}
// directional light with with shadow
vec3 lightContribution = u_DirectionalLights.Multiplier > 0.0 ? Lighting(F0) * shadowFactor : vec3(0.0);
vec3 lightContribution = u_DirectionalLights.Intensity > 0.0 ? Lighting(F0) * shadowFactor : vec3(0.0);
if(u_PointLightCount > 0)
lightContribution += ComputePointLight(u_PointLights, F0, m_Params, vs_Input.WorldPosition);
if(u_SpotLightCount > 0)
lightContribution += ComputeSpotLight(u_SpotLights, F0, m_Params, vs_Input.WorldPosition);
vec3 iblContribution = IBL(F0, Lr) * u_IBLContribution;

View File

@ -0,0 +1,135 @@
#type compute
#version 450 core
const float PI = 3.141592;
layout(binding = 0, rgba32f) restrict writeonly uniform imageCube o_CubeMap;
uniform vec3 u_TurbidityAzimuthInclination;
vec3 GetCubeMapTexCoord()
{
vec2 st = gl_GlobalInvocationID.xy / vec2(imageSize(o_CubeMap));
vec2 uv = 2.0 * vec2(st.x, 1.0 - st.y) - vec2(1.0);
vec3 ret;
if (gl_GlobalInvocationID.z == 0) ret = vec3( 1.0, uv.y, -uv.x);
else if (gl_GlobalInvocationID.z == 1) ret = vec3( -1.0, uv.y, uv.x);
else if (gl_GlobalInvocationID.z == 2) ret = vec3( uv.x, 1.0, -uv.y);
else if (gl_GlobalInvocationID.z == 3) ret = vec3( uv.x, -1.0, uv.y);
else if (gl_GlobalInvocationID.z == 4) ret = vec3( uv.x, uv.y, 1.0);
else if (gl_GlobalInvocationID.z == 5) ret = vec3(-uv.x, uv.y, -1.0);
return normalize(ret);
}
#define PI 3.14159265359
float saturatedDot( in vec3 a, in vec3 b )
{
return max( dot( a, b ), 0.0 );
}
vec3 YxyToXYZ( in vec3 Yxy )
{
float Y = Yxy.r;
float x = Yxy.g;
float y = Yxy.b;
float X = x * ( Y / y );
float Z = ( 1.0 - x - y ) * ( Y / y );
return vec3(X,Y,Z);
}
vec3 XYZToRGB( in vec3 XYZ )
{
// CIE/E
mat3 M = mat3
(
2.3706743, -0.9000405, -0.4706338,
-0.5138850, 1.4253036, 0.0885814,
0.0052982, -0.0146949, 1.0093968
);
return XYZ * M;
}
vec3 YxyToRGB( in vec3 Yxy )
{
vec3 XYZ = YxyToXYZ( Yxy );
vec3 RGB = XYZToRGB( XYZ );
return RGB;
}
void calculatePerezDistribution( in float t, out vec3 A, out vec3 B, out vec3 C, out vec3 D, out vec3 E )
{
A = vec3( 0.1787 * t - 1.4630, -0.0193 * t - 0.2592, -0.0167 * t - 0.2608 );
B = vec3( -0.3554 * t + 0.4275, -0.0665 * t + 0.0008, -0.0950 * t + 0.0092 );
C = vec3( -0.0227 * t + 5.3251, -0.0004 * t + 0.2125, -0.0079 * t + 0.2102 );
D = vec3( 0.1206 * t - 2.5771, -0.0641 * t - 0.8989, -0.0441 * t - 1.6537 );
E = vec3( -0.0670 * t + 0.3703, -0.0033 * t + 0.0452, -0.0109 * t + 0.0529 );
}
vec3 calculateZenithLuminanceYxy( in float t, in float thetaS )
{
float chi = ( 4.0 / 9.0 - t / 120.0 ) * ( PI - 2.0 * thetaS );
float Yz = ( 4.0453 * t - 4.9710 ) * tan( chi ) - 0.2155 * t + 2.4192;
float theta2 = thetaS * thetaS;
float theta3 = theta2 * thetaS;
float T = t;
float T2 = t * t;
float xz =
( 0.00165 * theta3 - 0.00375 * theta2 + 0.00209 * thetaS + 0.0) * T2 +
(-0.02903 * theta3 + 0.06377 * theta2 - 0.03202 * thetaS + 0.00394) * T +
( 0.11693 * theta3 - 0.21196 * theta2 + 0.06052 * thetaS + 0.25886);
float yz =
( 0.00275 * theta3 - 0.00610 * theta2 + 0.00317 * thetaS + 0.0) * T2 +
(-0.04214 * theta3 + 0.08970 * theta2 - 0.04153 * thetaS + 0.00516) * T +
( 0.15346 * theta3 - 0.26756 * theta2 + 0.06670 * thetaS + 0.26688);
return vec3( Yz, xz, yz );
}
vec3 calculatePerezLuminanceYxy( in float theta, in float gamma, in vec3 A, in vec3 B, in vec3 C, in vec3 D, in vec3 E )
{
return ( 1.0 + A * exp( B / cos( theta ) ) ) * ( 1.0 + C * exp( D * gamma ) + E * cos( gamma ) * cos( gamma ) );
}
vec3 calculateSkyLuminanceRGB( in vec3 s, in vec3 e, in float t )
{
vec3 A, B, C, D, E;
calculatePerezDistribution( t, A, B, C, D, E );
float thetaS = acos( saturatedDot( s, vec3(0,1,0) ) );
float thetaE = acos( saturatedDot( e, vec3(0,1,0) ) );
float gammaE = acos( saturatedDot( s, e ) );
vec3 Yz = calculateZenithLuminanceYxy( t, thetaS );
vec3 fThetaGamma = calculatePerezLuminanceYxy( thetaE, gammaE, A, B, C, D, E );
vec3 fZeroThetaS = calculatePerezLuminanceYxy( 0.0, thetaS, A, B, C, D, E );
vec3 Yp = Yz * ( fThetaGamma / fZeroThetaS );
return YxyToRGB( Yp );
}
layout(local_size_x = 32, local_size_y = 32, local_size_z = 1) in;
void main()
{
vec3 cubeTC = GetCubeMapTexCoord();
float turbidity = u_TurbidityAzimuthInclination.x;
float azimuth = u_TurbidityAzimuthInclination.y;;
float inclination = u_TurbidityAzimuthInclination.z;
vec3 sunDir = normalize( vec3( sin(inclination) * cos(azimuth), cos(inclination), sin(inclination) * sin(azimuth) ) );
vec3 viewDir = cubeTC;
vec3 skyLuminance = calculateSkyLuminanceRGB( sunDir, viewDir, turbidity );
vec4 color = vec4(skyLuminance * 0.05, 1.0);
imageStore(o_CubeMap, ivec3(gl_GlobalInvocationID), color);
}

View File

@ -5,12 +5,12 @@
layout(location = 0) in vec3 a_Position;
uniform mat4 u_ViewProjection;
uniform mat4 u_LightViewProjection;
uniform mat4 u_Transform;
void main()
{
gl_Position = u_ViewProjection * u_Transform * vec4(a_Position, 1.0);
gl_Position = u_LightViewProjection * u_Transform * vec4(a_Position, 1.0);
}
#type fragment

View File

@ -8,7 +8,7 @@ layout(location = 0) in vec3 a_Position;
layout(location = 5) in ivec4 a_BoneIndices;
layout(location = 6) in vec4 a_BoneWeights;
uniform mat4 u_ViewProjection;
uniform mat4 u_LightViewProjection;
uniform mat4 u_Transform;
const int MAX_BONES = 100;
@ -22,7 +22,7 @@ void main()
boneTransform += u_BoneTransforms[a_BoneIndices[3]] * a_BoneWeights[3];
vec4 localPosition = boneTransform * vec4(a_Position, 1.0);
gl_Position = u_ViewProjection * u_Transform * localPosition;
gl_Position = u_LightViewProjection * u_Transform * localPosition;
}
#type fragment