diff --git a/shaders/lang/zh_CN.lang b/shaders/lang/zh_CN.lang index 0d6a0db8..8734f9f2 100644 --- a/shaders/lang/zh_CN.lang +++ b/shaders/lang/zh_CN.lang @@ -228,10 +228,22 @@ suffix.sunPathRotation=° option.PCSS_SEARCH_SAMPLES=PCSS检索采样数 option.PCSS_FILTER_SAMPLES=PCSS过滤采样数 option.COLORED_SHADOWS=彩色阴影 +option.SHADOW_SOFT_TYPE=阴影过滤 +value.SHADOW_SOFT_TYPE.0=不过滤 +value.SHADOW_SOFT_TYPE.1=PCF过滤(较慢) +value.SHADOW_SOFT_TYPE.2=PCSS过滤(慢) +option.PCF_SOFT_RADIUS=PCF模糊半径 +option.SHADOW_BIAS_STRENGTH=阴影偏移 +option.SHADOW_BIAS_STRENGTH.comment=如果表面出现阴影错误请提高此数值 +option.SHADOW_CONTRAST_STRENGTH=PCF的次表面散射补偿 + + + + option.SHADOW_BACKFACE_CULLING=阴影背面剔除 option.SHADOW_DISTORTION_STRENGTH=阴影形变强度 - +option.SHADOW_DISTORTION=阴影扭曲开关 option.SCREEN_SPACE_SHADOWS=屏幕空间阴影 option.SCREEN_SPACE_SHADOWS_SAMPLES=屏幕空间阴影采样数 @@ -307,8 +319,8 @@ option.SUBSURFACE_SCATTERING_STRENGTH=次表面散射强度 option.SUBSURFACE_SCATTERING_BRIGHTNESS=次表面散射亮度 option.SUBSURFACE_SCATTERING_RADIUS=次表面散射半径 -option.SSRT_MAX_SAMPLES=光线追踪采样数 -option.SSRT_REFINEMENT=光线追踪重采样 +option.SSRT_MAX_SAMPLES=反射采样数 +option.SSRT_REFINEMENT=反射重采样 option.SSRT_REFINEMENT_STEPS=重采样精度 option.SSRT_SKY_TRACING=追踪天空 @@ -321,6 +333,20 @@ option.REFLECTION_FILTER=反射过滤 option.RAYTRACED_REFRACTION=光线追踪折射 option.REFRACTION_STRENGTH=折射强度 +option.REFLECTION_SCREEN_SPACE=屏幕空间反射 +option.REFLECTION_SCREEN_SPACE.comment=方块的屏幕空间反射开关,关闭可以在保留质感的情况下提升性能 +option.REFLECTION_SKY=天空反射 +option.REFLECTION_SKY.comment=控制方块是否反射天空,以防有的人不喜欢 +option.LIGHT_REFLECTION_SAMPLES=反射最高采样数 +option.LIGHT_REFLECTION_SAMPLES.comment=屏幕空间反射和光线追踪反射的采样数都不会高于本选项的数值 +option.FRESNEL_EXPONENT=菲涅尔透镜亮度 +option.FRESNEL_EXPONENT.comment=关闭屏幕空间反射时的金属反光效果亮度 +option.FRESNEL_STRENGTH=菲涅尔透镜强度 +option.FRESNEL_STRENGTH.comment=关闭屏幕空间反射时的金属反光效果强度 +option.REF_MIN=菲涅尔透镜最小亮度 +option.REF_MIN.comment=关闭屏幕空间反射时的金属反光效果最小亮度 +option.REF_MAX=菲涅尔透镜最大亮度 +option.REF_MAX.comment=关闭屏幕空间反射时的金属反光效果亮度 # Transparent option.WATER_PARALLAX=水体视差 option.WATER_CAUSTICS=水体焦散 diff --git a/shaders/lib/SpatialUpscale.glsl b/shaders/lib/SpatialUpscale.glsl index 87ce7f63..796a84c5 100644 --- a/shaders/lib/SpatialUpscale.glsl +++ b/shaders/lib/SpatialUpscale.glsl @@ -1,63 +1,103 @@ +//================================================================================================// +// SVGF Upscale Diffuse Indirect (with quality & blur radius control) +//================================================================================================// #if defined PASS_DEFERRED_LIGHTING #if defined SSILVB_ENABLED && defined SVGF_ENABLED - vec3 UpscaleDiffuseIndirect(in ivec2 texelPos, in vec3 worldNormal, in float viewDistance, in float NdotV) { - // ivec2 randTexel = ivec2(vec2(texelPos >> 1) + BlueNoise(texelPos, frameCounter + 3)); - texelPos >>= 1; - - vec3 sum = texelFetch(colortex3, texelPos, 0).rgb; - float sumWeight = 1.0; - - float sigmaZ = -4.0 * NdotV; - - ivec2 texelEnd = ivec2(halfViewEnd) - 1; - - for (uint i = 0u; i < 8u; ++i) { - ivec2 sampleTexel = clamp(texelPos + offset3x3N[i], ivec2(1), texelEnd); - - vec3 sampleAux = texelFetch(colortex14, sampleTexel, 0).rgb; - - float weight = pow16(saturate(dot(OctDecodeSnorm(sampleAux.xy), worldNormal))); - weight *= exp2(distance(sampleAux.z, viewDistance) * sigmaZ); - vec3 sampleLight = texelFetch(colortex3, sampleTexel, 0).rgb; - - sum += sampleLight * weight; - sumWeight += weight; - } - - return sum * rcp(sumWeight); - } +// ========== 可调节参数 ========== +#ifndef SVGF_QUALITY + #define SVGF_QUALITY 0 // [0 1 2] // 0=仅自身, 1=十字形(4样本), 2=完整3x3(8样本) +#endif +#ifndef SVGF_BLUR_RADIUS + #define SVGF_BLUR_RADIUS 15.0 // [1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 1.0 15.0 20.0 25.0 30.0 50.0] 采样半径倍数(1.0=原始相邻像素,>1.0扩大模糊范围) +#endif +// ================================ + +vec3 UpscaleDiffuseIndirect(in ivec2 texelPos, in vec3 worldNormal, in float viewDistance, in float NdotV) { + texelPos >>= 1; + ivec2 texelEnd = ivec2(halfViewEnd) - 1; + + vec3 sum = texelFetch(colortex3, texelPos, 0).rgb; + float sumWeight = 1.0; + + #if SVGF_QUALITY == 0 + return sum; + #endif + + float sigmaZ = -4.0 * NdotV; + float depthCenter = viewDistance; + + // 计算缩放后的偏移量(模糊半径) + float radius = max(1.0, SVGF_BLUR_RADIUS); + ivec2 offsetScale = ivec2(round(radius)); + + #if SVGF_QUALITY == 1 + // 十字形采样(4个方向) + const ivec2 offsets[4] = ivec2[](ivec2(1,0), ivec2(-1,0), ivec2(0,1), ivec2(0,-1)); + for (int i = 0; i < 4; ++i) { + ivec2 sampleTexel = clamp(texelPos + offsets[i] * offsetScale, ivec2(1), texelEnd); + vec3 sampleAux = texelFetch(colortex14, sampleTexel, 0).rgb; + float ndot = saturate(dot(OctDecodeSnorm(sampleAux.xy), worldNormal)); + float weight = ndot * ndot; // 平方代替 pow16 + float depthDiff = abs(sampleAux.z - depthCenter); + weight *= exp2(depthDiff * sigmaZ); + if (weight < 0.001) continue; + vec3 sampleLight = texelFetch(colortex3, sampleTexel, 0).rgb; + sum += sampleLight * weight; + sumWeight += weight; + } + #else + // 完整3x3采样(8个邻居) + for (uint i = 0u; i < 8u; ++i) { + ivec2 sampleTexel = clamp(texelPos + offset3x3N[i] * offsetScale, ivec2(1), texelEnd); + vec3 sampleAux = texelFetch(colortex14, sampleTexel, 0).rgb; + float ndot = saturate(dot(OctDecodeSnorm(sampleAux.xy), worldNormal)); + float weight = ndot * ndot; + weight = weight * weight; // ndot^4 (接近原始pow16) + float depthDiff = abs(sampleAux.z - depthCenter); + weight *= exp2(depthDiff * sigmaZ); + if (weight < 0.001) continue; + vec3 sampleLight = texelFetch(colortex3, sampleTexel, 0).rgb; + sum += sampleLight * weight; + sumWeight += weight; + } + #endif + + return sum * rcp(sumWeight); +} #endif #endif //================================================================================================// - +// Volumetric Fog Upscale (original logic, unchanged) +//================================================================================================// #if defined PASS_COMPOSITE #if defined VOLUMETRIC_FOG || defined UW_VOLUMETRIC_FOG - mat2x3 UnpackFogData(in uvec2 data) { - return mat2x3(DecodeRGBE8U(data.x), DecodeRGBE8U(data.y)); - } - mat2x3 UpscaleVolumetricFog(in ivec2 texelPos, in float linearDepth) { - ivec2 randTexel = ivec2(vec2(texelPos >> 1) + BlueNoise(texelPos, frameCounter + 7)); - float sigmaZ = -64.0 / linearDepth; +mat2x3 UnpackFogData(in uvec2 data) { + return mat2x3(DecodeRGBE8U(data.x), DecodeRGBE8U(data.y)); +} + +mat2x3 UpscaleVolumetricFog(in ivec2 texelPos, in float linearDepth) { + ivec2 randTexel = ivec2(vec2(texelPos >> 1) + BlueNoise(texelPos, frameCounter + 7)); + float sigmaZ = -64.0 / linearDepth; - mat2x3 sum = UnpackFogData(texelFetch(colortex11, randTexel, 0).xy); - float sumWeight = 1.0; + mat2x3 sum = UnpackFogData(texelFetch(colortex11, randTexel, 0).xy); + float sumWeight = 1.0; - for (uint i = 0u; i < 8u; ++i) { - ivec2 sampleTexel = randTexel + offset3x3N[i]; - uvec3 sampleFogData = texelFetch(colortex11, sampleTexel, 0).xyz; + for (uint i = 0u; i < 8u; ++i) { + ivec2 sampleTexel = randTexel + offset3x3N[i]; + uvec3 sampleFogData = texelFetch(colortex11, sampleTexel, 0).xyz; - float sampleDepth = uintBitsToFloat(sampleFogData.z); - float weight = exp2(abs(sampleDepth - linearDepth) * sigmaZ); + float sampleDepth = uintBitsToFloat(sampleFogData.z); + float weight = exp2(abs(sampleDepth - linearDepth) * sigmaZ); - sum += UnpackFogData(sampleFogData.xy) * weight; - sumWeight += weight; - } + sum += UnpackFogData(sampleFogData.xy) * weight; + sumWeight += weight; + } - sum *= rcp(sumWeight); - return sum; - } + sum *= rcp(sumWeight); + return sum; +} #endif #endif \ No newline at end of file diff --git a/shaders/lib/atmosphere/AtmosphericFog.glsl b/shaders/lib/atmosphere/AtmosphericFog.glsl index 66a77a3b..355b05ff 100644 --- a/shaders/lib/atmosphere/AtmosphericFog.glsl +++ b/shaders/lib/atmosphere/AtmosphericFog.glsl @@ -1,3 +1,7 @@ +// 玩家可调参数:体积雾阴影质量 [1, 2, 3] +// 1: 性能模式(隔步采样阴影) | 2: 默认平衡 | 3: 极致(每步采样,原版画质) +#define VF_SHADOW_QUALITY 1 // [1 2 3] + #include "/lib/atmosphere/Rainbow.glsl" #include "/lib/atmosphere/clouds/Shadows.glsl" @@ -11,195 +15,183 @@ uniform float biomeGreenVapor; const vec2 falloffScale = -1.0 / vec2(32.0, 12.0); vec2 CalculateFogDensity(in vec3 rayPos, in float uniformFog) { - rayPos += cameraPosition; + rayPos += cameraPosition; - // float rayLength = length(rayPos + vec3(0.0, planetRadius, 0.0)); - vec2 density = exp2(abs(rayPos.y - VF_HEIGHT) * oms(step(rayPos.y, VF_HEIGHT) * 0.5) * falloffScale); + vec2 density = exp2(abs(rayPos.y - VF_HEIGHT) * oms(step(rayPos.y, VF_HEIGHT) * 0.5) * falloffScale); #if VF_NOISE_QUALITY == LOW - rayPos.xz -= vec2(1.0, 0.75) * worldTimeCounter; - - float noise = texture(noisetex, rayPos.xz * 0.002).z; + rayPos.xz -= vec2(1.0, 0.75) * worldTimeCounter; + float noise = texture(noisetex, rayPos.xz * 0.002).z; #elif VF_NOISE_QUALITY == MEDIUM - vec3 windOffset = vec3(0.07, 0.04, 0.05) * worldTimeCounter; + vec3 windOffset = vec3(0.07, 0.04, 0.05) * worldTimeCounter; + rayPos *= 0.03; + rayPos -= windOffset; - rayPos *= 0.03; - rayPos -= windOffset; - - float noise = Pseudo3DNoise(rayPos) * 2.5; - noise -= Pseudo3DNoise(rayPos * 4.0 - windOffset); + float noise = Pseudo3DNoise(rayPos) * 2.5; + noise -= Pseudo3DNoise(rayPos * 4.0 - windOffset); #endif - density.y *= sqr(noise) * (2.0 + biomeSandstorm * 8.0 + biomeSnowstorm * 4.0); - density += uniformFog; + density.y *= sqr(noise) * (2.0 + biomeSandstorm * 8.0 + biomeSnowstorm * 4.0); + density += uniformFog; - return density * linearstep(cumulusTopAltitude, cumulusBottomAltitude, rayPos.y); + return density * linearstep(cumulusTopAltitude, cumulusBottomAltitude, rayPos.y); } //================================================================================================// #if !defined CLOUD_SHADOWS || defined PASS_SKY_MAP - #undef VF_CLOUD_SHADOWS + #undef VF_CLOUD_SHADOWS #endif mat2x3 RaymarchAtmosphericFog(in vec3 startPos, in vec3 endPos, in float dither, in bool skyMask, in uint steps) { - float rayLength = sdot(endPos - startPos); - float norm = inversesqrt(rayLength); - rayLength *= norm; - - vec3 worldDir = (endPos - startPos) * norm; - - // Adaptive step count - steps = min(steps, uint(float(steps) * 0.4 + rayLength * rcp(16.0))); - - float maxDist = min(lodRenderDist, 4096.0); // Limit to avoid visible noise - if (skyMask) { - // vec2 intersection = RaySphericalShellIntersection(atmosphereViewHeight, worldDir.y, planetRadius, cumulusTopRadius); - - // // Not intersecting the volume - // if (intersection.y < 0.0 || atmosphereViewHeight > cumulusBottomRadius) return mat2x3(vec3(0.0), vec3(1.0)); + float rayLength = sdot(endPos - startPos); + float norm = inversesqrt(rayLength); + rayLength *= norm; - rayLength = clamp((cumulusTopRadius - atmosphereViewHeight) / max0(worldDir.y), 0.0, maxDist); - } + vec3 worldDir = (endPos - startPos) * norm; - float rSteps = rcp(float(steps)); + // Adaptive step count + steps = min(steps, uint(float(steps) * 0.4 + rayLength * rcp(16.0))); - float stepLength = rayLength * rSteps; - vec3 rayStep = stepLength * worldDir; - vec3 rayPos = startPos + rayStep * dither; + float maxDist = min(lodRenderDist, 4096.0); + if (skyMask) { + rayLength = clamp((cumulusTopRadius - atmosphereViewHeight) / max0(worldDir.y), 0.0, maxDist); + } - vec3 shadowViewStart = transMAD(shadowModelView, startPos); - vec3 shadowStart = projMAD(shadowProjection, shadowViewStart); + float rSteps = rcp(float(steps)); + float stepLength = rayLength * rSteps; + vec3 rayStep = stepLength * worldDir; + vec3 rayPos = startPos + rayStep * dither; - vec3 shadowViewStep = mat3(shadowModelView) * rayStep; - vec3 shadowStep = diagonal3(shadowProjection) * shadowViewStep; - vec3 shadowPos = shadowStart + shadowStep * dither; + vec3 shadowViewStart = transMAD(shadowModelView, startPos); + vec3 shadowStart = projMAD(shadowProjection, shadowViewStart); - float LdotV = dot(worldLightDir, worldDir); - vec2 phase = AtmospherePhase(LdotV); + vec3 shadowViewStep = mat3(shadowModelView) * rayStep; + vec3 shadowStep = diagonal3(shadowProjection) * shadowViewStep; + vec3 shadowPos = shadowStart + shadowStep * dither; - float mieDensityMult = VF_MIE_DENSITY * 5e2 * (1.0 + wetness * VF_MIE_DENSITY_RAIN_MULT); + float LdotV = dot(worldLightDir, worldDir); + vec2 phase = AtmospherePhase(LdotV); - #ifdef VF_TIME_FADE - mieDensityMult *= max(wetness, 1.5 - approxSqrt(timeNoon) * 1.5 - timeSunset * 0.75 - timeMidnight * 0.5); - #endif - - vec3 fogMieExtinction = atmosphere.mieExtinction * mieDensityMult; - vec3 fogMieScattering = atmosphere.mieScattering * mieDensityMult; + float mieDensityMult = VF_MIE_DENSITY * 5e2 * (1.0 + wetness * VF_MIE_DENSITY_RAIN_MULT); + #ifdef VF_TIME_FADE + mieDensityMult *= max(wetness, 1.5 - approxSqrt(timeNoon) * 1.5 - timeSunset * 0.75 - timeMidnight * 0.5); + #endif - #ifdef PER_BIOME_FOG - vec3 biomeAlbedo = mix(vec3(1.0), vec3(1.1, 0.9, 0.7), biomeSandstorm); - biomeAlbedo = mix(biomeAlbedo, vec3(0.95, 1.1, 1.0), biomeGreenVapor); - fogMieScattering *= biomeAlbedo; - #endif + vec3 fogMieExtinction = atmosphere.mieExtinction * mieDensityMult; + vec3 fogMieScattering = atmosphere.mieScattering * mieDensityMult; - mat2x3 fogExtinctionCoeff = mat2x3( - atmosphere.rayleighScattering * (VF_RAYLEIGH_DENSITY * 16.0), - fogMieExtinction - ); + #ifdef PER_BIOME_FOG + vec3 biomeAlbedo = mix(vec3(1.0), vec3(1.1, 0.9, 0.7), biomeSandstorm); + biomeAlbedo = mix(biomeAlbedo, vec3(0.95, 1.1, 1.0), biomeGreenVapor); + fogMieScattering *= biomeAlbedo; + #endif - mat2x3 fogScatteringCoeff = mat2x3( - atmosphere.rayleighScattering * (VF_RAYLEIGH_DENSITY * 16.0), - fogMieScattering - ); + mat2x3 fogExtinctionCoeff = mat2x3(atmosphere.rayleighScattering * (VF_RAYLEIGH_DENSITY * 16.0), fogMieExtinction); + mat2x3 fogScatteringCoeff = mat2x3(atmosphere.rayleighScattering * (VF_RAYLEIGH_DENSITY * 16.0), fogMieScattering); - float uniformFog = (16.0 + wetness * VF_MIE_DENSITY_RAIN_MULT * 16.0) / maxDist; + float uniformFog = (16.0 + wetness * VF_MIE_DENSITY_RAIN_MULT * 16.0) / maxDist; - vec3 scatteringSun = vec3(0.0); - vec3 scatteringSky = vec3(0.0); - vec3 transmittance = vec3(1.0); + vec3 scatteringSun = vec3(0.0); + vec3 scatteringSky = vec3(0.0); + vec3 transmittance = vec3(1.0); - for (uint i = 0u; i < steps; ++i, rayPos += rayStep, shadowPos += shadowStep) { - vec2 stepDensity = CalculateFogDensity(rayPos, uniformFog); + // [优化]:阴影步进缓存设置 + #if VF_SHADOW_QUALITY == 1 + uint shadowSkip = 2u; + #else + uint shadowSkip = 1u; + #endif + vec3 cachedSampleShadow = vec3(1.0); - if (dot(stepDensity, vec2(1.0)) < EPS) continue; // Faster than maxOf() + for (uint i = 0u; i < steps; ++i, rayPos += rayStep, shadowPos += shadowStep) { + vec2 stepDensity = CalculateFogDensity(rayPos, uniformFog); + if (dot(stepDensity, vec2(1.0)) < EPS) continue; #if defined PASS_VOLUMETRIC_FOG - vec3 shadowScreenPos = DistortShadowSpace(shadowPos) * 0.5 + 0.5; - #ifdef COLORED_VOLUMETRIC_FOG - vec3 sampleShadow = vec3(1.0); - if (saturate(shadowScreenPos) == shadowScreenPos) { - ivec2 shadowTexel = ivec2(shadowScreenPos.xy * realShadowMapRes); - sampleShadow = step(shadowScreenPos.z, vec3(texelFetch(shadowtex1, shadowTexel, 0).x)); - - float sampleDepth0 = step(shadowScreenPos.z, texelFetch(shadowtex0, shadowTexel, 0).x); - if (sampleShadow.x != sampleDepth0) { - vec3 shadowColorSample = cube(texelFetch(shadowcolor0, shadowTexel, 0).rgb); - sampleShadow = shadowColorSample * (sampleShadow - sampleDepth0) + vec3(sampleDepth0); - } - } - #else - float sampleShadow = 1.0; - if (saturate(shadowScreenPos) == shadowScreenPos) { - ivec2 shadowTexel = ivec2(shadowScreenPos.xy * realShadowMapRes); - sampleShadow = step(shadowScreenPos.z, texelFetch(shadowtex1, shadowTexel, 0).x); - } - #endif + // [优化]:根据跳帧间隔采样阴影 + if (i % shadowSkip == 0u) { + vec3 shadowScreenPos = DistortShadowSpace(shadowPos) * 0.5 + 0.5; + if (saturate(shadowScreenPos) == shadowScreenPos) { + ivec2 shadowTexel = ivec2(shadowScreenPos.xy * realShadowMapRes); + + #ifdef COLORED_VOLUMETRIC_FOG + float sampleShadowX = step(shadowScreenPos.z, texelFetch(shadowtex1, shadowTexel, 0).x); + float sampleDepth0 = step(shadowScreenPos.z, texelFetch(shadowtex0, shadowTexel, 0).x); + if (sampleShadowX != sampleDepth0) { + vec3 shadowColorSample = cube(texelFetch(shadowcolor0, shadowTexel, 0).rgb); + cachedSampleShadow = shadowColorSample * (sampleShadowX - sampleDepth0) + vec3(sampleDepth0); + } else { + cachedSampleShadow = vec3(sampleShadowX); + } + #else + cachedSampleShadow = vec3(step(shadowScreenPos.z, texelFetch(shadowtex1, shadowTexel, 0).x)); + #endif + } else { + cachedSampleShadow = vec3(1.0); + } + } + vec3 sampleShadow = cachedSampleShadow; #else - float sampleShadow = 1.0; + vec3 sampleShadow = vec3(1.0); #endif - #ifdef VF_CLOUD_SHADOWS - vec2 cloudShadowCoord = WorldToCloudShadowScreenPos(rayPos).xy; - - if (saturate(cloudShadowCoord) == cloudShadowCoord) { - float cloudShadow = texture(cloudShadowTex, cloudShadowCoord).x; - sampleShadow *= cloudShadow; - } - #endif - - // Raymarch sunlight transmittance - vec2 opticalDepthSun = vec2(0.0); - - float stepSize = 4.0; - vec3 lightPos = rayPos; - for (uint i = 0u; i < 3u; ++i) { - stepSize *= 1.5; - lightPos += worldLightDir * stepSize; - - vec2 density = CalculateFogDensity(lightPos, uniformFog); - opticalDepthSun += density * stepSize; - } - - // https://zhuanlan.zhihu.com/p/457997155 - vec2 msV = 0.9 * oms(exp2(-8.0 * stepDensity)); - vec2 msEnergy = phase * exp(-opticalDepthSun); - msEnergy += uniformPhase * msV / (oms(msV) * (1.0 + opticalDepthSun * 0.25)); - - vec3 stepExtinction = fogExtinctionCoeff * stepDensity; - vec3 stepTransmittance = exp(-stepLength * stepExtinction); - - vec3 stepIntegral = transmittance * oms(stepTransmittance) / maxEps(stepExtinction); - - scatteringSun += fogScatteringCoeff * (stepDensity * msEnergy) * stepIntegral * sampleShadow; - scatteringSky += fogScatteringCoeff * stepDensity * stepIntegral; - - transmittance *= stepTransmittance; - - // Break if the transmittance is too small (optimization) - if (dot(transmittance, vec3(1.0)) < 1e-3) break; - } - - #ifndef VF_CLOUD_SHADOWS - scatteringSun *= 1.0 - wetness * CLOUD_SHADOW_STRENGTH; - #endif - #if !defined PASS_VOLUMETRIC_FOG - scatteringSun *= eyeSkylightSmooth; - #endif + #ifdef VF_CLOUD_SHADOWS + // 云阴影由于频率低,可以常态开启 + vec2 cloudShadowCoord = WorldToCloudShadowScreenPos(rayPos).xy; + if (saturate(cloudShadowCoord) == cloudShadowCoord) { + sampleShadow *= texture(cloudShadowTex, cloudShadowCoord).x; + } + #endif + + // [优化]:内部透射步数由质量决定 + vec2 opticalDepthSun = vec2(0.0); + float innerStepSize = 4.0; + vec3 lightPos = rayPos; + uint innerSamples = (VF_SHADOW_QUALITY > 1) ? 3u : 1u; + + for (uint j = 0u; j < innerSamples; ++j) { + innerStepSize *= 1.5; + lightPos += worldLightDir * innerStepSize; + opticalDepthSun += CalculateFogDensity(lightPos, uniformFog) * innerStepSize; + } + + vec2 msV = 0.9 * oms(exp2(-8.0 * stepDensity)); + vec2 msEnergy = phase * exp(-opticalDepthSun); + msEnergy += uniformPhase * msV / (oms(msV) * (1.0 + opticalDepthSun * 0.25)); + + vec3 stepExtinction = fogExtinctionCoeff * stepDensity; + vec3 stepTransmittance = exp(-stepLength * stepExtinction); + vec3 stepIntegral = transmittance * oms(stepTransmittance) / maxEps(stepExtinction); + + scatteringSun += fogScatteringCoeff * (stepDensity * msEnergy) * stepIntegral * sampleShadow; + scatteringSky += fogScatteringCoeff * stepDensity * stepIntegral; + + transmittance *= stepTransmittance; + + if (dot(transmittance, vec3(1.0)) < 1e-3) break; + } + + // 后处理混合与彩虹效果保持原样 + #ifndef VF_CLOUD_SHADOWS + scatteringSun *= 1.0 - wetness * CLOUD_SHADOW_STRENGTH; + #endif + #if !defined PASS_VOLUMETRIC_FOG + scatteringSun *= eyeSkylightSmooth; + #endif - scatteringSky *= eyeSkylightSmooth; + scatteringSky *= eyeSkylightSmooth; - // Apply rainbows - #ifdef RAINBOWS - float visibility = wetness * oms(rainStrength); - if (visibility > EPS) { - float distanceFade = saturate(rayLength / maxDist) * visibility; - scatteringSun *= 1.0 + RenderRainbows(LdotV) * distanceFade; - } - #endif + #ifdef RAINBOWS + float visibility = wetness * oms(rainStrength); + if (visibility > EPS) { + float distanceFade = saturate(rayLength / maxDist) * visibility; + scatteringSun *= 1.0 + RenderRainbows(LdotV) * distanceFade; + } + #endif - vec3 scattering = scatteringSun * global.directIlluminance; - scattering += scatteringSky * global.skyUpIlluminance; + vec3 scattering = scatteringSun * global.directIlluminance; + scattering += scatteringSky * uniformPhase * global.skyUpIlluminance; - return mat2x3(scattering, transmittance); + return mat2x3(scattering, transmittance); } \ No newline at end of file diff --git a/shaders/lib/atmosphere/Celestial.glsl b/shaders/lib/atmosphere/Celestial.glsl index b0fa9aa2..133c957e 100644 --- a/shaders/lib/atmosphere/Celestial.glsl +++ b/shaders/lib/atmosphere/Celestial.glsl @@ -10,7 +10,8 @@ vec3 RenderSun(in vec3 worldDir, in vec3 sunVector) { const float cosRadius = cos(sunAngularRadius); - const vec3 sunRadiance = sunIrradiance / (TAU * oms(cosRadius)); + const vec3 sunIlluminance = sunIrradiance * 128.0; + const vec3 sunRadiance = sunIlluminance / (TAU * oms(cosRadius)); float cosTheta = dot(worldDir, sunVector); if (cosTheta >= cosRadius) { diff --git a/shaders/lib/atmosphere/Common.glsl b/shaders/lib/atmosphere/Common.glsl index 4a219051..a4dd181e 100644 --- a/shaders/lib/atmosphere/Common.glsl +++ b/shaders/lib/atmosphere/Common.glsl @@ -19,9 +19,9 @@ #define ATMOSPHERE_TURBIDITY 1.0 // [0.0 0.25 0.5 0.75 1.0 1.25 1.5 1.75 2.0 2.25 2.5 2.75 3.0 3.25 3.5 3.75 4.0 4.25 4.5 4.75 5.0] -#define ATMOSPHERE_SKY_SAMPLES 32 // [16 24 32 40 48 56 64 72 80 88 96 104 112 120 128] -#define ATMOSPHERE_TLUT_SAMPLES 64 // [16 24 32 40 48 56 64 72 80 88 96 104 112 120 128] -#define ATMOSPHERE_MSLUT_SAMPLES 24 // [16 24 32 40 48 56 64 72 80 88 96 104 112 120 128] +#define ATMOSPHERE_SKY_SAMPLES 32 // [2 4 6 8 16 24 32 40 48 56 64 72 80 88 96 104 112 120 128] +#define ATMOSPHERE_TLUT_SAMPLES 64 // [2 4 6 8 16 24 32 40 48 56 64 72 80 88 96 104 112 120 128] +#define ATMOSPHERE_MSLUT_SAMPLES 24 // [2 4 6 8 16 24 32 40 48 56 64 72 80 88 96 104 112 120 128] #define ProjectSky OctEncodeUnorm #define UnprojectSky OctDecodeUnorm @@ -59,7 +59,7 @@ const AtmosphereParameters atmosphere = AtmosphereParameters( mieCoeffBase * 0.9, mieCoeffBase, vec3(8.304280072e-7, 1.314911970e-6, 5.440679729e-8), - vec3(0.2, 0.25, 0.45) + vec3(0.1, 0.12, 0.2) ); const mat3 atmosphereExtinction = mat3( diff --git a/shaders/lib/atmosphere/clouds/Render.glsl b/shaders/lib/atmosphere/clouds/Render.glsl index 639bc82b..e3438d9a 100644 --- a/shaders/lib/atmosphere/clouds/Render.glsl +++ b/shaders/lib/atmosphere/clouds/Render.glsl @@ -341,14 +341,14 @@ void CompositeClouds(inout vec3 skyRadiance, in vec4 cloudData, in vec3 rayDir) // Compute illumination to clouds vec3 sunIlluminance = sunIrradiance * AtmosphereTransmittanceToSun(cloudPos, worldSunDir); vec3 moonIlluminance = sunIrradiance * AtmosphereTransmittanceToSun(cloudPos, -worldSunDir) * moonlightMult; - vec3 directIlluminance = sunIlluminance + moonIlluminance; + vec3 directIlluminance = 128.0 * (sunIlluminance + moonIlluminance); // Normalized height in clouds float heightFraction = saturate((length(cloudPos) - cumulusBottomRadius) * rcp(cumulusThickness)); vec3 skyIlluminance = mix(ReconstructSH3(global.skySH, vec3(0.0, -1.0, 0.0)), global.skyUpIlluminance, heightFraction); vec3 scattering = cloudData.x * directIlluminance; - scattering += cloudData.y * skyIlluminance; + scattering += cloudData.y * rPI * skyIlluminance; scattering += LightningContribution(cloudPos - atmosphereViewPos) * sqr(cloudData.y); diff --git a/shaders/lib/atmosphere/clouds/Shape.glsl b/shaders/lib/atmosphere/clouds/Shape.glsl index 25eca75b..c709f9c9 100644 --- a/shaders/lib/atmosphere/clouds/Shape.glsl +++ b/shaders/lib/atmosphere/clouds/Shape.glsl @@ -1,24 +1,24 @@ /* -------------------------------------------------------------------------------- - References: - [Schneider, 2015] Andrew Schneider. “The Real-Time Volumetric Cloudscapes Of Horizon: Zero Dawn”. SIGGRAPH 2015. - https://www.slideshare.net/guerrillagames/the-realtime-volumetric-cloudscapes-of-horizon-zero-dawn - [Schneider, 2016] Andrew Schneider. "GPU Pro 7: Real Time Volumetric Cloudscapes". p.p. (97-128) CRC Press, 2016. - https://www.taylorfrancis.com/chapters/edit/10.1201/b21261-11/real-time-volumetric-cloudscapes-andrew-schneider - [Schneider, 2017] Andrew Schneider. "Nubis: Authoring Realtime Volumetric Cloudscapes with the Decima Engine". SIGGRAPH 2017. - https://advances.realtimerendering.com/s2017/Nubis%20-%20Authoring%20Realtime%20Volumetric%20Cloudscapes%20with%20the%20Decima%20Engine%20-%20Final.pptx - [Schneider, 2022] Andrew Schneider. "Nubis, Evolved: Real-Time Volumetric Clouds for Skies, Environments, and VFX". SIGGRAPH 2022. - https://advances.realtimerendering.com/s2022/SIGGRAPH2022-Advances-NubisEvolved-NoVideos.pdf - [Schneider, 2023] Andrew Schneider. "Nubis Cubed: Methods (and madness) to model and render immersive real-time voxel-based clouds". SIGGRAPH 2023. - https://advances.realtimerendering.com/s2023/Nubis%20Cubed%20(Advances%202023).pdf - [Hillaire, 2016] Sebastien Hillaire. “Physically based Sky, Atmosphere and Cloud Rendering”. SIGGRAPH 2016. - https://blog.selfshadow.com/publications/s2016-shading-course/ - https://www.ea.com/frostbite/news/physically-based-sky-atmosphere-and-cloud-rendering + References: + [Schneider, 2015] Andrew Schneider. “The Real-Time Volumetric Cloudscapes Of Horizon: Zero Dawn”. SIGGRAPH 2015. + https://www.slideshare.net/guerrillagames/the-realtime-volumetric-cloudscapes-of-horizon-zero-dawn + [Schneider, 2016] Andrew Schneider. "GPU Pro 7: Real Time Volumetric Cloudscapes". p.p. (97-128) CRC Press, 2016. + https://www.taylorfrancis.com/chapters/edit/10.1201/b21261-11/real-time-volumetric-cloudscapes-andrew-schneider + [Schneider, 2017] Andrew Schneider. "Nubis: Authoring Realtime Volumetric Cloudscapes with the Decima Engine". SIGGRAPH 2017. + https://advances.realtimerendering.com/s2017/Nubis%20-%20Authoring%20Realtime%20Volumetric%20Cloudscapes%20with%20the%20Decima%20Engine%20-%20Final.pptx + [Schneider, 2022] Andrew Schneider. "Nubis, Evolved: Real-Time Volumetric Clouds for Skies, Environments, and VFX". SIGGRAPH 2022. + https://advances.realtimerendering.com/s2022/SIGGRAPH2022-Advances-NubisEvolved-NoVideos.pdf + [Schneider, 2023] Andrew Schneider. "Nubis Cubed: Methods (and madness) to model and render immersive real-time voxel-based clouds". SIGGRAPH 2023. + https://advances.realtimerendering.com/s2023/Nubis%20Cubed%20(Advances%202023).pdf + [Hillaire, 2016] Sebastien Hillaire. “Physically based Sky, Atmosphere and Cloud Rendering”. SIGGRAPH 2016. + https://blog.selfshadow.com/publications/s2016-shading-course/ + https://www.ea.com/frostbite/news/physically-based-sky-atmosphere-and-cloud-rendering [Högfeldt, 2016] Rurik Högfeldt. "Convincing Cloud Rendering: An Implementation of Real-Time Dynamic Volumetric Clouds in Frostbite". Department of Computer Science and Engineering, Gothenburg, Sweden, 2016. https://publications.lib.chalmers.se/records/fulltext/241770/241770.pdf - [Bauer, 2019] Fabian Bauer. "Creating the Atmospheric World of Red Dead Redemption 2: A Complete and Integrated Solution". SIGGRAPH 2019. - https://www.advances.realtimerendering.com/s2019/slides_public_release.pptx + [Bauer, 2019] Fabian Bauer. "Creating the Atmospheric World of Red Dead Redemption 2: A Complete and Integrated Solution". SIGGRAPH 2019. + https://www.advances.realtimerendering.com/s2019/slides_public_release.pptx [Wrenninge et al., 2013] Magnus Wrenninge, Chris Kulla, Viktor Lundqvist. “Oz: The Great and Volumetric”. SIGGRAPH 2013 Talks. https://dl.acm.org/doi/10.1145/2504459.2504518 @@ -30,6 +30,10 @@ #include "/lib/atmosphere/clouds/Common.glsl" +// 控制选项:噪声采样迭代次数 +#define CLOUD_BASE_NOISE_ITERATIONS 1 // [1 2 3] 基础形状采样迭代 +#define CLOUD_DETAIL_ITERATIONS 1 // [1 2 3] 细节侵蚀采样迭代 + //================================================================================================// // [Schneider, 2023] @@ -38,154 +42,159 @@ float ValueErosion(in float value, in float oldMin) { } float CloudMidDensity(in vec2 rayPos) { - return 0.0; + return 0.0; } // Adapted from [Schneider, 2022] float CloudHighDensity(in vec2 rayPos) { - // Wind field - const float windAngle = radians(CLOUD_HIGH_WIND_ANGLE); - const vec2 windVelocity = vec2(cos(windAngle), sin(windAngle)) * CLOUD_HIGH_WIND_SPEED; - vec2 windOffset = windVelocity * worldTimeCounter; - - rayPos -= windOffset; - rayPos += cameraPosition.xz; - - // Curl noise to simulate wind, makes the positioning of the clouds more natural - vec2 curlNoise = texture(curlNoise2D, rayPos * 5e-5).xy * 0.25; - vec2 position = rayPos * 2e-4 + curlNoise; - - float density = 0.0; - - #ifdef CLOUD_CIRRUS - /* Cirrus clouds */ - { - float coverage = CLOUD_CI_COVERAGE - 0.5 + texture(noisetex, position * 0.01).z; - coverage = saturate(coverage - texture(cloudMapTex, (position * 0.01)).y); - - if (coverage > 0.25) { - vec2 p = position + coverage * 0.5 - windOffset * 1e-4; - float cirrus = texture(cirroLutTex, p * 0.25).y; - - cirrus *= smoothstep(0.25, 1.0, coverage); - density += cirrus * cirrus; - } - } - #endif - #ifdef CLOUD_CIRROCUMULUS - /* Cirrocumulus clouds */ - { - float coverage = CLOUD_CC_COVERAGE - saturate(texture(noisetex, position * 0.01).z * 1.5); - coverage = saturate(texture(cloudMapTex, (position * 0.01)).x * 0.75 + coverage); - - if (coverage > 0.25) { - vec2 p = position + coverage * 0.5 - windOffset * 1e-4; - float cirrocumulus = sqr(texture(cirroLutTex, p * 0.25).x); - - cirrocumulus *= smoothstep(0.25, 1.0, coverage); - density += cirrocumulus; - } - } - #endif - - return density; + // Wind field + const float windAngle = radians(CLOUD_HIGH_WIND_ANGLE); + const vec2 windVelocity = vec2(cos(windAngle), sin(windAngle)) * CLOUD_HIGH_WIND_SPEED; + vec2 windOffset = windVelocity * worldTimeCounter; + + rayPos -= windOffset; + rayPos += cameraPosition.xz; + + // Curl noise to simulate wind, makes the positioning of the clouds more natural + vec2 curlNoise = texture(curlNoise2D, rayPos * 5e-5).xy * 0.25; + vec2 position = rayPos * 2e-4 + curlNoise; + + float density = 0.0; + + #ifdef CLOUD_CIRRUS + /* Cirrus clouds */ + { + float coverage = CLOUD_CI_COVERAGE - 0.5 + texture(noisetex, position * 0.01).z; + coverage = saturate(coverage - texture(cloudMapTex, (position * 0.01)).y); + + if (coverage > 0.25) { + vec2 p = position + coverage * 0.5 - windOffset * 1e-4; + float cirrus = texture(cirroLutTex, p * 0.25).y; + + cirrus *= smoothstep(0.25, 1.0, coverage); + density += cirrus * cirrus; + } + } + #endif + #ifdef CLOUD_CIRROCUMULUS + /* Cirrocumulus clouds */ + { + float coverage = CLOUD_CC_COVERAGE - saturate(texture(noisetex, position * 0.01).z * 1.5); + coverage = saturate(texture(cloudMapTex, (position * 0.01)).x * 0.75 + coverage); + + if (coverage > 0.25) { + vec2 p = position + coverage * 0.5 - windOffset * 1e-4; + float cirrocumulus = sqr(texture(cirroLutTex, p * 0.25).x); + + cirrocumulus *= smoothstep(0.25, 1.0, coverage); + density += cirrocumulus; + } + } + #endif + + return density; } //================================================================================================// #if 0 - float GetVerticalProfile(in float heightFraction, in float cloudType) { - return texture(verticalLut, vec2(cloudType, heightFraction)).x; - } + float GetVerticalProfile(in float heightFraction, in float cloudType) { + return texture(verticalLut, vec2(cloudType, heightFraction)).x; + } #else - float GetVerticalProfile(in float h, in float t) { - float stratus = saturate(h * 16.0) * linearstep(0.2, 0.1, h); - float stratocumulus = saturate(h * 6.0) * linearstep(0.6, 0.2, h); - float cumulus = saturate(h * 8.0) * linearstep(1.0, 0.7, h); - - float gradient = mix(stratus, stratocumulus, smoothstep(0.0, 0.5, t)); - return mix(gradient, cumulus, smoothstep(0.5, 1.0, t)); - } + float GetVerticalProfile(in float h, in float t) { + float stratus = saturate(h * 16.0) * linearstep(0.2, 0.1, h); + float stratocumulus = saturate(h * 6.0) * linearstep(0.6, 0.2, h); + float cumulus = saturate(h * 8.0) * linearstep(1.0, 0.7, h); + + float gradient = mix(stratus, stratocumulus, smoothstep(0.0, 0.5, t)); + return mix(gradient, cumulus, smoothstep(0.5, 1.0, t)); + } #endif float CloudVolumeDensity(in vec3 rayPos, in float heightFraction, out float dimensionalProfile, in bool detail) { - // Wind field - const float windAngle = radians(CLOUD_LOW_WIND_ANGLE); - const vec3 windDir = vec3(cos(windAngle), 0.5, sin(windAngle)); - const vec3 windVelocity = windDir * CLOUD_LOW_WIND_SPEED; - vec3 windOffset = windVelocity * worldTimeCounter; - - rayPos -= windOffset; - rayPos.xz += cameraPosition.xz; - - // Sample cloud map - vec2 cloudMap = texture(cloudMapTex, (rayPos.xz * rcp(cloudMapExtend))).xy; - - // Coveage profile - vec2 stepEdge = mix(vec2(0.5, 1.0) - CLOUD_CU_COVERAGE * 0.4, vec2(0.15, 0.5), sqr(wetness)); - float coverage = linearstep(stepEdge.x, stepEdge.y, cloudMap.x); - - float localCoverage = texture(noisetex, rayPos.xz * rcp(512e3) + 0.75).z; - coverage *= linearstep(stepEdge.x * 1.1, stepEdge.y * 0.8, localCoverage); - - // Vertical profile - float type = cloudMap.y * approxSqrt(coverage); - // heightFraction = ValueErosion(heightFraction, oms(cloudMap.y) * 0.3); - float gradient = GetVerticalProfile(heightFraction, type); - - #if 0 - dimensionalProfile = (gradient * coverage); - #else - dimensionalProfile = saturate(gradient + coverage - 1.0); - #endif - if (dimensionalProfile < 0.1) return 0.0; - - vec3 noisePos = (rayPos - windDir * heightFraction * cumulusTopOffset) * rcp(2e3); - noisePos.y += dot(noisePos.xz, vec2(0.2, 0.3)); // Reduce repetition pattern - - // Add curl noise - #if !defined PASS_SKY_MAP - if (detail) { - vec3 curlNoise = texture(curlNoise3D, noisePos * vec3(2.0, 3.0, 2.0)).xyz; - noisePos += curlNoise * gradient * oms(coverage) * 0.4; - } - #endif - - #if 0 - vec2 billowyNoise = texture(baseNoiseTex, fract(noisePos)).xy; - - // Blend between HF and LF according to dimensionalProfile - float baseNoise = mix(billowyNoise.x, billowyNoise.y, approxSqrt(dimensionalProfile)); - #else - float baseNoise = texture(baseNoiseTex, noisePos).x; - #endif - - // See [Schneider, 2022] - float cloudDensity = dimensionalProfile + (baseNoise - 1.0) * 0.75; - if (cloudDensity < cloudEpsilon) return 0.0; - - float heightFade = smoothstep(0.1, 0.5, heightFraction); - - // Detail erosion - // float detailNoise = 0.1; - - // #if !defined PASS_SKY_MAP - // if (detail) { - // noisePos -= baseNoise * 0.1 * windDir + windOffset * 1e-4; - - // detailNoise = texture(detailNoiseTex, noisePos * 8.0).x; - - // // Transition from wispy shapes to billowy shapes over height - // detailNoise = sqr(mix(detailNoise, 0.75 - detailNoise * 0.5, heightFade)) * 0.4; - // } - // #endif - - // cloudDensity = ValueErosion(cloudDensity, detailNoise); - // cloudDensity = saturate(cloudDensity - detailNoise * oms(cloudDensity)); - - // Density profile - cloudDensity *= mix(1.0, inversesqrt(cloudDensity), heightFraction); - return cloudDensity * mix(CLOUD_CU_DENSITY_B, CLOUD_CU_DENSITY_T, heightFade); + // Wind field + const float windAngle = radians(CLOUD_LOW_WIND_ANGLE); + const vec3 windDir = vec3(cos(windAngle), 0.5, sin(windAngle)); + const vec3 windVelocity = windDir * CLOUD_LOW_WIND_SPEED; + vec3 windOffset = windVelocity * worldTimeCounter; + + rayPos -= windOffset; + rayPos.xz += cameraPosition.xz; + + // Sample cloud map + vec2 cloudMap = texture(cloudMapTex, (rayPos.xz * rcp(cloudMapExtend))).xy; + + // Coveage profile + vec2 stepEdge = mix(vec2(0.5, 1.0) - CLOUD_CU_COVERAGE * 0.4, vec2(0.15, 0.5), sqr(wetness)); + float coverage = linearstep(stepEdge.x, stepEdge.y, cloudMap.x); + + float localCoverage = texture(noisetex, rayPos.xz * rcp(512e3) + 0.75).z; + coverage *= linearstep(stepEdge.x * 1.1, stepEdge.y * 0.8, localCoverage); + + // Vertical profile + float type = cloudMap.y * approxSqrt(coverage); + // heightFraction = ValueErosion(heightFraction, oms(cloudMap.y) * 0.3); + float gradient = GetVerticalProfile(heightFraction, type); + + #if 0 + dimensionalProfile = (gradient * coverage); + #else + dimensionalProfile = saturate(gradient + coverage - 1.0); + #endif + if (dimensionalProfile < 0.1) return 0.0; + + vec3 noisePos = (rayPos - windDir * heightFraction * cumulusTopOffset) * rcp(2e3); + noisePos.y += dot(noisePos.xz, vec2(0.2, 0.3)); // Reduce repetition pattern + + // Add curl noise + #if !defined PASS_SKY_MAP + if (detail) { + for (int i = 0; i < CLOUD_BASE_NOISE_ITERATIONS; i++) { + vec3 curlNoise = texture(curlNoise3D, noisePos * vec3(2.0, 3.0, 2.0)).xyz; + noisePos += curlNoise * gradient * oms(coverage) * 0.4; + } + } + #endif + + #if 0 + vec2 billowyNoise = texture(baseNoiseTex, fract(noisePos)).xy; + + // Blend between HF and LF according to dimensionalProfile + float baseNoise = mix(billowyNoise.x, billowyNoise.y, approxSqrt(dimensionalProfile)); + #else + float baseNoise = 0.0; + for (int i = 0; i < CLOUD_BASE_NOISE_ITERATIONS; i++) { + baseNoise += texture(baseNoiseTex, noisePos * (1.0 + float(i) * 0.5)).x; + } + baseNoise /= float(CLOUD_BASE_NOISE_ITERATIONS); + #endif + + // See [Schneider, 2022] + float cloudDensity = dimensionalProfile + (baseNoise - 1.0) * 0.75; + if (cloudDensity < cloudEpsilon) return 0.0; + + float heightFade = smoothstep(0.1, 0.5, heightFraction); + + // Detail erosion + // float detailNoise = 0.1; + + // #if !defined PASS_SKY_MAP + // if (detail) { + // for (int i = 0; i < CLOUD_DETAIL_ITERATIONS; i++) { + // vec3 detailPos = noisePos - baseNoise * 0.1 * windDir + windOffset * 1e-4; + // float d = texture(detailNoiseTex, detailPos * (8.0 + float(i) * 4.0)).x; + // detailNoise = sqr(mix(d, 0.75 - d * 0.5, heightFade)) * 0.4; + // } + // } + // #endif + + // cloudDensity = ValueErosion(cloudDensity, detailNoise); + // cloudDensity = saturate(cloudDensity - detailNoise * oms(cloudDensity)); + + // Density profile + cloudDensity *= mix(1.0, inversesqrt(cloudDensity), heightFraction); + return cloudDensity * mix(CLOUD_CU_DENSITY_B, CLOUD_CU_DENSITY_T, heightFade); } #endif // INCLUDE_CLOUDS_SHAPE \ No newline at end of file diff --git a/shaders/lib/lighting/RSM.glsl b/shaders/lib/lighting/RSM.glsl new file mode 100644 index 00000000..fd2217aa --- /dev/null +++ b/shaders/lib/lighting/RSM.glsl @@ -0,0 +1,78 @@ +/* Reflective Shadow Maps */ +// Reference: https://users.soe.ucsc.edu/~pang/160/s13/proposal/mijallen/proposal/media/p203-dachsbacher.pdf +#define RSM_ENABLED +#define RSM_SAMPLES 16 // [4 8 12 16 20 24 32 48 64 96 128 256] +#define RSM_RADIUS 8.0 // [1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0 12.0 15.0 20.0 25.0 30.0 40.0 50.0 70.0 100.0] +#define RSM_BRIGHTNESS 1.0 // [0.5 0.6 0.7 0.8 0.9 1.0 1.1 1.2 1.3 1.4 1.6 1.8 2.0 2.5 3.0 5.0 7.0 10.0 15.0 20.0 30.0 40.0 50.0 70.0 100.0] +in vec3 sRGBtoLinearApprox; +//================================================================================================// + +uniform sampler2D shadowtex1; +uniform sampler2D shadowcolor0; +uniform sampler2D shadowcolor1; + +#include "/lib/lighting/shadow/Common.glsl" + +//================================================================================================// +#ifdef RSM_ENABLED +vec3 CalculateRSM(in vec3 viewPos, in vec3 worldNormal, in vec2 noise, in float skyLightmap) { + const float realShadowMapRes = float(shadowMapResolution) * MC_SHADOW_QUALITY; + + vec3 shadowNormal = mat3(shadowModelView) * worldNormal; + vec3 projectionScale = diagonal3(shadowProjection); + vec3 projectionInvScale = diagonal3(shadowProjectionInverse); + + vec3 worldPos = transMAD(gbufferModelViewInverse, viewPos); + vec3 shadowPos = transMAD(shadowModelView, worldPos); + vec3 shadowClipPos = projectionScale * shadowPos + shadowProjection[3].xyz; + + const float sqRadius = RSM_RADIUS * RSM_RADIUS; + const float rSteps = 1.0 / float(RSM_SAMPLES); + + const mat2 goldenRotate = mat2(cos(goldenAngle), -sin(goldenAngle), sin(goldenAngle), cos(goldenAngle)); + + vec2 offsetRadius = RSM_RADIUS * projectionScale.xy; + vec2 dir = sincos(noise.x * 16.0 * PI) * offsetRadius; + + vec3 sum = vec3(0.0); + for (uint i = 0u; i < RSM_SAMPLES; ++i, dir *= goldenRotate) { + float sampleRad = (float(i) + noise.y) * rSteps; + + vec2 sampleClipCoord = shadowClipPos.xy + dir * sampleRad; + vec2 sampleScreenCoord = sampleClipCoord * CalcDistortionFactor(sampleClipCoord) * 0.5 + 0.5; + ivec2 sampleTexel = ivec2(sampleScreenCoord * realShadowMapRes); + + float sampleDepth = texelFetch(shadowtex1, sampleTexel, 0).x * 10.0 - 5.0; + + vec3 sampleDelta = vec3(sampleClipCoord, sampleDepth) - shadowClipPos; + sampleDelta = projectionInvScale * sampleDelta; + + float sampleSqLen = sdot(sampleDelta); + if (sampleSqLen > sqRadius) continue; + + vec3 sampleDir = sampleDelta * inversesqrt(sampleSqLen); + + float diffuse = dot(shadowNormal, sampleDir); + if (diffuse < EPS) continue; + + vec3 sampleColor = texelFetch(shadowcolor1, sampleTexel, 0).rgb; + + vec3 sampleNormal = OctDecodeUnorm(sampleColor.xy); + + float bounce = dot(sampleNormal, -sampleDir); + if (bounce < EPS) continue; + + float falloff = sampleRad / (sampleSqLen + EPS); + + float skylightWeight = saturate(1.0 - sqr(sampleColor.z - skyLightmap) * 2.0); + + // vec3 albedo = sRGBtoLinearApprox(texelFetch(shadowcolor0, sampleTexel, 0).rgb); + + // sum += diffuse * bounce * falloff * skylightWeight * albedo; + } + + sum *= sqRadius * rSteps * RSM_BRIGHTNESS * PI; + + return saturate(sum); +} +#endif \ No newline at end of file diff --git a/shaders/lib/lighting/SCNBGI.glsl b/shaders/lib/lighting/SCNBGI.glsl new file mode 100644 index 00000000..e69de29b diff --git a/shaders/lib/lighting/SSILVB.glsl b/shaders/lib/lighting/SSILVB.glsl index 8012d158..99c1f247 100644 --- a/shaders/lib/lighting/SSILVB.glsl +++ b/shaders/lib/lighting/SSILVB.glsl @@ -7,9 +7,20 @@ #define SSILVB_SLICE_COUNT 1 // [1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16] #define SSILVB_SAMPLE_COUNT 32 // [4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40 42 44 46 48 50 52 54 56 58 60 62 64] -#define SSILVB_SECTOR_COUNT 32 // [4 8 16 32 64 128] +#define SSILVB_SECTOR_COUNT 32 // [4 8 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 64 128] #define SSILVB_HIT_THICKNESS 1.0 // [0.25 0.5 1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0 5.5 6.0 6.5 7.0 7.5 8.0] +// ====== 调节 GI 亮度的选项 ====== +#ifndef SSILVB_GI_BRIGHTNESS + #define SSILVB_GI_BRIGHTNESS 1.3 // [0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2.0] +#endif +// ================================= + +// 手部排除阈值(深度小于此值的像素不参与间接光照贡献) +#ifndef SSILVB_HAND_DEPTH_THRESHOLD + #define SSILVB_HAND_DEPTH_THRESHOLD 0.56 // [0.0 0.1 0.2 0.3 0.4 0.5 0.56 0.6 0.7 0.8 0.9 1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2.0] +#endif + //================================================================================================// #include "/lib/utility/ShaderFastMathLib.glsl" @@ -240,6 +251,10 @@ vec4 CalculateSSILVB(in vec2 fragCoord, in vec3 viewPos, in vec3 worldNormal, in if (sampleDepth > 1.0 - EPS) continue; + // ========== 排除手部:深度小于手部阈值的像素不参与间接光照 ========== + if (sampleDepth < SSILVB_HAND_DEPTH_THRESHOLD) continue; + // ==================================================================== + vec3 samplePos = ScreenToViewPos(vec3(sampleUV, sampleDepth)); vec3 sampleDirFront = samplePos - viewPos; @@ -284,5 +299,9 @@ vec4 CalculateSSILVB(in vec2 fragCoord, in vec3 viewPos, in vec3 worldNormal, in vec3 skyIrradiance = ConvolvedReconstructSH3(global.skySH, worldNormal); irradiance.rgb += skyIrradiance * irradiance.a * cube(skylight); + + // 应用亮度调节 + irradiance.rgb *= SSILVB_GI_BRIGHTNESS; + return irradiance; -} +} \ No newline at end of file diff --git a/shaders/lib/lighting/SSPT.glsl b/shaders/lib/lighting/SSPT.glsl new file mode 100644 index 00000000..e064563f --- /dev/null +++ b/shaders/lib/lighting/SSPT.glsl @@ -0,0 +1,86 @@ +/* Screen-Space Path Tracing */ +#define SSPT_ENABLED +#define SSPT_SPP 2 // [1 2 3 4 5 6 7 8 9 10 11 12 14 16 18 20 22 24] +#define SSPT_BOUNCES 1 // [1] +#define SSPT_RT_STEPS 16 // [1 2 3 4 5 6 7 8 9 10 11 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40 42 44 46 48 50 52 54 56 58 60 62 64] + +#define SSPT_RR_MIN_BOUNCES 1 // [1 2 3 4 5 6 7 8 9 10 11 12 14 16 18 20 22 24] +in uint noiseGenerator; +//================================================================================================// + +vec3 SampleRaytrace(in vec3 viewPos, in vec3 viewDir, in float dither, in vec3 rayPos) { + if (viewDir.z > max0(-viewPos.z)) return vec3(1e6); + + vec3 endPos = ViewToScreenPos(viewDir + viewPos); + vec3 rayDir = normalize(endPos - rayPos); + + float stepLength = minOf((step(0.0, rayDir) - rayPos) / rayDir) * rcp(float(SSPT_RT_STEPS)); + + vec3 rayStep = rayDir * stepLength; + rayPos += rayStep * dither; + + rayPos.xy *= viewSize; + rayStep.xy *= viewSize; + + for (uint i = 0u; i < SSPT_RT_STEPS; ++i, rayPos += rayStep) { + if (clamp(rayPos.xy, vec2(0.0), viewSize) != rayPos.xy) break; + float sampleDepth = loadDepth0(ivec2(rayPos.xy)); + + if (sampleDepth < rayPos.z) { + float sampleDepthLinear = ScreenToViewDepth(sampleDepth); + float traceDepthLinear = ScreenToViewDepth(rayPos.z); + #if defined LOD_MOD + if (sampleDepth > 1.0 - EPS) sampleDepthLinear = ScreenToViewDepthLod(loadDepth0Lod(ivec2(rayPos.xy))); + #endif + + if (traceDepthLinear - sampleDepthLinear > 0.2 * traceDepthLinear) return vec3(rayPos.xy, sampleDepth); + } + } + + return vec3(1e6); +} + +vec3 CalculateSSPT(in vec3 screenPos, in vec3 viewPos, in vec3 worldNormal, in vec2 lightmap) { + lightmap.y *= lightmap.y * lightmap.y; + + vec3 viewNormal = mat3(gbufferModelView) * worldNormal; + + NoiseGenerator noiseGenerator = initNoiseGenerator(gl_GlobalInvocationID.xy, uint(frameCounter)); + + #if defined LOD_MOD + float screenDepthMax = ViewToScreenDepth(ScreenToViewDepthLod(1.0)); + #else + #define screenDepthMax 1.0 + #endif + + vec3 sum = vec3(0.0); + + for (uint spp = 0u; spp < SSPT_SPP; ++spp) { + // for (uint bounce = 1u; bounce <= SSPT_BOUNCES; ++bounce) { + vec3 rayDir = SampleCosineHemisphere(viewNormal, nextVec2(noiseGenerator)); + + float dither = nextFloat(noiseGenerator); + vec3 hitPos = SampleRaytrace(viewPos + viewNormal * 1e-2, rayDir, dither, screenPos); + + if (hitPos.z < screenDepthMax) { + vec3 sampleRadiance = texelFetch(colortex4, ivec2(hitPos.xy) >> 1, 0).rgb; + sum += sampleRadiance; + } else if (lightmap.y > EPS) { + vec3 rayDirWorld = mat3(gbufferModelViewInverse) * rayDir; + vec3 skyRadiance = texture(skyMapTex, ProjectSky(rayDirWorld)).rgb; + + sum += skyRadiance * lightmap.y; + break; + } + + // Russian roulette + // if (bounce >= SSPT_RR_MIN_BOUNCES) { + // float probability = saturate(luminance(target.contribution)); + // if (probability < dither) break; + // target.contribution *= rcp(probability); + // } + // } + } + + return sum * rcp(float(SSPT_SPP)); +} \ No newline at end of file diff --git a/shaders/lib/lighting/shadow/Common.glsl b/shaders/lib/lighting/shadow/Common.glsl index 871b32c5..dc8773c5 100644 --- a/shaders/lib/lighting/shadow/Common.glsl +++ b/shaders/lib/lighting/shadow/Common.glsl @@ -1,19 +1,34 @@ -// Method from GeForceLegend -// https://discord.com/channels/237199950235041794/525510804494221312/1379718853872848896 - -#define SHADOW_DISTORTION_STRENGTH 4.0 // [1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0 5.5 6.0 6.5 7.0 7.5 8.0] +// --- 阴影设置 --- +#define SHADOW_DISTORTION // [OFF ON] 阴影形变开关 +#define SHADOW_DISTORTION_STRENGTH 4.0 // [1.0 1.5 2.0 2.25 2.5 2.75 3.0 3.25 3.5 4.0 4.5 5.0 5.5 6.0 6.5 7.0 7.5 8.0] +// 计算形变因子的函数 float CalcDistortionFactor(in vec2 shadowClipPos) { - float invClipLength = inversesqrt(sdot(shadowClipPos)); - float distortionCurve = log((exp(SHADOW_DISTORTION_STRENGTH) - 1.0) / invClipLength + 1.0); - return distortionCurve * invClipLength * rcp(SHADOW_DISTORTION_STRENGTH); + #ifndef SHADOW_DISTORTION + return 1.0; // 如果开关关闭,直接返回1.0,不进行任何形变计算 + #else + // 原始形变算法 + float invClipLength = inversesqrt(sdot(shadowClipPos)); + float distortionCurve = log((exp(SHADOW_DISTORTION_STRENGTH) - 1.0) / invClipLength + 1.0); + return distortionCurve * invClipLength * rcp(SHADOW_DISTORTION_STRENGTH); + #endif } +// 形变应用函数(重载1) vec3 DistortShadowSpace(in vec3 shadowClipPos, in float distortionFactor) { - return shadowClipPos * vec3(vec2(distortionFactor), 0.2); + #ifndef SHADOW_DISTORTION + return shadowClipPos; // 开关关闭时,不乘以系数,保持原始坐标 + #else + return shadowClipPos * vec3(vec2(distortionFactor), 0.2); + #endif } +// 形变应用函数(重载2) vec3 DistortShadowSpace(in vec3 shadowClipPos) { - float distortionFactor = CalcDistortionFactor(shadowClipPos.xy); - return shadowClipPos * vec3(vec2(distortionFactor), 0.2); + #ifndef SHADOW_DISTORTION + return shadowClipPos; // 同上 + #else + float distortionFactor = CalcDistortionFactor(shadowClipPos.xy); + return shadowClipPos * vec3(vec2(distortionFactor), 0.2); + #endif } \ No newline at end of file diff --git a/shaders/lib/lighting/shadow/Render.glsl b/shaders/lib/lighting/shadow/Render.glsl index 454df13b..574dab7e 100644 --- a/shaders/lib/lighting/shadow/Render.glsl +++ b/shaders/lib/lighting/shadow/Render.glsl @@ -1,24 +1,36 @@ +/* +-------------------------------------------------------------------------------- + Revelation Shaders - Shadow Module (Modified) + Added: PCSS Toggle & Manual Shadow Bias Control +-------------------------------------------------------------------------------- +*/ -#define PCSS_SEARCH_SAMPLES 8 // [4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 48 64] -#define PCSS_FILTER_SAMPLES 16 // [4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 48 64] +// [0 1 2] 0: Hard Shadow (Fastest), 1: PCF (Medium), 2: PCSS (Physical Soft) +#define SHADOW_SOFT_TYPE 1 // [0 1 2] +#define PCF_SOFT_RADIUS 0.1 // [0.05 0.1 0.15 0.2 0.25 0.3 0.35 0.4 0.45 0.5 1.0 1.5 2.0 2.5 3.0 3.5 4.0] +#define SHADOW_BIAS_STRENGTH 2.0 // [1.0 1.25 1.5 1.75 2.0 2.25 2.5 2.75 3.0 4.0 5.0 8.0 10.0 20.0] 调整此数值以消除阴影纹路(Shadow Acne)。数值越大,纹路越少,但影子越容易“飘起”。 + + +#define PCSS_SEARCH_SAMPLES 8 // [0 1 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 48 64] +#define PCSS_FILTER_SAMPLES 16 // [1 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 48 64] //================================================================================================// #include "Common.glsl" vec3 WorldToShadowScreenSpace(in vec3 worldPos) { - vec3 shadowClipPos = transMAD(shadowModelView, worldPos); - shadowClipPos = projMAD(shadowProjection, shadowClipPos); + vec3 shadowClipPos = transMAD(shadowModelView, worldPos); + shadowClipPos = projMAD(shadowProjection, shadowClipPos); - return DistortShadowSpace(shadowClipPos) * 0.5 + 0.5; + return DistortShadowSpace(shadowClipPos) * 0.5 + 0.5; } vec3 WorldToShadowScreenSpace(in vec3 worldPos, out float distortionFactor) { - vec3 shadowClipPos = transMAD(shadowModelView, worldPos); - shadowClipPos = projMAD(shadowProjection, shadowClipPos); + vec3 shadowClipPos = transMAD(shadowModelView, worldPos); + shadowClipPos = projMAD(shadowProjection, shadowClipPos); - distortionFactor = CalcDistortionFactor(shadowClipPos.xy); - return DistortShadowSpace(shadowClipPos, distortionFactor) * 0.5 + 0.5; + distortionFactor = CalcDistortionFactor(shadowClipPos.xy); + return DistortShadowSpace(shadowClipPos, distortionFactor) * 0.5 + 0.5; } //================================================================================================// @@ -28,157 +40,168 @@ uniform sampler2D shadowtex0; uniform sampler2D shadowcolor0; uniform sampler2D shadowcolor1; +// 遮挡物搜索 (PCSS专用) float BlockerSearch(in vec3 shadowScreenPos, in float dither, in float searchScale) { - float blockerDepth = 0.0; - - vec2 searchRadius = searchScale * diagonal2(shadowProjection); - - for (uint i = 0u; i < PCSS_SEARCH_SAMPLES; ++i) { - vec2 sampleCoord = shadowScreenPos.xy + sampleVogelDisk(i, PCSS_SEARCH_SAMPLES, dither) * searchRadius; - - float sampleDepth = texelFetch(shadowtex0, ivec2(sampleCoord * realShadowMapRes), 0).x; - blockerDepth += saturate(shadowScreenPos.z - sampleDepth); - } - - blockerDepth *= -5.0 / float(PCSS_SEARCH_SAMPLES); - return blockerDepth * shadowProjectionInverse[2].z; + float blockerDepth = 0.0; + + // 防止除以0导致的崩溃 + #if PCSS_SEARCH_SAMPLES > 0 + vec2 searchRadius = searchScale * diagonal2(shadowProjection); + + for (uint i = 0u; i < PCSS_SEARCH_SAMPLES; ++i) { + vec2 sampleCoord = shadowScreenPos.xy + sampleVogelDisk(i, PCSS_SEARCH_SAMPLES, dither) * searchRadius; + float sampleDepth = texelFetch(shadowtex0, ivec2(sampleCoord * realShadowMapRes), 0).x; + blockerDepth += saturate(shadowScreenPos.z - sampleDepth); + } + + blockerDepth *= -5.0 / float(PCSS_SEARCH_SAMPLES); + return blockerDepth * shadowProjectionInverse[2].z; + #else + return 0.0; + #endif } +// 水底焦散计算 vec3 CalculateWaterCaustics(in vec3 worldPos, in float waterDepth, in float dither) { - vec3 surfacePos = worldPos - vec3(0.0, 1.0, 0.0); - - float caustics = 0.0; - for (uint i = 0u; i < 16u; ++i) { - vec3 samplePos = worldPos; - samplePos.xz += sampleVogelDisk(i, 16, dither) * 0.15; + vec3 surfacePos = worldPos - vec3(0.0, 1.0, 0.0); + float caustics = 0.0; + for (uint i = 0u; i < 16u; ++i) { + vec3 samplePos = worldPos; + samplePos.xz += sampleVogelDisk(i, 16, dither) * 0.15; - vec2 sampleCoord = WorldToShadowScreenSpace(samplePos).xy; - vec3 waveNormal = OctDecodeUnorm(texture(shadowcolor1, sampleCoord).xy); + vec2 sampleCoord = WorldToShadowScreenSpace(samplePos).xy; + vec3 waveNormal = OctDecodeUnorm(texture(shadowcolor1, sampleCoord).xy); - vec3 refractDir = refract(vec3(0.0, 1.0, 0.0), waveNormal, 1.0 / WATER_IOR); - vec3 refractedPos = samplePos + refractDir * abs(1.0 / refractDir.y); + vec3 refractDir = refract(vec3(0.0, 1.0, 0.0), waveNormal, 1.0 / WATER_IOR); + vec3 refractedPos = samplePos + refractDir * abs(1.0 / refractDir.y); - caustics += saturate(1.0 - 20.0 * distance(surfacePos, refractedPos)); - } - - return -smin(-caustics, -0.1, 0.15) * saturate(exp2(-rLOG2 * waterExtinction * waterDepth)); + caustics += saturate(1.0 - 20.0 * distance(surfacePos, refractedPos)); + } + return -smin(-caustics, -0.1, 0.15) * saturate(exp2(-rLOG2 * waterExtinction * waterDepth)); } +// 核心采样过滤 (PCF/PCSS) vec3 PercentageCloserFilter(in vec3 shadowScreenPos, in vec3 worldPos, in float dither, in float blockerDepth) { - const float rSteps = 1.0 / float(PCSS_FILTER_SAMPLES); - - vec2 penumbraRadius = min(sunAngularRadius * 2.0 * blockerDepth, 0.25) * diagonal2(shadowProjection); - - vec3 result = vec3(0.0); - vec2 waterData = vec2(0.0); - // float sssDepth = 0.0; - - for (uint i = 0u; i < PCSS_FILTER_SAMPLES; ++i) { - vec2 offset = sampleVogelDisk(i, PCSS_FILTER_SAMPLES, dither) * penumbraRadius; - vec2 sampleCoord = shadowScreenPos.xy + offset; - - float sampleDepth1 = textureLod(shadowtex1, vec3(sampleCoord, shadowScreenPos.z), 0).x; - - #ifdef COLORED_SHADOWS - ivec2 sampleTexel = ivec2(sampleCoord * realShadowMapRes); - float sampleDepth0 = texelFetch(shadowtex0, sampleTexel, 0).x; - // sssDepth += saturate(shadowScreenPos.z - sampleDepth0); - - if (step(shadowScreenPos.z, sampleDepth0) != sampleDepth1) { - float waterMask = texelFetch(shadowcolor1, sampleTexel, 0).w; - if (waterMask > EPS) { - waterData += vec2(sampleDepth0 - shadowScreenPos.z, 1.0); - } else { - result += cube(texelFetch(shadowcolor0, sampleTexel, 0).rgb) * sampleDepth1; - } - } else - #endif - result += sampleDepth1; - } - - result *= rSteps; - // sssDepth *= rSteps; - - #ifdef WATER_CAUSTICS - if (waterData.y > EPS) { - waterData.x /= waterData.y; - - float waterDepth = waterData.x * shadowProjectionInverse[2].z * 5.0; - vec3 caustics = CalculateWaterCaustics(worldPos, waterDepth, dither); - result = mix(result, caustics, waterData.y * rSteps); - } - #endif - - // result += exp2(32.0 * shadowProjectionInverse[2].z * sssDepth) * sssAmount; - - return result; + const float rSteps = 1.0 / float(PCSS_FILTER_SAMPLES); + + vec2 penumbraRadius; + // 使用宏定义的 PCF_SOFT_RADIUS 进行模糊半径控制 + #if SHADOW_SOFT_TYPE == 2 + penumbraRadius = min(sunAngularRadius * 2.0 * blockerDepth, 0.25) * diagonal2(shadowProjection); + #elif SHADOW_SOFT_TYPE == 1 + penumbraRadius = PCF_SOFT_RADIUS * diagonal2(shadowProjection); + #else + penumbraRadius = vec2(0.0); + #endif + + vec3 result = vec3(0.0); + vec2 waterData = vec2(0.0); + + for (uint i = 0u; i < PCSS_FILTER_SAMPLES; ++i) { + vec2 offset = sampleVogelDisk(i, PCSS_FILTER_SAMPLES, dither) * penumbraRadius; + vec2 sampleCoord = shadowScreenPos.xy + offset; + float sampleDepth1 = textureLod(shadowtex1, vec3(sampleCoord, shadowScreenPos.z), 0).x; + + #ifdef COLORED_SHADOWS + ivec2 sampleTexel = ivec2(sampleCoord * realShadowMapRes); + float sampleDepth0 = texelFetch(shadowtex0, sampleTexel, 0).x; + + if (step(shadowScreenPos.z, sampleDepth0) != sampleDepth1) { + float waterMask = texelFetch(shadowcolor1, sampleTexel, 0).w; + if (waterMask > EPS) { + waterData += vec2(sampleDepth0 - shadowScreenPos.z, 1.0); + } else { + result += cube(texelFetch(shadowcolor0, sampleTexel, 0).rgb) * sampleDepth1; + } + } else + #endif + result += sampleDepth1; + } + + result *= rSteps; + + #ifdef WATER_CAUSTICS + if (waterData.y > EPS) { + waterData.x /= waterData.y; + float waterDepth = waterData.x * shadowProjectionInverse[2].z * 5.0; + vec3 caustics = CalculateWaterCaustics(worldPos, waterDepth, dither); + result = mix(result, caustics, waterData.y * rSteps); + } + #endif + + return result; } +// 对外接口:计算PCSS/阴影 vec3 CalculatePCSS(in vec3 worldPos, in vec3 normalOffset, in float dither, out float blockerDepth) { - blockerDepth = 0.0; - - float distortionFactor; - vec3 shadowScreenPos = WorldToShadowScreenSpace(worldPos + normalOffset, distortionFactor); - shadowScreenPos.z -= 3e-8 * (1.0 + dither) * shadowProjectionInverse[1].y * distortionFactor; - - vec3 pcss = vec3(1.0); - if (saturate(shadowScreenPos) == shadowScreenPos) { - blockerDepth = BlockerSearch(shadowScreenPos, dither * TAU, 0.15 * distortionFactor); - - const float minRadius = 0.008 / sunAngularRadius; - float sharpenFactor = saturate(blockerDepth * rcp(minRadius)); - - pcss = PercentageCloserFilter(shadowScreenPos, worldPos, dither * TAU, max(blockerDepth, minRadius) * distortionFactor); - pcss = mix(smoothstep(0.3, 0.7, pcss), pcss, sharpenFactor); // Sharpen the edges of the shadow - } - - return pcss; + blockerDepth = 0.0; + float distortionFactor; + + // 应用自定义偏移倍率 + vec3 shadowScreenPos = WorldToShadowScreenSpace(worldPos + normalOffset * SHADOW_BIAS_STRENGTH, distortionFactor); + shadowScreenPos.z -= 3e-8 * (1.0 + dither) * shadowProjectionInverse[1].y * distortionFactor * SHADOW_BIAS_STRENGTH; + + vec3 shadowResult = vec3(1.0); + + if (saturate(shadowScreenPos) == shadowScreenPos) { + #if SHADOW_SOFT_TYPE == 2 + blockerDepth = BlockerSearch(shadowScreenPos, dither * TAU, 0.15 * distortionFactor); + const float minRadius = 0.008 / sunAngularRadius; + float sharpenFactor = saturate(blockerDepth * rcp(minRadius)); + + shadowResult = PercentageCloserFilter(shadowScreenPos, worldPos, dither * TAU, max(blockerDepth, minRadius) * distortionFactor); + shadowResult = mix(smoothstep(0.3, 0.7, shadowResult), shadowResult, sharpenFactor); + #else + shadowResult = PercentageCloserFilter(shadowScreenPos, worldPos, dither * TAU, 0.0); + #endif + } + + return shadowResult; } -//================================================================================================// - +// 屏幕空间接触阴影 (SSS) float ScreenSpaceShadow(in vec3 rayPos, in vec3 viewPos, in float dither, in float sssAmount) { - vec3 rayDir = ViewToScreenPos(viewLightDir * abs(viewPos.z) + viewPos) - rayPos; - rayDir *= minOf((step(0.0, rayDir) - rayPos) / rayDir); - rayDir *= inversesqrt(sdot(rayDir.xy)); + vec3 rayDir = ViewToScreenPos(viewLightDir * abs(viewPos.z) + viewPos) - rayPos; + rayDir *= minOf((step(0.0, rayDir) - rayPos) / rayDir); + rayDir *= inversesqrt(sdot(rayDir.xy)); - vec3 rayStep = rayDir * (0.05 / float(SCREEN_SPACE_SHADOWS_SAMPLES)); - rayPos += (dither + 0.5) * rayStep; + vec3 rayStep = rayDir * (0.05 / float(SCREEN_SPACE_SHADOWS_SAMPLES)); + rayPos += (dither + 0.5) * rayStep; - float viewDistInv = inversesqrt(sdot(viewPos)); - float diffTolerance = 5e-4 * viewDistInv; + float viewDistInv = inversesqrt(sdot(viewPos)); + float diffTolerance = 5e-4 * viewDistInv; float absorption = exp2(-0.125 / (viewDistInv * sssAmount)); - float result = 1.0; + float result = 1.0; - for (uint i = 0u; i < SCREEN_SPACE_SHADOWS_SAMPLES; ++i, rayPos += rayStep) { - if (saturate(rayPos.xy) != rayPos.xy || result < 1e-2) break; + for (uint i = 0u; i < SCREEN_SPACE_SHADOWS_SAMPLES; ++i, rayPos += rayStep) { + if (saturate(rayPos.xy) != rayPos.xy || result < 1e-2) break; - ivec2 sampleTexel = uvToTexel(rayPos.xy); - float sampleDepth = loadDepth0(sampleTexel); - bool hit = abs(sampleDepth - rayPos.z + diffTolerance) < diffTolerance; + ivec2 sampleTexel = uvToTexel(rayPos.xy); + float sampleDepth = loadDepth0(sampleTexel); + bool hit = abs(sampleDepth - rayPos.z + diffTolerance) < diffTolerance; - #if defined LOD_MOD - if (sampleDepth > 1.0 - EPS) { - sampleDepth = loadDepth0Lod(sampleTexel); - sampleDepth = ViewToScreenDepth(ScreenToViewDepthLod(sampleDepth)); - hit = abs(sampleDepth - rayPos.z + diffTolerance) < diffTolerance; - } else - #endif - if (hit) { - vec2 samplePos = rayPos.xy * viewSize + 0.5; - vec2 samplePosFloor = floor(samplePos); - vec2 samplePosFract = samplePos - samplePosFloor; + #if defined LOD_MOD + if (sampleDepth > 1.0 - EPS) { + sampleDepth = loadDepth0Lod(sampleTexel); + sampleDepth = ViewToScreenDepth(ScreenToViewDepthLod(sampleDepth)); + hit = abs(sampleDepth - rayPos.z + diffTolerance) < diffTolerance; + } else + #endif + if (hit) { + vec2 samplePos = rayPos.xy * viewSize + 0.5; + vec2 samplePosFloor = floor(samplePos); + vec2 samplePosFract = samplePos - samplePosFloor; - vec4 sh = textureGather(depthtex0, samplePosFloor * viewPixelSize); - vec2 temp = mix(sh.wx, sh.zy, vec2(samplePosFract.x)); - sampleDepth = mix(temp.x, temp.y, samplePosFract.y); + vec4 sh = textureGather(depthtex0, samplePosFloor * viewPixelSize); + vec2 temp = mix(sh.wx, sh.zy, vec2(samplePosFract.x)); + sampleDepth = mix(temp.x, temp.y, samplePosFract.y); - hit = abs(sampleDepth - rayPos.z + diffTolerance) < diffTolerance; - } + hit = abs(sampleDepth - rayPos.z + diffTolerance) < diffTolerance; + } - result *= saturate(absorption + 1.0 - float(hit)); - } + result *= saturate(absorption + 1.0 - float(hit)); + } - return result; + return result; } \ No newline at end of file diff --git a/shaders/lib/surface/Reflection.glsl b/shaders/lib/surface/Reflection.glsl index 37c27e09..302edec2 100644 --- a/shaders/lib/surface/Reflection.glsl +++ b/shaders/lib/surface/Reflection.glsl @@ -1,57 +1,100 @@ #include "/lib/surface/SSRT.glsl" +// --- 玩家自定义选项 --- +#define FRESNEL_EXPONENT 5.0 // [0.01 0.5 1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0] +#define FRESNEL_STRENGTH 2.0 // [0.01 0.5 1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0] + +// --- 核心:反光区间锁定 (防止死白死黑) --- +#define REF_MIN 0.10 // [0.0 0.01 0.02 0.03 0.04 0.05 0.06 0.07 0.08 0.09 0.1 0.11 0.12 0.13 0.14 0.15 0.16 0.17 0.18 0.19 0.2 0.21 0.22 0.23 0.24 0.25 0.26 0.27 0.28 0.29 0.3 0.31 0.32 0.33 0.34 0.35 0.36 0.37 0.38 0.39 0.4 0.41 0.42 0.43 0.44 0.45 0.46 0.47 0.48 0.49 0.5 0.51 0.52 0.53 0.54 0.55 0.56 0.57 0.58 0.59 0.6 0.61 0.62 0.63 0.64 0.65 0.66 0.67 0.68 0.69 0.7 0.71 0.72 0.73 0.74 0.75 0.76 0.77 0.78 0.79 0.8 0.81 0.82 0.83 0.84 0.85 0.86 0.87 0.88 0.89 0.9 0.91 0.92 0.93 0.94 0.95 0.96 0.97 0.98 0.99 1.0] + +#define REF_MAX 0.85 // [0.0 0.01 0.02 0.03 0.04 0.05 0.06 0.07 0.08 0.09 0.1 0.11 0.12 0.13 0.14 0.15 0.16 0.17 0.18 0.19 0.2 0.21 0.22 0.23 0.24 0.25 0.26 0.27 0.28 0.29 0.3 0.31 0.32 0.33 0.34 0.35 0.36 0.37 0.38 0.39 0.4 0.41 0.42 0.43 0.44 0.45 0.46 0.47 0.48 0.49 0.5 0.51 0.52 0.53 0.54 0.55 0.56 0.57 0.58 0.59 0.6 0.61 0.62 0.63 0.64 0.65 0.66 0.67 0.68 0.69 0.7 0.71 0.72 0.73 0.74 0.75 0.76 0.77 0.78 0.79 0.8 0.81 0.82 0.83 0.84 0.85 0.86 0.87 0.88 0.89 0.9 0.91 0.92 0.93 0.94 0.95 0.96 0.97 0.98 0.99 1.0] + + +// --- 宏定义 --- +#define REFLECTION_GLOBAL +#define REFLECTION_SCREEN_SPACE +#define REFLECTION_SKY +#define LIGHT_REFLECTION_SAMPLES 10 // [1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 24 28 32 36 40 48 64 128 256 512] + vec4 CalculateSpecularReflections(Material material, in vec3 worldNormal, in vec3 screenPos, in vec3 worldDir, in vec3 viewPos, in float skylight, in float dither) { - viewPos += mat3(gbufferModelView) * worldNormal * saturate(length(viewPos) * 3e-4); + + // 1. 基础视角计算 + vec3 viewDirV = normalize(-viewPos); + float VdotN = saturate(dot(viewDirV, worldNormal)); + + // 2. 计算菲涅尔梯度 + float fresnel = pow(1.0 - VdotN, FRESNEL_EXPONENT); -#ifdef ROUGH_REFLECTIONS - if (material.isRough) { - mat3 tbnMatrix = BuildOrthonormalBasis(worldNormal); + // --- [核心逻辑:区间压缩] --- + // 计算原始亮度输入 + float rawIntensity = (skylight * 0.9 + 0.1) * FRESNEL_STRENGTH; + + // 关键点:不再让 center 为 0,不再让 edge 为无穷大 + // 使用 mix 在我们定义的 [MIN, MAX] 区间内插值 + // 这保证了金属表面永远有“灰度差”,也就永远有质感 + float reflectiveLogic = mix(REF_MIN, REF_MAX, fresnel) * rawIntensity; - vec2 noise = SampleStbnVec2(ivec2(gl_FragCoord.xy), frameCounter + 3); - vec3 halfway = tbnMatrix * SampleGGXVNDF(-worldDir * tbnMatrix, material.roughness, noise); + // 应用金属度 + vec3 ambientFallback = vec3(reflectiveLogic) * material.metalness; - vec3 lightDir = reflect(worldDir, halfway); + // 初始化返回向量 + vec4 reflection = vec4(ambientFallback, 30000.0); - float NdotL = dot(worldNormal, lightDir); - if (NdotL < EPS) return vec4(0.0); +#ifndef REFLECTION_GLOBAL + return reflection; +#endif - vec4 reflection = vec4(0.0, 0.0, 0.0, FP16_MAX); - if (skylight > EPS && isEyeInWater == 0) { - vec3 skyRadiance = textureBicubic(skyMapTex, saturate(ProjectSky(lightDir))).rgb; - reflection.rgb = skyRadiance * smoothstep(0.3, 0.7, skylight); - } + // 3. 计算反射方向 + vec3 offsetViewPos = viewPos + mat3(gbufferModelView) * worldNormal * saturate(length(viewPos) * 3e-4); + vec3 reflectWorldDir = reflect(worldDir, worldNormal); + if (dot(worldNormal, reflectWorldDir) < EPS) return reflection; - if (ScreenSpaceRaytrace(viewPos, mat3(gbufferModelView) * lightDir, dither, uint(SSRT_MAX_SAMPLES * oms(material.roughness)), screenPos)) { - float edgeFade = screenPos.x * screenPos.y * oms(screenPos.x) * oms(screenPos.y); - edgeFade *= 1e2 + cube(saturate(1.0 - gbufferModelViewInverse[2].y)) * 1e3; - reflection.rgb += (texture(colortex4, screenPos.xy * 0.5).rgb - reflection.rgb) * saturate(edgeFade); +#ifdef ROUGH_REFLECTIONS + vec3 finalReflectDir = reflectWorldDir; + if (material.isRough) { + mat3 tbnMatrix = BuildOrthonormalBasis(worldNormal); + vec2 noise = SampleStbnVec2(ivec2(gl_FragCoord.xy), frameCounter + 3); + vec3 halfway = tbnMatrix * SampleGGXVNDF(-worldDir * tbnMatrix, material.roughness, noise); + finalReflectDir = reflect(worldDir, halfway); + if (dot(worldNormal, finalReflectDir) < EPS) return reflection; + } +#else + vec3 finalReflectDir = reflectWorldDir; +#endif - ivec2 texel = uvToTexel(screenPos.xy); - vec3 reflectViewPos = ScreenToViewPos(vec3(screenPos.xy, loadDepth0(texel))); - reflection.a = distance(reflectViewPos, viewPos); - } + // 4. 静态天空反射阶段 +#ifdef REFLECTION_SKY + if (skylight > EPS && isEyeInWater == 0) { + vec3 skyRadiance = textureBicubic(skyMapTex, saturate(ProjectSky(finalReflectDir))).rgb; + vec3 skyColor = skyRadiance * smoothstep(0.3, 0.7, skylight); + + // 天空反射也要经过区间限制,防止过曝 + skyColor = min(skyColor, vec3(REF_MAX)); + + reflection.rgb = max(reflection.rgb, skyColor); + } +#endif + + // 5. 屏幕空间反射 (SSR) 阶段 +#ifdef REFLECTION_SCREEN_SPACE + uint maxSamples = uint(LIGHT_REFLECTION_SAMPLES * (material.isRough ? oms(material.roughness) : 1.0)); + + if (ScreenSpaceRaytrace(offsetViewPos, mat3(gbufferModelView) * finalReflectDir, dither, maxSamples, screenPos)) { + float edgeFade = screenPos.x * screenPos.y * oms(screenPos.x) * oms(screenPos.y); + edgeFade = saturate(edgeFade * 50.0); + + vec3 ssrColor = texture(colortex4, screenPos.xy * 0.5).rgb; + + // 同样对 SSR 结果进行亮度锁定,防止那种“不可名状的 Bug” + ssrColor = min(ssrColor, vec3(REF_MAX + 0.2)); + + reflection.rgb = mix(reflection.rgb, ssrColor, edgeFade); - return reflection; - } else + ivec2 texel = uvToTexel(screenPos.xy); + vec3 reflectViewPos = ScreenToViewPos(vec3(screenPos.xy, loadDepth0(texel))); + reflection.a = distance(reflectViewPos, viewPos); + } #endif - { - vec3 lightDir = reflect(worldDir, worldNormal); - - float NdotL = dot(worldNormal, lightDir); - if (NdotL < EPS) return vec4(0.0); - - vec3 reflection = vec3(0.0); - if (skylight > EPS && isEyeInWater == 0) { - vec3 skyRadiance = textureBicubic(skyMapTex, ProjectSky(lightDir)).rgb; - reflection = skyRadiance * smoothstep(0.3, 0.7, skylight); - } - - if (ScreenSpaceRaytrace(viewPos, mat3(gbufferModelView) * lightDir, dither, SSRT_MAX_SAMPLES, screenPos)) { - float edgeFade = screenPos.x * screenPos.y * oms(screenPos.x) * oms(screenPos.y); - edgeFade *= 1e2 + cube(saturate(1.0 - gbufferModelViewInverse[2].y)) * 1e3; - reflection += (texture(colortex4, screenPos.xy * 0.5).rgb - reflection) * saturate(edgeFade); - } - - return vec4(reflection, 0.0); - } + + return reflection; } \ No newline at end of file diff --git a/shaders/lib/surface/SSRT.glsl b/shaders/lib/surface/SSRT.glsl index ff3ea75c..2233847d 100644 --- a/shaders/lib/surface/SSRT.glsl +++ b/shaders/lib/surface/SSRT.glsl @@ -2,11 +2,11 @@ // Morgan McGuire, Michael Mara. "Efficient GPU Screen-Space Ray Tracing". JCGT, 2014. // https://jcgt.org/published/0003/04/04/paper.pdf -#define SSRT_MAX_SAMPLES 20 // [4 8 12 16 18 20 24 28 32 36 40 48 64 128 256 512] +#define SSRT_MAX_SAMPLES 20 // [1 2 4 8 12 16 18 20 24 28 32 36 40 48 64 128 256 512] #define SSRT_SKY_TRACING // #define SSRT_REFINEMENT -#define SSRT_REFINEMENT_STEPS 4 // [2 3 4 5 6 7 8 9 10 12 14 16 18 20 22 24 26 28 30 32] +#define SSRT_REFINEMENT_STEPS 4 // [0 1 2 3 4 5 6 7 8 9 10 12 14 16 18 20 22 24 26 28 30 32] //================================================================================================// diff --git a/shaders/lib/utility/SH.glsl b/shaders/lib/utility/SH.glsl index c186924e..78a4e3eb 100644 --- a/shaders/lib/utility/SH.glsl +++ b/shaders/lib/utility/SH.glsl @@ -66,4 +66,51 @@ vec3 ConvolvedReconstructSH3(in vec3[9] coeff, in vec3 dir) { + coeff[6] * basis[6] * kernel.z + coeff[7] * basis[7] * kernel.z + coeff[8] * basis[8] * kernel.z; +} + +struct AdhocSH2 { + vec4 coeff; + vec2 chroma; +}; + +AdhocSH2 InitAdhocSH2() { + return AdhocSH2(vec4(0.0), vec2(0.0)); +} + +void AddAdhocSH2(inout AdhocSH2 a, in AdhocSH2 b) { + a.coeff += b.coeff; + a.chroma += b.chroma; +} + +void MulAdhocSH2(inout AdhocSH2 a, in float b) { + a.coeff *= b; + a.chroma *= b; +} + +void DivAdhocSH2(inout AdhocSH2 a, in float b) { + a.coeff /= b; + a.chroma /= b; +} + +AdhocSH2 MixAdhocSH2(in AdhocSH2 a, in AdhocSH2 b, in float t) { + return AdhocSH2(mix(a.coeff, b.coeff, t), mix(a.chroma, b.chroma, t)); +} + +vec3 SHToIrradiance(AdhocSH2 sh, in vec3 dir) { + float L = dot(sh.coeff.yzw, dir) * rcp(sh.coeff.x + EPS); + L = L * sqrt(1.0 / 3.0) + sqrt(1.0 / 4.0); + + vec3 irradiance = YCoCgToRGB(vec3(sqrt(4.0 * PI) * sh.coeff.x, sh.chroma)); + return max0(irradiance * L); +} + +AdhocSH2 IrradianceToSH(in vec3 irradiance, in vec3 dir) { + vec3 YCoCg = RGBToYCoCg(irradiance); + + AdhocSH2 sh; + sh.coeff.x = sqrt(1.0 / (4.0 * PI)) * YCoCg.x; + sh.coeff.yzw = sqrt(3.0 / (4.0 * PI)) * YCoCg.x * dir; + sh.chroma = YCoCg.yz; + + return sh; } \ No newline at end of file diff --git a/shaders/lib/water/WaterWave.glsl b/shaders/lib/water/WaterWave.glsl index 49e3590c..45efc482 100644 --- a/shaders/lib/water/WaterWave.glsl +++ b/shaders/lib/water/WaterWave.glsl @@ -4,107 +4,101 @@ const mat2 goldenRotate = mat2(cos(goldenAngle), -sin(goldenAngle), sin(goldenAngle), cos(goldenAngle)); float FetchNoise(in vec2 coord, in float t) { - coord.y = coord.y * 2.0 + t; - return sqr(1.0 - texture(noisetex, coord).z); + coord.y = coord.y * 2.0 + t; + return sqr(1.0 - texture(noisetex, coord).z); } float FetchNoiseSmooth(in vec2 coord, in float t) { - coord.y = coord.y * 2.0 + t; - return sqr(1.0 - textureBicubic(noisetex, coord).z); + coord.y = coord.y * 2.0 + t; + return sqr(1.0 - textureBicubic(noisetex, coord).z); } +// 保留此函数:虽然去掉了视差,但顶点着色器 (vsh) 可能依然需要它来做物理顶点位移 float CalculateWaterHeight(in vec2 position) { - #if RENDER_MODE == 1 - float waveTime = 0.02 * WATER_WAVE_SPEED * frameTimeCounter; - #else - float waveTime = 0.0; - #endif - vec2 pos = 0.0075 * position; - - float waveHeight = WATER_WAVE_HEIGHT * 0.4; - #if !defined PASS_SHADOW - float lfNoise = texture(noisetex, pos * 0.2 + waveTime * 0.1).z; - waveHeight *= saturate(lfNoise * 2.0 - 0.75) + 0.25; - pos += lfNoise * 0.05; - #endif - - float waves = FetchNoise(pos, waveTime); - - pos = goldenRotate * (1.75 * pos) + waves * 0.03; - waveTime *= 1.25; - waves += FetchNoise(pos, waveTime) * 0.75; - - pos = goldenRotate * (1.75 * pos) + waves * 0.03; - waveTime *= 1.25; - waves += FetchNoise(pos, waveTime) * 0.15; - - pos = goldenRotate * (1.5 * pos); - waves += FetchNoise(pos, waveTime) * 0.1; - - return waveHeight * waves; + #if RENDER_MODE == 1 + float waveTime = 0.02 * WATER_WAVE_SPEED * frameTimeCounter; + #else + float waveTime = 0.0; + #endif + vec2 pos = 0.0075 * position; + + float waveHeight = WATER_WAVE_HEIGHT * 0.4; + #if !defined PASS_SHADOW + float lfNoise = texture(noisetex, pos * 0.2 + waveTime * 0.1).z; + waveHeight *= saturate(lfNoise * 2.0 - 0.75) + 0.25; + pos += lfNoise * 0.05; + #endif + + float waves = FetchNoise(pos, waveTime); + + pos = goldenRotate * (1.75 * pos) + waves * 0.03; + waveTime *= 1.25; + waves += FetchNoise(pos, waveTime) * 0.75; + + pos = goldenRotate * (1.75 * pos) + waves * 0.03; + waveTime *= 1.25; + waves += FetchNoise(pos, waveTime) * 0.15; + + pos = goldenRotate * (1.5 * pos); + waves += FetchNoise(pos, waveTime) * 0.1; + + return waveHeight * waves; } +// 全精度高度计算,用于生成细腻的水面法线贴图 float CalculateWaterHeightFull(in vec2 position) { - #if RENDER_MODE == 1 - float waveTime = 0.02 * WATER_WAVE_SPEED * frameTimeCounter; - #else - float waveTime = 0.0; - #endif - vec2 pos = 0.0075 * position; + #if RENDER_MODE == 1 + float waveTime = 0.02 * WATER_WAVE_SPEED * frameTimeCounter; + #else + float waveTime = 0.0; + #endif + vec2 pos = 0.0075 * position; - float waveHeight = WATER_WAVE_HEIGHT * 0.4; - #if !defined PASS_SHADOW - float lfNoise = texture(noisetex, pos * 0.2 + waveTime * 0.1).z; - waveHeight *= saturate(lfNoise * 2.0 - 0.75) + 0.25; - pos += lfNoise * 0.05; - #endif + float waveHeight = WATER_WAVE_HEIGHT * 0.4; + #if !defined PASS_SHADOW + float lfNoise = texture(noisetex, pos * 0.2 + waveTime * 0.1).z; + waveHeight *= saturate(lfNoise * 2.0 - 0.75) + 0.25; + pos += lfNoise * 0.05; + #endif - float waves = FetchNoiseSmooth(pos, waveTime); + float waves = FetchNoiseSmooth(pos, waveTime); - pos = goldenRotate * (1.75 * pos) + waves * 0.03; - waveTime *= 1.25; - waves += FetchNoiseSmooth(pos, waveTime) * 0.75; + pos = goldenRotate * (1.75 * pos) + waves * 0.03; + waveTime *= 1.25; + waves += FetchNoiseSmooth(pos, waveTime) * 0.75; - pos = goldenRotate * (1.75 * pos) + waves * 0.03; - waveTime *= 1.25; - waves += FetchNoiseSmooth(pos, waveTime) * 0.15; + pos = goldenRotate * (1.75 * pos) + waves * 0.03; + waveTime *= 1.25; + waves += FetchNoiseSmooth(pos, waveTime) * 0.15; - pos = goldenRotate * (1.5 * pos); - waves += FetchNoise(pos, waveTime) * 0.1; + pos = goldenRotate * (1.5 * pos); + waves += FetchNoise(pos, waveTime) * 0.1; - pos = goldenRotate * (1.25 * pos); - waves += FetchNoise(pos, waveTime) * 0.1; + pos = goldenRotate * (1.25 * pos); + waves += FetchNoise(pos, waveTime) * 0.1; - return waveHeight * waves; + return waveHeight * waves; } //================================================================================================// +// 基础法线计算 (保留原样) vec3 CalculateWaterNormal(in vec2 position) { - const float delta = 0.05; + const float delta = 0.05; - float height0 = CalculateWaterHeightFull(position); - float height1 = CalculateWaterHeightFull(position + vec2(delta, 0.0)); - float height2 = CalculateWaterHeightFull(position + vec2(0.0, delta)); + float height0 = CalculateWaterHeightFull(position); + float height1 = CalculateWaterHeightFull(position + vec2(delta, 0.0)); + float height2 = CalculateWaterHeightFull(position + vec2(0.0, delta)); - vec2 waveNormal = vec2(height0 - height1, height0 - height2); - return normalize(vec3(waveNormal, delta * (1.0 + dot(fwidth(position), vec2(0.2))))); + vec2 waveNormal = vec2(height0 - height1, height0 - height2); + return normalize(vec3(waveNormal, delta * (1.0 + dot(fwidth(position), vec2(0.2))))); } +// 核心修改:移除视差循环,直接返回基础法线 vec3 CalculateWaterNormal(in vec3 rayPos, in vec3 rayDir) { - const uint steps = WATER_PARALLAX_SAMPLES; - - vec3 rayStep = vec3(rayDir.xy / rayDir.z, 1.0) * inversesqrt(steps); - - float height = CalculateWaterHeight(rayPos.xz); - vec3 offset = vec3(0.0, 0.0, 1.0) + height * rayStep; - - for (uint i = 0u; i < steps && height < offset.z; ++i) { - height = CalculateWaterHeight(rayPos.xz + offset.xy); - offset += (height - offset.z) * rayStep; - } - - return CalculateWaterNormal(rayPos.xz + offset.xy); + // 视差步进已移除,大幅降低 GPU 计算开销 + // 直接使用原始坐标系进行法线采样 + return CalculateWaterNormal(rayPos.xz); } #endif // INCLUDE_WATER_WATERWAVE \ No newline at end of file diff --git a/shaders/program/DeferredLight.frag b/shaders/program/DeferredLight.frag index a07dbd03..a87d6c98 100644 --- a/shaders/program/DeferredLight.frag +++ b/shaders/program/DeferredLight.frag @@ -1,19 +1,20 @@ /* -------------------------------------------------------------------------------- + Revelation Shaders + Copyright (C) 2026 HaringPro + Apache License 2.0 - Revelation Shaders - - Copyright (C) 2026 HaringPro - Apache License 2.0 - - Pass: Deferred lighting and sky combination - Compute specular reflections - + Pass: Deferred lighting and sky combination + Modified: SSS auto-disabled when SHADOW_SOFT_TYPE is 0 (No Filtering) -------------------------------------------------------------------------------- */ #define PASS_DEFERRED_LIGHTING +#ifndef SHADOW_CONTRAST_STRENGTH + #define SHADOW_CONTRAST_STRENGTH 2.0 // [1.0 2.0 3.0 4.0 5.0 6.0 8.0 10.0 20.0 50.0 100.0] +#endif + //======// Utility //=============================================================================// #include "/lib/Utility.glsl" @@ -26,7 +27,6 @@ out vec3 sceneOut; //======// Uniform //=============================================================================// writeonly uniform uimage2D colorimg7; - uniform sampler2D cloudOriginTex; #include "/lib/universal/Uniform.glsl" @@ -55,309 +55,266 @@ uniform sampler2D cloudOriginTex; #include "/lib/lighting/shadow/Render.glsl" #if AO_ENABLED > 0 && !defined SSILVB_ENABLED - #include "/lib/lighting/SSAO.glsl" - #include "/lib/lighting/GTAO.glsl" + #include "/lib/lighting/SSAO.glsl" + #include "/lib/lighting/GTAO.glsl" #endif #include "/lib/SpatialUpscale.glsl" #ifdef RAIN_PUDDLES - #include "/lib/surface/RainPuddle.glsl" + #include "/lib/surface/RainPuddle.glsl" #endif //======// Main //================================================================================// void main() { - ivec2 texelPos = ivec2(gl_FragCoord.xy); + ivec2 texelPos = ivec2(gl_FragCoord.xy); vec2 screenCoord = gl_FragCoord.xy * viewPixelSize; - vec3 screenPos = vec3(screenCoord, loadDepth0(texelPos)); - - #if defined LOD_MOD - bool lodMask = screenPos.z > 1.0 - EPS; - if (lodMask) { - screenPos.z = ViewToScreenDepth(ScreenToViewDepthLod(loadDepth0Lod(texelPos))); - } - #endif - - // Hand-depth correction - if (screenPos.z < 0.56) { - screenPos.z = screenPos.z * rcp(MC_HAND_DEPTH) + (0.5 - 0.5 / MC_HAND_DEPTH); - } - - vec3 viewPos = ScreenToViewPos(screenPos); - - vec3 worldPos = mat3(gbufferModelViewInverse) * viewPos; - vec3 worldDir = normalize(worldPos); - - uvec4 materialPack = loadMaterialPack(texelPos); - uint materialID = materialPack.y; - - vec3 albedo = sRGBToLinear(loadAlbedo(texelPos)); - - float dither = BlueNoise(texelPos, frameCounter); - - sceneOut = vec3(0.0); - - if (materialID == 0u) { // Sky - vec3 transmittance = AtmosphereTransmittanceToPoint(atmosphereViewPos, worldDir); - vec3 skyRadiance = AtmosphereSkyView(atmosphereViewPos, worldDir, worldSunDir); - - sceneOut = skyRadiance; - - #ifdef CLOUDS - #ifdef CLOUD_TAAU_ENABLED - vec4 cloudData = texture(cloudReconstructTex, screenCoord); - #else - // Dither offset - screenCoord += viewPixelSize * (dither - 0.5); - vec4 cloudData = textureBicubic(cloudOriginTex, screenCoord); - #endif - - CompositeClouds(sceneOut, cloudData, worldDir); - transmittance *= cloudData.w; - #endif - - if (dot(transmittance, vec3(1.0)) > EPS) { - vec3 celestial = RenderSun(worldDir, worldSunDir); - vec3 vanillaMoon = albedo; - - #ifdef GALAXY - celestial += mix(RenderGalaxy(worldDir), vanillaMoon, step(0.06, vanillaMoon.g)); - #else - celestial += mix(RenderStars(worldDir), vanillaMoon, step(0.06, vanillaMoon.g)); - #endif - - sceneOut += celestial * transmittance; - } - - imageStore(colorimg7, texelPos, uvec4(0)); - } else { - worldPos += gbufferModelViewInverse[3].xyz; - - vec3 geoNormal, worldNormal; - FetchNormalData(texelPos, geoNormal, worldNormal); - vec3 viewNormal = mat3(gbufferModelView) * worldNormal; - - vec2 lightmap = Unpack2x8U(materialPack.x); - - #if defined MC_SPECULAR_MAP - vec4 specularTex = ExtractSpecularTex(materialPack); - #else - vec4 specularTex = vec4(0.0); - #endif - - // Compute rain puddles - #ifdef RAIN_PUDDLES - if (wetnessCustom > EPS) { - // Skip foliage - if (clamp(materialID, 1000u, 1002u) != materialID) { - CalculateRainPuddles(albedo, worldNormal, specularTex.rgb, worldPos, geoNormal, lightmap.y); - - materialPack.z = Packup2x8U(specularTex.xy); - imageStore(colorimg7, texelPos, materialPack); - } - } - #endif - - Material material = GetMaterialData(specularTex); - - float sssAmount = 0.0; - #if SUBSURFACE_SCATTERING_MODE < 2 - // Hard-coded sss amount for certain materials - switch (materialID) { - case 1000u: case 1001u: case 1002u: case 1003u: case 27u: case 28u: // Plants - sssAmount = 0.6; - break; - case 13u: // Leaves - sssAmount = 0.8; - break; - case 37u: case 39u: // Weak SSS - sssAmount = 0.5; - break; - case 38u: case 51u: // Strong SSS - sssAmount = 0.8; - break; - case 40u: // Particles - sssAmount = 0.3; - break; - } - #endif - #if TEXTURE_FORMAT == 0 && SUBSURFACE_SCATTERING_MODE > 0 && defined MC_SPECULAR_MAP - sssAmount = max(sssAmount, specularTex.b * step(64.5 * rcp255, specularTex.b)); - #endif - - // Remap sss amount to [0, 1] range - sssAmount = linearstep(64.0 * rcp255, 1.0, sssAmount) * eyeSkylightSmooth * SUBSURFACE_SCATTERING_STRENGTH; - - // Cloud shadows - #ifdef CLOUD_SHADOWS - // float cloudShadow = CalculateCloudShadows(worldPos); - vec2 cloudShadowCoord = WorldToCloudShadowScreenPos(worldPos).xy + (dither - 0.5) / textureSize(cloudShadowTex, 0); - float cloudShadow = textureBicubic(cloudShadowTex, saturate(cloudShadowCoord)).x; - #else - float cloudShadow = 1.0 - wetness * 0.96; - #endif - - // Sunlight - vec3 sunlightBase = cloudShadow * saturate(lightmap.y * 1e6 + float(isEyeInWater)) * global.directIlluminance; - vec3 specularDirect = vec3(0.0); - - float worldDistSquared = sdot(worldPos); - float distanceFade = linearstep(shadowDistance - 8.0, shadowDistance, length(worldPos.xz)); - #if defined LOD_MOD - distanceFade = saturate(distanceFade + float(lodMask)); - #endif - - float NdotL = saturate(dot(worldNormal, worldLightDir)); - - // Shadows and SSS + vec3 screenPos = vec3(screenCoord, loadDepth0(texelPos)); + + #if defined LOD_MOD + bool lodMask = screenPos.z > 1.0 - EPS; + if (lodMask) { + screenPos.z = ViewToScreenDepth(ScreenToViewDepthLod(loadDepth0Lod(texelPos))); + } + #endif + + if (screenPos.z < 0.56) { + screenPos.z = screenPos.z * rcp(MC_HAND_DEPTH) + (0.5 - 0.5 / MC_HAND_DEPTH); + } + + vec3 viewPos = ScreenToViewPos(screenPos); + vec3 worldPos = mat3(gbufferModelViewInverse) * viewPos; + vec3 worldDir = normalize(worldPos); + + uvec4 materialPack = loadMaterialPack(texelPos); + uint materialID = materialPack.y; + vec3 albedo = sRGBToLinear(loadAlbedo(texelPos)); + float dither = BlueNoise(texelPos, frameCounter); + + sceneOut = vec3(0.0); + + if (materialID == 0u) { // Sky + vec3 transmittance = AtmosphereTransmittanceToPoint(atmosphereViewPos, worldDir); + vec3 skyRadiance = AtmosphereSkyView(atmosphereViewPos, worldDir, worldSunDir); + sceneOut = skyRadiance; + + #ifdef CLOUDS + #ifdef CLOUD_TAAU_ENABLED + vec4 cloudData = texture(cloudReconstructTex, screenCoord); + #else + screenCoord += viewPixelSize * (dither - 0.5); + vec4 cloudData = textureBicubic(cloudOriginTex, screenCoord); + #endif + CompositeClouds(sceneOut, cloudData, worldDir); + transmittance *= cloudData.w; + #endif + + if (dot(transmittance, vec3(1.0)) > EPS) { + vec3 celestial = RenderSun(worldDir, worldSunDir); + vec3 vanillaMoon = albedo; + #ifdef GALAXY + celestial += mix(RenderGalaxy(worldDir), vanillaMoon, step(0.06, vanillaMoon.g)); + #else + celestial += mix(RenderStars(worldDir), vanillaMoon, step(0.06, vanillaMoon.g)); + #endif + sceneOut += celestial * transmittance; + } + imageStore(colorimg7, texelPos, uvec4(0)); + } else { + worldPos += gbufferModelViewInverse[3].xyz; + vec3 geoNormal, worldNormal; + FetchNormalData(texelPos, geoNormal, worldNormal); + vec3 viewNormal = mat3(gbufferModelView) * worldNormal; + vec2 lightmap = Unpack2x8U(materialPack.x); + + #if defined MC_SPECULAR_MAP + vec4 specularTex = ExtractSpecularTex(materialPack); + #else + vec4 specularTex = vec4(0.0); + #endif + + #ifdef RAIN_PUDDLES + if (wetnessCustom > EPS) { + if (clamp(materialID, 1000u, 1002u) != materialID) { + CalculateRainPuddles(albedo, worldNormal, specularTex.rgb, worldPos, geoNormal, lightmap.y); + materialPack.z = Packup2x8U(specularTex.xy); + imageStore(colorimg7, texelPos, materialPack); + } + } + #endif + + Material material = GetMaterialData(specularTex); + float sssAmount = 0.0; + + // --- 修改点:如果关闭了阴影过滤 (SHADOW_SOFT_TYPE == 0),则彻底跳过 SSS 计算 --- + #if SHADOW_SOFT_TYPE > 0 + #if SUBSURFACE_SCATTERING_MODE < 2 + switch (materialID) { + case 1000u: case 1001u: case 1002u: case 1003u: case 27u: case 28u: sssAmount = 0.6; break; + case 13u: sssAmount = 0.8; break; + case 37u: case 39u: sssAmount = 0.5; break; + case 38u: case 51u: sssAmount = 0.8; break; + case 40u: sssAmount = 0.3; break; + } + #endif + #if TEXTURE_FORMAT == 0 && SUBSURFACE_SCATTERING_MODE > 0 && defined MC_SPECULAR_MAP + sssAmount = max(sssAmount, specularTex.b * step(64.5 * rcp255, specularTex.b)); + #endif + + sssAmount = linearstep(64.0 * rcp255, 1.0, sssAmount) * eyeSkylightSmooth * SUBSURFACE_SCATTERING_STRENGTH; + #endif + + #ifdef CLOUD_SHADOWS + vec2 cloudShadowCoord = WorldToCloudShadowScreenPos(worldPos).xy + (dither - 0.5) / textureSize(cloudShadowTex, 0); + float cloudShadow = textureBicubic(cloudShadowTex, saturate(cloudShadowCoord)).x; + #else + float cloudShadow = 1.0 - wetness * 0.96; + #endif + + vec3 sunlightBase = cloudShadow * saturate(lightmap.y * 1e6 + float(isEyeInWater)) * global.directIlluminance; + vec3 specularDirect = vec3(0.0); + float worldDistSquared = sdot(worldPos); + float distanceFade = linearstep(shadowDistance - 8.0, shadowDistance, length(worldPos.xz)); + #if defined LOD_MOD + distanceFade = saturate(distanceFade + float(lodMask)); + #endif + + float NdotL = saturate(dot(worldNormal, worldLightDir)); + + // Shadows and SSS if (NdotL + sssAmount > EPS) { - vec3 shadow = vec3(saturate(NdotL * FLT_MAX)); - float surfaceDepth = 0.0; - - float normalOffsetBase = (approxSqrt(worldDistSquared) * 2e-3 + 2e-2) * (2.0 - NdotL); - - // PCSS - if (distanceFade < EPS) { - shadow *= CalculatePCSS(worldPos, geoNormal * normalOffsetBase, dither, surfaceDepth); - } - - #ifdef SCREEN_SPACE_SHADOWS - float contactShadow = ScreenSpaceShadow(screenPos, viewPos + viewNormal * normalOffsetBase, dither, sssAmount); - #else - const float contactShadow = 1.0; - #endif - - float LdotV = dot(worldLightDir, -worldDir); - - // Subsurface scattering - if (sssAmount > EPS) { - vec3 beta = approxSqrt(saturate(normalize(albedo))); - vec3 sigmaA = oms(beta) * 16.0 / (sssAmount * SUBSURFACE_SCATTERING_STRENGTH); - vec3 sigmaS = 4.0 * beta * sssAmount; - - float phase = HenyeyGreensteinPhase(-LdotV, 0.7) * 0.25 + uniformPhase * 0.75; - vec3 sss = sigmaS * phase * exp2(-rLOG2 * surfaceDepth * (sigmaS + sigmaA)); - - float cutout = float(clamp(materialID, 1000u, 1003u) == materialID || clamp(materialID, 27u, 28u) == materialID); - sss *= mix(1.0, contactShadow, saturate(distanceFade + cutout * 0.5)); - - sceneOut += sunlightBase * sss * SUBSURFACE_SCATTERING_BRIGHTNESS; - } - if (dot(shadow, vec3(1.0)) > EPS) { - shadow *= contactShadow * sunlightBase; - - // Apply parallax shadows - #ifdef PARALLAX_SHADOW - #if defined PARALLAX && !defined PARALLAX_DEPTH_WRITE - shadow *= oms(loadSceneMain(texelPos).x); - #endif - #endif - - vec3 halfway = normalize(worldLightDir - worldDir); - float NdotV = abs(dot(worldNormal, worldDir)); - float NdotH = dot(worldNormal, halfway); - float LdotH = dot(worldLightDir, halfway); - - sceneOut += shadow * DiffuseBurley(LdotH, NdotV, NdotL, material.roughness); - - #if defined MC_SPECULAR_MAP - vec3 f0 = GetMaterialF0(material.metalness, albedo); - #else - const vec3 f0 = vec3(DEFAULT_DIELECTRIC_F0); - #endif - - specularDirect = shadow * SpecularGGX(LdotH, NdotV, NdotL, NdotH, material.roughness, f0); - } - } - - // Ambient occlusion - #if AO_ENABLED > 0 && !defined SSILVB_ENABLED - vec3 ao = vec3(1.0); - #if AO_ENABLED == 1 - ao.x = CalculateSSAO(screenCoord, viewPos, viewNormal, SampleStbnUnitvec2(texelPos, frameCounter)); - #else - ao.x = CalculateGTAO(screenCoord, viewPos, viewNormal, SampleStbnVec2(texelPos, frameCounter)); - #endif - - #ifdef AO_MULTI_BOUNCE - ao = ApproxMultiBounce(ao.x, albedo); - #else - ao = vec3(ao.x); - #endif - #else - const float ao = 1.0; - #endif - - // Skylight and bounced sunlight - #ifndef SSILVB_ENABLED - if (lightmap.y > EPS) { - // Spherical harmonics skylight - vec3 skylight = ConvolvedReconstructSH3(global.skySH, worldNormal); - sceneOut += skylight * cube(lightmap.y) * ao; - - // Fake bounced light - float bounce = CalculateFakeBouncedLight(worldNormal); - sceneOut += bounce * pow5(lightmap.y) * sunlightBase * ao; - } - #endif - - // Emissive & Blocklight - #if EMISSIVE_MODE > 0 && defined MC_SPECULAR_MAP - sceneOut += material.emissiveness * dot(albedo, vec3(0.75)); - #endif - #if EMISSIVE_MODE < 2 - // Hard-coded emissive - vec4 emissive = HardCodeEmissive(materialID, albedo, worldPos, blocklightColor); - #ifndef SSILVB_ENABLED - if (emissive.a * lightmap.x > EPS) { - lightmap.x = CalculateBlocklightFalloff(lightmap.x); - sceneOut += lightmap.x * emissive.a * (ao * oms(lightmap.x) + lightmap.x) * blocklightColor; - } - #endif - - sceneOut += emissive.rgb * EMISSIVE_BRIGHTNESS; - #elif !defined SSILVB_ENABLED - lightmap.x = CalculateBlocklightFalloff(lightmap.x); - sceneOut += lightmap.x * (ao * oms(lightmap.x) + lightmap.x) * blocklightColor; - #endif - - // Handheld light - #ifdef HANDHELD_LIGHTING - if (heldBlockLightValue + heldBlockLightValue2 > EPS) { - float NdotL = saturate(dot(worldNormal, -worldDir)); - float attenuation = rcp(1.0 + worldDistSquared) * NdotL; - float irradiance = max(heldBlockLightValue, heldBlockLightValue2) * HELD_LIGHT_BRIGHTNESS; - - sceneOut += irradiance * attenuation * blocklightColor; - } - #endif - - // Lightning - sceneOut += LightningContribution(worldPos, worldNormal); - - // Indirect diffuse lighting - #ifdef SSILVB_ENABLED - #ifdef SVGF_ENABLED - float NdotV = abs(dot(worldNormal, worldDir)); - vec3 radiance = UpscaleDiffuseIndirect(texelPos, worldNormal, length(viewPos), NdotV); - #else - vec3 radiance = texelFetch(colortex3, texelPos >> 1, 0).rgb; - #endif - sceneOut += YCoCgToRGB(radiance); - #endif - - // Minimal ambient light - sceneOut += (worldNormal.y * 0.4 + 0.6) * max(MINIMUM_AMBIENT_BRIGHTNESS, 5e-3 * nightVision) * ao; - - // Apply albedo (for diffuse) - sceneOut *= albedo; - - // Metallic diffuse elimination - material.metalness *= 0.2 * lightmap.y + 0.8; - sceneOut *= oms(material.metalness); - - // Direct specular lighting - sceneOut += specularDirect; - } -} + vec3 shadow = vec3(saturate(NdotL * FLT_MAX)); + float surfaceDepth = 0.0; + float normalOffsetBase = (approxSqrt(worldDistSquared) * 2e-3 + 2e-2) * (2.0 - NdotL); + + vec3 rawShadow = vec3(1.0); + + if (distanceFade < EPS) { + // 根据软阴影类型决定是否应用 Contrast + #if SHADOW_SOFT_TYPE == 1 // PCF + rawShadow = CalculatePCSS(worldPos, geoNormal * normalOffsetBase, dither, surfaceDepth); + shadow *= pow(rawShadow, vec3(SHADOW_CONTRAST_STRENGTH)); + shadow = smoothstep(-0.01, 1.01, shadow); + #else // PCSS 或 Hard Shadow + rawShadow = CalculatePCSS(worldPos, geoNormal * normalOffsetBase, dither, surfaceDepth); + shadow *= rawShadow; + #endif + } + + #ifdef SCREEN_SPACE_SHADOWS + float contactShadow = ScreenSpaceShadow(screenPos, viewPos + viewNormal * normalOffsetBase, dither, sssAmount); + #else + const float contactShadow = 1.0; + #endif + + float LdotV = dot(worldLightDir, -worldDir); + + // Subsurface scattering + if (sssAmount > EPS) { + vec3 beta = approxSqrt(saturate(normalize(albedo))); + vec3 sigmaA = oms(beta) * 16.0 / (sssAmount * SUBSURFACE_SCATTERING_STRENGTH); + vec3 sigmaS = 4.0 * beta * sssAmount; + float phase = HenyeyGreensteinPhase(-LdotV, 0.7) * 0.25 + uniformPhase * 0.75; + vec3 sss = sigmaS * phase * exp2(-rLOG2 * surfaceDepth * (sigmaS + sigmaA)); + + // 仅在 PCF 模式下应用 SSS 的掩码补偿 + #if SHADOW_SOFT_TYPE == 1 + float sssMask = saturate(dot(rawShadow, vec3(0.3333))); + sss *= pow(sssMask, 1.2 / SHADOW_CONTRAST_STRENGTH); + #endif + + float cutout = float(clamp(materialID, 1000u, 1003u) == materialID || clamp(materialID, 27u, 28u) == materialID); + sss *= mix(1.0, contactShadow, saturate(distanceFade + cutout * 0.5)); + sceneOut += sunlightBase * sss * SUBSURFACE_SCATTERING_BRIGHTNESS; + } + + if (dot(shadow, vec3(1.0)) > EPS) { + shadow *= contactShadow * sunlightBase; + #ifdef PARALLAX_SHADOW + #if defined PARALLAX && !defined PARALLAX_DEPTH_WRITE + shadow *= oms(loadSceneMain(texelPos).x); + #endif + #endif + + vec3 halfway = normalize(worldLightDir - worldDir); + float NdotV = abs(dot(worldNormal, worldDir)), NdotH = dot(worldNormal, halfway), LdotH = dot(worldLightDir, halfway); + sceneOut += shadow * DiffuseBurley(LdotH, NdotV, NdotL, material.roughness); + + #if defined MC_SPECULAR_MAP + vec3 f0 = GetMaterialF0(material.metalness, albedo); + #else + const vec3 f0 = vec3(DEFAULT_DIELECTRIC_F0); + #endif + specularDirect = shadow * SpecularGGX(LdotH, NdotV, NdotL, NdotH, material.roughness, f0); + } + } + + // ====== 剩余渲染部分保持原样 ====== + #if AO_ENABLED > 0 && !defined SSILVB_ENABLED + vec3 ao = vec3(1.0); + #if AO_ENABLED == 1 + ao.x = CalculateSSAO(screenCoord, viewPos, viewNormal, SampleStbnUnitvec2(texelPos, frameCounter)); + #else + ao.x = CalculateGTAO(screenCoord, viewPos, viewNormal, SampleStbnVec2(texelPos, frameCounter)); + #endif + #ifdef AO_MULTI_BOUNCE + ao = ApproxMultiBounce(ao.x, albedo); + #else + ao = vec3(ao.x); + #endif + #else + const float ao = 1.0; + #endif + + #ifndef SSILVB_ENABLED + if (lightmap.y > EPS) { + vec3 skylight = ConvolvedReconstructSH3(global.skySH, worldNormal); + sceneOut += skylight * cube(lightmap.y) * ao; + float bounce = CalculateFakeBouncedLight(worldNormal); + sceneOut += bounce * pow5(lightmap.y) * sunlightBase * ao; + } + #endif + + #if EMISSIVE_MODE > 0 && defined MC_SPECULAR_MAP + sceneOut += material.emissiveness * dot(albedo, vec3(0.75)); + #endif + #if EMISSIVE_MODE < 2 + vec4 emissive = HardCodeEmissive(materialID, albedo, worldPos, blocklightColor); + #ifndef SSILVB_ENABLED + if (emissive.a * lightmap.x > EPS) { + lightmap.x = CalculateBlocklightFalloff(lightmap.x); + sceneOut += lightmap.x * emissive.a * (ao * oms(lightmap.x) + lightmap.x) * blocklightColor; + } + #endif + sceneOut += emissive.rgb * EMISSIVE_BRIGHTNESS; + #elif !defined SSILVB_ENABLED + lightmap.x = CalculateBlocklightFalloff(lightmap.x); + sceneOut += lightmap.x * (ao * oms(lightmap.x) + lightmap.x) * blocklightColor; + #endif + + #ifdef HANDHELD_LIGHTING + if (heldBlockLightValue + heldBlockLightValue2 > EPS) { + float attenuation = rcp(1.0 + worldDistSquared) * saturate(dot(worldNormal, -worldDir)); + sceneOut += max(heldBlockLightValue, heldBlockLightValue2) * HELD_LIGHT_BRIGHTNESS * attenuation * blocklightColor; + } + #endif + + sceneOut += LightningContribution(worldPos, worldNormal); + + #ifdef SSILVB_ENABLED + #ifdef SVGF_ENABLED + vec3 radiance = UpscaleDiffuseIndirect(texelPos, worldNormal, length(viewPos), abs(dot(worldNormal, worldDir))); + #else + vec3 radiance = texelFetch(colortex3, texelPos >> 1, 0).rgb; + #endif + sceneOut += YCoCgToRGB(radiance); + #endif + + sceneOut += (worldNormal.y * 0.4 + 0.6) * max(MINIMUM_AMBIENT_BRIGHTNESS, 5e-3 * nightVision) * ao; + sceneOut *= albedo; + material.metalness *= 0.2 * lightmap.y + 0.8; + sceneOut *= oms(material.metalness); + sceneOut += specularDirect; + } +} \ No newline at end of file diff --git a/shaders/program/atmosphere/MultiScatter.comp b/shaders/program/atmosphere/MultiScatter.comp index 7d1dcb2e..e857dc9b 100644 --- a/shaders/program/atmosphere/MultiScatter.comp +++ b/shaders/program/atmosphere/MultiScatter.comp @@ -60,7 +60,7 @@ mat2x3 RaymarchMultiScattering(vec3 rayPos, vec3 rayDir, vec3 sunDir) { fms += scatteringIntegral * transmittance * earthShadow; vec3 sunTransmittance = AtmosphereTransmittanceToSun(rayPos, sunDir); - scatteringIntegral *= uniformPhase * sunTransmittance; + scatteringIntegral *= uniformPhase * sunIrradiance * sunTransmittance; lum += scatteringIntegral * transmittance; transmittance *= sampleTransmittance; @@ -74,7 +74,7 @@ mat2x3 RaymarchMultiScattering(vec3 rayPos, vec3 rayDir, vec3 sunDir) { float sunZenithCos = dot(upVector, sunDir); vec3 transmittanceToSun = AtmosphereTransmittanceToSun(planetHeight, sunZenithCos); - lum += atmosphere.groundAlbedo * rPI * saturate(sunZenithCos) * transmittance * transmittanceToSun; + lum += sunIrradiance * atmosphere.groundAlbedo * rPI * saturate(sunZenithCos) * transmittance * transmittanceToSun; } return mat2x3(lum, fms); diff --git a/shaders/program/atmosphere/SkyView.comp b/shaders/program/atmosphere/SkyView.comp index 55dc871d..4bdd1d59 100644 --- a/shaders/program/atmosphere/SkyView.comp +++ b/shaders/program/atmosphere/SkyView.comp @@ -126,7 +126,7 @@ vec3 RenderSkyView(vec2 uv) { lum += RaymarchScattering(atmosphereViewPos, rayDir, -sunDir) * moonlightMult; lum = desaturate(lum, wetness * 0.5); // Post-process - return lum; + return lum * 128.0; } //======// Main //================================================================================// diff --git a/shaders/program/diffuse/Accumulate.frag b/shaders/program/diffuse/Accumulate.frag index 1cd06044..4210df7a 100644 --- a/shaders/program/diffuse/Accumulate.frag +++ b/shaders/program/diffuse/Accumulate.frag @@ -101,7 +101,7 @@ void TemporalFilter(in ivec2 texelPos, in vec3 screenPos, in vec3 worldNormal) { integratedDiffuse.a = min(prevDiffuse.a * confidence + 1.0, SSILVB_MAX_ACCUM_FRAMES); - float mipLevel = 3.0 * saturate(1.0 - integratedDiffuse.a * rcp(8.0)); + float mipLevel = 3.0 * saturate(1.0 - integratedDiffuse.a * rcp(12.0)); integratedDiffuse.rgb = textureLod(colortex3, currCoord, mipLevel).rgb; float alpha = rcp(integratedDiffuse.a); diff --git a/shaders/program/diffuse/EAWF.comp b/shaders/program/diffuse/EAWF.comp index e5dbba11..9a27232e 100644 --- a/shaders/program/diffuse/EAWF.comp +++ b/shaders/program/diffuse/EAWF.comp @@ -107,7 +107,7 @@ void main() { float16_t centerLuma = filteredColVar.r; // We use YCoCg color space float frameIndex = texelFetch(colortex2, texelPos, 0).a; - float historyFactor = saturate(frameIndex * 0.1 - 0.5) * 0.8 + 0.2; + float16_t historyFactor = float16_t(saturate(frameIndex * rcp(12.0)) * 0.9 + 0.1); // float variance = CalculateVariance(ivec2(localPos + padRadius)); float16_t variance = filteredColVar.a; diff --git a/shaders/program/diffuse/VarianceEstimate.comp b/shaders/program/diffuse/VarianceEstimate.comp index 2ba47ab1..3af3e468 100644 --- a/shaders/program/diffuse/VarianceEstimate.comp +++ b/shaders/program/diffuse/VarianceEstimate.comp @@ -34,7 +34,7 @@ writeonly restrict uniform image2D colorimg3; //======// Function //============================================================================// -const uint padRadius = 1; +const uint padRadius = 2; const uvec2 sharedSize = gl_WorkGroupSize.xy + (padRadius << 1); shared uint sharedMoments[sharedSize.y][sharedSize.x]; @@ -78,13 +78,13 @@ void main() { uvec2 sharedTexel = gl_LocalInvocationID.xy + padRadius; vec2 moments = unpackHalf2x16(sharedMoments[sharedTexel.y][sharedTexel.x]); - for (uint i = 0u; i < 8u; ++i) { - ivec2 sampleOffset = offset3x3N[i]; + for (uint i = 0u; i < 24u; ++i) { + ivec2 sampleOffset = offset5x5N[i]; uvec2 sampleTexel = sharedTexel + sampleOffset; vec2 sampleMoments = unpackHalf2x16(sharedMoments[sampleTexel.y][sampleTexel.x]); moments += sampleMoments; } - moments *= rcp(9.0); + moments *= rcp(25.0); ivec2 texelPos = ivec2(gl_GlobalInvocationID.xy); vec4 integratedDiffuse = texelFetch(colortex2, texelPos, 0); @@ -94,6 +94,6 @@ void main() { blocklight = pow5(blocklight) * exp2(-16.0 * integratedDiffuse.x * exposure.value); integratedDiffuse.rgb += RGBToYCoCg(blocklightColor) * saturate(blocklight) * SSILVB_BLENDED_LIGHTMAP; - float variance = 16.0 * max0(moments.y - moments.x * moments.x); + float variance = 32.0 * max0(moments.y - moments.x * moments.x); imageStore(colorimg3, texelPos, vec4(integratedDiffuse.rgb, variance)); } \ No newline at end of file diff --git a/shaders/program/prepare/GenSkySH.comp b/shaders/program/prepare/GenSkySH.comp index fc764772..3404932d 100644 --- a/shaders/program/prepare/GenSkySH.comp +++ b/shaders/program/prepare/GenSkySH.comp @@ -72,7 +72,7 @@ void main() { vec3 skyRadiance = texture(skyMapTex, ProjectSky(direction)).rgb; #ifndef PLANET_GROUND - skyRadiance *= mix(atmosphere.groundAlbedo, vec3(1.0), step(0.0, direction.y)); + skyRadiance *= step(0.0, direction.y) * 0.8 + 0.2; #endif vec3[9] shCoeff; diff --git a/shaders/program/prepare/GlobalStorage.comp b/shaders/program/prepare/GlobalStorage.comp index 1f45671f..c6eb2506 100644 --- a/shaders/program/prepare/GlobalStorage.comp +++ b/shaders/program/prepare/GlobalStorage.comp @@ -39,7 +39,7 @@ void main() { // Compute illuminance vec3 sunIlluminance = sunIrradiance * AtmosphereTransmittanceToSun(atmosphereViewPos, worldSunDir); vec3 moonIlluminance = sunIrradiance * AtmosphereTransmittanceToSun(atmosphereViewPos, -worldSunDir) * moonlightMult; - global.directIlluminance = sunIlluminance + moonIlluminance; + global.directIlluminance = 128.0 * (sunIlluminance + moonIlluminance); // Avoid the misalignment at sunrise and sunset global.directIlluminance *= smoothstep(0.0, 0.01, worldLightDir.y); diff --git a/shaders/settings.glsl b/shaders/settings.glsl index d539ed81..68427048 100644 --- a/shaders/settings.glsl +++ b/shaders/settings.glsl @@ -18,8 +18,8 @@ #define INFO 0 // [0 1 2] #define AUTHOR 0 // [0] -const int shadowMapResolution = 2048; // [1024 2048 4096 8192 16384 32768] -const float shadowDistance = 128.0; // [64.0 80.0 96.0 112.0 128.0 160.0 192.0 224.0 256.0 320.0 384.0 512.0 768.0 1024.0 2048.0 4096.0 8192.0 16384.0 32768.0 65536.0] +const int shadowMapResolution = 2048; // [512 768 1024 2048 4096 8192 16384 32768] +const float shadowDistance = 128.0; // [32.0 48.0 64.0 80.0 96.0 112.0 128.0 160.0 192.0 224.0 256.0 320.0 384.0 512.0 768.0 1024.0 2048.0 4096.0 8192.0 16384.0 32768.0 65536.0] const float shadowDistanceRenderMul = 1.0; // [-1.0 1.0] const float realShadowMapRes = float(shadowMapResolution) * MC_SHADOW_QUALITY; @@ -28,7 +28,7 @@ const float realShadowMapRes = float(shadowMapResolution) * MC_SHADOW_QUALITY; #define SUN_RADIUS_MULT 2.0 // Multiplier of the sun radius (1.0 = real sun radius). [1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0 11.0 12.0 13.0 14.0 15.0 16.0 17.0 18.0 19.0 20.0 21.0 22.0 23.0 24.0 25.0 26.0 27.0 28.0 29.0 30.0 31.0 32.0 33.0 34.0 35.0 36.0 37.0 38.0 39.0 40.0] -const vec3 sunIrradiance = vec3(1.0, 0.949, 0.937) * 126.0; // kW/m^2 +const vec3 sunIrradiance = vec3(1.0, 0.949, 0.937); const float sunAngularRadius = 0.004675 * SUN_RADIUS_MULT; /* Clouds */ @@ -136,15 +136,15 @@ const float sunAngularRadius = 0.004675 * SUN_RADIUS_MULT; #define SSILVB_ENABLED #define SVGF_ENABLED // Enables spatiotemporal variance-guided filtering - // #define SSPT_ENABLED // Enables screen-space path tracing - // #define RSM_ENABLED // Enables reflective shadow maps + #define SSPT_ENABLED // Enables screen-space path tracing + #define RSM_ENABLED // Enables reflective shadow maps #define SSILVB_BLENDED_LIGHTMAP 0.25 // [0.0 0.01 0.02 0.05 0.07 0.1 0.15 0.2 0.25 0.3 0.35 0.4 0.45 0.5 0.6 0.7 0.8 0.9 1.0] - // #define SSPT_BLENDED_LIGHTMAP 0.25 // [0.0 0.01 0.02 0.05 0.07 0.1 0.15 0.2 0.25 0.3 0.35 0.4 0.45 0.5 0.6 0.7 0.8 0.9 1.0] + #define SSPT_BLENDED_LIGHTMAP 0.25 // [0.0 0.01 0.02 0.05 0.07 0.1 0.15 0.2 0.25 0.3 0.35 0.4 0.45 0.5 0.6 0.7 0.8 0.9 1.0] - #define SSILVB_MAX_ACCUM_FRAMES 64.0 // [20.0 24.0 28.0 32.0 36.0 40.0 48.0 56.0 64.0 72.0 80.0 96.0 112.0 128.0 144.0 160.0 192.0 224.0 256.0 320.0 384.0 448.0 512.0 640.0 768.0 896.0 1024.0] - // #define SSPT_MAX_ACCUM_FRAMES 72.0 // [20.0 24.0 28.0 32.0 36.0 40.0 48.0 56.0 64.0 72.0 80.0 96.0 112.0 128.0 144.0 160.0 192.0 224.0 256.0 320.0 384.0 448.0 512.0 640.0 768.0 896.0 1024.0] - // #define RSM_MAX_ACCUM_FRAMES 64.0 // [20.0 24.0 28.0 32.0 36.0 40.0 48.0 56.0 64.0 72.0 80.0 96.0 112.0 128.0 144.0 160.0 192.0 224.0 256.0 320.0 384.0 448.0 512.0 640.0 768.0 896.0 1024.0] + #define SSILVB_MAX_ACCUM_FRAMES 64.0 // [5.0 10.0 15.0 20.0 24.0 28.0 32.0 36.0 40.0 48.0 56.0 64.0 72.0 80.0 96.0 112.0 128.0 144.0 160.0 192.0 224.0 256.0 320.0 384.0 448.0 512.0 640.0 768.0 896.0 1024.0] + #define SSPT_MAX_ACCUM_FRAMES 72.0 // [20.0 24.0 28.0 32.0 36.0 40.0 48.0 56.0 64.0 72.0 80.0 96.0 112.0 128.0 144.0 160.0 192.0 224.0 256.0 320.0 384.0 448.0 512.0 640.0 768.0 896.0 1024.0] + #define RSM_MAX_ACCUM_FRAMES 64.0 // [20.0 24.0 28.0 32.0 36.0 40.0 48.0 56.0 64.0 72.0 80.0 96.0 112.0 128.0 144.0 160.0 192.0 224.0 256.0 320.0 384.0 448.0 512.0 640.0 768.0 896.0 1024.0] /* Ambient Occlusion */ #define OFF 0 @@ -159,7 +159,7 @@ const float sunAngularRadius = 0.004675 * SUN_RADIUS_MULT; #define SCREEN_SPACE_SHADOWS // Enables screen space shadows #define SCREEN_SPACE_SHADOWS_SAMPLES 16 // Sample count of screen space shadows. [2 4 6 8 9 10 12 14 15 16 18 20 22 24 26 28 30 40 50 70 100 150 200 300 500] - // #define SHADOW_BACKFACE_CULLING // Enables backface culling for shadows + #define SHADOW_BACKFACE_CULLING // Enables backface culling for shadows //======// Materials //===========================================================================// diff --git a/shaders/shaders.properties b/shaders/shaders.properties index f33d634e..373f825b 100644 --- a/shaders/shaders.properties +++ b/shaders/shaders.properties @@ -40,7 +40,7 @@ screen.Environment = [Atmosphere] [OuterSpace] \ screen.Fog = BORDER_FOG BORDER_FOG_FALLOFF BLOOMY_FOG BLOOMY_FOG_INTENSITY PER_BIOME_FOG [Volumetric] LAVA_FOG POWDERED_SNOW_FOG BLINDNESS_DARKNESS_FOG - screen.Volumetric = VOLUMETRIC_FOG VF_CLOUD_SHADOWS VF_NOISE_QUALITY VF_TIME_FADE VF_MAX_SAMPLES VF_HEIGHT COLORED_VOLUMETRIC_FOG UW_VOLUMETRIC_FOG VF_RAYLEIGH_DENSITY VF_MIE_DENSITY UW_VF_MAX_SAMPLES VF_MIE_DENSITY_RAIN_MULT + screen.Volumetric = VOLUMETRIC_FOG VF_CLOUD_SHADOWS VF_NOISE_QUALITY VF_TIME_FADE VF_MAX_SAMPLES VF_HEIGHT COLORED_VOLUMETRIC_FOG UW_VOLUMETRIC_FOG VF_RAYLEIGH_DENSITY VF_MIE_DENSITY UW_VF_MAX_SAMPLES VF_SHADOW_QUALITY VF_MIE_DENSITY_RAIN_MULT screen.Foliage = WAVING_FOLIAGE WAVING_FOLIAGE_SPEED WAVING_FOLIAGE_STRENGTH UNLABELLED_FOILAGE_DETECTION screen.Foliage.columns = 1 @@ -49,13 +49,13 @@ screen.Environment = [Atmosphere] [OuterSpace] \ screen.Lighting = [Shadows] [GlobalIllumination] [Reflections] [AmbientOcclusion] [Refractions] [MiscLighting] - screen.GlobalIllumination = SSILVB_ENABLED SSILVB_SLICE_COUNT SSILVB_SAMPLE_COUNT SSILVB_SECTOR_COUNT SSILVB_HIT_THICKNESS SSILVB_BLENDED_LIGHTMAP SVGF_ENABLED SSILVB_MAX_ACCUM_FRAMES + screen.GlobalIllumination = SSILVB_ENABLED SSILVB_SLICE_COUNT SSILVB_SAMPLE_COUNT SSILVB_SECTOR_COUNT SSILVB_HIT_THICKNESS SSILVB_BLENDED_LIGHTMAP SVGF_ENABLED SSILVB_MAX_ACCUM_FRAMES SVGF_QUALITY SVGF_BLUR_RADIUS SSILVB_GI_BRIGHTNESS SSILVB_HAND_DEPTH_THRESHOLD screen.AmbientOcclusion = AO_ENABLED AO_MULTI_BOUNCE SSAO_SAMPLES SSAO_STRENGTH GTAO_SLICES GTAO_DIRECTION_SAMPLES GTAO_RADIUS - screen.Shadows = PCSS_SEARCH_SAMPLES PCSS_FILTER_SAMPLES shadowMapResolution shadowDistance COLORED_SHADOWS SCREEN_SPACE_SHADOWS SHADOW_BACKFACE_CULLING SCREEN_SPACE_SHADOWS_SAMPLES CLOUD_SHADOWS PARALLAX_SHADOW + screen.Shadows = SHADOW_DISTORTION SHADOW_DISTORTION_STRENGTH SHADOW_SOFT_TYPE PCF_SOFT_RADIUS SHADOW_BIAS_STRENGTH PCSS_SEARCH_SAMPLES PCSS_FILTER_SAMPLES shadowMapResolution shadowDistanceRenderMul shadowDistance COLORED_SHADOWS SCREEN_SPACE_SHADOWS SHADOW_BACKFACE_CULLING SCREEN_SPACE_SHADOWS_SAMPLES CLOUD_SHADOWS PARALLAX_SHADOW SHADOW_CONTRAST_STRENGTH - screen.Reflections = ROUGH_REFLECTIONS ROUGH_REFLECTIONS_THRESHOLD REFLECTION_FILTER SSRT_REFINEMENT SSRT_MAX_SAMPLES SSRT_REFINEMENT_STEPS SSRT_SKY_TRACING SPECULAR_IMPORTANCE_SAMPLING_BIAS + screen.Reflections = REFLECTION_SCREEN_SPACE REFLECTION_SKY LIGHT_REFLECTION_SAMPLES ROUGH_REFLECTIONS ROUGH_REFLECTIONS_THRESHOLD REFLECTION_FILTER SSRT_REFINEMENT SSRT_MAX_SAMPLES SSRT_REFINEMENT_STEPS SSRT_SKY_TRACING SPECULAR_IMPORTANCE_SAMPLING_BIAS FRESNEL_EXPONENT FRESNEL_STRENGTH REF_MIN REF_MAX screen.Refractions = RAYTRACED_REFRACTION REFRACTION_STRENGTH @@ -77,7 +77,7 @@ screen.Materials = TEXTURE_FORMAT MOD_BLOCK_SUPPORT \ screen.Emissive = EMISSIVE_MODE EMISSIVE_BRIGHTNESS EMISSIVE_CURVE screen.Emissive.columns = 1 - screen.SubsurfaceScattering = SUBSURFACE_SCATTERING_MODE SUBSURFACE_SCATTERING_STRENGTH SUBSURFACE_SCATTERING_BRIGHTNESS SUBSURFACE_SCATTERING_RADIUS + screen.SubsurfaceScattering = SUBSURFACE_SCATTERING_MODE SUBSURFACE_SCATTERING_STRENGTH SUBSURFACE_SCATTERING_BRIGHTNESS SUBSURFACE_SCATTERING_RADIUS SHADOW_CONTRAST_STRENGTH screen.SubsurfaceScattering.columns = 1 screen.Transparent = WATER_PARALLAX WATER_CAUSTICS WATER_PARALLAX_SAMPLES [WaterParams] GLASS_IOR TRANSLUCENT_ROUGHNESS @@ -127,9 +127,9 @@ sliders = VIEWER_BASE_ALTITUDE ATMOSPHERE_THICKNESS ATMOSPHERE_TURBIDITY SUN_RAD VF_MAX_SAMPLES VF_HEIGHT VF_RAYLEIGH_DENSITY VF_MIE_DENSITY VF_MIE_DENSITY_RAIN_MULT UW_VF_MAX_SAMPLES \ WAVING_FOLIAGE_SPEED WAVING_FOLIAGE_STRENGTH \ AO_ENABLED SSAO_SAMPLES SSAO_STRENGTH GTAO_SLICES GTAO_DIRECTION_SAMPLES GTAO_RADIUS \ - sunPathRotation shadowMapResolution shadowDistance SHADOW_DISTORTION_STRENGTH PCSS_SEARCH_SAMPLES PCSS_FILTER_SAMPLES SCREEN_SPACE_SHADOWS_SAMPLES \ + sunPathRotation shadowMapResolution shadowDistance SHADOW_DISTORTION_STRENGTH PCSS_SEARCH_SAMPLES PCSS_FILTER_SAMPLES SCREEN_SPACE_SHADOWS_SAMPLES PCF_SOFT_RADIUS SHADOW_BIAS_STRENGTH SHADOW_CONTRAST_STRENGTH \ HELD_LIGHT_BRIGHTNESS BLOCKLIGHT_BRIGHTNESS BLOCKLIGHT_COLOR_R BLOCKLIGHT_COLOR_G BLOCKLIGHT_COLOR_B MINIMUM_AMBIENT_BRIGHTNESS NIGHT_BRIGHTNESS \ - SSILVB_SLICE_COUNT SSILVB_SAMPLE_COUNT SSILVB_SECTOR_COUNT SSILVB_HIT_THICKNESS SSILVB_BLENDED_LIGHTMAP SSILVB_MAX_ACCUM_FRAMES \ + SSILVB_SLICE_COUNT SSILVB_SAMPLE_COUNT SSILVB_SECTOR_COUNT SSILVB_HIT_THICKNESS SSILVB_BLENDED_LIGHTMAP SSILVB_MAX_ACCUM_FRAMES SVGF_QUALITY SVGF_BLUR_RADIUS SSILVB_GI_BRIGHTNESS SSILVB_HAND_DEPTH_THRESHOLD \ SSAO_SAMPLES SSAO_STRENGTH GTAO_SLICES GTAO_DIRECTION_SAMPLES GTAO_RADIUS \ WATER_IOR WATER_PARALLAX_SAMPLES WATER_WAVE_HEIGHT WATER_WAVE_SPEED WATER_FOG_DENSITY WATER_ABSORPTION_R WATER_ABSORPTION_G WATER_ABSORPTION_B WATER_SCATTERING_R WATER_SCATTERING_G WATER_SCATTERING_B \ GLASS_IOR TRANSLUCENT_ROUGHNESS REFRACTION_STRENGTH \ @@ -139,7 +139,7 @@ sliders = VIEWER_BASE_ALTITUDE ATMOSPHERE_THICKNESS ATMOSPHERE_TURBIDITY SUN_RAD DEFAULT_DIELECTRIC_F0 \ AGN_STRENGTH AGN_RESOLUTION \ PARALLAX_DEPTH PARALLAX_SAMPLES PARALLAX_REFINEMENT_STEPS \ - ROUGH_REFLECTIONS_THRESHOLD SSRT_MAX_SAMPLES SSRT_REFINEMENT_STEPS SPECULAR_IMPORTANCE_SAMPLING_BIAS \ + ROUGH_REFLECTIONS_THRESHOLD SSRT_MAX_SAMPLES SSRT_REFINEMENT_STEPS SPECULAR_IMPORTANCE_SAMPLING_BIAS FRESNEL_EXPONENT FRESNEL_STRENGTH REF_MIN REF_MAX LIGHT_REFLECTION_SAMPLES \ EMISSIVE_BRIGHTNESS EMISSIVE_CURVE \ SUBSURFACE_SCATTERING_STRENGTH SUBSURFACE_SCATTERING_BRIGHTNESS SUBSURFACE_SCATTERING_RADIUS \ TAA_AGGRESSION TAA_ANTIFLICKER TAA_MAX_ACCUM_FRAMES \ diff --git a/shaders/world0/deferred.vsh b/shaders/world0/deferred.vsh new file mode 100644 index 00000000..262bb8ce --- /dev/null +++ b/shaders/world0/deferred.vsh @@ -0,0 +1,46 @@ +#version 460 compatibility + +/* +-------------------------------------------------------------------------------- + + Revelation Shaders - Deferred Vertex Stage + + Copyright (C) 2026 HaringPro + Apache License 2.0 + +-------------------------------------------------------------------------------- +*/ + +//======// Output //==============================================================================// + +out vec2 texCoord; +out vec3 sunDir; +out vec3 moonDir; +out vec3 worldSunDir; + +//======// Uniform //=============================================================================// + +uniform mat4 gbufferModelView; +uniform mat4 gbufferModelViewInverse; +uniform mat4 shadowModelView; +uniform mat4 shadowProjection; + +uniform vec3 sunPosition; +uniform vec3 moonPosition; + +//======// Main //================================================================================// +void main() { + // 标准全屏四边形变换 + // gl_Vertex 通常是 (-1,-1) 到 (1,1) + gl_Position = vec4(gl_Vertex.xy, 0.0, 1.0); + + // 纹理坐标映射到 [0, 1] + texCoord = gl_Vertex.xy * 0.5 + 0.5; + + // 计算视图空间下的太阳/月亮方向 (用于后续光照计算) + sunDir = normalize(mat3(gbufferModelView) * sunPosition); + moonDir = normalize(mat3(gbufferModelView) * moonPosition); + + // 计算世界空间下的太阳方向 (用于大气散射) + worldSunDir = normalize(sunPosition); +} \ No newline at end of file