一、背景
最近在做数字孪生项目,使用threejs渲染模型,UI要求地面反射建筑物,也就是模型要有倒影。
二、调研
在官网找到一个镜面反射的例子(https://threejs.org/examples/?q=refle#webgl_mirror)
如图:





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> }` };





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> }` }
评论已关闭。