在ThreeJS中,模型半透明设置只需要很简单的两行代码

curMat.opacity = 0.5 // 50%透明度
curMat.transparent = curMat.opacity < 1 // transparent属性必须为True才能半透明

然而.....

我们的模型有时候出现这种情况,正面看半透明效果似乎正常,

正面看半透明效果似乎正常

一旦旋转到侧面,就会发现大量的碎三角面片(我的妈呀,这是什么鬼?!!)

侧面看出现大量的碎三角面

开始以为是模型数据问题,经过排查发现并非如此,这个半透明不是材质本身问题,而是绘制顺序。在OpenGL渲染中的解决方案是开启 depth peeling,但素(41's tone),threejs中并没有这个选项,需要自己实现。
实现基本原理ShrekShao是芥末解释的

The idea is straight forward as shown by the name: we draw the objects multiple times. For each pass, we peel one layer from front and one layer from back (front or back is purely depending on their depth, not if it’s from front face or back face).

主要实现函数如下:

1. depthOnbeforeCompile

depthOnbeforeCompile(shader) {shader.uniforms.uScreenSize = this.globalPeelUniforms.uScreenSizeshader.uniforms.uPrevDepthTexture = this.globalPeelUniforms.uPrevDepthTextureshader.uniforms.uLayer = this.globalPeelUniforms.uLayershader.uniforms.uDepthOffset = this.globalPeelUniforms.uDepthOffsetshader.uniforms.uDepthPeel = this.globalPeelUniforms.uDepthPeelshader.fragmentShader = `uniform vec2 uScreenSize;uniform sampler2D uPrevDepthTexture;uniform int uLayer;uniform int uDepthPeel;uniform float uDepthOffset;${shader.fragmentShader}`//peel depthshader.fragmentShader = shader.fragmentShader.replace(/}$/gm,`if(uDepthPeel == 0) return;if(uLayer != 0 ){vec2 screenPos = gl_FragCoord.xy * uScreenSize;float prevDepth = unpackRGBAToDepth(texture2D(uPrevDepthTexture,screenPos));if(prevDepth + uDepthOffset - gl_FragCoord.z >= 0. ){discard;}}}`)
}

该函数在深度材质shader编译前(THREE.MeshDepthMaterial)和原始材质shader编译前中调用

this.depthMaterial = new THREE.MeshDepthMaterial()
this.depthMaterial.side = this.getSide()
this.depthMaterial.depthPacking = THREE.RGBADepthPacking
this.depthMaterial.onBeforeCompile = this.depthOnbeforeCompile.bind(this)

2. colorOnBeforeCompile

colorOnBeforeCompile(shader){shader.fragmentShader = shader.fragmentShader.replace('#include <packing>','')shader.fragmentShader = `#include <packing>uniform sampler2D uPrevColorTexture;${shader.fragmentShader}`//this has early returnthis.depthOnbeforeCompile(shader)shader.fragmentShader = shader.fragmentShader.replace(/}$/gm,`gl_FragColor.xyz *= gl_FragColor.a;}`)
}

该函数在原始材质shader编译前调用

attach(node){if (node.isMesh) {node.renderOrder = 1this.transparentObjects.push(node) // 索引,方便快速调用node.material.onBeforeCompile = this.colorOnBeforeCompile.bind(this);}
}

3.全局参数设置

this.globalPeelUniforms = {uLayer: { value: 0 },uPrevDepthTexture: { value: null },uPrevColorTexture: { value: null },uScreenSize: { value: new THREE.Vector2(1,1) },uDepthPeel: { value: Number(this.options.enabled) },uDepthOffset: { value: 0 },
}

值得注意的是,this.globalPeelUniforms.uDepthPeel.value 作为是否开启半透明开关,而不是用threejs 默认的curMat.transparent 属性。并且,curMat.transparent 必须设置为false,透明度依然由curMat.opacity控制。

4. 最后使用depth peel的render 替换threejs默认的renderer.render()


render(renderer, scene, camera){//clear main framerenderer.setClearColor(0x000,1)renderer.clear()this.globalPeelUniforms.uLayer.value = 0//render first depthscene.overrideMaterial = this.depthMaterialrenderer.setClearColor(0xffffff,1)renderer.render( scene, camera, this.targets[0], true)//first colorscene.overrideMaterial = nullrenderer.setClearColor(0x000,0)renderer.render( scene, camera, this.targets[2], true)for( let i = 0 ; i < this.options.layers ; i ++ ){const a = i % 3 //shift these aroundconst b = (i+1) % 3const c = (i+2) % 3const d = 3 //peel into thisthis.globalPeelUniforms.uPrevDepthTexture.value = this.targets[a]this.globalPeelUniforms.uLayer.value = i + 1//render next depthscene.overrideMaterial = this.depthMaterialrenderer.setClearColor(0xffffff,1)renderer.render( scene, camera, this.targets[b], true)//peelscene.overrideMaterial = nullrenderer.setClearColor(0x000,0)renderer.render( scene, camera, this.targets[d], true)//combinethis.compositeMaterial.uniforms.uTextureA.value = this.targets[c]this.compositeMaterial.uniforms.uTextureB.value = this.targets[d]renderer.render( this.compositeScene, camera, this.targets[a], true)}//render final result over opaque objectsthis.globalPeelUniforms.uPrevDepthTexture.value = nullthis. transparentObjects.forEach(o=>o.visible = false)renderer.render( scene, camera )renderer.render( this.compositeScene, camera )this.transparentObjects.forEach(o=>o.visible = true)//renderer.render( this.debugScene , this.debugCamera )
}

大功告成!最后效果如下:

开启 depth peel 前后对比

在线DEMO

【ThreeJs】利用Depth Peel技术,解决模型半透明出现的碎三角面问题相关推荐

  1. 小技巧|利用分块压缩技术解决微信传送大型文件的尝试

    利用分块压缩技术解决微信传送大型文件的尝试 问题背景 工具说明 操作教程 致谢 问题背景 微信只支持最大传输100M文件,百度网盘速度太慢,one-drive和google drive在国内没有大规模 ...

  2. 利用区块链技术解决传统物流贸易金融诸多痛点

    传统贸易金融存在诸多痛点 目前贸易金融的处理流程主要包含销售.运输和交货,都记录在提单中.提单为发货者收到的凭证,证明 货物已经发出以及发货者在货物海运过程中对货物的所有权.一旦货物到达目的地,发货人 ...

  3. 利用区块链技术解决科研问题的前景

    任何研究都需要科学家们付出相当多的努力,大多数研究都无法取得显著的成果,但它们可以成为导致大规模研究链条的一部分,而一些发展最初被认为是人类最重要的发明之一. 不幸的是,现代科学正处于危机之中,特别是 ...

  4. 基于VMM的Rootkit检测技术及模型分析

    Linux通过其特有的虚拟文件系统(Virtual Filesystem)实现对多种文件系统的兼容.虚拟文件系统又称虚拟文件系统转换(Virtual Filesystem Switch vFs),是一 ...

  5. CVPR 2018 | ETH Zurich提出利用对抗策略,解决目标检测的域适配问题

    CVPR 2018 | ETH Zurich提出利用对抗策略,解决目标检测的域适配问题 原创: Panzer 极市平台 今天 ↑ 点击蓝字关注极市平台 识别先机 创造未来 论文地址:https://a ...

  6. DL之Attention-ED:基于TF NMT利用带有Attention的 ED模型训练、测试(中英文平行语料库)实现将英文翻译为中文的LSTM翻译模型过程全记录

    DL之Attention-ED:基于TF NMT利用带有Attention的 ED模型训练(中英文平行语料库)实现将英文翻译为中文的LSTM翻译模型过程全记录 目录 测试输出结果 模型监控 训练过程全 ...

  7. 如何利用云原生技术构建现代化应用

    简介:在2021研发效能峰会上,阿里云中间件首席架构师愚奇就"如何利用云原生技术构建现代化应用"的主题分享了,如何利用云的特性及云原生的技术及产品,帮助企业的传统应用转化为现代化应 ...

  8. 利用管道检测技术成果对城市地下空洞进行筛查分析的探讨

    近年来,由于地下管线老化.破损等原因导致城路道路产生空洞塌陷的事故频发,严重威胁公众的生命财产安全.全国各大城市的管理者都在积极努力的开展各项工作,寻找解决办法.深圳市从2019年起全面开展主要道路地 ...

  9. Web3.0 · 基础层技术 · SCQA模型趣谈密码学

    [小木箱成长营]密码学系列教程: Web3.0 · 基础层技术 ·密码学在移动端应用与实践 一.序言 Hello,我是小木箱,欢迎来到小木箱成长营密码学系列教程,今天将分享 Web3.0 · 基础层技 ...

最新文章

  1. 最老程序员创业札记:全文检索、数据挖掘、推荐引擎应用8
  2. LeetCode 91. 解码方法
  3. 取消ajax请求时页面闪烁,用Ajax+js+jQuery实现无闪烁定时刷新页面
  4. Shell 监控文件变化
  5. HTML中IE条件注释判断语句(!--[if XX IE X]![endif]--)
  6. 转:java 进阶之路
  7. 苹果ID激活锁查询工具v1.2
  8. 开源框架ZedGraph的使用
  9. 苹果恢复出厂设置系统也会还原吗_手机经常恢复出厂设置会怎么样?对手机有害处吗?这下终于清楚了...
  10. lol 8.21服务器维护,《LOL》8.16版本维护到几点 8月21日更新维护内容汇总
  11. C语言中常用的标准数学函数
  12. 【技术沙龙】星火计划 | 腾讯自研Kona JDK技术分享沙龙火热报名中
  13. [python]用爬虫下载某站小说并生成epub格式电子书(用mkepub库)
  14. 文件上传漏洞总结(含原因+防御措施)+白名单+黑名单+内容、头+解析漏洞/修补方案
  15. 云原生的 CICD 框架:Tekton
  16. gitBlit的使用
  17. IT女纸3年换5家公司的真实写照
  18. Python必学的4个实战项目,拿走不谢
  19. pc端如何把URL参数隐藏
  20. quarkus数据库篇之一:比官方demo更简单的基础操作

热门文章

  1. 区块链技术应用场景设想
  2. Mac上如何利用itunes恢复存放在移动硬盘的iPhone数据
  3. 超级计算机预测2月有雪寒潮,科学网—被证实的预测:寒潮影响马上结束,倒春寒要退场了! - 杨学祥的博文...
  4. 保持好距离才会保持好爱情!情侣间最好的距离!很值得一看!
  5. Python %取模小故事
  6. linux kernel -- oops场景奈何桥
  7. TX2跑通yolov4
  8. React Native常用第三方组件汇总【建议收藏】
  9. 【活体检测】人脸活体检测、红外人脸数据集整理
  10. Java开发之——Date时间差