图形杂记-Decal贴花

article/2025/8/29 2:09:46

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(贴花) ;


http://chatgpt.dhexx.cn/article/TopkH62M.shtml

相关文章

SQL中的declare用法

&#xfeff;&#xfeff; 平时写SQL查询、存储过程都是凭着感觉来&#xff0c;没有探究过SQL的具体语法&#xff0c;一直都是按c#那一套往SQL上模仿&#xff0c;前几天项目中碰到一个问题引起了我对declare定义变量的作用域的兴趣。 大家都知道c#中的局部变量&#xff0c;在i…

UE4使用贴花(Decal)

一、创建贴花材质 1:新建一个Material材质&#xff0c;修改Material Domain 为Deferred Decal (延迟贴花)&#xff0c;设置Blend Mode 为Translucent(半透明) 2&#xff1a;新建节点 3&#xff1a;给节点赋予材质&#xff0c;根据要求链接透明度和粗糙度 二、使用使用贴花 …

Deferred Decal(延迟贴花)

Decal渲染是一个引擎中重要的一部分&#xff0c;记忆中印象最深刻的就是以前CS中的弹痕与爆炸痕迹了。目前来说&#xff0c;Decal的实现方法也比较多&#xff0c;而且感觉还跟游戏类型有关&#xff0c;比如子弹乱飞的射击类FPS游戏中对贴花系统的要求就比较高&#xff0c;因为本…

【游戏开发小技】Unity中实现Dota里的角色技能地面贴花效果(URP | ShaderGraph | Decal)

本文最终效果 文章目录 一、前言二、环境准备1、URP环境准备2、技能范围图案 二、方案一&#xff1a;写Shader实现1、Shader脚本&#xff1a;UrpDecal.shader2、材质球3、创建Cube4、地面场景5、添加Renderer Feature: Decal6、移动DecalCube&#xff0c;与地面交叉7、运行效果…

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

文章目录 1.实现目标2.实现过程2.1 实现原理2.1.1 Render Target2.1.2 Polygon2.2 具体过程3.参考资料1.实现目标 在之前的文章中基于CesiumForUnreal实现了对地形3DTileset的贴地面绘制效果,在这里基于UE自带的RT和Decal实现更加通用的贴地面效果。依旧是加载在线的Cesium W…

Unity Shader-Decal贴花(SelfDecal,Alpha Blend,Mesh Decal,Projector,Deferred Decal)

前言 最近通关了《What Remains of Edith Finch》&#xff08;艾迪芬奇的记忆&#xff09;&#xff0c;总体来说应该算是一个剧情解密向的游戏&#xff0c;故事表现手法十分出色。 游戏主要是叙述一个神秘的家族遭遇了一系列类似《死神来了》的故事&#xff0c;家族的人离奇死…

Unity Decal 贴花效果测试

贴花效果&#xff0c;就和名字的直接意思类似&#xff0c;把一张图贴到另一个物体上显示&#xff0c;经常被用于表现一些重复出现的图案&#xff0c;比如弹孔&#xff0c;涂鸦&#xff0c;污渍等。效果图&#xff1a; 常规贴花实现 Unity官方提供了一个工程&#xff0c;这个…

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

Decal顶点没有烘焙的光照数据&#xff0c;因此无法在含前向管线的阴影下绘制。特效贴花为unlit自发光材质&#xff0c;阴影下表现影响不大。而场景Decal需要计算光照。 1. Decal实现原理 MobileDecalRendering.cpp 通过矩阵变换 得到FrustumComponentToClip&#xff0c;传入…

游戏中的Decal(贴花)

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

UE4 Decal实现简介

Decal 是游戏中常见的一个东西&#xff0c;经常被用在 显示弹痕&#xff0c;地面叠加花纹等。 Decal 绘制流程 Decal可以认为是&#xff0c;将一个面的画面沿Decal的Box的X轴方向投影到物体表面。 Decal的绘制实际只有一个Box绘制。 RHICmdList.DrawIndexedPrimitive(GetUn…

跳出for循环

跳出for循环有三种方式&#xff1a; 1&#xff1a;continue&#xff1b;跳出当次循环&#xff0c;可继续进行下一个循环&#xff1b; function ceshi(){for(var i 0 ; i < 6 ; i){if(i 3){continue;}console.log(,i);} }ceshi(); 效果图&#xff1a; 2&#xff1a;brea…

跳出forEach循环

我们平时用到的循环有很多种。for, map, while, forEach, for...of, for...in等等&#xff0c;每种循环都有在某一次循环语句中跳出本次循环的方法&#xff0c;但是除了forEach。 有小伙伴说不用不就好了。其实这些循环里边&#xff0c;当属for的效率最高&#xff0c;for...in最…

Java 8 跳出foreach循环,跳出本次循环,继续执行,之前的for each循环如何跳出本次循环,跳出循环,跳出多层for循环。

在Java8之前&#xff0c;最开始使用for i 循环&#xff0c;很老旧&#xff0c; 后来有了高级的for each 循环&#xff0c;然后这个跳出本次循环和跳出所有的for循环&#xff0c;都简单&#xff0c;稍微没见过的就是跳出多层for循环。 然后就是Java8出的foreach循环&#xff0…

js中的for循环如何跳出,js中for循环的两种语法

js几种for循环的几种用法 谷歌人工智能写作项目&#xff1a;小发猫 js&#xff0c;for循环是怎么运行的&#xff1f; typescript有哪些变化。 最普遍的介绍&#xff1a;for循环是JavaScript中最常用的循环&#xff0c;标准for循环代码格式为&#xff1a;for(定义变量初始值;…

if/while/do-while/for循环以及跳出循环break/return/continue

流程控制对任何一门编程语言都是至关重要的&#xff0c;它为我们提供了控制程序步骤的基本手段。常见对主要分为&#xff0c;条件语句、循环语句、跳转语句。 1、if语句 if 语句是一种判断语句。 语法&#xff1a; if(条件){条件成立时执行的代码 } if...else 语句当条件成…

Python中跳出循环的两种方法

我们经常遇到循环在进行到某一个特定的值时&#xff0c;需要跳出循环&#xff0c;或跳过这个值&#xff0c;python中早已为我们准备了这样的参数:break,continue 比如下面的for循环&#xff1a; for i in range(1,10):print(循环了,i,次) 结果显而易见如图&#xff1a; 当我…

js foreach与for循环之return跳出循环

因为自己比较大只&#xff0c;容易忘记&#xff0c;仅此用来记录一下~ 各种循环中使用return或者退出循环的机制。 1、forEach 使用 return 可以退出循环吗&#xff1f;下面代码打印啥&#xff1f; const list [1, 2, 3, 4, 5]list.forEach(e > {if (e 3) {return}consol…

JS中如何跳出.forEach循环

写在前面 提到在一段程序中如果碰到需要终止&#xff0c;结束一个循环&#xff0c;函数或者一段代码&#xff0c;一般会想到以下这几个关键字return、continue、break 简述一下三者的区别&#xff1a; break: 终止整个循环(有内层循环时终止的是内层循环)&#xff0c;退出swi…

JavaScript foreach 方法跳出循环

通常&#xff0c;在 for循环中跳出循环可以用 break或者 continue 来跳出循环。 break&#xff1a;跳出循环&#xff1b; continue&#xff1a;跳过当次循环。 而有时候需要在 foreach 中跳出循环&#xff0c;该怎么做呢&#xff1f; forEach() 方法用于调用数组的每个元素&am…

C++跳出for循环的方式

注&#xff1a;continue只能跳出当前层的循环&#xff0c;无法直接跳出for循环 方法1&#xff1a; for循环中满足条件后使用break语句&#xff1b; #include <iostream> using namespace std;int main(int argc,char *argv[]) {int i;for(i0;i<5;i){if(i2)break;}cou…