视角角度驱动纹理混合异常,求透镜表面效果实现指导
片段着色器实现透镜表面效果的问题
我想用片段着色器实现**透镜表面(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;
问题分析与解决方案
核心问题点
- 视角方向计算错误
你从viewMatrix提取的viewDir是视图空间的-z轴方向,并非世界空间中相机指向物体的实际方向,导致角度计算仅在极小范围内有效。 - 混合阈值逻辑颠倒
acos(dot(normal, viewDir))的结果是法线与视角的夹角,范围0~π。当前smoothstep逻辑会让weight1在角度超过阈值时趋近于1,导致texture1在大角度(视角边缘)显示,texture2仅在小角度(接近正面)显示,完全违背预期。 - 左右视角切换逻辑缺失
透镜效果需要区分左右偏移:左侧超过阈值显示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
相关产品推荐
相关产品推荐

