使用 Emission Map 对一个对象的特定部分进行 ThreeJS 选择性布隆

如何解决使用 Emission Map 对一个对象的特定部分进行 ThreeJS 选择性布隆

在我的项目中,我想显示有时带有小 LED 灯的 3d 对象。这个想法是,这些小灯需要发出某种绽放,使其看起来像是在发光。

我已尝试应用 UnrealBloom,但它被考虑用于整个场景,而不仅仅是具有实际发射值的部分(使用发射纹理贴图)。场景也变得非常模糊。

>

enter image description here

这显然不是我想要的。我只需要红色的小 LED 灯泡就可以发光而不是整个物体。但是,我还没有找到一种方法来告诉引擎只将泛光应用到排放贴图所指向的位置。

我使用了一个非常简单的代码设置,它几乎与 UnrealBloom Example 相同:

如何正确设置发射纹理并仅使对象的发射部分发光并防止出现不切实际的闪亮表面和非常模糊的视觉效果?

UPDATE: Editable example of my setup is now available on JSFiddle!

<div id="bloom-solution" style="margin:0px; overflow:hidden;">  
    <div id="body">
    
        <h1 id="info" style="
          color: rgb(255,255,255);
          position: fixed;
          top: 45%;
          left: 50%;
          transform: translate(-50%,-50%);
        ">loading scene,this might take a few seconds..</h1>
    
        <script type="x-shader/x-vertex" id="vertexshader">

            varying vec2 vUv;

            void main() {

                vUv = uv;

                gl_Position = projectionMatrix * modelViewMatrix * vec4( position,1.0 );

            }

        </script>

        <script type="x-shader/x-fragment" id="fragmentshader">

            uniform sampler2D baseTexture;
            uniform sampler2D bloomTexture;

            varying vec2 vUv;

            void main() {

                gl_FragColor = ( texture2D( baseTexture,vUv ) + vec4( 1.0 ) * texture2D( bloomTexture,vUv ) );

            }

        </script>
    
        <script type="module">
        
        import * as THREE from 'https://threejs.org/build/three.module.js'

        import { OrbitControls } from 'https://threejs.org/examples/jsm/controls/OrbitControls.js'
        import { GLTFLoader } from 'https://threejs.org/examples/jsm/loaders/GLTFLoader.js'
        import { RGBELoader } from 'https://threejs.org/examples/jsm/loaders/RGBELoader.js'
        import { EffectComposer } from 'https://threejs.org/examples/jsm/postprocessing/EffectComposer.js';
        import { RenderPass } from 'https://threejs.org/examples/jsm/postprocessing/RenderPass.js';
        import { UnrealBloomPass } from 'https://threejs.org/examples/jsm/postprocessing/UnrealBloomPass.js';

        // RESOURCES ///////////////////////////////////////////////////////////////////////////////////////////////////////////////

        const COLOR_TEXTURE =       "https://cdn.jsdelivr.net/gh/MJegerlehnerBio/bloom-solution/RecordPlayer_Color.jpeg"
        const METALNESS_TEXTURE =   "https://cdn.jsdelivr.net/gh/MJegerlehnerBio/bloom-solution/RecordPlayer_Metalness.jpeg"
        const EMISSION_TEXTURE =    "https://cdn.jsdelivr.net/gh/MJegerlehnerBio/bloom-solution/RecordPlayer_Emission.jpeg"
        const ALPHA_TEXTURE =       "https://cdn.jsdelivr.net/gh/MJegerlehnerBio/bloom-solution/RecordPlayer_Alpha.jpeg"
        
        const TURNTABLE_MODEL =     "https://cdn.jsdelivr.net/gh/MJegerlehnerBio/bloom-solution/turntable_a111.glb"
        const HDRI_MAP =            "https://cdn.jsdelivr.net/gh/MJegerlehnerBio/bloom-solution/forest.hdr"

        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        function $(e){return document.getElementById(e)}

        const container = document.createElement( 'div' )
        document.body.appendChild( container )

        const scene = new THREE.Scene()
        scene.background = new THREE.Color( new THREE.Color("rgb(250,244,227)") )
        scene.fog = new THREE.Fog( new THREE.Color("rgb(100,100,100)"),10,50 )

        const camera = new THREE.PerspectiveCamera( 45,window.innerWidth / window.innerHeight,0.1,1000 )
        camera.position.set( 7,3,7 )

        const renderer = new THREE.WebGLRenderer( { antialias: true } )
        renderer.setPixelRatio( window.devicePixelRatio )
        renderer.setSize( window.innerWidth,window.innerHeight )
        renderer.toneMapping = THREE.ACESFilmicToneMapping
        renderer.outputEncoding = THREE.sRGBEncoding
        renderer.shadowMap.enabled = true
        renderer.shadowMap.type = THREE.PCFSoftShadowMap
        container.appendChild( renderer.domElement )
        
        const controls = new OrbitControls( camera,renderer.domElement )
        controls.minDistance = 1
        controls.enablePan = true
        controls.enableZoom = true;
        controls.enableDamping = true
        controls.dampingFactor = 0.1
        controls.rotateSpeed = 0.5
        
        const directionalLight = new THREE.DirectionalLight( new THREE.Color("rgb(255,255)"),1 )
        directionalLight.castShadow = true
        directionalLight.shadow.camera.top = 4
        directionalLight.shadow.camera.bottom = - 4
        directionalLight.shadow.camera.left = - 4
        directionalLight.shadow.camera.right = 4
        directionalLight.shadow.camera.near = 0.1
        directionalLight.shadow.camera.far = 40
        directionalLight.shadow.camera.far = 40
        directionalLight.shadow.bias = - 0.002
        directionalLight.position.set( 0,20,20 )
        directionalLight.shadow.mapSize.width = 1024*4
        directionalLight.shadow.mapSize.height = 1024*4
        scene.add( directionalLight )

        scene.add( new THREE.CameraHelper( directionalLight.shadow.camera ) )

        var gltfLoader
        var model
        var mesh
        
        const pmremGenerator = new THREE.PMREMGenerator( renderer )
        pmremGenerator.compileEquirectangularShader()

        new RGBELoader().setDataType( THREE.UnsignedByteType ).load( HDRI_MAP,function ( texture ) {           
            const envMap = pmremGenerator.fromEquirectangular( texture ).texture
            scene.environment = envMap
            texture.dispose()
            pmremGenerator.dispose()
                
            gltfLoader = new GLTFLoader()
            gltfLoader.load( TURNTABLE_MODEL,function ( gltf ) {   
                model = gltf.scene
                model.position.y = 1
                model.traverse( function ( child ) {
                    if ( child.isMesh ) {
                        mesh = child
                        child.castShadow = true
                        child.receiveShadow = true
                        child.material.transparent = true           
                        child.material.envMapIntensity = 1
                        
                        $("info").style.display = "none";
                    }
                } );

                model.scale.set(15,15,15)

                scene.add( model )
                animate()
            } )
        });
        
        const animate = function () {

            requestAnimationFrame( animate )

            controls.update()

            renderer.render( scene,camera )

        };
        
        window.addEventListener( 'resize',function () {
            const width = window.innerWidth
            const height = window.innerHeight
            renderer.setSize( width,height )
            camera.aspect = width / height
            camera.updateProjectionMatrix()
        } )
        </script>
    </div>
</div>

解决方法

在我看来,那个官方示例过于复杂。但是选择性绽放本身的概念很简单:

  1. 使所有未开花的物体全黑
  2. 使用 bloomComposer 渲染场景
  3. 将材料/颜色恢复到以前的状态
  4. 使用 finalComposer 渲染场景

就是这样。如何管理变暗/变黑的非开花对象并恢复它们的材料,这取决于您。

这是一个例子(看起来很复杂,但实际上并没有那么多):

body{
  overflow: hidden;
  margin: 0;
}
<script type="x-shader/x-vertex" id="vertexshader">
  varying vec2 vUv;
  void main() {
    vUv = uv;
    gl_Position = projectionMatrix * modelViewMatrix * vec4( position,1.0 );
  }
</script>
<script type="x-shader/x-fragment" id="fragmentshader">
  uniform sampler2D baseTexture;
  uniform sampler2D bloomTexture;
  varying vec2 vUv;
  void main() {
    gl_FragColor = ( texture2D( baseTexture,vUv ) + vec4( 1.0 ) * texture2D( bloomTexture,vUv ) );
  }
</script>
<script type="module">
import * as THREE from 'https://cdn.jsdelivr.net/npm/three@0.127.0/build/three.module.js';

import { OrbitControls } from 'https://cdn.jsdelivr.net/npm/three@0.127.0/examples/jsm/controls/OrbitControls.js';

import { EffectComposer } from 'https://cdn.jsdelivr.net/npm/three@0.127.0/examples/jsm/postprocessing/EffectComposer.js';
import { RenderPass } from 'https://cdn.jsdelivr.net/npm/three@0.127.0/examples/jsm/postprocessing/RenderPass.js';
import { ShaderPass } from 'https://cdn.jsdelivr.net/npm/three@0.127.0/examples/jsm/postprocessing/ShaderPass.js';
import { UnrealBloomPass } from 'https://cdn.jsdelivr.net/npm/three@0.127.0/examples/jsm/postprocessing/UnrealBloomPass.js';

let scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera(60,innerWidth / innerHeight,1,100);
camera.position.set(0,3,5);
let renderer = new THREE.WebGLRenderer();
renderer.setSize(innerWidth,innerHeight);
//renderer.setClearColor(0x404040);
document.body.appendChild(renderer.domElement);

let controls = new OrbitControls(camera,renderer.domElement);

let light = new THREE.DirectionalLight(0xffffff,0.5);
light.position.setScalar(1);
scene.add(light,new THREE.AmbientLight(0xffffff,0.5));

let uniforms = {
  globalBloom: {value: 1}
}

// texture
new THREE.TextureLoader().load("https://threejs.org/examples/textures/hardwood2_diffuse.jpg",tex => {
  //console.log(tex);
  
  let img = tex.image;
  
  let c = document.createElement("canvas");
  let min = Math.min(img.width,img.height);
  c.width = c.height = min;
  let ctx = c.getContext("2d");
  ctx.drawImage(img,0);
  
  let c2 = document.createElement("canvas");
  c2.width = c2.height = min;
  let ctx2 = c2.getContext("2d");
  ctx2.clearRect(0,min,min); 
  
  ["#f00","#0f0","#ff0","#f0f","#0ff"].forEach( (col,i,a) => {
      let id = i - ((a.length - 1) / 2);
      let dist = id * 150;
      //console.log(dist,col,c.width,c.height);
      ctx.beginPath();
      ctx.arc(min * 0.5 + dist,min * 0.5,25,2 * Math.PI);
      ctx.fillStyle = col;
      ctx.fill();
    }
  );
  
  let cTex = new THREE.CanvasTexture(c);
  let c2Tex = new THREE.CanvasTexture(c2);
  
  setInterval(() => {
    ctx2.clearRect(0,min);
    let id = THREE.MathUtils.randInt(0,4) - 2;
    let dist = id * 150;
    ctx2.beginPath();
    ctx2.arc(min * 0.5 + dist,2 * Math.PI);
    ctx2.fillStyle = "#fff";
    ctx2.fill();
    c2Tex.needsUpdate = true;
  },125);
  
  let g = new THREE.PlaneGeometry(5,5);
  g.rotateX(Math.PI * -0.5);
  let m = new THREE.MeshStandardMaterial(
    {
      roughness: 0.6,metalness: 0.5,map: cTex,emissiveMap: c2Tex,onBeforeCompile: shader => {
        shader.uniforms.globalBloom = uniforms.globalBloom;
        shader.fragmentShader = `
            uniform float globalBloom;
          ${shader.fragmentShader}
        `.replace(
            `#include <dithering_fragment>`,`#include <dithering_fragment>
            vec3 col = texture2D( map,vUv).rgb;
            float em = texture2D( emissiveMap,vUv ).g;
            col *= em;
            gl_FragColor.rgb = mix(gl_FragColor.rgb,globalBloom);
            
          `
        );
        console.log(shader.fragmentShader);
      }
    }
  );
  let o = new THREE.Mesh(g,m);
  scene.add(o);
  
})

window.onresize = function () {

  const width = window.innerWidth;
  const height = window.innerHeight;

  camera.aspect = width / height;
  camera.updateProjectionMatrix();

  renderer.setSize( width,height );

  bloomComposer.setSize( width,height );
  finalComposer.setSize( width,height );

};

// bloom
const renderScene = new RenderPass( scene,camera );

const bloomPass = new UnrealBloomPass( new THREE.Vector2( window.innerWidth,window.innerHeight ),1.5,0.1 );

const bloomComposer = new EffectComposer( renderer );
bloomComposer.renderToScreen = false;
bloomComposer.addPass( renderScene );
bloomComposer.addPass( bloomPass );

const finalPass = new ShaderPass(
  new THREE.ShaderMaterial( {
    uniforms: {
      baseTexture: { value: null },bloomTexture: { value: bloomComposer.renderTarget2.texture }
    },vertexShader: document.getElementById( 'vertexshader' ).textContent,fragmentShader: document.getElementById( 'fragmentshader' ).textContent,defines: {}
  } ),"baseTexture"
);
finalPass.needsSwap = true;

const finalComposer = new EffectComposer( renderer );
finalComposer.addPass( renderScene );
finalComposer.addPass( finalPass );

renderer.setAnimationLoop( _ => {
    
  renderer.setClearColor(0x000000);
  uniforms.globalBloom.value = 1;
  
  bloomComposer.render();
  
  renderer.setClearColor(0x202020);
  uniforms.globalBloom.value = 0;
  
    finalComposer.render();
  //renderer.render(scene,camera);
})

</script>

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-
参考1 参考2 解决方案 # 点击安装源 协议选择 http:// 路径填写 mirrors.aliyun.com/centos/8.3.2011/BaseOS/x86_64/os URL类型 软件库URL 其他路径 # 版本 7 mirrors.aliyun.com/centos/7/os/x86
报错1 [root@slave1 data_mocker]# kafka-console-consumer.sh --bootstrap-server slave1:9092 --topic topic_db [2023-12-19 18:31:12,770] WARN [Consumer clie
错误1 # 重写数据 hive (edu)&gt; insert overwrite table dwd_trade_cart_add_inc &gt; select data.id, &gt; data.user_id, &gt; data.course_id, &gt; date_format(
错误1 hive (edu)&gt; insert into huanhuan values(1,&#39;haoge&#39;); Query ID = root_20240110071417_fe1517ad-3607-41f4-bdcf-d00b98ac443e Total jobs = 1
报错1:执行到如下就不执行了,没有显示Successfully registered new MBean. [root@slave1 bin]# /usr/local/software/flume-1.9.0/bin/flume-ng agent -n a1 -c /usr/local/softwa
虚拟及没有启动任何服务器查看jps会显示jps,如果没有显示任何东西 [root@slave2 ~]# jps 9647 Jps 解决方案 # 进入/tmp查看 [root@slave1 dfs]# cd /tmp [root@slave1 tmp]# ll 总用量 48 drwxr-xr-x. 2
报错1 hive&gt; show databases; OK Failed with exception java.io.IOException:java.lang.RuntimeException: Error in configuring object Time taken: 0.474 se
报错1 [root@localhost ~]# vim -bash: vim: 未找到命令 安装vim yum -y install vim* # 查看是否安装成功 [root@hadoop01 hadoop]# rpm -qa |grep vim vim-X11-7.4.629-8.el7_9.x
修改hadoop配置 vi /usr/local/software/hadoop-2.9.2/etc/hadoop/yarn-site.xml # 添加如下 &lt;configuration&gt; &lt;property&gt; &lt;name&gt;yarn.nodemanager.res