【Threejs基础教程-光影篇】5.2 Threejs 阴影系统

news2024/11/24 17:03:27

5.2 Threejs阴影系统

  • 学习ThreeJS的捷径
  • 在用光影系统之前
    • threejs是实时光影
    • web端目前没有优质的实时光影
    • 实时光影会大幅增加渲染压力
    • 没有独显的电脑不建议添加实时光影
  • 阴影配置
    • 什么样的灯光可以产生阴影
    • 什么样的物体可以产生阴影和接受阴影
    • 注意开启阴影渲染
    • 灵活运用阴影
  • 平行光阴影
  • 点光源阴影
  • 聚光灯阴影
  • 优化阴影
    • 阴影范围外一片漆黑,可以优化一下吗
    • 增加阴影的精度
    • 消除伪影
  • 静态阴影渲染 / 渐进式阴影渲染
  • 烘培阴影
  • 使用阴影时需要注意的点
  • Threejs基础教程在此完结,后续请查阅本人的: Threejs进阶教程

学习ThreeJS的捷径

本段内容会写在0篇以外所有的,本人所编写的Threejs教程中

对,学习ThreeJS有捷径
当你有哪个函数不懂的时候,第一时间去翻一翻文档
当你有哪个效果不会做的时候,第一时间去翻一翻所有的案例,也许就能找到你想要的效果
最重要的一点,就是,绝对不要怕问问题,越怕找找别人问题,你的问题就会被拖的越久

如果你确定要走WebGL/ThreeJS的开发者路线的话,以下行为可以让你更快的学习ThreeJS

  1. 没事就把所有的文档翻一遍,哪怕看不懂,也要留个印象,至少要知道Threejs有什么
  2. 没事多看看案例效果,当你记忆的案例效果足够多时,下次再遇到相似问题时,你就有可能第一时间来找对应的案例,能更快解决你自己的问题
  3. 上述案例不只是官网的案例,郭隆邦技术博客,跃焱邵隼,暮志未晚等站点均有不少优质案例,记得一并收藏
    http://www.yanhuangxueyuan.com/ 郭隆邦技术博客
    https://www.wellyyss.cn/ 跃焱邵隼
    http://www.wjceo.com/ 暮志未晚 (暮老的站点最近挂了,如果有人发现了最新的地址请告知博主)
    这三个站点是我最常逛的站点,推荐各位有事没事逛一下,看看他们的案例和写法思路,绝对没坏处

在用光影系统之前

threejs是实时光影

实时光影是指:光影会随着灯光的改变而改变,且变化速率非常高,参与渲染过程,

web端目前没有优质的实时光影

实时光影一般在B端不会有太好的效果,只有在C端环境下,且需要更强力的硬件支持,才能达到更好的实时光影渲染效果,像实时光追系统,就是C端专属的功能,且对显卡消耗也是巨大,也许未来某一天,随着WebGPU发展的更多,对显卡的使用更加完美后,实时光影或许也会登陆B端,这里我们可以暂且期待一下

实时光影会大幅增加渲染压力

物体越多,光影渲染增加的渲染压力就越高,对大多数情况下,渲染压力会增加一倍,如果你的设备,本身能承担的面数,大概是1000万,那么你用了实时光影后,大概这个极限会缩减到500万甚至更低,所以谨慎使用实时光影

元素越多,点线面越多,都会对最终实时光影的渲染计算量有影响

没有独显的电脑不建议添加实时光影

没有独显的电脑,本身渲染压力已经非常大了,再添加光影,cpu只会压力更大

笔记本一定要看清楚,你的浏览器是否使用独显渲染,别让CPU干渲染的活

阴影配置

什么样的灯光可以产生阴影

在目前threejs的系统下,一共有三个灯光可以产生阴影,分别是

PointLight 点光源
DirectionalLight 平行光
SpotLight 聚光灯

什么样的物体可以产生阴影和接受阴影

不是所有的物体都可以产生阴影和接受阴影

灯光只能产生阴影,不能接收阴影

可以产生并接受阴影的物体有:Mesh,Line等

不能直接产生阴影且不能接收阴影的有:Object3D,Group,

完全不产生阴影且不能接收阴影的有:Sprite(精灵),Points(粒子系统)

注意开启阴影渲染

阴影渲染是有开关的,在renderer下
在这里插入图片描述
当允许渲染阴影的时候,Threejs才会开启阴影渲染系统,否则你怎么让物体产生接收阴影,你都看不到任何的光影效果

	//开启阴影渲染
	renderer.shadowMap.enabled = true;

阴影可以做一些设置,比如说,允许自动更新光影,以及定义阴影类型,一般我们要使得实时光影效果最好,建议使用
THREE.PCFSoftShadowMap,如果你觉得性能太差,使用PCFShadowMap或者更低的BasicShadowMap

关于VSM阴影,这里挖个坑,后续有机会详细讲解,如果你在用PCFSoft的时候,调整不出来正确的光影效果,可以尝试使用VSM阴影来解决

灵活运用阴影

上面提到了,阴影系统可以设置产生阴影和接收阴影,所以你也可以指定某个物体仅接收阴影,但是不产生阴影,也可以设置某个物体只产生阴影不接收阴影,来实现某些效果
也可以通过设置渲染器是否自动更新阴影,来让阴影的渲染变成单帧渲染

平行光阴影

在这里插入图片描述
这里官方描述的并不是很清楚,我们写一段代码来研究这个阴影

首先,我们要先让阴影系统生效

    import * as THREE from "../three/build/three.module.js";
    import {OrbitControls} from "../three/examples/jsm/controls/OrbitControls.js";

    window.addEventListener('load',e=>{
        init();
        addLight();//添加灯光
        addLand();//添加地面
        addMesh();//添加物体
        render();
    })

    let scene,renderer,camera;
    let orbit;
    let mesh;

    function init(){

        scene = new THREE.Scene();
        renderer = new THREE.WebGLRenderer({
            alpha:true,
            antialias:true
        });
        renderer.setSize(window.innerWidth,window.innerHeight);

        //渲染器开启阴影,并使用PCF算法过滤阴影,且使用软阴影
        renderer.shadowMap.enabled = true;
        renderer.shadowMap.type = THREE.PCFSoftShadowMap;

        document.body.appendChild(renderer.domElement);

        camera = new THREE.PerspectiveCamera(50,window.innerWidth/window.innerHeight,0.1,2000);
        camera.position.set(10,10,10);

        orbit = new OrbitControls(camera,renderer.domElement);
        orbit.enableDamping = true;
    }

    function addLight() {

        //添加平行光
        let directionalLight = new THREE.DirectionalLight(0xffffff,1.0);
        //设置平行光产生阴影
        directionalLight.castShadow = true;
        //设置平行光位置
        directionalLight.position.set(0,20,20);

        //平行光辅助线
        let directionalLightHelper = new THREE.DirectionalLightHelper(directionalLight,1,0xff0000);

        //平行光光影辅助线
        let cameraHelper = new THREE.CameraHelper(directionalLight.shadow.camera);

        //将物体添加到场景中
        scene.add(directionalLight);
        scene.add(directionalLightHelper);
        scene.add(cameraHelper);
    }

    function addMesh() {
        let geometry = new THREE.TorusKnotGeometry(5,1,64,8);
        let material = new THREE.MeshBasicMaterial({color:0xffffff * Math.random()});
        mesh = new THREE.Mesh(geometry,material);
        //给物体添加生成阴影和接收阴影的设置
        mesh.castShadow = true;
        mesh.receiveShadow = true;
        mesh.position.y = 3;
        scene.add(mesh);
    }

    function addLand() {
        //添加一个地面
        let geometry = new THREE.PlaneGeometry(100,100).rotateX(-Math.PI/2);
        let material = new THREE.MeshStandardMaterial({
            color:0xffffff
        });
        let mesh = new THREE.Mesh(geometry,material);
        //设定地面仅接收阴影  因为我们使用的是个地面,所以没必要再让它生成阴影浪费算力,除非你的地下有东西
        mesh.receiveShadow = true;
        scene.add(mesh);
    }

    function render() {
        renderer.render(scene,camera);
        orbit.update();
        requestAnimationFrame(render);
        mesh.rotation.x += 0.01;
        mesh.rotation.y += 0.01;
    }

在这里插入图片描述
从效果中,我们可以清晰的看到,阴影是有范围的,这个范围就是cameraHelper的范围
这是因为,阴影的计算,是借助了相机的算法来计算的,平行光内部使用了正交相机OrthographicCamera来计算阴影

我们可以打印平行光,来看到阴影的基本数据,也可以通过添加CameraHelper来查看实际计算的阴影范围

在这里插入图片描述
现在我们上面的效果,遇到了明显的问题,就是阴影不完整,这个时候,我们可以通过调整相机的实际范围来增加阴影的生成区域

控制正交相机的6个要素,分别为 left,right,top,bottom,near, far

正交相机在之前的相机篇已经做了介绍,不懂的可以回顾一下【ThreeJS基础教程-初识Threejs】1.5 选择合适的相机与相机切换

首先我们得知道,默认的值是多少
在这里插入图片描述
在这里插入图片描述

从侧面拉远了之后,我们看到,物体并没有超出near和far的范畴,所以near和far此时不需要更改

在这里插入图片描述
从正面看,明显比物体小一圈,所以我们此时把left,right,top,bottom都翻一倍即可

        directionalLight.shadow.camera.left = -10;
        directionalLight.shadow.camera.right = 10;
        directionalLight.shadow.camera.top = 10;
        directionalLight.shadow.camera.bottom = -10;

在这里插入图片描述
这时,我们的阴影就正常了

点光源阴影

我们将上述代码,替换 addLight() 的部分

    function addLight() {
        let pointLight = new THREE.PointLight(0xffffff,1.0,40,0.1);
        pointLight.position.set(0,20,20);
        pointLight.castShadow = true;

        console.log(pointLight);

        let pointLightHelper = new THREE.PointLightHelper(pointLight,1,0xff0000);
        let cameraHelper = new THREE.CameraHelper(pointLight.shadow.camera);

        scene.add(pointLight);
        scene.add(pointLightHelper);
        scene.add(cameraHelper);
    }

在这里插入图片描述
在这里插入图片描述

官方对于点光源阴影的介绍实在太过简单。。。

点光源实际使用透视相机来计算阴影,但是我们实际上通过透视相机辅助线并不能看到透视相机的变化,这个原因就由你们自行研究了

可以说的是,点光源的阴影也是有范围的,且范围跟随distance属性的变化而变化,我们在代码中,把distance设置到了40,并没有完全覆盖物体后面的光影区域,所以不仅照不亮那一块,连阴影也不会产生

我们修改了distance后,达到了比较好的效果

在这里插入图片描述
可以看得出,点光源的阴影,是会随着距离点光源的距离,而越来越大,与现实中的灯泡是完全一致的

聚光灯阴影

//和上面一样,我们依然修改addLight()即可
    function addLight() {

        //聚光灯的属性这里不再赘述
        let spotLight = new THREE.SpotLight(0xffffff,10,50,0.5,0.2,0.2);
        spotLight.castShadow = true;
        spotLight.position.set(0,20,20);
        let spotLightHelper = new THREE.SpotLightHelper(spotLight,0xff0000);

        scene.add(spotLight);
        scene.add(spotLightHelper);
    }

在这里插入图片描述
聚光灯内部也是使用透视相机进行阴影计算的,和点光源的越远阴影越大一样

如果想对聚光灯调节照射范围,只需要修改distance,angle,等属性即可,无需像上面平行光一样需要手动修改相机

优化阴影

以下方案对上述所有光源均有效

阴影范围外一片漆黑,可以优化一下吗

我们以刚写完的聚光灯案例入手,其实我们只需要加一个亮度不高的环境光,效果就会好很多
这样,即使物体不处在聚光灯光源之下,也能看清楚物体

        scene.add(new THREE.AmbientLight(0xffffff,0.2));

在这里插入图片描述

一般情况下,场景中是需要一个全局光照的,这个全局光照,可以是一个纯环境光,也可以是一个半球光,根据自己的需求来设置即可,这样开启阴影后,就不会产生某个地方特别漆黑的情况,也能有比较贴近现实的感觉,环境光亮度根据自己的实际需求来调节即可

增加阴影的精度

阴影的本质,其实就是计算一块阴影,然后覆盖到物体的原有的贴图上

所以阴影也是有纹理的性质的,比如说分辨率
如果你的阴影效果比较差,可以使用下面的方式来提高阴影精度

	//建议两个参数值相等,且数值为2的幂次倍
	// 如: 256 * 256,512 * 512,1024 * 1024,2048 * 2048
	light.shadow.mapSize.set(512,512);

由于在demo中,修改此值没有太大区别,所以这里不做演示了

注意:提高了一倍的阴影精度,计算量大约会增加4倍,最高仅建议到4096

消除伪影

摩尔纹效果图来自百度
摩尔纹效果来源百度,如有侵权请联系笔者

有时候阴影会出现类似上图的摩尔纹效果,可以用调整bias来解决

笔者刚才在尝试的过程中,没有一次能复现摩尔纹效果,所以这里仅找一张百度的图片来代替演示效果,实际上

在这里插入图片描述

	light.shadow.bias -= 0.0001;

静态阴影渲染 / 渐进式阴影渲染

Threejs官方案例shadowmap_progressive

这里的光影,我们可以从效果中看到,阴影不是第一时间渲染完成的,而是在物体或灯光移动后的几秒钟后完成的,这种阴影渲染叫:静态光影渲染,或渐进式阴影渲染

这种渲染的好处是,我们不需要实时的去更新阴影,只需要在改变物体的一瞬间重新渲染光影即可

这种阴影渲染可以用在不经常移动的物体上

具体的实现方式,请自行查看threejs官方案例的源代码

烘培阴影

烘培阴影已经在上一篇做了简单介绍,这里就不再赘述了
【Threejs基础教程-光影篇】5.1 常用的灯光

使用阴影时需要注意的点

  1. 阴影计算非常消耗性能,要根据实际需求去决定如何使用阴影渲染
  2. 你的模型已经很大的情况下,不推荐使用实时阴影
  3. 减少产生阴影的灯光,产生阴影的光源越多,也会呈指数级的额外消耗性能

Threejs基础教程在此完结,后续请查阅本人的: Threejs进阶教程

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1559473.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Linux】普通用户提升权限

概述 在Linux环境下,给普通用户提权的方式,su与sudo命令,su是将一个普通用户登录为root,而sudo则是将普通用户短暂提升权限 普通用户使用$ root使用# 使用su提升权限 如果我们使用su将用户提升为root,此时需要输入…

计算机网络:数据链路层 - 封装成帧 透明传输 差错检测

计算机网络:数据链路层 - 封装成帧 & 透明传输 & 差错检测 数据链路层概述封装成帧透明传输差错检测 数据链路层概述 从数据链路层来看,主机 H1 到 H2 的通信可以看成是在四段不同的链路上的通信组成的,所谓链路就是从一个节点到相邻…

回溯算法|39.组合总和

力扣题目链接 class Solution { private:vector<vector<int>> result;vector<int> path;void backtracking(vector<int>& candidates, int target, int sum, int startIndex) {if (sum > target) {return;}if (sum target) {result.push_back…

CISP考前笔记

1软件需求分析是保证软件质量的重要步骤&#xff0c;它的实施应该是在 软件定义阶段 1语句覆盖最弱&#xff0c;只需要让程序中的语句都执行一遍即可 。分支覆盖又称判定覆盖&#xff1a;使得程序中每个判断的取真分支和取假分支至少经历一次&#xff0c;即判断的真假均曾被满足…

K8S命令行可视化实验

以下为K8s命令行可视化工具的实验内容&#xff0c;相比于直接使用命令行&#xff0c;可视化工具可能更直观、更易于操作。 Lens Lens是用于监控和调试的K8S IDE。可以在Windows、Linux以及Mac桌面上完美运行。在 Kubernetes 上&#xff1a; 托管地址&#xff1a;github/lensa…

登录者个人信息查询

目录 &#x1f95e;1.vo层描述 &#x1f37f;2..vo层创建 &#x1f32d;3.编写controller层 &#x1f953;4.service层 &#x1f9c2;5.测试 1.vo层描述 Spring Boot项目中的实体类通常用于映射数据库表&#xff0c;包含了业务对象的所有属性。然而&#xff0c;前端或其…

Verilog基础【一】

文章目录 1.1 第一个verilog设计1.2 Verilog 简介1.3 Verilog环境搭建1.4 Verilog 设计方法设计方法设计流程 2.1 Verilog 基础语法格式注释标识符与关键字 2.2 Verilog 数值表示数值种类整数数值表示方法实数表示方法十进制&#xff1a;科学计数法&#xff1a; 字符串表示方法…

如何做慢查询优化?

如何做慢查询优化&#xff1f; 这个问题可以说是一个高频问题&#xff0c;总是被问到&#xff0c;今天来总结下这个问题。 慢查询&#xff0c;就是 SQL 查询中耗时比较大的一类查询&#xff0c;MySQL 提供了慢查询日志 slow_query_log&#xff0c; 它会记录耗时大于我们设置…

【Chapter2】进程、线程与作业,计算机操作系统教程,第四版,左万利,王英

文章目录 [toc] 一、多道程序设计1.1单道程序设计的缺点1.2多道程序设计的提出1.3多道程序设计存在的问题 二、进程的引入2.1进程的概念2.2进程的组成2.2.1进程控制块2.2.2程序 2.3进程的类型及特征2.3.1进程的类型2.3.2进程的特征 2.4进程的状态及转换2.4.1进程的状态创建态就…

项目亮点—动态线程池管理工具

问题 你是否在项目中使用线程池遇到过以下问题&#xff1f; 1.创建线程池核心参数不好评估&#xff0c;随着业务流量的波动&#xff0c;极有可能出现生产故障。 2.不支持优雅关闭&#xff0c;当项目关闭时&#xff0c;大量正在运行的线程池任务被丢弃。 3.不支持运行时监控…

java每日一题——分礼物(多线程运用)

前言&#xff1a; 运用了多线程的综合案例&#xff0c;顺便熟悉一下其他语法。打好基础&#xff0c;daydayup!!! 题目&#xff1a; 有100份礼物&#xff0c;小红小明同时发送&#xff0c;当剩下的礼物小于10份的时候则不再送出&#xff0c;利用多线程模拟该过程并将线程的名称…

R2GenCMN中的Encoder_Decoder结构

R2GenCMN中的 Encoder_Decoder 结构 Encoder_Decoder 结构直接关系到文本的生成&#xff0c;它结构参考的transformer的结构 我们这里主要看代码的实现&#xff0c;从视觉编码器的输出开始 1. 模型结构 首先介绍一下整体结构&#xff0c;这里的baseCMN其实就是一个包装了的T…

2024年noc指导教师认证测评参考试题题目5-6合集

[noc指导教师认证] 测评参考试题 说明:NOC教师指导认证考试题目是从题库里抽题,因此每位老师每次考试题目都不一样以下题目为测试考试时收集到的一些题目,作为辅助提供给各位老师,老师们可以记住题目及答案的具体内容 (选项顺序会变),以免考试时遇到。2024年的做的题目有的…

两张图片相似度匹配算法学习路线

大纲&#xff1a;​​​​​​目标跟踪基础&#xff1a;两张图片相似度算法-腾讯云开发者社区-腾讯云 (tencent.com) 目标跟踪基础&#xff1a;两张图片相似度算法 (qq.com) 一、传统方法 1.欧式距离&#xff08;用于判断是否完全相同&#xff09; [三维重建] [机器学习] 图…

RAG进阶笔记:RAG进阶

1 查询/索引部分 1.1 层次索引 创建两个索引——一个由摘要组成&#xff0c;另一个由文档块组成分两步进行搜索&#xff1a;首先通过摘要过滤出相关文档&#xff0c;接着只在这个相关群体内进行搜索 1.2 假设性问题 让LLM为每个块生成一个假设性问题&#xff0c;并将这些问…

一文看懂DDR1到DDR5的演变

DDR内存技术自问世以来&#xff0c;已经经历了多代的迭代和优化。每一代DDR内存都在性能、容量、功耗和功能上有所提升。以下是从DDR1到DDR5的主要区别和特点&#xff1a; DDR1 DDR1是第一代双倍数据速率同步动态随机存取存储器&#xff0c;标志着内存技术的一个重要里程碑。…

网安基础2-Sniffer的使用与防范

1. 嗅探器sniffer的工作原理 能捕获经过该网络设备的报文&#xff0c;通过分析网络流量&#xff0c;找出关键信息&#xff0c;解决网络问题。 不同于键盘捕获程序&#xff0c;如keylogger利用中断或钩子技术&#xff0c;Sniffer将网络接口置成适当的模式&#xff0c;如杂收。…

瑞吉外卖实战学习--9、mybatisPlus公共字段自动填充

mybatisPlus公共字段自动填充 前言实现步骤实体类添加注解按照框架要求编写元数据对象处理器&#xff0c;在此类中统一为公共字段赋值&#xff0c;此类需要实现MetaObjectHandler接口1、在创建和更新的时候修改创建和更新的时候自动填充时间2、如何获取到当前的id 测试结果 前言…

Vision Mamba 双向状态空间模型下的高效视觉表示学习

论文题目&#xff1a;Vision Mamba: Efficient Visual Representation Learning with Bidirectional State Space Model 双向状态空间模型下的高效视觉表示学习 论文链接&#xff1a;http://arxiv.org/abs/2401.09417 代码链接&#xff1a;https://github.com/hustvl/Vim 1、…

Java中的多线程和线程安全问题

线程 线程是操作系统进行调度的最小单位。一个进程至少包含一个主线程&#xff0c;而一个线程可以启动多个子线程。线程之间共享进程的资源&#xff0c;但也有自己的局部变量。多线程程序和普通程序的区别&#xff1a;每个线程都是一个独立的执行流&#xff1b;多个线程之间是…