前言
辞旧迎新,鞭炮阵阵。不知不觉中又过完了一年,我们即将迎来龙年,在这里先给大家拜个早年了!㊗️大家:
- 在新的一年里,愿您的代码写得更加优雅,程序运行更加顺畅,生活也如同一段完美的代码,没有bug。
- 2024年已经启动,愿您在新的计算年代里,事业能够实现高性能,幸福值达到最大。
- 代码如龙,优化如风,祝您在新的一年里编织出更多精彩的程序,实现无限可能。
- 新年伊始,让我们一起来解决新的问题,开发新的功能,共同构建更加智能的未来。
- 愿您的网络连接畅通无阻,数据存储永不溢出,新年如同高速光纤一样快速而稳定。
- 祝您的生活像源代码一样清晰易读,bug越来越少,功能升级越来越多。
- 2024年,让我们一同迎接更高版本的自己,程序人生更上一层楼。
- 祝您在新的一年里,操作系统稳如老石,软件升级如春风,硬件配置更上一层楼。
- 愿您的密码坚如磐石,网络防火墙牢不可破,新年安全无虞。
- 在这个数据时代,祝您的新年充满无限的数据流,生活充满精彩的算法。
当然,以上祝福语来自于ChatGPT,这也符合咱们程序员的一贯作风😁。
咳咳,好像有点跑题,咱们今天来整点活。龙年,让我想起来了计算机图形学界也有一条龙,它就是大名鼎鼎的斯坦福龙(Stanford Dragon),就是下面这条啦。这条龙竟然被用来当做各类渲染例子的模特。
而我们今天的主角就是这条龙没错啦!我们今天就在这条龙上涂鸦,打造一条属于我们自己的龙。
编码实现及思路介绍
我们今天采用的技术栈当然就是使用THREEJS啦~ THREEJS为我们提供了非常便利的贴花功能来实现。大家可以直接参考THREEJS提供的官方Demo three.js examples (threejs.org) 和代码three.js/examples/we…
而我们今天做的就是对其进行小小的调整就可以啦~
官方的demo可以说是非常的详细了。贴花这项技术在官方demo中被称为“Decals”,其原理今天就过多赘述,后续再出一篇文章专门来讲decals背后的原理,今天我们就着重于使用即可。
不过在这之前我们还是对代码逻辑稍作梳理。
逻辑梳理
纵观整个代码,还是比较简单。主要分为以下几块:
-
加载模型
- 加载模型文件本身
- 加载贴花贴图
- 加载天空盒背景图
-
搭建场景
- 设置相机
- 设置光照
-
射线检测
- 当鼠标在场景中移动时,检测是否与模型相交
-
添加贴花到场景中
- 如果鼠标点击时与模型相交,就在此处加一个贴花
-
执行主循环渲染
我们要做的主要就是增加贴花的纹理数量,还有就是对写一个自定义的材质让贴花看起来更加的明显。
增加纹理数量这个很简单,我们只需要使用一个数组来保存就好啦~比如:
const decalTextureUrls = [
withBase('decals/bianpao.png'),
withBase('decals/denglong.png'),
withBase('decals/dragon.png'),
withBase('decals/fu-cute.png'),
withBase('decals/fu.png'),
withBase('decals/qianbi.png'),
withBase('decals/qiandai.png'),
withBase('decals/yuanbao.png'),
];
const decalTextures = decalTextureUrls.map(url => {
return textureLoader.load(url);
});
接下来我们需要声明一个自定义Shader的材质来让贴花的渲染更加的明显!!!
function initDecalMaterial(): THREE.Material {
const customMaterial = new THREE.ShaderMaterial({
vertexShader: plainVert,
fragmentShader: plainFrag,
depthTest: true,
depthWrite: false,
uniforms: {
mainTex: {
value: decalTextures[0],
},
},
polygonOffset: true,
polygonOffsetFactor: -4,
wireframe: false,
blending: THREE.CustomBlending,
blendSrc: THREE.SrcAlphaFactor,
blendDst: THREE.OneMinusSrcAlphaFactor,
});
return customMaterial;
}
我们需要注意的是我们需要开启透明度混合,否则PNG图片在模型上透明的部分看起来就是黑黢黢的一片。所以我们要通过设置 blending, blendSrc和 blendDst 这三个值来保证混合的正确性。
还有另外一点也很重要,就是polygonOffset与polygonOffsetFactor。polygonOffset 和 polygonOffsetFactor 是两个与深度缓冲相关的属性,它们通常用于解决Z-缓冲冲突(Z-fighting)的问题。
-
polygonOffset:polygonOffset是一个布尔值,默认为false。当设置为true时,它启用多边形偏移。多边形偏移是一种通过微调深度值来解决Z-缓冲冲突的技术。
-
polygonOffsetFactor:polygonOffsetFactor是一个浮点数,表示在深度值偏移中的比例因子。它控制多边形偏移的具体程度。值越大,偏移越明显。
当启用了 polygonOffset 并设置了合适的 polygonOffsetFactor 和 polygonOffsetUnits(后者是另一个属性,表示深度值偏移的单位),渲染引擎会根据多边形的深度值进行微小的调整,从而避免Z-缓冲冲突。这对于处理近似平行的表面或者使用透明度的材质时特别有用。
我们上面使用的自定义shader的代码如下:
顶点着色器 plainVert:
#include <common>
varying vec2 vUv;
void main() {
vUv = uv;
vec4 mvPosition = vec4(position, 1.0);
mvPosition = modelViewMatrix * mvPosition;
gl_Position = projectionMatrix * mvPosition;
}
片段着色器:
uniform sampler2D mainTex;
varying vec2 vUv;
void main () {
vec4 color = texture(mainTex, vUv);
gl_FragColor = color;
}
上面的代码都非常的简单,就不再赘述啦~
最后的最后我们只需要在shoot函数中生成贴花的地方给贴图加入一点随机性,就好啦
function shoot() {
position.copy(intersection.point);
orientation.copy(mouseHelper.rotation);
if (params.rotate) orientation.z = Math.random() * 2 * Math.PI;
const scale =
params.minScale +
Math.random() * (params.maxScale - params.minScale);
size.set(scale, scale, scale);
const material = customMaterial.clone();
let index = Math.floor(Math.random() * decalTextures.length);
(material as THREE.ShaderMaterial).uniforms.mainTex.value =
decalTextures[index];
// 省略下面的代码
}
经过我们的略微改造,我们的最终实现效果如下:
总结
今天我们利用THREEJS的贴花功能和斯坦福龙完成了一个龙年的小小创意。代码很简单,希望这简短的代码能为你的生活带来一抹亮色。
最后的最后,还是要祝各位新年快乐啦~