当前位置: 首页 > news >正文

第十九节:阴影进阶 - 软阴影与性能平衡技术

第十九节:阴影进阶 - 软阴影与性能平衡技术

PCF滤波与级联阴影映射实战

在这里插入图片描述

1. 核心概念解析

1.1 阴影映射原理
技术原理优点缺点
阴影贴图从光源视角渲染深度图实现简单,通用性强锯齿明显,分辨率依赖
PCF软阴影多重采样深度图边缘平滑边缘柔和,视觉效果自然性能开销较大
VSM使用方差计算阴影概率软阴影质量高,性能较好容易出现光渗现象
CSM多个阴影贴图覆盖不同距离区域远近阴影质量均衡实现复杂,内存占用高
1.2 阴影质量影响因素
阴影质量
分辨率
滤波算法
偏移设置
裁剪策略
高分辨率-高质量
低分辨率-低质量
PCF-柔和边缘
None-硬边缘
适当偏移-无失真
过大偏移-彼得潘现象
CSM-远近均衡
单一贴图-近处模糊

2. PCF软阴影实现

2.1 基础PCF实现
// 创建方向光与阴影
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(5, 10, 7);
directionalLight.castShadow = true;// 阴影贴图配置
directionalLight.shadow.mapSize.width = 2048; // 分辨率
directionalLight.shadow.mapSize.height = 2048;
directionalLight.shadow.camera.near = 0.5;    // 裁剪平面
directionalLight.shadow.camera.far = 50;
directionalLight.shadow.camera.top = 20;      // 正交相机范围
directionalLight.shadow.camera.right = 20;
directionalLight.shadow.camera.bottom = -20;
directionalLight.shadow.camera.left = -20;// 启用PCF软阴影
directionalLight.shadow.radius = 3; // PCF采样半径// 添加到场景
scene.add(directionalLight);
2.2 自定义PCF着色器
// 自定义PCF软阴影材质
const softShadowMaterial = new THREE.ShaderMaterial({uniforms: {shadowMap: { value: directionalLight.shadow.map },lightProjectionMatrix: { value: directionalLight.shadow.camera.projectionMatrix },lightViewMatrix: { value: directionalLight.shadow.camera.matrixWorldInverse },shadowBias: { value: 0.003 },shadowRadius: { value: 3.0 }},vertexShader: `varying vec4 vShadowCoord;void main() {gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);// 计算阴影坐标vShadowCoord = lightProjectionMatrix * lightViewMatrix * modelMatrix * vec4(position, 1.0);}`,fragmentShader: `uniform sampler2D shadowMap;uniform float shadowBias;uniform float shadowRadius;varying vec4 vShadowCoord;// PCF采样函数float pcfShadow(sampler2D shadowMap, vec2 uv, float compare, float radius) {float result = 0.0;int samples = 0;for(int x = -2; x <= 2; x++) {for(int y = -2; y <= 2; y++) {vec2 offset = vec2(x, y) * radius / textureSize(shadowMap, 0);float depth = texture2D(shadowMap, uv + offset).r;result += compare > depth + shadowBias ? 0.0 : 1.0;samples++;}}return result / float(samples);}void main() {// 透视除法vec3 shadowCoord = vShadowCoord.xyz / vShadowCoord.w;shadowCoord = shadowCoord * 0.5 + 0.5; // 转换到[0,1]范围// 检查是否在阴影贴图范围内if(shadowCoord.z > 1.0 || shadowCoord.x < 0.0 || shadowCoord.x > 1.0 || shadowCoord.y < 0.0 || shadowCoord.y > 1.0) {gl_FragColor = vec4(1.0); // 不在范围内,完全照亮return;}// 计算阴影float shadow = pcfShadow(shadowMap, shadowCoord.xy, shadowCoord.z, shadowRadius);gl_FragColor = vec4(vec3(shadow), 1.0);}`
});

3. 级联阴影映射(CSM)

3.1 CSM原理与配置
// 创建CSM实例
import { CSM } from 'three/addons/csm/CSM.js';const csm = new CSM({maxFar: 100,           // 最远视距cascades: 4,           // 级联数量mode: 'practical',     // 模式: practical, logarithmic, uniformparent: scene,         // 父场景shadowMapSize: 1024,   // 每个级联的阴影贴图大小lightDirection: new THREE.Vector3(1, -1, 1).normalize(),camera: camera         // 主相机
});// 更新CSM(每帧调用)
function updateCSM() {csm.update(camera.matrix);
}// 材质适配(需要支持接收阴影)
csm.setupMaterial(mesh.material);
3.2 自定义CSM着色器
// CSM兼容的自定义着色器
const csmMaterial = new THREE.ShaderMaterial({defines: {USE_CSM: 1,CSM_CASCADES: 4,     // 与CSM实例中的级联数量一致CSM_FADE: 1          // 启用级联间渐变},uniforms: {...THREE.UniformsLib['csm'], // 包含CSM所需uniforms// 其他自定义uniforms},vertexShader: `#include <common>#include <csm_pars_vertex>void main() {#include <csm_vertex>// 其他顶点着色器代码}`,fragmentShader: `#include <common>#include <csm_pars_fragment>void main() {#include <csm_fragment>// 其他片元着色器代码}`
});

4. 性能优化策略

4.1 阴影贴图分辨率优化
// 动态分辨率调整
function adjustShadowResolution(quality) {let resolution;switch(quality) {case 'low':resolution = 512;directionalLight.shadow.radius = 1; // 减少PCF采样break;case 'medium':resolution = 1024;directionalLight.shadow.radius = 2;break;case 'high':resolution = 2048;directionalLight.shadow.radius = 3;break;case 'ultra':resolution = 4096;directionalLight.shadow.radius = 4;break;}directionalLight.shadow.mapSize.width = resolution;directionalLight.shadow.mapSize.height = resolution;directionalLight.shadow.map.dispose(); // 释放旧贴图directionalLight.shadow.map = null;    // 强制重新创建
}// 基于性能自动调整
let frameTime = 0;
let lastTime = performance.now();function checkPerformance() {const currentTime = performance.now();const delta = currentTime - lastTime;lastTime = currentTime;// 计算平均帧时间frameTime = frameTime * 0.9 + delta * 0.1;// 根据帧率调整阴影质量if (frameTime > 18) { // 低于55FPSadjustShadowResolution('medium');} else if (frameTime > 25) { // 低于40FPSadjustShadowResolution('low');} else if (frameTime < 12) { // 高于83FPSadjustShadowResolution('high');}
}
4.2 基于距离的阴影优化
// 距离相关的阴影质量调整
function updateShadowQualityByDistance() {// 计算相机到场景中心的距离const sceneCenter = new THREE.Vector3();sceneCenter.setFromMatrixPosition(scene.matrixWorld);const distance = camera.position.distanceTo(sceneCenter);// 根据距离调整阴影参数if (distance > 50) {// 远距离:降低质量directionalLight.shadow.mapSize.width = 1024;directionalLight.shadow.mapSize.height = 1024;directionalLight.shadow.radius = 1;} else if (distance > 20) {// 中距离:中等质量directionalLight.shadow.mapSize.width = 2048;directionalLight.shadow.mapSize.height = 2048;directionalLight.shadow.radius = 2;} else {// 近距离:高质量directionalLight.shadow.mapSize.width = 4096;directionalLight.shadow.mapSize.height = 4096;directionalLight.shadow.radius = 3;}
}
4.3 可见性检测与裁剪
// 基于视锥体的阴影优化
const shadowCamera = directionalLight.shadow.camera;
const frustum = new THREE.Frustum();
const cameraViewProjectionMatrix = new THREE.Matrix4();function updateShadowCulling() {// 更新相机视锥体cameraViewProjectionMatrix.multiplyMatrices(camera.projectionMatrix,camera.matrixWorldInverse);frustum.setFromProjectionMatrix(cameraViewProjectionMatrix);// 检测场景中的对象scene.traverse((object) => {if (object.isMesh && object.castShadow) {const sphere = new THREE.Sphere();object.geometry.boundingSphere.clone().applyMatrix4(object.matrixWorld);// 如果对象不在视锥体内,不投射阴影object.visible = frustum.intersectsSphere(sphere);}});// 调整阴影相机范围以匹配视锥体const cameraFrustum = getCameraFrustumInLightSpace();shadowCamera.left = cameraFrustum.min.x;shadowCamera.right = cameraFrustum.max.x;shadowCamera.bottom = cameraFrustum.min.y;shadowCamera.top = cameraFrustum.max.y;shadowCamera.updateProjectionMatrix();
}

5. 完整案例:高质量动态阴影系统

class DynamicShadowSystem {constructor(scene, camera) {this.scene = scene;this.camera = camera;this.lights = [];this.csm = null;this.init();}init() {// 创建主方向光this.mainLight = new THREE.DirectionalLight(0xffffff, 1);this.mainLight.position.set(10, 15, 10);this.mainLight.castShadow = true;this.configureShadow(this.mainLight);this.scene.add(this.mainLight);// 创建辅助填充光this.fillLight = new THREE.DirectionalLight(0x4455aa, 0.3);this.fillLight.position.set(-5, 5, -5);this.scene.add(this.fillLight);// 初始化CSMif (this.supportsCSM()) {this.initCSM();}// 性能监控this.stats = {frameTime: 0,shadowUpdates: 0};}configureShadow(light) {light.shadow.mapSize.width = 2048;light.shadow.mapSize.height = 2048;light.shadow.camera.near = 0.5;light.shadow.camera.far = 50;light.shadow.camera.top = 20;light.shadow.camera.right = 20;light.shadow.camera.bottom = -20;light.shadow.camera.left = -20;light.shadow.radius = 2;light.shadow.bias = -0.001;}supportsCSM() {// 检测设备是否支持CSMreturn !navigator.userAgent.match(/(iPhone|iPod|iPad|Android)/) &&renderer.capabilities.maxTextures >= 8;}initCSM() {this.csm = new CSM({maxFar: 100,cascades: 4,mode: 'practical',parent: this.scene,shadowMapSize: 1024,lightDirection: this.mainLight.position.clone().normalize(),camera: this.camera});}update() {const startTime = performance.now();// 更新光源方向const time = Date.now() * 0.001;this.mainLight.position.x = Math.cos(time) * 15;this.mainLight.position.z = Math.sin(time) * 15;// 更新CSMif (this.csm) {this.csm.update(this.camera.matrix);this.csm.lightDirection = this.mainLight.position.clone().normalize();} else {// 更新传统阴影相机this.mainLight.shadow.camera.updateProjectionMatrix();this.mainLight.target.position.set(0, 0, 0);this.mainLight.target.updateMatrixWorld();}// 性能自适应this.adaptiveQuality();this.stats.shadowUpdates++;this.stats.frameTime = performance.now() - startTime;}adaptiveQuality() {// 基于性能调整阴影质量const targetFPS = 60;const targetFrameTime = 1000 / targetFPS;if (this.stats.frameTime > targetFrameTime * 1.2) {// 性能不足,降低质量this.reduceShadowQuality();} else if (this.stats.frameTime < targetFrameTime * 0.8) {// 性能充足,提高质量this.increaseShadowQuality();}}reduceShadowQuality() {if (this.csm) {this.csm.shadowMapSize = Math.max(512, this.csm.shadowMapSize / 2);} else {this.mainLight.shadow.mapSize.width = Math.max(1024, this.mainLight.shadow.mapSize.width / 2);this.mainLight.shadow.mapSize.height = Math.max(1024, this.mainLight.shadow.mapSize.height / 2);this.mainLight.shadow.radius = Math.max(1, this.mainLight.shadow.radius - 1);}}increaseShadowQuality() {if (this.csm) {this.csm.shadowMapSize = Math.min(2048, this.csm.shadowMapSize * 2);} else {this.mainLight.shadow.mapSize.width = Math.min(4096, this.mainLight.shadow.mapSize.width * 2);this.mainLight.shadow.mapSize.height = Math.min(4096, this.mainLight.shadow.mapSize.height * 2);this.mainLight.shadow.radius = Math.min(4, this.mainLight.shadow.radius + 1);}}// 为材质启用阴影支持setupMaterial(material) {if (this.csm) {this.csm.setupMaterial(material);}// 确保材质接收和投射阴影material.needsUpdate = true;}
}// 使用示例
const shadowSystem = new DynamicShadowSystem(scene, camera);function animate() {requestAnimationFrame(animate);shadowSystem.update();renderer.render(scene, camera);
}// 为场景中的材质启用阴影
scene.traverse((object) => {if (object.isMesh) {object.castShadow = true;object.receiveShadow = true;shadowSystem.setupMaterial(object.material);}
});

6. 注意事项与重点核心

6.1 阴影失真问题解决

彼得潘现象(阴影分离)

  • 原因:阴影偏移过大
  • 解决:减小shadow.bias值或使用自动偏移
directionalLight.shadow.bias = -0.001; // 轻微负值可减少彼得潘现象
directionalLight.shadow.autoUpdate = true; // 启用自动更新

阴影痤疮(表面自阴影)

  • 原因:阴影偏移不足
  • 解决:增加shadow.bias值或使用法线偏移
directionalLight.shadow.bias = 0.003;
directionalLight.shadow.normalBias = 0.04; // 沿法线方向偏移
6.2 性能监控与调试
// 阴影性能监控面板
const shadowDebug = {enabled: true,showShadowCamera: false,visualizeCascades: false
};// 阴影相机辅助器
const shadowHelper = new THREE.CameraHelper(directionalLight.shadow.camera);
shadowHelper.visible = shadowDebug.showShadowCamera;
scene.add(shadowHelper);// 级联可视化
if (csm) {csm.showCascades = shadowDebug.visualizeCascades;
}// 性能统计
const stats = new Stats();
stats.showPanel(2); // 显示FPS面板
document.body.appendChild(stats.dom);function animate() {stats.begin();// 渲染逻辑stats.end();
}
6.3 移动端优化策略
  1. 降低分辨率:使用512x512或1024x1024阴影贴图
  2. 减少PCF采样:设置shadow.radius = 1或使用硬阴影
  3. 限制级联数量:CSM最多使用2级级联
  4. 禁用远处阴影:根据距离动态禁用阴影投射
  5. 使用烘焙阴影:静态场景使用光照贴图替代实时阴影

下一节预告:3D文本渲染

第二十节:字体几何体生成与特效

核心内容

  • FontLoader字体加载与解析
  • TextGeometry参数详解与自定义
  • 文字描边、挤出与材质特效
  • 性能优化与大文本渲染技术

重点掌握:创建各种风格的3D文字效果,从基础文字到高级特效渲染。

http://www.dtcms.com/a/351518.html

相关文章:

  • FileMenu Tools for Win:突破 Windows 文件管理困境的利器
  • Git:基本使用
  • 数字化转型三阶段:从信息化、数字化到数智化的战略进化
  • Leetcode+Java+动态规划II
  • 知行——同为科技24周年庆典
  • Thingsboard 租户管理员权限,增加租户普通用户权限
  • Go errgroup:高效并发控制与错误处理
  • WPF基于LiveCharts2图形库,实现:折线图,柱状图,饼状图
  • 03. 协程入门_Android异步处理机制
  • 系统架构设计师备考第7天——网络协议中间件软件构件
  • WebSocket简单了解
  • 线性代数之深入理解旋转矩阵
  • lesson46-2:Linux 高级指令全解析:从文件操作到系统管理
  • mybatisplus 配置二级缓存
  • 【系统编程】线程简介
  • 【人工智能】2025年AI代理开源革命:社区驱动的智能体生态重塑未来
  • Linux--seLinux的概述
  • FRET、PLA、Co-IP和GST pull-down有何区别? 应该如何选择?
  • 原型模式系统开发中的原型分类全景:水平、垂直、抛弃式与演化式
  • nvm切换node版本之后报错,无法将“node”项识别为 cmdlet、函数、脚本文件或可运行程序的名称
  • 嵌入式C语言进阶:结构体封装函数的艺术与实践
  • IUV5G专网排障(上)
  • 支持向量机(SVM)学习笔记
  • SOME/IP服务发现PRS_SOMEIPSD_00277的解析
  • 服务器数据恢复—热备盘上线失败如何恢复数据?
  • 【Android】webview强制Crash后再自恢复设计
  • 服务器初始化
  • 影响服务器托管费用的因素​
  • ROS2 Helloworld 入门——包含完整pdf手册
  • Linux驱动开发笔记(九)——内核定时器