簡體   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