繁体   English   中英

将自定义SKShader应用于SKScene,使用Swift在iOS 8 SpriteKit中对整个渲染场景进行像素化处理

[英]Applying a custom SKShader to SKScene that pixelates the whole rendered scene in iOS 8 SpriteKit with Swift

我正在尝试在SKScene上创建一个全屏像素化效果。 我了解到应该有两种方法可以做到这一点:

  • 使用GLES 2.0使用自定义SKShader
  • 使用核心图像过滤器。

我试图添加一个自定义SKShader,它应该通过像素化来修改整个屏幕。 我不确定如果可能,但来自SKScene (它是SKEffectNode的子类)的SKEffectNode表明:

SKEffectNode对象将其子项呈现为缓冲区,并可选择将Core Image过滤器应用于此呈现的输出。

可以将SKShader分配给SKScene,如GameScene : SKScene

override func didMoveToView(view: SKView) {
    let shader = SKShader(fileNamed: "pixelation.fsh")
    self.shader = shader
    self.shouldEnableEffects = true
}

...但似乎渲染的缓冲区不作为u_texture传递给GLES:

void main()
{
    vec2 coord = v_tex_coord;
    coord.x = floor(coord.x * 10.0) / 10.0;
    coord.y = floor(coord.y * 10.0) / 10.0;
    vec4 texture = texture2D(u_texture, coord);
    gl_FragColor = texture;
}

...所以之前的着色器不起作用。

如果我将该着色器分配给基于纹理的SKSpriteNode ,它可以工作。

因此,在渲染完所有节点后,是否可以修改整个帧缓冲区(例如像素化它)作为后处理措施?

编辑 :我找到了一种在OS X中使用Core Image过滤器进行像素化的方法( 如何将CIPixellate核心图像过滤器添加到Sprite Kit场景? ),但复制该实现不会在iOS上产生任何结果。 根据文档, CIPixellate应该Available in OS X v10.4 and later and in iOS 6.0 and later.

为了让您的.shader上运行SKScene ,你需要设置shouldEnableEffects为true现场(同样的事情也适用于SKEffectNode )。

虽然从技术上来说,“工作”(应用了着色器),之后渲染场景中的一个错误会稍微调整一下。

因此,到目前为止,使用CoreImage过滤器是最好的方法。

我设法使用Core Image过滤器CIPixellate使其工作。 我使用的是作为SKEffectNode的过滤器来产生像素化效果。 有几点需要注意:

  • SKScene是的子类SKEffectNode ,但将过滤器应用于SKScene不起作用。 它会弄乱背景并且不会进行任何像素化。
  • 您需要创建一个SKEffectNode并添加要在其下进行像素化的节点。

这是基于使用Swift选择Game类型项目时生成的代码的解决方案:

import SpriteKit

class GameScene: SKScene {
    var effectNode : SKEffectNode = SKEffectNode.node()

    override func didMoveToView(view: SKView) {
        let filter = CIFilter(name: "CIPixellate")
        filter.setDefaults()
        filter.setValue(5.0, forKey: "inputScale")

        self.effectNode.filter = filter
        self.effectNode.shouldEnableEffects = true
        self.addChild(effectNode)
    }

    override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
        for touch: AnyObject in touches {
            let location = touch.locationInNode(self)

            let sprite = SKSpriteNode(imageNamed:"Spaceship")

            sprite.xScale = 0.5
            sprite.yScale = 0.5
            sprite.position = location

            let action = SKAction.rotateByAngle(CGFloat(M_PI), duration:1)

            sprite.runAction(SKAction.repeatActionForever(action))

            self.effectNode.addChild(sprite)
        }
    }

    override func update(currentTime: CFTimeInterval) {
        /* Called before each frame is rendered */
    }
}

实际上,我必须为最近的一个项目做同样的事情,作为一种在各级之间进行转换的方法,并最终为它做了一个解决方案。 基本上,我在代码中截取了屏幕截图,然后当我加载下一个级别时,我调用了之前保存的屏幕截图,显示了加载时关卡的外观。 我将之前的级别屏幕截图添加为SKSpriteNode,然后多次运行着色器,直到它出现难以置信的像素化。 然后我对该级别的屏幕截图做了同样的操作并替换了两个,然后我将第二个屏幕截图取消像素化,所以看起来只要水平被击败所有像素化自己然后非像素化自己以显示一个新的水平。

          UIGraphicsBeginImageContextWithOptions(UIScreen.mainScreen().bounds.size, false, 0);
            self.view!.drawViewHierarchyInRect(view!.bounds, afterScreenUpdates: true)
            let image:UIImage = UIGraphicsGetImageFromCurrentImageContext();
            UIGraphicsEndImageContext();
            //UIGraphicsEndImageContext()

            protoImage = SKSpriteNode(texture: SKTexture(CGImage: image.CGImage!))
            protoImage.size = CGSizeMake(self.frame.size.width, self.frame.size.height)
            node.position = CGPointMake(self.frame.size.width/2, self.frame.size.height/2)
            protoImage.zPosition = 9000

第二场景

           let shader: SKShader = SKShader(fileNamed: "RWTGradient2.fsh")
           let ratioX: Float = divisor/Float(protoImage.frame.size.width)
           let ratioY: Float = divisor/Float(protoImage.frame.size.height)
           shader.uniforms = [

                SKUniform(name: "ratioX", float: ratioX),
                SKUniform(name: "ratioY", float: ratioY),

           ]
           protoImage.shader = shader;

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM