Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.1k views
in Technique[技术] by (71.8m points)

three.js - How to update shadows in modified MeshPhysicalMaterial shader

I am having trouble getting my model with a custom shader to cast correct shadows.

I have made a slightly modified vertex shader chunk for the THREE.MeshPhysicalMaterial that allows me to tweak the behavior of the morph targets. Specifically, I am moving the position variable transformed around in the vertex shader to morph different parts of the model based on an input uniform instead of all at once like is normal. This works as intended for the rendered geometry, but that shadow that it projects onto objects like the ground and itself does not reflect the changes. I have attached an image that illustrates the problem. As the morph is applied the model breaks into little chunks, but the shadow on the ground still shows a solid ring shape.

I think it has something to do with needing a customDepthMaterial, but I can't find much information on it to know how I should integrate my tweaks. Any insight you can provide would be appreciated.

Here is my vertex fragment chunk that replaces the standard morph target chunk: 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

Here is my code to create a modified MeshPhysicalMaterial that uses my custom vertex shader chunk

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 + "
" + shader.vertexShader.replace("#include <morphtarget_vertex>", body);
            };

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

enter image description here


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The answer really came from WestLangley's comment, but here is some additional details:

You create an instance of THREE.MeshDepthMaterial with the same settings for vertex colors, and morph targets, and then make the same modifications to the vertexShader as the main material. Then you assign this new material to the mesh.customDepthMaterial slot. One thing you may run into is that the while the main shader will have access to normals and vertex color the depth shader will not. You can work around this by adding an entry to the shader's defines like this: shaders.defines.ISDEPTH = "" which you can then check for in the shader with #ifdef ISDEPTH

My material function became this

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 + "
" + 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 + "
" + shader.vertexShader.replace("#include <morphtarget_vertex>", body);
            };

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

And you can assign the results like this

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

                resolve();
            });

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...