问题描述
我无法使用自定义着色器让我的模型投射正确的阴影。
我为 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);
});
});
}
解决方法
答案确实来自 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();
});