一、背景
最近在做数字孪生项目,使用threejs渲染模型,UI要求地面反射建筑物,也就是模型要有倒影。
二、调研
在官网找到一个镜面反射的例子(https://threejs.org/examples/?q=refle#webgl_mirror
如图:
和UI要的功能类似,但有缺陷
1、反射出了地面上所有的元素,连天空盒都反射出来了,我只想反射建筑物
2、没反射的地方,有颜色,盖住了镜面平面下的内容
如图:
三、如何只反射指定物体
通过阅读源码,我们发现在 Three.js 中,Reflector 类有一个 onBeforeRender 方法,我们可以称它为“渲染前回调”或“预渲染处理函数”。
如果能拿到这个钩子,在这个钩子里做过滤,问题就迎刃而解了。
但是,最怕有但是,但是这个方法没放出来,不能直接调,怎么办呢?
那就只能曲线救国,改它的方法!
如图:
 
解决方法,就是重写reflector对象的onBeforeRender方法
onBeforeRender方法传入一个 scene 对象,代表视图,也就是反射的内容,过滤scene,不需要的模型对象,隐藏掉(visible = false)
 
效果如下:
现在已经不反射天空盒了,只反射我想要的模型
但是,最怕有但是,但是不反射的地方变成黑色的了,修改镜面平面的材质透明度(opacity)不起效果,怎么办呢?
修改片元着色器。
四、修改片元着色器,使不反射的地方透明
reflector有shander属性,用于传入自定义着色器,着色器(Shader)是图形编程中的一个术语,指的是在图形处理单元(GPU)上运行的程序。这块涉及WebGL的知识,我不懂,不多言。
Reflector.ReflectorShader = {

    uniforms: {

        'color': {
value: null
}, 'tDiffuse': {
value: null
}, 'textureMatrix': {
value: null
} }, vertexShader: /* glsl */`
uniform mat4 textureMatrix;
varying vec4 vUv; #include <common>
#include <logdepthbuf_pars_vertex> void main() { vUv = textureMatrix * vec4( position, 1.0 ); gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); #include <logdepthbuf_vertex> }`, fragmentShader: /* glsl */`
uniform vec3 color;
uniform sampler2D tDiffuse;
varying vec4 vUv; #include <logdepthbuf_pars_fragment> float blendOverlay( float base, float blend ) { return( base < 0.5 ? ( 2.0 * base * blend ) : ( 1.0 - 2.0 * ( 1.0 - base ) * ( 1.0 - blend ) ) ); } vec3 blendOverlay( vec3 base, vec3 blend ) { return vec3( blendOverlay( base.r, blend.r ), blendOverlay( base.g, blend.g ), blendOverlay( base.b, blend.b ) ); } void main() { #include <logdepthbuf_fragment> vec4 base = texture2DProj( tDiffuse, vUv );
gl_FragColor = vec4( blendOverlay( base.rgb, color ), 1.0 ); #include <tonemapping_fragment>
#include <encodings_fragment> }`
};
这是threejs源码,reflector类里定义的默认着色器,拷出来,改一下,再通过shader属性传进去。
 
加上这三行代码即可
效果如图
镜面平面透明了
五、如何调整镜面平面的透明度
刚才说了,调整镜面的透明度(opacity)已经不起效果了,我也不想细查为啥了
透明度也可以直接通过片元着色器修改
把红框内值改了就行了,数值范围是0~1
改为0.4后,效果如图:
 
注意:要想让镜面平面透明,一定要把镜面材质的是否支持透明度属性改为true,也就是支持透明
完整代码如下
import { Reflector } from 'three/examples/jsm/objects/Reflector.js';
import { transparentMirrorShader } from '../xxxx.js' // 创建镜面反射平面
createMirrorPlane() {
// 创建一个圆形几何体
const radius = 640; // 圆的半径
const segments = 256; // 圆的细分数,细分越多圆形越平滑
const geometry = new THREE.CircleGeometry(radius, segments); // 创建 Reflector
const reflector = new Reflector(geometry, {
color: 0x000000, // 设置反射颜色
textureWidth: window.innerWidth * window.devicePixelRatio, // 反射纹理宽度
textureHeight: window.innerHeight * window.devicePixelRatio, // 反射纹理高度
shader: transparentMirrorShader,
clipBias: 0.000 // 裁剪偏移量
}); const subModelNameList = ['wall', '天空盒']
reflector.originOnBeforeRender = reflector.onBeforeRender;
reflector.onBeforeRender = function (renderer, scene, camera) {
const scene1 = scene.clone();
scene1.traverse(child => {
if (!child.isScene && !child.isLight && child.name && subModelNameList.some(ele => child.name.includes(ele))) {
if (['wall'].some(ele => child.name.includes(ele))) {
child.visible = true;
} else {
child.visible = false;
}
}
}); reflector.originOnBeforeRender(renderer, scene1, camera);
} // 设置位置和旋转
reflector.position.set(0, -0.01, 0);
reflector.rotation.x = -Math.PI / 2; reflector.name = '镜面反射平面'; reflector.material.transparent = true; // 添加到场景
this.scene.add(reflector);
},
export const transparentMirrorShader = {
uniforms: {
'color': {
value: null
}, 'tDiffuse': {
value: null
}, 'textureMatrix': {
value: null
} }, vertexShader: /* glsl */`
uniform mat4 textureMatrix;
varying vec4 vUv; #include <common>
#include <logdepthbuf_pars_vertex> void main() { vUv = textureMatrix * vec4( position, 1.0 ); gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); #include <logdepthbuf_vertex> }`, fragmentShader: /* glsl */`
uniform vec3 color;
uniform sampler2D tDiffuse;
varying vec4 vUv; #include <logdepthbuf_pars_fragment> float blendOverlay( float base, float blend ) { return( base < 0.5 ? ( 2.0 * base * blend ) : ( 1.0 - 2.0 * ( 1.0 - base ) * ( 1.0 - blend ) ) ); } vec3 blendOverlay( vec3 base, vec3 blend ) { return vec3( blendOverlay( base.r, blend.r ), blendOverlay( base.g, blend.g ), blendOverlay( base.b, blend.b ) ); } void main() { #include <logdepthbuf_fragment> vec4 base = texture2DProj( tDiffuse, vUv ); // 检查是否有有效的反射纹理,如果没有则透明
if (base.a < 0.1) {
discard;
} gl_FragColor = vec4( blendOverlay( base.rgb, color ), 1 ); #include <tonemapping_fragment>
#include <encodings_fragment> }`
}
我是用立方体做例子,如果有实际模型,效果会更好一些。
欢迎指正。
 
 
 
 
 
 
 
 

threejs 实现镜面反射,只反射指定物体,背景透明的更多相关文章

  1. HTML input="file" 浏览时只显示指定文件类型 xls、xlsx、csv

    html input="file" 浏览时只显示指定文件类型 xls.xlsx.csv <input id="fileSelect" type=" ...

  2. <input type="file" />浏览时只显示指定文件类型

    <input type="file" />浏览时只显示指定文件类型 <input type="file" accept="appli ...

  3. MySQL 如何只导出 指定的表 的表结构和数据 ( 转 )

    MySQL 如何只导出 指定的表 的表结构和数据 ( 转 ) 2011-01-04 15:03:33 分类: MySQL MySQL 如何只导出 指定的表 的表结构和数据 导出更个库的表结构如下:my ...

  4. 通过ASP禁止指定IP和只允许指定IP访问网站的代码

    过ASP禁止指定IP和只允许指定IP访问网站的代码,需要的朋友可以参考下. 一.禁止指定IP防问网站,并执行相应操作: 代码如下: <% Dim IP,IPString,VisitIP '设置I ...

  5. log4j配置只打印指定jar或包的DEBUG信息

    有的时候查问题的时候需要打印第三方jar里面的debug信息,假如全部jar都打印的话日志文件会很大,这个时候可以配置log4j只打印指定jar的debug信息或者包,同时输出到了一个新的文件中. 比 ...

  6. findstr 只搜寻指定文件类型

    Title:findstr 只搜寻指定文件类型 --2012-05-04 09:27 findstr /i /m /S /C:"关键字"  *.php *.asp *.jsp

  7. 只允许指定的ip访问本机的指定端口22:

    只允许指定的ip访问本机的指定端口22: 允许的的ip:192.168.1.123, 192.168.1.124, 192.168.1.100,其他ip都禁止访问. 切换到root用户 1.在tcp协 ...

  8. charles只获取指定的请求的设置方法

    过滤网络请求 通常情况下,需要对网络请求进行过滤,只监控指定服务器的请求.有3种办法: 方法一:在主界面的中部的 Filter 栏中输入需要过滤出来的关键字.例如我们的服务器的地址(host)是:ww ...

  9. iptables只允许指定ip地址访问指定端口

    首先,清除所有预设置 iptables -F#清除预设表filter中的所有规则链的规则 iptables -X#清除预设表filter中使用者自定链中的规则 其次,设置只允许指定ip地址访问指定端口 ...

  10. linux下通过iptables只允许指定ip地址访问指定端口的设置方法

    这篇文章主要介绍了linux下通过iptables只允许指定ip地址访问指定端口的设置方法,需要的朋友可以参考下. 首先,清除所有预设置 iptables -F#清除预设表filter中的所有规则链的 ...

随机推荐

  1. 解读Karmada多云容器编排技术,加速分布式云原生应用升级

    本文分享自来源:<华为云DTSE>第五期开源专刊,作者:任洪彩 华为云高级软件工程师,Karmada社区Maintainer.   管理和协调跨多个云平台的容器化应用是当前企业面临的复杂性 ...

  2. QOJ 5020. 举办乘凉州喵,举办乘凉州谢谢喵

    QOJ 5020. 举办乘凉州喵,举办乘凉州谢谢喵 飞天数据结构. 思路 设 \(f[u][k]\) 为 \(u\) 子树内距离 \(u\) 小于等于 \(k\) 的点的个数,\(g[u][k]\) ...

  3. CF2027D The Endspeaker (Hard Version) 题解

    题面 给你一个长度为 \(n\) 的数组 \(a\) 和一个长度为 \(m\) 的数组 \(b\) (所有 \(1 \le i < m\) 满足 \(b_i > b_{i+1}\) ).最 ...

  4. YIi2 Object 报错问题

    yii2的老版本会有在PHP7运行的错误提示 Cannot use yii\base\Object as Object because 'Object' is a special class name ...

  5. meta-analysis初学--笔记摘抄

    meta-analysis的定义 Meta-analysis是指对研究的研究,可以翻译为元分析.后设分析.整合分析.荟萃分析等.最常用的翻译是荟萃分析.Meta-analysis是用统计的概念与方法, ...

  6. 如何使用,操作Redis数据库

    本博客不再维护,搬家到 http://zthinker.com .个人微信小程序(分布式编程) Redis是一个开源的内存中键值数据存储.Redis是NoSQL数据库,它不使用结构化查询语言,也称为S ...

  7. MySQL之sql_mode

    sql_mode是个很容易被忽视的变量,默认值是空值,在这种设置下是可以允许一些非法操作的,比如允许一些非法数据的插入.在生产环境必须将这个值设置为严格模式,所以开发.测试环境的数据库也必须要设置,这 ...

  8. 《JavaScript 模式》读书笔记(7)— 设计模式1

    这些模式已经出现了相当长的一段时间,并被证明在许多情况下都非常有用.这也是为什么需要自己熟悉并谈论这些模式的原因. 虽然这些设计模式是与语言和实现方式无关的,并且人们已经对此研究了多年,但都主要是从强 ...

  9. vue中this.$nextTick()

    this.$nextTick()是在下一次DOM更新后执行其指定回调函数 this.$nextTick(回调函数) 使用场景:在改变数据后,要对更新后的DOM进行操作时使用

  10. 2024年1月Java项目开发指南1:环境与工具准备

    准备工作 基础能力 开发能力的事咱先不谈,有两个基础技能要学一下. 1.学习使用Markdown编写文档 2.学会使用git拉取代码和提交代码 软件准备 电脑需要安装以下软件: IDEA 2023.2 ...