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

How to update shadows in modified MeshPhysicalMaterial shader

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

我为 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, 1.0, 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);
        });
    });
}

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

您创建了一个 THREE.MeshDepthMaterial 的实例,其顶点颜色和变形目标设置相同,然后对顶点着色器进行与主 material 相同的修改。然后将这个新的 material 分配给 mesh.customDepthMaterial 槽。您可能 运行 了解的一件事是,虽然主着色器可以访问法线和顶点颜色,但深度着色器不能。您可以通过向着色器的定义添加一个条目来解决此问题,如下所示: shaders.defines.ISDEPTH = "" 然后您可以使用 #ifdef ISDEPTH

在着色器中检查它

我的material函数变成了这个

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();
            });