Three.js WebGPU 节点材质系统 控制instances的某个实例单独的透明度,颜色等属性

news2024/9/26 3:32:15

文章目录

            • 1. 声明一个实例必要的属性`instanceMatrix`同级别的属性
            • 2. 在设置位置矩阵的时候填充这个数组
            • 3. 在shader中获取当前的索引
            • 4. 增加uniform
            • 5. 对比当前着色的实例是否是选中的实例
            • 6. 如果是选中的实例
            • 7. 影响片元着色器透明度参数
          • 8.源码

写在前面
本文环境是 原生js 没使用框架
因为目前r167节点材质系统还不太稳定试了几个打包工具对有些特性支持不好 遂不在框架中写代码
并且直接引用 three.webgpu.js文件 更方便更改源代码 插入自己的元素 也是本文的实现方式

官方案例
在这里插入图片描述
效果如图 第二个实例透明度为0.1 其他的为1
在这里插入图片描述

实现思路:

1. 声明一个实例必要的属性instanceMatrix同级别的属性
child.instanceIndex = new THREE.InstancedBufferAttribute(
	new Float32Array(实例数量),
	1
);
2. 在设置位置矩阵的时候填充这个数组
for (let i = 0; i < 实例数量; i++) {
	//,,,
	child.instanceIndex.array[i] = i ;
}
3. 在shader中获取当前的索引

修改InstanceNode的源码的setup函数

if(instanceMesh.instanceIndex){

	const indexBuffer = new InstancedBufferAttribute( instanceMesh.instanceIndex.array, 1 );
	
	const _index = instancedBufferAttribute( indexBuffer ) 
}

_index就是当前着色的实例索引

4. 增加uniform
// 提供uniform
// 选中的实例索引
child.selectInstanceIndex = uniform(1, "float");
// 选中的实例索引的透明度
child.selectInstanceIndexOpacity = uniform(0.1, "float");
5. 对比当前着色的实例是否是选中的实例
if(instanceMesh.instanceIndex){

	const indexBuffer = new InstancedBufferAttribute( instanceMesh.instanceIndex.array, 1 );
	
	const _index = instancedBufferAttribute( indexBuffer ) 
	
	If(_index.equal(instanceMesh.selectInstanceIndex),() => {
		//...			
	})
}
6. 如果是选中的实例

加入一个varying变量vInstanceIndexOpacity影响选中的实例的透明度(也可以影响其他材质参数 这里以透明度为例)

if(instanceMesh.instanceIndex){

	const indexBuffer = new InstancedBufferAttribute( instanceMesh.instanceIndex.array, 1 );
	
	const _index = instancedBufferAttribute( indexBuffer ) 
	
	If(_index.equal(instanceMesh.selectInstanceIndex),() => {
+		varyingProperty( 'float', 'vInstanceIndexOpacity' ).assign(instanceMesh.selectInstanceIndexOpacity );
	})
}
7. 影响片元着色器透明度参数

NodeMaterial对象的setupDiffuseColor方法中将透明度乘以vInstanceIndexOpacity的值或者直接设置为vInstanceIndexOpacity的值

const vInstanceIndexOpacity = varyingProperty( 'float', 'vInstanceIndexOpacity' ); 

// OPACITY

const opacityNode = this.opacityNode ? float( this.opacityNode ) : materialOpacity;

diffuseColor.a.assign( diffuseColor.a.mul( opacityNode ).mul(vInstanceIndexOpacity) );

如此便可通过更改uniform来决定某个实例的透明度了
以此思路其他材质属性也均可单独指定

8.源码

html

<!DOCTYPE html>
<html lang="en">
	<head>
		<title>three.js webgpu - skinning instancing</title>
		<meta charset="utf-8" />
		<meta
			name="viewport"
			content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"
		/>
		<link type="text/css" rel="stylesheet" href="../main.css" />
	</head>
	<body>
		<div id="info">
			<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a>
			webgpu - skinning instancing
		</div>

		<script type="importmap">
			{
				"imports": {
					"three": "../../build/three.webgpu.js",
					"three/tsl": "../../build/three.webgpu.js",
					"three/addons/": "../jsm/"
				}
			}
		</script>

		<script type="module">
			import * as THREE from "three";
			import {
				pass,
				mix,
				range,
				color,
				oscSine,
				timerLocal,
				texture,
				TextureNode,
				normalLocal,
				min,
				max,
				abs,
				uniform
			} from "three/tsl";

			import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
			import { OrbitControls } from "three/addons/controls/OrbitControls.js";
			import { RectAreaLightHelper } from "three/addons/helpers/RectAreaLightHelper.js";
			import { RectAreaLightTexturesLib } from "three/addons/lights/RectAreaLightTexturesLib.js";

			let camera, scene, renderer, controls;
			let postProcessing;

			let mixer, clock;

			init();

			function init() {
				THREE.RectAreaLightNode.setLTC(RectAreaLightTexturesLib.init());

				camera = new THREE.PerspectiveCamera(
					50,
					window.innerWidth / window.innerHeight,
					0.01,
					40
				);
				// camera.position.set( 1, 2, 3 );
				camera.position.set(0, 0, 0);

				scene = new THREE.Scene();
				scene.add(new THREE.AxesHelper(1));
				camera.lookAt(0, 1, 0);

				clock = new THREE.Clock();

				// lights

				const centerLight = new THREE.PointLight(0xff9900, 2, 100);
				centerLight.position.y = 4.5;
				centerLight.power = 400;
				// scene.add(centerLight);

				const cameraLight = new THREE.PointLight(0xffffff, 1, 100);
				cameraLight.power = 400;
				cameraLight.position.set(0, 2, 3);
				// camera.add(cameraLight);
				// scene.add(camera);
				// scene.add(cameraLight);

				const rectLight1 = new THREE.RectAreaLight(0xffffff, 10, 10, 0.5);
				rectLight1.position.set(0, 2, 0);
				rectLight1.lookAt(0, -1, 0);
				scene.add(rectLight1);
				{
					const rectLight1 = new THREE.RectAreaLight(0xffffff, 10, 10, 0.1);
					rectLight1.position.set(0, 0, 2);
					rectLight1.lookAt(0, 0, 0);
					scene.add(rectLight1);
				}
				scene.add(new RectAreaLightHelper(rectLight1));

				const thickness = 10;
				const geometry = new THREE.BoxGeometry(100, 2, thickness);
				geometry.translate(0, 0, -thickness / 2);
				geometry.rotateX(-Math.PI / 2);

				const plane = new THREE.Mesh(
					geometry,
					new THREE.MeshStandardMaterial({
						color: 0x000000,
						roughness: 1,
						metalness: 0.6,
					})
				);
				scene.add(plane);

				const loader = new GLTFLoader();
				loader.load("../models/gltf/Michelle.glb", function (gltf) {
					const object = gltf.scene;

					mixer = new THREE.AnimationMixer(object);

					const action = mixer.clipAction(gltf.animations[0]);
					action.play();

					const instanceCount = 3;
					const dummy = new THREE.Object3D();

					object.traverse((child) => {
						if (child.isMesh) {
							// const oscNode = max(0,oscSine(timerLocal(0.1)));
							const oscNode = abs(oscSine(timerLocal(0.1)));
							// const oscNode = oscSine(timerLocal(0.1));

							const randomColors = range(
								new THREE.Color(0x0000),
								new THREE.Color(0xffffff)
							);

							const randomMetalness = range(0, 1);
							const prevMap = child.material.map;
							child.material = new THREE.MeshStandardNodeMaterial({
								transparent: true,
							});

							// child.material.onBeforeCompile = (shader) => {
							// 	console.log("onBeforeCompile:", shader);
							// };

							// roughnessNode是变化的 roughness是固定的
							child.material.roughnessNode = oscNode;

							child.material.metalnessNode =
								0.5 || mix(0.0, randomMetalness, oscNode);

							child.material.colorNode = mix(
								texture(prevMap),
								randomColors,
								oscNode
							);

							child.isInstancedMesh = true;
							child.instanceMatrix = new THREE.InstancedBufferAttribute(
								new Float32Array(instanceCount * 16),
								16
							);
							child.instanceIndex = new THREE.InstancedBufferAttribute(
								new Float32Array(instanceCount),
								1
							);

							// 提供uniform
							// 选中的实例索引
							child.selectInstanceIndex = uniform(1, "float");
							// 选中的实例索引的透明度
							child.selectInstanceIndexOpacity = uniform(0.1, "float");
							
							child.count = instanceCount;

							for (let i = 0; i < instanceCount; i++) {
								dummy.position.x = i * 70;

								dummy.position.y = Math.floor(i / 5) * -200;

								dummy.updateMatrix();

								dummy.matrix.toArray(child.instanceMatrix.array, i * 16);

								child.instanceIndex.array[i] = i ;
							}
							// child.instanceIndex.array[0] = 0 ;
							// child.instanceIndex.array[1] = 1 ;
							// child.instanceIndex.array[2] = 5 ;
						}
					});

					scene.add(object);
				});

				// renderer

				renderer = new THREE.WebGPURenderer({ antialias: true });
				renderer.setPixelRatio(window.devicePixelRatio);
				renderer.setSize(window.innerWidth, window.innerHeight);
				renderer.setAnimationLoop(animate);
				document.body.appendChild(renderer.domElement);

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

				controls.target.set(0, 1, 0);
				controls.object.position.set(0, 1, 4);

				// post processing

				const scenePass = pass(scene, camera);
				const scenePassColor = scenePass.getTextureNode();
				const scenePassDepth = scenePass
					.getLinearDepthNode()
					.remapClamp(0.15, 0.3);

				const scenePassColorBlurred = scenePassColor.gaussianBlur();
				scenePassColorBlurred.directionNode = scenePassDepth;

				// postProcessing = new THREE.PostProcessing(renderer);
				// postProcessing.outputNode = scenePassColorBlurred;

				// events

				window.addEventListener("resize", onWindowResize);
			}

			function onWindowResize() {
				camera.aspect = window.innerWidth / window.innerHeight;
				camera.updateProjectionMatrix();

				renderer.setSize(window.innerWidth, window.innerHeight);
			}

			function animate() {
				const delta = clock.getDelta();

				if (mixer) mixer.update(delta);

				// postProcessing.render();
				renderer.render(scene, camera);
			}
		</script>
	</body>
</html>

两个three模块核心函数修改后的代码
NodeMaterial.setupDiffuseColor

setupDiffuseColor( { object, geometry } ) {

		let colorNode = this.colorNode ? vec4( this.colorNode ) : materialColor;

		// VERTEX COLORS

		if ( this.vertexColors === true && geometry.hasAttribute( 'color' ) ) {

			colorNode = vec4( colorNode.xyz.mul( attribute( 'color', 'vec3' ) ), colorNode.a );

		}

		// Instanced colors

		if ( object.instanceColor ) {

			const instanceColor = varyingProperty( 'vec3', 'vInstanceColor' );

			colorNode = instanceColor.mul( colorNode );
			
		}

		const vInstanceIndexOpacity = varyingProperty( 'float', 'vInstanceIndexOpacity' ); 

		// COLOR

		diffuseColor.assign( colorNode );

		// OPACITY

		const opacityNode = this.opacityNode ? float( this.opacityNode ) : materialOpacity;
		diffuseColor.a.assign( diffuseColor.a.mul( opacityNode ).mul(vInstanceIndexOpacity) );

		// ALPHA TEST

		if ( this.alphaTestNode !== null || this.alphaTest > 0 ) {

			const alphaTestNode = this.alphaTestNode !== null ? float( this.alphaTestNode ) : materialAlphaTest;

			diffuseColor.a.lessThanEqual( alphaTestNode ).discard();

		}

		if ( this.transparent === false && this.blending === NormalBlending && this.alphaToCoverage === false ) {

			diffuseColor.a.assign( 1.0 );

		}

	}

InstanceNode.setup

	setup( /*builder*/ ) {

		let instanceMatrixNode = this.instanceMatrixNode;
		let instanceColorNode = this.instanceColorNode;
		let instanceIndexNode;
		
		const instanceMesh = this.instanceMesh;
		

		if ( instanceMatrixNode === null ) {

			const instanceAttribute = instanceMesh.instanceMatrix;

			// Both WebGPU and WebGL backends have UBO max limited to 64kb. Matrix count number bigger than 1000 ( 16 * 4 * 1000 = 64kb ) will fallback to attribute.

			if ( instanceMesh.count <= 1000 ) {

				instanceMatrixNode = buffer( instanceAttribute.array, 'mat4', instanceMesh.count ).element( instanceIndex );
				console.log('instanceMatrixNode:',instanceMatrixNode)

			} else {

				const buffer = new InstancedInterleavedBuffer( instanceAttribute.array, 16, 1 );

				this.buffer = buffer;

				const bufferFn = instanceAttribute.usage === DynamicDrawUsage ? instancedDynamicBufferAttribute : instancedBufferAttribute;

				const instanceBuffers = [
					// F.Signature -> bufferAttribute( array, type, stride, offset )
					bufferFn( buffer, 'vec4', 16, 0 ),
					bufferFn( buffer, 'vec4', 16, 4 ),
					bufferFn( buffer, 'vec4', 16, 8 ),
					bufferFn( buffer, 'vec4', 16, 12 )
				];

				instanceMatrixNode = mat4( ...instanceBuffers );

			}

			this.instanceMatrixNode = instanceMatrixNode;

			if( instanceMesh.instanceIndex ){

				const insertInstanceIndex = instanceMesh.instanceIndex;
				
				// instanceIndexNode = buffer(insertInstanceIndex.array, "float", instanceMesh.count).element(instanceIndex);
				// console.log("插入实例索引:",instanceIndexNode)
			}
		}

		const instanceColorAttribute = instanceMesh.instanceColor;

		if ( instanceColorAttribute && instanceColorNode === null ) {

			const buffer = new InstancedBufferAttribute( instanceColorAttribute.array, 3 );

			const bufferFn = instanceColorAttribute.usage === DynamicDrawUsage ? instancedDynamicBufferAttribute : instancedBufferAttribute;

			this.bufferColor = buffer;

			instanceColorNode = vec3( bufferFn( buffer, 'vec3', 3, 0 ) );

			this.instanceColorNode = instanceColorNode;

		}

		// POSITION

		const instancePosition = instanceMatrixNode.mul( positionLocal ).xyz;

		// NORMAL

		const m = mat3( instanceMatrixNode );

		const transformedNormal = normalLocal.div( vec3( m[ 0 ].dot( m[ 0 ] ), m[ 1 ].dot( m[ 1 ] ), m[ 2 ].dot( m[ 2 ] ) ) );

		const instanceNormal = m.mul( transformedNormal ).xyz;

		// ASSIGNS

		positionLocal.assign( instancePosition );
		normalLocal.assign( instanceNormal );

		// COLOR

		if ( this.instanceColorNode !== null ) {

			varyingProperty( 'vec3', 'vInstanceColor' ).assign( this.instanceColorNode );
			
		}
		
		if(instanceMesh.instanceIndex){

			const indexBuffer = new InstancedBufferAttribute( instanceMesh.instanceIndex.array, 1 );
			
			const _index = instancedBufferAttribute( indexBuffer ) 

			// 当前的索引
			varyingProperty( 'float', 'vInstanceIndexOpacity' ).assign( 1 );
			// 当前index是uniform selectInstanceIndex 的实例
			If(_index.equal(instanceMesh.selectInstanceIndex),() => {
				varyingProperty( 'float', 'vInstanceIndexOpacity' ).assign( instanceMesh.selectInstanceIndexOpacity );
			})

		}


	}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1971045.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

EV代码签名证书申请流程

EV代码签名证书可以有效提高用户信赖。可以用于任何软件&#xff0c;支持Microsoft SmartScreen应用程序信誉功能以及对Windows 10内核驱动程序进行签名。 下面是EV代码签名证书的申请流程 代码签名证书_代码签名证书申请购买-JoySSL代码签名证书是对可执行脚本、软件代码和内容…

500+伙伴齐聚上海:纷享销客生态伙伴大会·上海站成功举办

近日&#xff0c;纷享销客生态伙伴大会上海站成功举办&#xff0c;此次会议汇聚了500余位来自各行各业的伙伴&#xff0c;齐聚一堂&#xff0c;共同探讨行业的未来发展趋势。 01、展望CRM市场 国内外双轮驱动&#xff0c;SaaS巅峰在价值创造与效率运营 纷享销客创始人兼CEO罗…

vulhub:nginx解析漏洞CVE-2013-4547

此漏洞为文件名逻辑漏洞&#xff0c;该漏洞在上传图片时&#xff0c;修改其16进制编码可使其绕过策略&#xff0c;导致解析为 php。当Nginx 得到一个用户请求时&#xff0c;首先对 url 进行解析&#xff0c;进行正则匹配&#xff0c;如果匹配到以.php后缀结尾的文件名&#xff…

零售门店客流统计系统支持回头客识别,更好维护老客户

随着市场竞争日益激烈&#xff0c;零售业面临着诸多挑战&#xff0c;尤其是如何吸引新客户的同时留住老客户。客流统计系统作为一项关键的技术手段&#xff0c;正在帮助零售门店解决这一难题。 一、零售门店客流统计痛点 1.数据准确性低&#xff1a;传统的人工统计方法往往存在…

MATLAB(10)分类算法

前言 MATLAB中实现分类算法的代码可以非常多样&#xff0c;取决于你具体想要使用的分类算法类型&#xff08;如决策树、逻辑回归、支持向量机、K近邻等&#xff09;。以下是一些常见分类算法的基本MATLAB实现示例。 一、逻辑回归 逻辑回归是分类问题中的一种基础算法&#xff0…

第十六天学习笔记2024.7.29

web yum -y install httpd systemctl start httpd.service systemctl stop firewalld systemctl disable firewalld 2、动态⻚⾯与静态⻚⾯的差别 &#xff08;1&#xff09;URL不同 静态⻚⾯链接⾥没有“&#xff1f;” 动态⻚⾯链接⾥包含“&#xff1f;” &#xff08…

第一 二章 小车硬件介绍-(全网最详细)基于STM32智能小车-蓝牙遥控、避障、循迹、跟随、PID速度控制、视觉循迹、openmv与STM32通信、openmv图像处理、smt32f103c8t6

第一篇-STM32智能小车硬件介绍 后续章节也放这里 持续更新中&#xff0c;视频发布在小B站 里面。这边也会更新。 B站视频合集: STM32智能小车V3-STM32入门教程-openmv与STM32循迹小车-stm32f103c8t6-电赛 嵌入式学习 PID控制算法 编码器电机 跟随 小B站链接:https://www.bilib…

贪心算法—股票交易时机Ⅱ

在此前我们已经介绍过贪心算法以及股票交易时机Ⅰ&#xff0c;有需要的话可以移步至贪心算法_Yuan_Source的博客-CSDN博客 题目介绍 122. 买卖股票的最佳时机 II - 力扣&#xff08;LeetCode&#xff09; 给你一个整数数组 prices &#xff0c;其中 prices[i] 表示某支股票第…

【Linux】问题解决:yum repolist出现“!”号

问题描述&#xff1a;在运行 yum repolist 时&#xff0c;出现以下状况&#xff1a; 原因&#xff1a;表示仓库里有过期的元数据&#xff0c;并不是最新版本。 解决方法&#xff1a; 清楚过期缓存 yum clean all 快速创建新yum缓存 yum makecache fast 结果&#xff1a;…

Qt——QTCreater ui界面如何统一设置字体

第一步&#xff1a;来到 ui 设计界面&#xff0c;鼠标右键点击 改变样式表 第二步&#xff1a;选择添加字体 第三步&#xff1a;选择字体样式和大小&#xff0c;点击 ok 第四步&#xff1a;点击ok或apply&#xff0c;完成设置

基于100G-PAM4技术的LinkX 线缆

LinkX线缆专注于加速数据中心和人工智能计算系统&#xff0c;这些产品不仅提供了高数据传输速率&#xff0c;还在设计上特别优化了低延迟性能&#xff0c;以满足现代计算系统对速度和效率的高要求。 一、主要特点与技术规格 1、传输距离与速率 数据中心应用&#xff1a;支持…

用 Bytebase 实现批量、多环境、多租户数据库的丝滑变更

Bytebase 提供了多种功能来简化批量变更管理&#xff0c;适用于多环境或多租户情况。本教程将指导您如何使用 部署配置 和 数据库组 在不同场景下进行数据库批量变更。 默认流水线 vs 部署配置 图片数据库 vs 数据库组 1. 准备 请确保已安装 Docker&#xff0c;如果本地没有重…

Ubuntu 24.04 LTS Noble安装OpenSSH服务器

OpenSSH 服务器在 Ubuntu Linux 上提供安全外壳 ( SSH) 协议&#xff0c;以便远程管理系统&#xff0c;同时提供高级别的加密&#xff0c;确保安全。虽然许多 Linux 系统默认配备 OpenSSH 服务器&#xff0c;但在 Ubuntu 24.04 上&#xff0c;我们必须手动安装它。因此&#x…

新版Next.js 15中5个令人惊叹的特性

前端岗位内推来了 Next.js 15已经到来&#xff0c;一切比以往更好&#xff01; 从全新的编译器到700倍更快的构建时间&#xff0c;创建具有卓越性能的全栈Web应用从未如此简单。 让我们探索v15的最新特性&#xff1a; 1. create-next-app升级&#xff1a;更清晰的UI&#xff0c…

【Spring Boot】手撕搜索引擎项目,深度复盘在开发中的重难点和总结(长达两万6千字的干货,系好安全带,要发车了......)

目录 搜索引擎搜索引擎的核心思路 一、解析模块1.1 枚举所有文件1.2 解析每个文件的标题&#xff0c;URL以及正文1.2.1 解析标题1.2.2 解析URL1.2.3 解析正文 1.3 线程池优化代码 二 、创建排序模块2.1 构建正排索引2.2 构建倒排索引2.3 序列化2.4 反序列化 三、搜索模块3.1 引…

CTF web cookie考题

CTF web cookie考题 题目名称&#xff1a;cookie 题目名称&#xff1a;cookie 1.进入服务器界面 &#xff1a; 2.打开 浏览器内 ——>开发者工具 网络 查看 或按F12 3. 根据自己服务器地址 后缀加入\cookie.php 进入查看 4.进入cookie.php文件 点击文件查看 5.找出flag

2023华为OD面试手撕代码经验分享

我们先来看下这个同学的面试经历吧&#xff0c;非常有借鉴的意义。 【22届考研渣渣的od求职之旅&#xff0c;推荐一下两个人&#xff0c;德科hr和牛客的老哥】 "*********"&#xff0c;hr给了机会吧&#xff0c;一开始我都没想到od这边。 还有我关注里面的老哥&#…

postgresql启动故障【已解决】

故障原因 暂时未知&#xff0c;电脑重启后postgresql无法正常运行&#xff0c;尝试重启遇到如下错误 本地计算机 上的 postgresql-x64-12 服务启动后停止。某些服务在未由其他服务或程序使用时将自动停止。 排查过程 查看系统日志&#xff0c;发现pid已存在【计算机管理——…

sqli-labs-master less1-less6

目录 通关前必看 1、判断是否存在sql注入以及是字符型还是数值型&#xff1a; 2、各种注入方式以及方法 有回显型&#xff1a; 报错注入&#xff08;只有ok和no的提示以及报错提示&#xff09;&#xff1a; 详细思路&#xff0c;后面的题都可以这样去思考 关卡实操 less…

vue2中使用jsx基础

1、基本的模板 <script lang"jsx"> export default {name: demo1,// 数据定义data() {return {info: {age: 18,}}},created() {},methods: {},render() {return (<div><div>我是小明&#xff0c;今年{this.info.age}</div></div>)} }…