Three.js Tri-panner (三面贴图/世界UV贴图) 材质 两种实现方式

请添加图片描述

在这里插入图片描述

介绍

Tri-panner 在babylonjs中有支持 但是three.js目前的基础材质并不支持
需要自己定义shader 或者使用目前还没有什么完善的文档的 NodeMaterial

解析

三面贴图 指的是xyz三个轴方向去采样 不使用uv映射转而使用position
x轴的颜色 就是 texture(tex, position.zy).rgb
y轴的颜色 就是 texture(tex, position.zx).rgb
z轴的颜色 就是 texture(tex, position.xy).rgb
而不管是x还是y或者z都是有值的 那颜色就会出现叠接 无法直接应用
这是就用到了法相 用法相也就是这个点朝向哪个轴 这个轴的比重就大
如果是垂直的 那么有两个方向的法相就为0 自然就只取一个轴的颜色 就不会叠加颜色

这里有一个细节
法相和position的取值
如果只取标准的position和normal 也可以
但这样就无法应用物体的旋转 缩放平移
为了适应物体的变化 position和normal都要应用 modelMatrix
vec3(modelMatrix * vec4(transformed,1.) )
vec3(modelMatrix * vec4(normal, 0.0))
这两项取值要同步 否则就会在物体变化后出现线条拉伸问题 就是位置和法相采样不一致导致的

下面展示两种实现方式

自定义shader
/**
 * @description: 替换三角面贴图  https://doc.babylonjs.com/toolsAndResources/assetLibraries/materialsLibrary/triPlanarMat
 * @param {SingleMaterialMesh} mesh
 * @return {*}
 */
export const useTriplanarMapping = (mesh: SingleMaterialMesh) => {
    const material = mesh.material.clone();
    mesh.material = material;
    material.map!.wrapS = THREE.RepeatWrapping;
    material.map!.wrapT = THREE.RepeatWrapping;
   
    material.onBeforeCompile = (shader) => {
        shader.vertexShader = shader.vertexShader.replace(
            "#include <common>",
            `
            #include <common>
            varying vec3 tripPosition;
            varying vec3 tripNormal;
        `
        );
        shader.vertexShader = shader.vertexShader.replace(
            "#include <fog_vertex>",
            `
            #include <fog_vertex>
            vec4 tripPosition4 = modelMatrix * vec4(position,1.) ;
            tripPosition = tripPosition4.xyz;

            vec3 world_space_normal = vec3(modelMatrix * vec4(normal, 0.0));
            tripNormal = world_space_normal;
        `
        );
        shader.fragmentShader = shader.fragmentShader.replace(
            "#include <common>",
            `
            #include <common>
            varying vec3 tripPosition;
            varying vec3 tripNormal;
            vec3 blendNormal(vec3 normal){
                vec3 blending = abs( normal );
                blending = normalize(max(blending, 0.00001)); // Force weights to sum to 1.0 
                float b = (blending.x + blending.y + blending.z);
                blending /= vec3(b, b, b);
                return blending;
            }
            
            vec3 triplanarMapping (sampler2D tex, vec3 normal, vec3 position) {
              vec3 normalBlend = blendNormal(normal);
              vec3 xColor = texture(tex, position.yz).rgb;
              vec3 yColor = texture(tex, position.xz).rgb;
              vec3 zColor = texture(tex, position.xy).rgb;
              return (xColor * normalBlend.x + yColor * normalBlend.y + zColor * normalBlend.z);
            }
        `
        );

        shader.fragmentShader = shader.fragmentShader.replace(
            "#include <map_fragment>",
            `
            #include <map_fragment>
            diffuseColor.rgb = vec3(triplanarMapping( map ,tripNormal,tripPosition));
        `
        );
        // shader.fragmentShader = shader.fragmentShader.replace(
        //     "#include <color_fragment>",
        //     `
        //     #include <color_fragment>
        //     diffuseColor.rgb = vec3(triplanar_mapping( map ,tripNormal,tripPosition,1.0));
        // `
        // );
    };
};

NodeMaterial

这是threejs新系统充满未来 目前还没有一个完善的文档 并且不太稳定 r132的时候支持这个材质 r138就被删除了 一些api也都有变化 可以先参考 https://raw.githack.com/sunag/three.js/dev-nodes-doc/docs/index.html#manual/en/introduction/How-to-use-node-material

import {
    MeshBasicNodeMaterial,
    texture,
    triplanarTexture,
} from "three/examples/jsm/nodes/Nodes.js";
import { nodeFrame } from "three/examples/jsm/renderers/webgl/nodes/WebGLNodes.js";

const skyMat = new MeshBasicNodeMaterial();

skyMat.colorNode = triplanarTexture(
    texture(
        this.helper.loadTexture(
            "/public/textures/coral_stone_wall_diff_1k.jpg",
            (map) => {
                map.colorSpace = THREE.SRGBColorSpace;
                map.wrapS = THREE.RepeatWrapping;
                map.wrapT = THREE.RepeatWrapping;
            }
        )
    )
);
skyMat.side = THREE.DoubleSide;

const sky = new THREE.Mesh(new THREE.SphereGeometry(2, 32, 15), skyMat);
scene.add(sky);


animation() {
    nodeFrame.update();
}

要注意每一次render 同时调用 nodeFrame.update(); 否则报错

修复:骨骼材质特殊处理

这个问题需要根据three版本进行区别处理

r160版本 使用的是 position
r155版本使用的是 nodeUniform2 * vec4( 忘了叫什么了, 1.0 )
总之每个版本可能不一样 因为 节点系统正在开发 需要对应版本对应处理

r160版本写法如下

material.onBeforeCompile = (shader) => {
    material.vertexShader = shader.vertexShader.replace(
        "#include <skinning_vertex>",
        `
        #include <skinning_vertex>
        nodeVarying2 = (modelMatrix * vec4(transformed,1.0)).xyz;
        `
    );
};

r155版本写法如下

material.onBeforeCompile = (shader) => {
   material.vertexShader = shader.vertexShader.replace(
         "#include <skinning_vertex>",
         `
         #include <skinning_vertex>
         nodeVarying2 = ( nodeUniform2 * vec4( transformed, 1.0 ) );
     `
     );
 };
修复:使用法相贴图时整体变色

这个问题nodeMaterial 没找到如何解决 下面给出自定义材质的解决方案

export const useTriplanarMapping = (mesh) => {
    const material = mesh.material.clone();
    mesh.material = material;
    material.map.colorSpace = THREE.SRGBColorSpace;
    material.map.wrapS = THREE.RepeatWrapping;
    material.map.wrapT = THREE.RepeatWrapping;
    if (material.normalMap) {
        material.normalMap.colorSpace = THREE.SRGBColorSpace;
        material.normalMap.wrapS = THREE.RepeatWrapping;
        material.normalMap.wrapT = THREE.RepeatWrapping;
    }

    material.onBeforeCompile = (shader) => {
        shader.vertexShader = shader.vertexShader.replace(
            "#include <common>",
            `
            #include <common>
            varying vec3 tripPosition;
            varying vec3 tripNormal;
        `
        );
        shader.vertexShader = shader.vertexShader.replace(
            "#include <skinning_vertex>",
            `
            #include <skinning_vertex>
            vec4 tripPosition4 = modelMatrix * vec4(transformed,1.) ;
            tripPosition = tripPosition4.xyz;
            
            vec3 world_space_normal = vec3(modelMatrix * vec4(normal, 0.0));
            tripNormal = world_space_normal;
        `
        );
        shader.fragmentShader = shader.fragmentShader.replace(
            "#include <common>",
            `
            #include <common>
            varying vec3 tripPosition;
            varying vec3 tripNormal;
            vec3 blendNormal(vec3 normal){
                vec3 blending = abs( normal );
                blending = normalize(max(blending, 0.00001)); // Force weights to sum to 1.0 
                float b = (blending.x + blending.y + blending.z);
                blending /= vec3(b, b, b);
                return blending;
            }
            
            vec3 triplanarMapping (sampler2D tex, vec3 normal, vec3 position) {
              vec3 normalBlend = blendNormal(normal);
              vec3 xColor = texture(tex, position.yz).rgb;
              vec3 yColor = texture(tex, position.xz).rgb;
              vec3 zColor = texture(tex, position.xy).rgb;
              return (xColor * normalBlend.x + yColor * normalBlend.y + zColor * normalBlend.z);
            }
        `
        );

        shader.fragmentShader = shader.fragmentShader.replace(
            "#include <map_fragment>",
            `
            #include <map_fragment>
            diffuseColor.rgb = vec3(triplanarMapping( map ,tripNormal,tripPosition));
        `
        );
        shader.fragmentShader = shader.fragmentShader.replace(
            "#include <normal_fragment_maps>",
            `
            #include <normal_fragment_maps>
            normal = vec3(triplanarMapping( normalMap ,tripNormal,tripPosition));
            normal = normalize( tbn * normal );
        `
        );
    };
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值