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

为何OpenGL片段着色器未呈现预期彩色波浪纹理,仅显示纯色?

着色器无法呈现预期波浪彩色图案的排查分析

问题描述

我有一个OpenGL片段着色器文件(shader.fsh),代码如下:

constant float M_PI  = 3.141592653589793;
constant float M_2PI =  6.283185307179586;

constant vec3 c1a = vec3(0.0, 0.0, 0.0);
constant vec3 c1b = vec3(0.9, 0.0, 0.4);
constant vec3 c2a = vec3(0.0, 0.5, 0.9);
constant vec3 c2b = vec3(0.0, 0.0, 0.0);

void main() {
    vec2 point = (0.5 * u_sprite_size - gl_FragCoord.xy) / u_sprite_size;
    float angle = atan(point.y, point.x);
    float turn = (angle + M_PI) / M_2PI;
    float radius = sqrt(point.x * point.x + point.y * point.y);
    
    float sine_kf = 19.0;
    float ka_wave_rate = 0.94;
    float ka_wave = sin(ka_wave_rate * u_time);
    float sine_ka = 0.35 * ka_wave;
    float sine2_ka = 0.47 * sin(0.87 * u_time);
    float turn_t = turn + -0.0 * u_time + sine_ka * sin(sine_kf * radius) + sine2_ka * sin(8.0 * angle);
    bool turn_bit = mod(10.0 * turn_t, 2.0) < 1.0;
    
    float blend_k = pow((ka_wave + 1.0) * 0.5, 1.0);
    vec3 c;
    if(turn_bit) {
        c = blend_k * c1a + (1.0 -blend_k) * c1b;
    } else {
        c = blend_k * c2a + (1.0 -blend_k) * c2b;
    }
    c *= 1.0 + 1.0 * radius;
    
    gl_FragColor = vec4(c, 1.0);
}

该着色器本应呈现带有波浪的彩色图案,但实际仅显示纯色。SpriteKit/Swift中的对象初始化代码如下:

override func didMove(to view: SKView) {
    doorShader = SKShader(fileNamed: "shader.fsh")
    leftDoor = SKSpriteNode(imageNamed: "wrong_door")
    leftDoor.shader = doorShader //<-- shader is non-null at assignment
    addChild(leftDoor)
}

核心原因及解决方法

1. 未绑定着色器依赖的Uniform变量

着色器中使用了u_sprite_size和u_time两个Uniform变量,但Swift代码中未对它们赋值:

  • u_sprite_size:用于计算片段在精灵内的相对位置,未赋值会导致point计算失效,所有基于位置的波浪逻辑无法运行。
  • u_time:控制动画的时间变量,未赋值会固定为0,波浪相关的正弦值保持静态甚至无变化,最终呈现纯色。

解决方法:
在Swift代码中添加Uniform变量的绑定与更新逻辑:

override func didMove(to view: SKView) {
    doorShader = SKShader(fileNamed: "shader.fsh")
    leftDoor = SKSpriteNode(imageNamed: "wrong_door")
    // 初始化Uniform变量
    doorShader.uniforms = [
        SKUniform(name: "u_sprite_size", vectorFloat2: vector_float2(Float(leftDoor.size.width), Float(leftDoor.size.height))),
        SKUniform(name: "u_time", float: 0.0)
    ]
    leftDoor.shader = doorShader
    addChild(leftDoor)
}

// 在update方法中实时更新时间变量
override func update(_ currentTime: TimeInterval) {
    doorShader?.uniforms.first(where: { $0.name == "u_time" })?.floatValue = Float(currentTime)
}

2. 时间偏移项无效

着色器中turn_t = turn + -0.0 * u_time + ...的-0.0 * u_time等于0,完全没有起到时间偏移的作用,这大概率是笔误,原本应设置非零系数来实现图案随时间旋转的动态效果。

解决方法:
修改为有意义的时间系数,例如:

float turn_t = turn + -0.1 * u_time + sine_ka * sin(sine_kf * radius) + sine2_ka * sin(8.0 * angle);

3. 坐标计算适配问题

gl_FragCoord.xy的原点在屏幕左下角,而SpriteKit精灵默认原点在中心,可能导致point计算逻辑与预期不符,波浪效果无法正确映射到精灵上。

解决方法:
改用SpriteKit内置的v_tex_coord纹理坐标(范围0-1,原点在左下角)计算相对位置,替换原point代码:

vec2 point = v_tex_coord - vec2(0.5); // 将原点移到中心,范围-0.5至0.5

该方式无需依赖u_sprite_size,更适配SpriteKit的纹理系统。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 17:10:37