0


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

文章目录

写在前面
本文环境是 原生js 没使用框架
因为目前r167节点材质系统还不太稳定试了几个打包工具对有些特性支持不好 遂不在框架中写代码
并且直接引用

three.webgpu.js

文件 更方便更改源代码 插入自己的元素 也是本文的实现方式

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

实现思路:

1. 声明一个实例必要的属性
instanceMatrix

同级别的属性

child.instanceIndex =newTHREE.InstancedBufferAttribute(newFloat32Array(实例数量),1);
2. 在设置位置矩阵的时候填充这个数组
for(let i =0; i < 实例数量; i++){//,,,
    child.instanceIndex.array[i]= i ;}
3. 在shader中获取当前的索引

修改

InstanceNode

的源码的

setup

函数

if(instanceMesh.instanceIndex){const indexBuffer =newInstancedBufferAttribute( 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 =newInstancedBufferAttribute( 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');// OPACITYconst opacityNode = this.opacityNode ?float( this.opacityNode ): materialOpacity;

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

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

其他 - 渐入渐出动画

如果想让透明度的值 自动变化 可以如下

const oscNode =abs(oscSine(timerLocal(0.1)));// 选中的实例索引的透明度- child.selectInstanceIndexOpacity =uniform(0.1,"float");+ child.selectInstanceIndexOpacity = oscNode;
ocsNode

的值就是时间放慢10倍并且使用sin函数约束值[-1,1 ]再使用abs取绝对值 使之在[0-1-0]之间循环
这样渐入渐出的动画就巧妙的完成了 这也是 节点材质系统的优越性和趣味性的体现

8.源码

html

<!DOCTYPEhtml><htmllang="en"><head><title>three.js webgpu - skinning instancing</title><metacharset="utf-8"/><metaname="viewport"content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"/><linktype="text/css"rel="stylesheet"href="../main.css"/></head><body><divid="info"><ahref="https://threejs.org"target="_blank"rel="noopener">three.js</a>
            webgpu - skinning instancing
        </div><scripttype="importmap">{"imports":{"three":"../../build/three.webgpu.js","three/tsl":"../../build/three.webgpu.js","three/addons/":"../jsm/"}}</script><scripttype="module">import*asTHREEfrom"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();functioninit(){THREE.RectAreaLightNode.setLTC(RectAreaLightTexturesLib.init());

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

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

                clock =newTHREE.Clock();// lightsconst centerLight =newTHREE.PointLight(0xff9900,2,100);
                centerLight.position.y =4.5;
                centerLight.power =400;// scene.add(centerLight);const cameraLight =newTHREE.PointLight(0xffffff,1,100);
                cameraLight.power =400;
                cameraLight.position.set(0,2,3);// camera.add(cameraLight);// scene.add(camera);// scene.add(cameraLight);const rectLight1 =newTHREE.RectAreaLight(0xffffff,10,10,0.5);
                rectLight1.position.set(0,2,0);
                rectLight1.lookAt(0,-1,0);
                scene.add(rectLight1);{const rectLight1 =newTHREE.RectAreaLight(0xffffff,10,10,0.1);
                    rectLight1.position.set(0,0,2);
                    rectLight1.lookAt(0,0,0);
                    scene.add(rectLight1);}
                scene.add(newRectAreaLightHelper(rectLight1));const thickness =10;const geometry =newTHREE.BoxGeometry(100,2, thickness);
                geometry.translate(0,0,-thickness /2);
                geometry.rotateX(-Math.PI/2);const plane =newTHREE.Mesh(
                    geometry,newTHREE.MeshStandardMaterial({color:0x000000,roughness:1,metalness:0.6,}));
                scene.add(plane);const loader =newGLTFLoader();
                loader.load("../models/gltf/Michelle.glb",function(gltf){const object = gltf.scene;

                    mixer =newTHREE.AnimationMixer(object);const action = mixer.clipAction(gltf.animations[0]);
                    action.play();const instanceCount =3;const dummy =newTHREE.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(newTHREE.Color(0x0000),newTHREE.Color(0xffffff));const randomMetalness =range(0,1);const prevMap = child.material.map;
                            child.material =newTHREE.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 =newTHREE.InstancedBufferAttribute(newFloat32Array(instanceCount *16),16);
                            child.instanceIndex =newTHREE.InstancedBufferAttribute(newFloat32Array(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 =newTHREE.WebGPURenderer({antialias:true});
                renderer.setPixelRatio(window.devicePixelRatio);
                renderer.setSize(window.innerWidth, window.innerHeight);
                renderer.setAnimationLoop(animate);
                document.body.appendChild(renderer.domElement);

                controls =newOrbitControls(camera, renderer.domElement);

                controls.target.set(0,1,0);
                controls.object.position.set(0,1,4);// post processingconst 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);}functiononWindowResize(){
                camera.aspect = window.innerWidth / window.innerHeight;
                camera.updateProjectionMatrix();

                renderer.setSize(window.innerWidth, window.innerHeight);}functionanimate(){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 COLORSif(this.vertexColors ===true&& geometry.hasAttribute('color')){

            colorNode =vec4( colorNode.xyz.mul(attribute('color','vec3')), colorNode.a );}// Instanced colorsif( object.instanceColor ){const instanceColor =varyingProperty('vec3','vInstanceColor');

            colorNode = instanceColor.mul( colorNode );}const vInstanceIndexOpacity =varyingProperty('float','vInstanceIndexOpacity');// COLOR

        diffuseColor.assign( colorNode );// OPACITYconst opacityNode =this.opacityNode ?float(this.opacityNode ): materialOpacity;
        diffuseColor.a.assign( diffuseColor.a.mul( opacityNode ).mul(vInstanceIndexOpacity));// ALPHA TESTif(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 =newInstancedInterleavedBuffer( 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 =newInstancedBufferAttribute( instanceColorAttribute.array,3);const bufferFn = instanceColorAttribute.usage === DynamicDrawUsage ? instancedDynamicBufferAttribute : instancedBufferAttribute;this.bufferColor = buffer;

            instanceColorNode =vec3(bufferFn( buffer,'vec3',3,0));this.instanceColorNode = instanceColorNode;}// POSITIONconst instancePosition = instanceMatrixNode.mul( positionLocal ).xyz;// NORMALconst 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 );// COLORif(this.instanceColorNode !==null){varyingProperty('vec3','vInstanceColor').assign(this.instanceColorNode );}if(instanceMesh.instanceIndex){const indexBuffer =newInstancedBufferAttribute( 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 );})}}

本文转载自: https://blog.csdn.net/printf_hello/article/details/140869553
版权归原作者 Jedi Hongbin 所有, 如有侵权,请联系我们删除。

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

还没有评论