ThreeJS WEBGL Decal

贴花 在图形学里主要指将特定图案,以类似粘贴或印刷的方式附着于其他物体上,能创造一种新颖的体验或逼真的效果,比较有趣,游戏中比较出名的有喷射战士splatoon123等。

unity HDRP中默认支持decal,不过我没有针对性研究,只是正好看到threejs里这个example,感觉做的挺好,这里面主要是通过将被贴的mesh顶点worldspace转成目标点的投影坐标,然后采用了clip裁切获取子mesh的方式,具体原理参考wolfire 。

import {BufferGeometry,Float32BufferAttribute,Matrix4,Vector3
} from 'three';/*** You can use this geometry to create a decal mesh, that serves different kinds of purposes.* e.g. adding unique details to models, performing dynamic visual environmental changes or covering seams.** Constructor parameter:** mesh — Any mesh object* position — Position of the decal projector* orientation — Orientation of the decal projector* size — Size of the decal projector** reference: http://blog.wolfire.com/2009/06/how-to-project-decals/**/class DecalGeometry extends BufferGeometry {constructor( mesh, position, orientation, size ) {super();// buffersconst vertices = [];const normals = [];const uvs = [];// helpersconst plane = new Vector3();// this matrix represents the transformation of the decal projectorconst projectorMatrix = new Matrix4();projectorMatrix.makeRotationFromEuler( orientation );projectorMatrix.setPosition( position );const projectorMatrixInverse = new Matrix4();projectorMatrixInverse.copy( projectorMatrix ).invert();// generate buffersgenerate();// build geometrythis.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );function generate() {let decalVertices = [];const vertex = new Vector3();const normal = new Vector3();// handle different geometry typesif ( mesh.geometry.isGeometry === true ) {console.error( 'THREE.DecalGeometry no longer supports THREE.Geometry. Use BufferGeometry instead.' );return;}const geometry = mesh.geometry;const positionAttribute = geometry.attributes.position;const normalAttribute = geometry.attributes.normal;// first, create an array of 'DecalVertex' objects// three consecutive 'DecalVertex' objects represent a single face//// this data structure will be later used to perform the clippingif ( geometry.index !== null ) {// indexed BufferGeometryconst index = geometry.index;for ( let i = 0; i < index.count; i ++ ) {vertex.fromBufferAttribute( positionAttribute, index.getX( i ) );normal.fromBufferAttribute( normalAttribute, index.getX( i ) );pushDecalVertex( decalVertices, vertex, normal );}} else {// non-indexed BufferGeometryfor ( let i = 0; i < positionAttribute.count; i ++ ) {vertex.fromBufferAttribute( positionAttribute, i );normal.fromBufferAttribute( normalAttribute, i );pushDecalVertex( decalVertices, vertex, normal );}}// second, clip the geometry so that it doesn't extend out from the projectordecalVertices = clipGeometry( decalVertices, plane.set( 1, 0, 0 ) );decalVertices = clipGeometry( decalVertices, plane.set( - 1, 0, 0 ) );decalVertices = clipGeometry( decalVertices, plane.set( 0, 1, 0 ) );decalVertices = clipGeometry( decalVertices, plane.set( 0, - 1, 0 ) );decalVertices = clipGeometry( decalVertices, plane.set( 0, 0, 1 ) );decalVertices = clipGeometry( decalVertices, plane.set( 0, 0, - 1 ) );// third, generate final vertices, normals and uvsfor ( let i = 0; i < decalVertices.length; i ++ ) {const decalVertex = decalVertices[ i ];// create texture coordinates (we are still in projector space)uvs.push(0.5 + ( decalVertex.position.x / size.x ),0.5 + ( decalVertex.position.y / size.y ));// transform the vertex back to world spacedecalVertex.position.applyMatrix4( projectorMatrix );// now create vertex and normal buffer datavertices.push( decalVertex.position.x, decalVertex.position.y, decalVertex.position.z );normals.push( decalVertex.normal.x, decalVertex.normal.y, decalVertex.normal.z );}}function pushDecalVertex( decalVertices, vertex, normal ) {// transform the vertex to world space, then to projector spacevertex.applyMatrix4( mesh.matrixWorld );vertex.applyMatrix4( projectorMatrixInverse );normal.transformDirection( mesh.matrixWorld );decalVertices.push( new DecalVertex( vertex.clone(), normal.clone() ) );}function clipGeometry( inVertices, plane ) {const outVertices = [];const s = 0.5 * Math.abs( size.dot( plane ) );// a single iteration clips one face,// which consists of three consecutive 'DecalVertex' objectsfor ( let i = 0; i < inVertices.length; i += 3 ) {let total = 0;let nV1;let nV2;let nV3;let nV4;const d1 = inVertices[ i + 0 ].position.dot( plane ) - s;const d2 = inVertices[ i + 1 ].position.dot( plane ) - s;const d3 = inVertices[ i + 2 ].position.dot( plane ) - s;const v1Out = d1 > 0;const v2Out = d2 > 0;const v3Out = d3 > 0;// calculate, how many vertices of the face lie outside of the clipping planetotal = ( v1Out ? 1 : 0 ) + ( v2Out ? 1 : 0 ) + ( v3Out ? 1 : 0 );switch ( total ) {case 0: {// the entire face lies inside of the plane, no clipping neededoutVertices.push( inVertices[ i ] );outVertices.push( inVertices[ i + 1 ] );outVertices.push( inVertices[ i + 2 ] );break;}case 1: {// one vertex lies outside of the plane, perform clippingif ( v1Out ) {nV1 = inVertices[ i + 1 ];nV2 = inVertices[ i + 2 ];nV3 = clip( inVertices[ i ], nV1, plane, s );nV4 = clip( inVertices[ i ], nV2, plane, s );}if ( v2Out ) {nV1 = inVertices[ i ];nV2 = inVertices[ i + 2 ];nV3 = clip( inVertices[ i + 1 ], nV1, plane, s );nV4 = clip( inVertices[ i + 1 ], nV2, plane, s );outVertices.push( nV3 );outVertices.push( nV2.clone() );outVertices.push( nV1.clone() );outVertices.push( nV2.clone() );outVertices.push( nV3.clone() );outVertices.push( nV4 );break;}if ( v3Out ) {nV1 = inVertices[ i ];nV2 = inVertices[ i + 1 ];nV3 = clip( inVertices[ i + 2 ], nV1, plane, s );nV4 = clip( inVertices[ i + 2 ], nV2, plane, s );}outVertices.push( nV1.clone() );outVertices.push( nV2.clone() );outVertices.push( nV3 );outVertices.push( nV4 );outVertices.push( nV3.clone() );outVertices.push( nV2.clone() );break;}case 2: {// two vertices lies outside of the plane, perform clippingif ( ! v1Out ) {nV1 = inVertices[ i ].clone();nV2 = clip( nV1, inVertices[ i + 1 ], plane, s );nV3 = clip( nV1, inVertices[ i + 2 ], plane, s );outVertices.push( nV1 );outVertices.push( nV2 );outVertices.push( nV3 );}if ( ! v2Out ) {nV1 = inVertices[ i + 1 ].clone();nV2 = clip( nV1, inVertices[ i + 2 ], plane, s );nV3 = clip( nV1, inVertices[ i ], plane, s );outVertices.push( nV1 );outVertices.push( nV2 );outVertices.push( nV3 );}if ( ! v3Out ) {nV1 = inVertices[ i + 2 ].clone();nV2 = clip( nV1, inVertices[ i ], plane, s );nV3 = clip( nV1, inVertices[ i + 1 ], plane, s );outVertices.push( nV1 );outVertices.push( nV2 );outVertices.push( nV3 );}break;}case 3: {// the entire face lies outside of the plane, so let's discard the corresponding verticesbreak;}}}return outVertices;}function clip( v0, v1, p, s ) {const d0 = v0.position.dot( p ) - s;const d1 = v1.position.dot( p ) - s;const s0 = d0 / ( d0 - d1 );const v = new DecalVertex(new Vector3(v0.position.x + s0 * ( v1.position.x - v0.position.x ),v0.position.y + s0 * ( v1.position.y - v0.position.y ),v0.position.z + s0 * ( v1.position.z - v0.position.z )),new Vector3(v0.normal.x + s0 * ( v1.normal.x - v0.normal.x ),v0.normal.y + s0 * ( v1.normal.y - v0.normal.y ),v0.normal.z + s0 * ( v1.normal.z - v0.normal.z )));// need to clip more values (texture coordinates)? do it this way:// intersectpoint.value = a.value + s * ( b.value - a.value );return v;}}}// helperclass DecalVertex {constructor( position, normal ) {this.position = position;this.normal = normal;}clone() {return new this.constructor( this.position.clone(), this.normal.clone() );}}export { DecalGeometry, DecalVertex };

还有其他实现手段,参考游戏中的Decal(贴花) ;

图形杂记-Decal贴花相关推荐

  1. 游戏中的Decal(贴花)

    在游戏中,decal是一种非常常见的效果,常用来实现弹孔,血迹,涂鸦等效果.最近研究了下Decal在游戏引擎中的实现方式,大致总结了一下: 1.基于面片实现: 直接用一个Quat的mesh,加上一张贴 ...

  2. (41)Decal [ 贴花] Actor

    添加decal到关卡中 向场景中添加decal的最简单的方法是:在内容浏览器中选中适当的decal材质,然后右击并从关联菜单中选择 Add Actor(添加 Actor) > Add Defer ...

  3. UE4 Decal 贴花不在静态光照下绘制

    Decal顶点没有烘焙的光照数据,因此无法在含前向管线的阴影下绘制.特效贴花为unlit自发光材质,阴影下表现影响不大.而场景Decal需要计算光照. 1. Decal实现原理 MobileDecal ...

  4. Unity3D内置Shader私房课(三)Decal贴花

    在Unity内建Shader的DefaultResourcesExtra的目录中,有一个很简单却很实用的shader--Decal.这是一个贴花着色器,可以在模型的表面添加一个贴花纹理. Unity内 ...

  5. 图形杂记--基础概念补充

    延迟着色法 延迟着色法基于我们延迟(Defer)或推迟(Postpone)大部分计算量非常大的渲染(像是光照)到后期进行处理的想法.它包含两个处理阶段(Pass):在第一个几何处理阶段(Geometr ...

  6. Unity内置Shader解读3——Decal

    1.Shader在什么情况下使用 Decal(贴花)贴花就是类似于CF里的喷漆,或者汽车/墙上等贴一个贴纸. image.png 2.Shader的价值(用的多不多),Shader的难度 这个应该是极 ...

  7. UE5实现贴地面效果(RT+Decal)

    文章目录 1.实现目标 2.实现过程 2.1 实现原理 2.1.1 Render Target 2.1.2 Polygon 2.2 具体过程 3.参考资料 1.实现目标 在之前的文章中基于Cesium ...

  8. Unity Shader 实现简单的贴花效果(二)

    这个月进入了找不倒工作的焦虑状态,花了两周时间去学OpenGL,发现以前课上的讲的内容过于浅显,也加深了对渲染管线的了解也算是相当不错的吧. 话不多说,先上最初的效果图吧. 本效果考了该篇博客Unit ...

  9. unity的贴花方案

    前言 最近通关了<What Remains of Edith Finch>(艾迪芬奇的记忆),总体来说应该算是一个剧情+解密向的游戏,故事+表现手法十分出色. 游戏主要是叙述一个神秘的家族 ...

最新文章

  1. 在Linux上进行内核参数调整
  2. 在Window Embedded CE(Wince)下使用OpenNETCF进行路由表的开发
  3. kmalloc/kfree,vmalloc/vfree函数用法和区别
  4. 小程序分享朋友圈_改造小程序,增加分享朋友圈代码
  5. Spark SQL使用window进行统计
  6. 全球计算机视觉顶会CVPR 2019论文出炉:腾讯优图25篇论文入选
  7. [转载] ANTLR——编译原理基础知识
  8. 不同工作组能访问吗_「长笛众测」你能听出3K-9W不同价位长笛的区别吗?
  9. 备份下ionic升级
  10. 开源软件 Cachet 被曝RCE漏洞
  11. Yii 自定义模型路径
  12. 【python】字典的嵌套
  13. DFS 下沙小面的(2)
  14. 2012年之前Mac Book pro 安装新系统macOS 10.15 Catalina 制作U盘启动盘
  15. php短信验证码接口免费,[php短信验证码接口]PHP 短信验证码
  16. 阿里云服务器到底有什么好处?
  17. php 英文小写转大写数字,php 英文字符大小写转换函数
  18. 4.微信支付之刷卡支付
  19. 微信小程序选择图片并转base64
  20. Nide.js安装配置

热门文章

  1. 手游推广,经营一家游戏公司需要多少人?
  2. 科普:学C++的以后能从事哪些岗位?
  3. 燕千云知识库,解决你的知识沉淀烦恼
  4. 谷歌colab“几键”运行图像超分辨率模型-ESRGAN,操作详解
  5. Unity发布的exe程序正常 显示窗口右上角的放大缩小功能
  6. This must be due to duplicate classes or playing wrongly with class loaders
  7. xp计算机用户名和密码忘记了怎么办,电脑的XP系统密码被不小心忘记了怎么办?...
  8. 数据挖掘课程第一章作业《认识数据挖掘与数据预处理》
  9. 1月6日科技资讯|小辣椒手机创始人王晓雁加入小米;手机 QQ 可显示对方实时电量
  10. 【小说】MR设备普及后的生活