[英]Applying a custom SKShader to SKScene that pixelates the whole rendered scene in iOS 8 SpriteKit with Swift
我正在尝试在SKScene上创建一个全屏像素化效果。 我了解到应该有两种方法可以做到这一点:
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.