如何在修改后的 MeshPhysicalMaterial 着色器中更新阴影

问题描述

我无法使用自定义着色器让我的模型投射正确的阴影。

我为 THREE.MeshPhysicalMaterial 制作了一个稍微修改的顶点着色器块,它允许我调整变形目标的行为。具体来说,我正在顶点着色器中移动位置变量 transformed 以根据输入 uniform 来变形模型的不同部分,而不是像正常那样一次性全部变形。这与渲染几何体的预期效果一致,但它投射到地面等对象上的阴影及其本身并不反映变化。我附上了一张说明问题的图片。应用变形后,模型会碎成小块,但地面上的阴影仍显示为实心环形。

我认为这与需要 customDepthMaterial 有关,但我找不到太多关于它的信息来知道我应该如何整合我的调整。您可以提供任何见解,我们将不胜感激。

这是我替换标准变形目标块的顶点片段块:morphtarget_vertex

float t = fillAmount;
float u = uv.x;
t = fit(t,0.0,1.0,-fillWidth,1.0 + fillWidth);
t = fit(u,t,t + fillWidth,0.0);

transformed *= morphTargetBaseInfluence;
transformed += morphTarget0 * t;
#ifndef USE_MORPHnorMALS
    transformed += morphTarget4 * t;
#endif

这是我使用自定义顶点着色器块创建修改后的 MeshPhysicalMaterial 的代码

function getModifiedMaterial()
{
    return new Promise((resolve,reject) =>
    {
        Promise.all([
            fetch("donut.prefix.vertex").then(res => res.text()),fetch("donut.vertex").then(res => res.text())
        ])
        .then(results =>
        {
            var prefix = results[0];
            var body = results[1];

            var material = new THREE.MeshPhysicalMaterial(
            {
                morphTargets: true,vertexColors: true
            }).clone();

            material.userData.fillAmount = { value: 0.9 };
            material.userData.fillWidth = { value: 0.0 };

            material.customProgramCacheKey = () => "donut";
            material.onBeforeCompile = shader =>
            {
                shader.uniforms.fillAmount = material.userData.fillAmount;
                shader.uniforms.fillWidth = material.userData.fillWidth;[![enter image description here][1]][1]

                shader.vertexShader = prefix + "\n" + shader.vertexShader.replace("#include <morphtarget_vertex>",body);
            };

            resolve(material);
        });
    });
}

enter image description here

解决方法

答案确实来自 WestLangley 的评论,但这里有一些额外的细节:

您创建一个 THREE.MeshDepthMaterial 的实例,对顶点颜色和变形目标具有相同的设置,然后对作为主要材质的 vertexShader 进行相同的修改。然后将这个新材质指定给 mesh.customDepthMaterial 槽。您可能会遇到的一件事是,虽然主着色器可以访问法线和顶点颜色,但深度着色器却不能。您可以通过在着色器的定义中添加一个条目来解决这个问题:shaders.defines.ISDEPTH = "" 然后您可以使用 #ifdef ISDEPTH

在着色器中检查它

我的材质功能变成了这个

function getModifiedMaterial()
{
    return new Promise((resolve,reject) =>
    {
        Promise.all([
            fetch("donut.prefix.vertex").then(res => res.text()),fetch("donut.vertex").then(res => res.text())
        ])
        .then(results =>
        {
            var prefix = results[0];
            var body = results[1];

            var material = new THREE.MeshPhysicalMaterial(
            {
                morphTargets: true,vertexColors: true
            });

            material.userData.fillAmount = { value: 0.9 };
            material.userData.fillWidth = { value: 0.0 };

            material.customProgramCacheKey = () => "donut";
            material.onBeforeCompile = shader =>
            {
                shader.defines.FULL = "";
                shader.uniforms.fillAmount = material.userData.fillAmount;
                shader.uniforms.fillWidth = material.userData.fillWidth;

                shader.vertexShader = prefix + "\n" + shader.vertexShader.replace("#include <morphtarget_vertex>",body);
            };

            var depthMaterial = new THREE.MeshDepthMaterial(
            { 
                depthPacking: THREE.RGBADepthPacking,morphTargets: true,vertexColors: true
            });
            depthMaterial.customProgramCacheKey = () => "donutdepth";
            depthMaterial.onBeforeCompile = shader =>
            {
                shader.uniforms.fillAmount = material.userData.fillAmount;
                shader.uniforms.fillWidth = material.userData.fillWidth;

                shader.vertexShader = prefix + "\n" + shader.vertexShader.replace("#include <morphtarget_vertex>",body);
            };

            resolve({ main: material,depth: depthMaterial });
        });
    });
}

你可以像这样分配结果

            getModifiedMaterial().then(materials =>
            {
                mesh.material = materials.main;
                mesh.customDepthMaterial = materials.depth;
                scene.add(mesh);

                resolve();
            });