首页 > 解决方案 > 为 SKShapeNode 添加着色器

问题描述

我想为SKShapeNode. 我在这里Emboss找到了一个着色器。

这是我的代码:

let node = SKShapeNode(rectOf: CGSize(width: w, height: h),cornerRadius: w / 4.0)
node.position = CGPoint(x: x, y: frame.size.height + h / 2)
node.fillColor = color
node.strokeColor = .clear
node.fillShader = createColorEmboss()
let v = simd_make_float2(Float(w),Float(h))
node.setValue(SKAttributeValue(vectorFloat2: v), forAttribute: "a_size")

func createColorEmboss() -> SKShader {
    let source = "void main() {" +
        "vec4 current_color = SKDefaultShading();" +
        "if (current_color.a > 0.0) {" +
            "vec2 pixel_size = 1.0 / a_size;" +
            "vec4 new_color = current_color;" +
            "new_color += texture2D(u_texture, v_tex_coord + pixel_size) * u_strength;" +
            "new_color -= texture2D(u_texture, v_tex_coord - pixel_size) * u_strength;" +
            "gl_FragColor = vec4(new_color.rgb, 1) * current_color.a * v_color_mix.a;" +
        "} else {" +
            "gl_FragColor = current_color;" +
        "}" +
    "}"
    let shader = SKShader(source: source, uniforms: [SKUniform(name: "u_strength", float: 1)])
    shader.attributes = [SKAttribute(name: "a_size", type: .vectorFloat2)]
    return shader  
  }

但是什么也没发生。任何想法?

标签: iosswiftshaderskspritenode

解决方案


带纹理的(*) SKShapeNode和这里有一些很大的区别SKSpriteNode

  • 如果SKSpriteNode有纹理和自定义着色器(shader属性),那么着色器将可以通过制服访问原始纹理u_texture,并且可以使用它来渲染节点,即通过对其应用各种效果,如浮雕、模糊, ETC。
  • SKShapeNode 可能有 afillTexture和 a fillShader,但它们的工作方式不同:
    • fillTexture是一个矩形纹理,它被转换为灰度,用于设置fillColor形状节点填充区域的亮度。
    • fillShader只能fillTexture在用于最终渲染之前看到这个矩形图像,无法修改甚至查看矩形的哪些区域将是可见的(填充的一部分),以及最终渲染的外观。
    • 如果没有明确fillTexture的集合,u_texture制服似乎假装它是一望无际的白色:无论你问什么坐标,它都会返回白色,即使你超出了它的界限。

(*)您也可以SKSpriteNode只用一种颜色创建一个;它的行为将是一个奇怪的混合SKShapeNode和一个纹理SKSpriteNode,它对我们的讨论没有帮助,所以我会忽略这种。


您可以做的是将您的光栅SKShapeNode化为纹理,并SKSpriteNode从中创建一个。其实很简单:

let node = SKShapeNode(rectOf: CGSize(width: w, height: h),cornerRadius: w / 4.0)
node.fillColor = color
node.strokeColor = .clear

let theTexture = view.texture(from:node)
let spriteNode = SKSpriteNode(texture:theTexture)

spriteNode.position = CGPoint(x: x, y: frame.size.height + h / 2)
spriteNode.shader = createColorEmboss()
let v = simd_make_float2(Float(w),Float(h))
spriteNode.setValue(SKAttributeValue(vectorFloat2: v), forAttribute: "a_size")

如您所见,您甚至不必将节点添加到场景中;视图会将其渲染为具有清晰背景的纹理。您还可以使用view.texture(from:crop:)指定更大的裁剪,如果它超出原始形状,则可以容纳投影。

但是,我必须警告您,您尝试使用的这个特定着色器可能不是您所需要的阴影……</p>


推荐阅读