我正在尝试在SKScene上创建全屏像素化效果。我了解到应该有两种选择:
SKShader
。我尝试添加一个自定义SKShader,它应该通过像素化来修改整个屏幕。我不确定是否可能,但来自SKScene
(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(How do you add a CIPixellate Core Image Filter to a Sprite Kit scene?)中使用Core Image过滤器进行像素化的方法,但复制该实现并不会在iOS上产生任何结果。根据{{3}} CIPixellate
应为Available in OS X v10.4 and later and in iOS 6.0 and later.
。
答案 0 :(得分:3)
为了让.shader
在SKScene
上运行,您需要在场景中将shouldEnableEffects
设置为true(同样适用于SKEffectNode
)。
虽然从技术上来说,"工作" (应用着色器),之后渲染场景中的错误会稍微调整一下。
到目前为止,使用CoreImage
过滤器是最好的方法。
答案 1 :(得分:2)
我设法使用核心图像过滤器CIPixellate
使其工作。我使用的是SKEffectNode
的过滤器来产生像素化效果。有几点需要注意:
SKScene
是SKEffectNode
的子类,但将过滤器应用于SKScene
不起作用。这会弄乱背景并且不会进行任何像素化。SKEffectNode
并添加要在其下进行像素化的节点。以下是基于使用Game
选择Swift
类型项目时生成的代码的解决方案:
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 */
}
}
答案 2 :(得分:0)
我实际上必须为最近的项目做同样的事情,作为在各级之间转换的方法,最后为它做一个解决方案。基本上,我在代码中截取了屏幕截图,然后当我加载下一个级别时,我调用了之前保存的屏幕截图,显示了加载时关卡的外观。我将之前的级别屏幕截图添加为SKSpriteNode,然后多次运行着色器,直到它出现难以置信的像素化。然后我对该级别的屏幕截图做了相同的操作并替换了两个,然后我将第二个屏幕截图取消像素化,所以它看起来就像水平被击败所有像素化自身然后非像素化以显示一个新的水平。 / p>
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;