You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

带Displacement Texture的归一化立方体网格法线不连续问题求助

归一化立方体法线不连续问题求助

我在项目中用六个平面网格创建了一个归一化立方体,为提升视觉效果添加了Displacement Texture并计算纹理法线以保证正确光照,但出现了边缘法线不连续的问题,底层立方体结构暴露出来,无法实现类球体的平滑外观。

以下是我的Shader代码:

顶点着色器

varying vec2 vUv;
uniform sampler2D u_tex;
uniform vec3 wTl;
uniform vec3 ps;

// 归一化每个平面
vec3 planeToSphere(vec3 p, vec3 localCenter){
    return 100.0*normalize(p-localCenter) + localCenter;
}

varying vec3 v3n;
void main() {
    vUv = uv;
    vec3 newPosition = planeToSphere(position,wTl);
    vec3 worldp = (modelMatrix * vec4(newPosition,1.0)).xyz;
    float n = texture2D(u_tex, vUv).x;
    // newPosition = newPosition + 1.0*n*normalize(position-wTl); // 启用该注释添加位移
    gl_Position = projectionMatrix * modelViewMatrix * vec4(newPosition, 1.0);
}

片元着色器

varying vec2 vUv;
uniform sampler2D u_tex;
varying vec3 v3n;

vec4 displacementMapToNormalMap(sampler2D displacementMap, vec2 vUv){
    float scale = 0.9;   // 控制位移量
    float epsilon = 0.01;  // 计算梯度的小值
    float strength = 1.;                   
    float center = texture2D(displacementMap, vUv).r; // 采样位移贴图
    float dx = texture2D(displacementMap, vUv + vec2(epsilon, 0.0)).r - center;  // 计算X方向梯度
    float dy = texture2D(displacementMap, vUv + vec2(0.0, epsilon)).r - center;  // 计算Y方向梯度
    vec3 normalMap = normalize(vec3(dx * scale, dy * scale, 1.0));               // 计算法向量
    normalMap *= strength;                                                       // 应用强度
    return vec4(normalMap * 0.5 + 0.5, 1.0);                                     // 将法线输出为颜色
}
    
void main() {
    vec3 lightDirection = normalize(vec3(0.,1.,1.));
    vec4 normalMapColor = displacementMapToNormalMap(u_tex, vUv);          // 获取法线贴图颜色
    float grayscale = dot(normalMapColor.rgb, vec3(0.299, 0.587, 0.114));  // 转换为灰度
    vec4 grayColor = vec4(grayscale, grayscale, grayscale, 1.0);
    float lightIntensity = dot(normalMapColor.rgb, lightDirection);       // 计算光照贡献
    vec4 finalColor = grayColor * lightIntensity;                         // 将光照应用到灰度颜色
    gl_FragColor = finalColor;                                            // 输出最终颜色
}

我已经为此问题钻研数周但未取得实质性进展。研究时看到相关问题中,有回复建议不使用Three.js提供的切线框架,而是用顶点数据生成切线以对齐接缝,但我对该技术不熟悉,希望得到解释。


2023年7月16日更新

使用dFd函数获取相邻像素值替代epsilon(因像素以2x2批次渲染),这是目前最优方案但仍不完善:

vec4 displacementMapToNormalMap(sampler2D displacementMap, vec2 vUv){
    float scale = 0.9;   // 控制位移量
    float strength = 1.;                   
    float center = texture2D(displacementMap, vUv).r; // 采样位移贴图
    float dx = texture2D(displacementMap, vUv + dFdx(vUv)).r - center;  // 计算X方向梯度
    float dy = texture2D(displacementMap, vUv + dFdy(vUv)).r - center;  // 计算Y方向梯度
    vec3 normalMap = normalize(vec3(dx * scale, dy * scale, 1.0));               // 计算法向量
    normalMap *= strength;                                                       // 应用强度
    return vec4(normalMap * 0.5 + 0.5, 1.0);                                     // 将法线输出为颜色
}

希望能得到解决法线不连续问题、实现带Displacement Texture的归一化立方体平滑外观的指导或建议,感谢帮助。


内容的提问来源于stack exchange,提问作者Memory1

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 13:50:26