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

视角角度驱动纹理混合异常,求透镜表面效果实现指导

片段着色器实现透镜表面效果的问题

我想用片段着色器实现**透镜表面(lenticular surface)**效果:随着视角(PoV)角度变化,一张纹理逐步替换另一张。我设置了混合过渡的角度阈值,希望超过阈值后只显示单张纹理,但当前代码有问题:视角边缘只显示第一张纹理,第二张纹理仅在[85,105]度范围(我期望的混合过渡区域)显示。

以下是我使用的两段GLSL代码,修改后仍未解决问题,求指导:

第一段原始代码

uniform sampler2D textureSampler1;
uniform sampler2D textureSampler2;
in vec2 UV;
uniform mat4 viewMatrix;
out vec4 fragColor;

void main()
{
    vec4 color1 = texture(textureSampler1, UV);
    vec4 color2 = texture(textureSampler2, UV);

    vec3 viewDir = normalize(vec3(viewMatrix[0][2], viewMatrix[1][2], viewMatrix[2][2]));
    vec3 normal = vec3(0.0, 0.0, 1.0);

    float angle = acos(dot(normal, viewDir));

    float blendAngleThreshold = radians(10.0); // Threshold angle for transitioning to single texture view

    float weight1 = smoothstep(blendAngleThreshold, blendAngleThreshold + 0.05, angle);

    float weight2 = 1.0 - weight1;

    // Apply grating effect based on UV coordinates
    // float gratingIntensity = sin(UV.y * 1200.0) * 0.5 + 0.5; // Adjust the parameters as desired
    // weight1 *= gratingIntensity;
    // weight2 *= gratingIntensity;

    float numSlices = 5.0;
    float sliceWidth = 1.0 / numSlices;
    float displacement = angle * sliceWidth;
    vec2 modifiedUV = UV + vec2(displacement, 0.0);

    vec3 reflectionDir = reflect(viewDir, normal);
    vec4 reflectionColor = texture(textureSampler1, modifiedUV);

    float reflectionIntensity = 0.5 + 0.5 * cos(angle);
    reflectionColor *= reflectionIntensity;

    float metallicIntensity = 1.0;
    vec4 metallicColor = vec4(0.0, 0.0, 0.0, 1.0);
    vec4 metallic = mix(color1, metallicColor, metallicIntensity);

    vec4 blendedColor = weight1 * (metallic + reflectionColor) + weight2 * color2;

    blendedColor.a = 1.0;

    fragColor = blendedColor;
}

修改后的第二段代码片段

vec4 color1 = texture(textureSampler1, UV);
vec4 color2 = texture(textureSampler2, UV);

vec3 viewDir = normalize(vec3(viewMatrix[0][2], viewMatrix[1][2], viewMatrix[2][2]));
vec3 normal = vec3(0.0, 0.0, 1.0);

float angle = acos(dot(normal, viewDir));

float blendAngleThreshold = radians(10.0); // Threshold angle for transitioning to single texture view

vec3 rightDir = normalize(vec3(viewMatrix[0][0], viewMatrix[1][0], viewMatrix[2][0]));
float rightness = dot(rightDir, viewDir); // How much the view is from the right side

float weight1 = smoothstep(blendAngleThreshold, blendAngleThreshold + 0.05, angle) * step(0.0, rightness);

float weight2 = 1.0 - weight1;

问题分析与解决方案

核心问题点

  1. 视角方向计算错误
    你从viewMatrix提取的viewDir是视图空间的-z轴方向,并非世界空间中相机指向物体的实际方向,导致角度计算仅在极小范围内有效。
  2. 混合阈值逻辑颠倒
    acos(dot(normal, viewDir))的结果是法线与视角的夹角,范围0~π。当前smoothstep逻辑会让weight1在角度超过阈值时趋近于1,导致texture1在大角度(视角边缘)显示,texture2仅在小角度(接近正面)显示,完全违背预期。
  3. 左右视角切换逻辑缺失
    透镜效果需要区分左右偏移:左侧超过阈值显示texture1,右侧超过阈值显示texture2,中间正面区域混合两张纹理,你的代码未实现该逻辑。

修正后的核心代码示例

uniform sampler2D textureSampler1;
uniform sampler2D textureSampler2;
in vec2 UV;
in vec3 vWorldPos; // 新增:世界空间片段位置
uniform vec3 cameraPos; // 新增:世界空间相机位置
out vec4 fragColor;

void main()
{
    vec4 color1 = texture(textureSampler1, UV);
    vec4 color2 = texture(textureSampler2, UV);

    vec3 normal = vec3(0.0, 0.0, 1.0); // 假设物体法线朝+Z世界空间
    vec3 viewDir = normalize(cameraPos - vWorldPos);
    
    // 计算视角与法线的夹角(转为角度)
    float angleDeg = degrees(acos(clamp(dot(normal, viewDir), 0.0, 1.0)));
    
    // 计算视角在物体左右方向的分量(X轴为左右)
    vec3 rightVec = vec3(1.0, 0.0, 0.0); // 世界空间右方向
    vec3 tangentView = normalize(viewDir - dot(viewDir, normal)*normal);
    bool isRight = dot(tangentView, rightVec) > 0.0;

    // 设置过渡参数:正面±10度内混合,±20度外完全切换
    float transitionStart = 10.0;
    float transitionEnd = 20.0;
    
    float blendFactor = 0.0;
    if(isRight) {
        // 右侧视角:从正面到右侧20度,从color2切换到color1
        blendFactor = smoothstep(transitionStart, transitionEnd, angleDeg);
    } else {
        // 左侧视角:从正面到左侧20度,从color2切换到color1
        blendFactor = 1.0 - smoothstep(transitionStart, transitionEnd, angleDeg);
    }
    
    // 混合纹理
    vec4 finalColor = mix(color2, color1, blendFactor);
    
    // 可选:添加透镜光栅条纹效果
    // float grating = step(0.5, fract(UV.y * 1200.0));
    // finalColor = mix(finalColor, color1, grating * blendFactor);
    
    fragColor = finalColor;
}

关键修正说明

  • 正确计算视角方向:使用世界空间相机位置到片段位置的向量,确保角度计算准确反映实际视角偏移。
  • 区分左右视角:通过视角在物体横向的投影判断左右,分别处理纹理切换逻辑。
  • 调整混合阈值:让过渡区域位于正对视角的两侧,超过阈值后完全切换到对应纹理,符合透镜效果的预期表现。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 20:42:09