threejs加载模型不显示阴影问题分析

科技一点鑫得 2024-03-08 03:20:45

我把chrome恐龙小游戏改造成了3D版本这篇文章中遗留了一个加载的glTF模型显示不出来阴影的问题,最近我找到了问题原因,这里记录下我是怎么解决它的,希望对遇到类似问题的朋友所有帮助。

阴影在计算机图形学中是个复杂且对性能影响很大的属性,一切阴影解决方案都是视觉和资源消耗的平衡,threejs中阴影并不是默认开启的,它需要通过配置来开启,这就一定程度增加了代码的复杂度。开启阴影一般需要下面几个步骤:

开启渲染器阴影属性const renderer = new WebGLRenderer({antialias: true});// threejs默认使用的是阴影贴图的渲染方式,需要使能才能进行阴影渲染renderer.shadowMap.enabled = true;设置光能投射阴影

场景中可以添加多个光源,如果每个光源都能够产生阴影的话,那场景渲染次数就会大大增加,性能也会急剧下降,一般情况下只对少量的灯光开启投射阴影的功能,castShadow为true表示开启光源的投射阴影功能。

const directionalLight = new DirectionalLight(0xFFFFFF, 1);directionalLight.castShadow = true;设置网格Mesh能被投射阴影、能投射阴影

场景中的网格Mesh,也就是现实中的物体表面会被投射出阴影,同时网格也可能因为遮挡关系将自身的阴影投射到其他的网格上,也就是说threejs中网格Mesh既可以被投射阴影、也可以投射阴影,根据具体情况进行开启,castShadow表示投射阴影,receiveShadow表示被投射阴影。

// 加载的模型开启投射阴影和被投射阴影const gltfLoader = new GLTFLoader();const url = "/modes/Velociraptor.glb";gltfLoader.load(url, (gltf) => { const root = gltf.scene; root.position.set(0, 0, 15); scene.add(root); console.log(root); root.traverse((obj) => { if (obj.castShadow !== undefined) { // 开启投射影响 obj.castShadow = true; // 开启被投射阴影 obj.receiveShadow = true; } });});// 创建一个扁平的长方体模拟地面,地面开启被投射阴影接受模型投射的阴影const box = new BoxGeometry(100, 0.2, 100);const material = new MeshStandardMaterial({color: "lightgray"});const mesh = new Mesh(box, material);mesh.position.set(0, -0.1, 0);mesh.receiveShadow = true;scene.add(mesh);

按理说使出这三板斧之后,阴影就应该显现出来了,但是我第一次尝试加载的模型就是没有显示出阴影,更奇怪的是我尝试了直接使用最简单的立方体、加载其他的模型进行测试竟然发现立方体可以正常显示阴影,而有的模型也能显示出阴影,这让我一度怀疑是不是使用的模型文件有什么玄机。最终解决了这个问题之后才发现官方文档其实已经给出了解决办法,只是当时还没有清晰理解threejs阴影的知识点再加上测试结果的迷惑性让我找错了解决问题的方向。尽管如此,我觉得对于新手来说这个阴影不显示的问题应该是很容易出现的,因为模型尺寸、位置各种各样,如果不清楚怎么排查这个问题的话就很容易抓瞎找不到解决问题的方向。

接下来在介绍如何排查问题之前先说明一个概念,threejs中的光会有一个光投影相机,这也是一个性能的折中的考虑,光计算机投影时只考虑在光投影相机范围内的场景进行阴影渲染,之外的一概忽略,光投影相机是一个正交投影相机,它的观察点位于光源的位置,默认投向坐标原点,可以通过将光投影摄像机的范围在场景中显示出来来排查原因。

//光投影相机const cam = directionalLight.shadow.camera;const cameraHelper = new CameraHelper( cam );scene.add( cameraHelper );cameraHelper.visible = true;// 光源const helper = new DirectionalLightHelper( directionalLight );scene.add( helper );helper.visible = true;

下图橘色边框包围的长方体区域就是光投影相机覆盖的区域,刚好加载的恐龙模型没有放置在其区域内,因此阴影被忽略了。通过添加光投影相机辅助可以一目了然地发现问题所在,默认情况下光投影相机区域是一个长宽高为10x10x500的长方体区域,光源投射方向为通过坐标原点。

因为加载的模型尺寸、位置各异,正确的做法是根据场景动态调整光源的投影相机区域,这里仅给出调整光投影相机区域的示例供参考

// 添加灯光const directionalLight = new DirectionalLight(0xFFFFFF, 1);directionalLight.castShadow = true;directionalLight.position.set(20, 20, -10);// shadowmap的区域,值越大阴影质量越好directionalLight.shadow.mapSize.width = 2048;directionalLight.shadow.mapSize.height = 2048;// 没有产生阴影的关键设置在这里const cam = directionalLight.shadow.camera;console.log(cam);cam.near = 1;cam.far = 500;cam.left = -100;cam.right = 100;cam.top = 100;cam.bottom = -100;//光投影相机const cameraHelper = new CameraHelper( cam );scene.add( cameraHelper );cameraHelper.visible = true;// 光源const helper = new DirectionalLightHelper( directionalLight );scene.add( helper );helper.visible = true;

调整了投影相机投影区域后发现模型阴影已经可以正常显示了

参考文献

[1]. 阴影 - three.js manual (threejs.org)[2]. 加载 .gltf 文件 - three.js manual (threejs.org)

0 阅读:0

科技一点鑫得

简介:感谢大家的关注