将一个自定义SKShader应用到SKScene中,使用Swift将iOS 8 SpriteKit中的整个渲染场景进行像素化

我正在尝试在SKScene上创build一个全屏的像素化效果。 我了解到,应该有两个select来做到这一点:

  • 使用使用GLES 2.0的自定义SKShader
  • 使用核心图像filter。

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

SKEffectNode对象将其子级渲染为缓冲区,并可select将Core Image滤镜应用于此渲染输出。

GameScene : SKScene一样,可以将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 ,它将起作用。

那么在所有的节点被渲染之后,是否有可能修改整个帧缓冲区(例如像素化)作为后期处理措施?

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

为了让你的.shader.shader运行,你需要在场景中设置shouldEnableEffects为true(对于SKEffectNode )。

虽然在技术上,这是“有效的”(着色器被应用),但是在渲染场景之后存在稍微resize的错误。

所以到目前为止,使用CoreImagefilter是最好的方法。

我设法使用核心图像filterCIPixellate 。 我使用的是SKEffectNode产生像素化效果的filter。 几件事要注意:

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

这里的解决scheme是基于你使用SwiftselectGametypes项目时生成的代码:

 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,然后多次运行着色器,直到它非常像素化。 然后我对截图进行了相同的处理,并replace了这两个截图,然后我对第二个截图进行了非像素化处理,所以看起来像一旦水平被击败,所有的像素都会自动像素化,从而显示出一个新的水平。

  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;