Alles, was ich tun möchte, ist, die grundlegende Arkit-Ansicht in eine Schwarzweiß-Ansicht umzuwandeln. Im Moment ist die Grundansicht einfach normal und ich habe keine Ahnung, wie der Filter hinzugefügt wird. Idealerweise wird beim Aufnehmen eines Screenshots der Schwarzweißfilter zum Screenshot hinzugefügt.
import UIKit
import SceneKit
import ARKit
class ViewController: UIViewController, ARSCNViewDelegate {
@IBOutlet var sceneView: ARSCNView!
override func viewDidLoad() {
super.viewDidLoad()
sceneView.delegate = self
sceneView.showsStatistics = true
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
let configuration = ARWorldTrackingConfiguration()
sceneView.session.run(configuration)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
sceneView.session.pause()
}
@IBAction func changeTextColour(){
let snapShot = self.augmentedRealityView.snapshot()
UIImageWriteToSavedPhotosAlbum(snapShot, self, #selector(image(_:didFinishSavingWithError:contextInfo:)), nil)
}
}
4 Antworten
ARSCNView-Snapshot filtern: Wenn Sie einen Schwarzweiß-Screenshot Ihres ARSCNView
erstellen möchten, können Sie so etwas tun, das in GrayScale ein UIImage
zurückgibt und dabei {{X2} } bezieht sich auf ein ARSCNView
:
/// Converts A UIImage To A High Contrast GrayScaleImage
///
/// - Returns: UIImage
func highContrastBlackAndWhiteFilter() -> UIImage?
{
//1. Convert It To A CIIamge
guard let convertedImage = CIImage(image: self) else { return nil }
//2. Set The Filter Parameters
let filterParameters = [kCIInputBrightnessKey: 0.0,
kCIInputContrastKey: 1.1,
kCIInputSaturationKey: 0.0]
//3. Apply The Basic Filter To The Image
let imageToFilter = convertedImage.applyingFilter("CIColorControls", parameters: filterParameters)
//4. Set The Exposure
let exposure = [kCIInputEVKey: NSNumber(value: 0.7)]
//5. Process The Image With The Exposure Setting
let processedImage = imageToFilter.applyingFilter("CIExposureAdjust", parameters: exposure)
//6. Create A CG GrayScale Image
guard let grayScaleImage = CIContext().createCGImage(processedImage, from: processedImage.extent) else { return nil }
return UIImage(cgImage: grayScaleImage, scale: self.scale, orientation: self.imageOrientation)
}
Ein Beispiel für die Verwendung könnte daher folgendermaßen aussehen:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
//1. Create A UIImageView Dynamically
let imageViewResult = UIImageView(frame: CGRect(x: 0, y: 0, width: self.view.bounds.width, height: self.view.bounds.height))
self.view.addSubview(imageViewResult)
//2. Create The Snapshot & Get The Black & White Image
guard let snapShotImage = self.augmentedRealityView.snapshot().highContrastBlackAndWhiteFilter() else { return }
imageViewResult.image = snapShotImage
//3. Remove The ImageView After A Delay Of 5 Seconds
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
imageViewResult.removeFromSuperview()
}
}
Was zu einem Ergebnis wie diesem führt:
Um Ihren Code wiederverwendbar zu machen, können Sie auch ein extension
von `UIImage erstellen:
//------------------------
//MARK: UIImage Extensions
//------------------------
extension UIImage
{
/// Converts A UIImage To A High Contrast GrayScaleImage
///
/// - Returns: UIImage
func highContrastBlackAndWhiteFilter() -> UIImage?
{
//1. Convert It To A CIIamge
guard let convertedImage = CIImage(image: self) else { return nil }
//2. Set The Filter Parameters
let filterParameters = [kCIInputBrightnessKey: 0.0,
kCIInputContrastKey: 1.1,
kCIInputSaturationKey: 0.0]
//3. Apply The Basic Filter To The Image
let imageToFilter = convertedImage.applyingFilter("CIColorControls", parameters: filterParameters)
//4. Set The Exposure
let exposure = [kCIInputEVKey: NSNumber(value: 0.7)]
//5. Process The Image With The Exposure Setting
let processedImage = imageToFilter.applyingFilter("CIExposureAdjust", parameters: exposure)
//6. Create A CG GrayScale Image
guard let grayScaleImage = CIContext().createCGImage(processedImage, from: processedImage.extent) else { return nil }
return UIImage(cgImage: grayScaleImage, scale: self.scale, orientation: self.imageOrientation)
}
}
Was Sie dann einfach so verwenden können:
guard let snapShotImage = self.augmentedRealityView.snapshot().highContrastBlackAndWhiteFilter() else { return }
Denken Sie daran, dass Sie Ihre Erweiterung über Ihrem class declaration
platzieren sollten, z.
extension UIImage{
}
class ViewController: UIViewController, ARSCNViewDelegate {
}
Basierend auf dem in Ihrer Frage angegebenen Code hätten Sie also ungefähr Folgendes:
/// Creates A Black & White ScreenShot & Saves It To The Photo Album
@IBAction func changeTextColour(){
//1. Create A Snapshot
guard let snapShotImage = self.augmentedRealityView.snapshot().highContrastBlackAndWhiteFilter() else { return }
//2. Save It The Photos Album
UIImageWriteToSavedPhotosAlbum(snapShotImage, self, #selector(image(_:didFinishSavingWithError:contextInfo:)), nil)
}
///Calback To Check Whether The Image Has Been Saved
@objc func image(_ image: UIImage, didFinishSavingWithError error: Error?, contextInfo: UnsafeRawPointer) {
if let error = error {
print("Error Saving ARKit Scene \(error)")
} else {
print("ARKit Scene Successfully Saved")
}
}
Live-Rendering in Schwarzweiß: Mit dieser brillanten Antwort hier von diviaki war ich auch Mit den folgenden Methoden kann der gesamte Kamera-Feed in Schwarzweiß gerendert werden:
1. Registrieren Sie sich für das ARSessionDelegate
wie folgt:
augmentedRealitySession.delegate = self
2 .. Fügen Sie dann im folgenden Rückruf für Delegierte Folgendes hinzu:
//-----------------------
//MARK: ARSessionDelegate
//-----------------------
extension ViewController: ARSessionDelegate{
func session(_ session: ARSession, didUpdate frame: ARFrame) {
/*
Full Credit To https://stackoverflow.com/questions/45919745/reliable-access-and-modify-captured-camera-frames-under-scenekit
*/
//1. Convert The Current Frame To Black & White
guard let currentBackgroundFrameImage = augmentedRealityView.session.currentFrame?.capturedImage,
let pixelBufferAddressOfPlane = CVPixelBufferGetBaseAddressOfPlane(currentBackgroundFrameImage, 1) else { return }
let x: size_t = CVPixelBufferGetWidthOfPlane(currentBackgroundFrameImage, 1)
let y: size_t = CVPixelBufferGetHeightOfPlane(currentBackgroundFrameImage, 1)
memset(pixelBufferAddressOfPlane, 128, Int(x * y) * 2)
}
}
Was den Kamera-Feed erfolgreich in Schwarzweiß rendert:
Filterelemente einer SCNS-Szene in Schwarzweiß:
Wie @Confused zu Recht sagte: Wenn Sie entschieden haben, dass das cameraFeed
farbig sein soll, der Inhalt Ihres AR Experience
jedoch schwarz-weiß sein soll, können Sie einen Filter direkt auf ein {{X2 anwenden }} mit der Eigenschaft filters
, die einfach:
Ein Array von Core Image-Filtern, die auf den gerenderten Inhalt des Knotens angewendet werden sollen.
Nehmen wir zum Beispiel an, wir erstellen dynamisch 3 SCNNodes
mit einem Sphere Geometry
und können ein CoreImageFilter
direkt auf diese anwenden, wie folgt:
/// Creates 3 Objects And Adds Them To The Scene (Rendering Them In GrayScale)
func createObjects(){
//1. Create An Array Of UIColors To Set As The Geometry Colours
let colours = [UIColor.red, UIColor.green, UIColor.yellow]
//2. Create An Array Of The X Positions Of The Nodes
let xPositions: [CGFloat] = [-0.3, 0, 0.3]
//3. Create The Nodes & Add Them To The Scene
for i in 0 ..< 3{
let sphereNode = SCNNode()
let sphereGeometry = SCNSphere(radius: 0.1)
sphereGeometry.firstMaterial?.diffuse.contents = colours[i]
sphereNode.geometry = sphereGeometry
sphereNode.position = SCNVector3( xPositions[i], 0, -1.5)
augmentedRealityView.scene.rootNode.addChildNode(sphereNode)
//a. Create A Black & White Filter
guard let blackAndWhiteFilter = CIFilter(name: "CIColorControls", withInputParameters: [kCIInputSaturationKey:0.0]) else { return }
blackAndWhiteFilter.name = "bw"
sphereNode.filters = [blackAndWhiteFilter]
sphereNode.setValue(CIFilter(), forKeyPath: "bw")
}
}
Was zu einem Ergebnis wie dem folgenden führt:
Eine vollständige Liste dieser Filter finden Sie unter: CoreImage-Filterreferenz
Beispielprojekt: Hier ist ein vollständiges Beispielprojekt, das Sie herunterladen und erkunden können für sich selbst.
Ich hoffe es hilft...
Wenn Sie den Filter in Echtzeit anwenden möchten, verwenden Sie am besten {{X0 }}. Techniken werden für die Nachbearbeitung verwendet und ermöglichen es uns, einen SCNView
Inhalt in mehreren Durchgängen zu rendern - genau das, was wir brauchen (erst eine Szene rendern, dann einen Effekt darauf anwenden).
Hier ist das Beispielprojekt.
Plist Setup
Zuerst müssen wir eine Technik in einer .plist
- Datei beschreiben.
Hier ist ein Screenshot eines plist
, den ich mir ausgedacht habe (zur besseren Visualisierung):
Und hier ist die Quelle:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>sequence</key>
<array>
<string>apply_filter</string>
</array>
<key>passes</key>
<dict>
<key>apply_filter</key>
<dict>
<key>metalVertexShader</key>
<string>scene_filter_vertex</string>
<key>metalFragmentShader</key>
<string>scene_filter_fragment</string>
<key>draw</key>
<string>DRAW_QUAD</string>
<key>inputs</key>
<dict>
<key>scene</key>
<string>COLOR</string>
</dict>
<key>outputs</key>
<dict>
<key>color</key>
<string>COLOR</string>
</dict>
</dict>
</dict>
</dict>
Das Thema von SCNTechnique
s ist sehr umfangreich und ich werde nur schnell auf die Dinge eingehen, die wir für den vorliegenden Fall benötigen. Um wirklich zu verstehen, wozu sie in der Lage sind, empfehle ich, umfassende Dokumentation von Apple zu Techniken zu lesen .
Technikbeschreibung
passes
ist ein Wörterbuch mit einer Beschreibung der Durchgänge, die ein SCNTechnique
ausführen soll.
sequence
ist ein Array, das eine Reihenfolge angibt, in der diese Durchläufe mit ihren Schlüsseln ausgeführt werden.
Sie geben hier nicht den Haupt-Render-Durchgang an (dh was auch immer gerendert wird, ohne SCNTechnique
s anzuwenden) - es ist impliziert und auf die resultierende Farbe kann mit der Konstante COLOR
zugegriffen werden (mehr dazu gleich).
Der einzige "zusätzliche" Durchgang (neben dem Hauptdurchgang), den wir durchführen werden, ist apply_filter
, der Farben in Schwarzweiß umwandelt (er kann beliebig benannt werden, stellen Sie einfach sicher, dass er denselben Schlüssel hat in passes
und sequence
).
Nun zur Beschreibung des apply_filter
Passes selbst.
Beschreibung des Renderpasses
metalVertexShader
und metalFragmentShader
- Namen von Metal
Shader-Funktionen, die zum Zeichnen verwendet werden sollen.
draw
definiert, was der Pass rendern soll. DRAW_QUAD
steht für:
Rendern Sie nur ein Rechteck, das die gesamten Grenzen der Ansicht abdeckt. Verwenden Sie diese Option zum Zeichnen von Durchläufen, die Bildpuffer verarbeiten, die von früheren Durchläufen ausgegeben wurden.
Was grob gesagt bedeutet, dass wir ein einfaches "Bild" ohne Renderpass rendern werden.
inputs
gibt Eingaberessourcen an, die wir in Shadern verwenden können. Wie ich bereits sagte, bezieht sich COLOR
auf Farbdaten, die von einem Haupt-Renderpass bereitgestellt werden.
outputs
gibt die Ausgaben an. Es kann color
, depth
oder stencil
sein, aber wir benötigen nur eine color
Ausgabe. Der Wert COLOR
bedeutet, dass wir einfach ausgedrückt "direkt" auf dem Bildschirm rendern werden (im Gegensatz zum Rendern in Zwischenziele zum Beispiel).
Metall-Shader
Erstellen Sie eine .metal
Datei mit folgendem Inhalt:
#include <metal_stdlib>
using namespace metal;
#include <SceneKit/scn_metal>
struct VertexInput {
float4 position [[ attribute(SCNVertexSemanticPosition) ]];
float2 texcoord [[ attribute(SCNVertexSemanticTexcoord0) ]];
};
struct VertexOut {
float4 position [[position]];
float2 texcoord;
};
// metalVertexShader
vertex VertexOut scene_filter_vertex(VertexInput in [[stage_in]])
{
VertexOut out;
out.position = in.position;
out.texcoord = float2((in.position.x + 1.0) * 0.5 , (in.position.y + 1.0) * -0.5);
return out;
}
// metalFragmentShader
fragment half4 scene_filter_fragment(VertexOut vert [[stage_in]],
texture2d<half, access::sample> scene [[texture(0)]])
{
constexpr sampler samp = sampler(coord::normalized, address::repeat, filter::nearest);
constexpr half3 weights = half3(0.2126, 0.7152, 0.0722);
half4 color = scene.sample(samp, vert.texcoord);
color.rgb = half3(dot(color.rgb, weights));
return color;
}
Beachten Sie, dass die Funktionsnamen für Fragment- und Vertex-Shader dieselben Namen sein sollten, die in der Datei plist
im Passdeskriptor angegeben sind.
Weitere Informationen zur Bedeutung der Strukturen VertexInput
und VertexOut
finden Sie unter > SCNProgram
Dokumentation.
Die angegebene Scheitelpunktfunktion kann so ziemlich in jedem DRAW_QUAD
Renderpass verwendet werden. Grundsätzlich erhalten wir normalisierte Koordinaten des Bildschirmbereichs (auf die mit vert.texcoord
im Fragment-Shader zugegriffen wird).
In der Fragmentfunktion geschieht die ganze "Magie". Dort können Sie die Textur bearbeiten, die Sie vom Hauptdurchgang erhalten haben. Mit diesem Setup können Sie möglicherweise eine Menge Filter / Effekte und mehr implementieren.
In unserem Fall habe ich eine Grundformel für die Entsättigung (Nullsättigung) verwendet, um die Schwarz-Weiß-Farben zu erhalten.
Schnelles Setup
Jetzt können wir all dies endlich in ARKit
/ SceneKit
verwenden.
let plistName = "SceneFilterTechnique" // the name of the plist you've created
guard let url = Bundle.main.url(forResource: plistName, withExtension: "plist") else {
fatalError("\(plistName).plist does not exist in the main bundle")
}
guard let dictionary = NSDictionary(contentsOf: url) as? [String: Any] else {
fatalError("Failed to parse \(plistName).plist as a dictionary")
}
guard let technique = SCNTechnique(dictionary: dictionary) else {
fatalError("Failed to initialize a technique using \(plistName).plist")
}
Und setzen Sie es einfach als technique
des ARSCNView
.
sceneView.technique = technique
Das ist es. Jetzt wird die gesamte Szene in Graustufen einschließlich gerendert, wenn Schnappschüsse aufgenommen werden.
Das snapshot
Objekt sollte ein UIImage
sein. Wenden Sie Filter auf dieses UIImage
Objekt an, indem Sie das CoreImage
Framework importieren, und wenden Sie dann Core Image-Filter darauf an. Sie sollten die Belichtungs- und Kontrollwerte auf dem Bild anpassen. Weitere Implementierungsdetails finden Sie unter Antwort. Unter iOS6 können Sie auch den Filter CIColorMonochrome
verwenden, um den gleichen Effekt zu erzielen.
Hier ist der Apfel Dokumentation für alle verfügbaren Filter. Klicken Sie auf jeden der Filter, um die visuellen Effekte auf das Bild beim Anwenden des Filters zu erfahren.
Hier ist der schnelle 4-Code.
func imageBlackAndWhite() -> UIImage?
{
if let beginImage = CoreImage.CIImage(image: self)
{
let paramsColor: [String : Double] = [kCIInputBrightnessKey: 0.0,
kCIInputContrastKey: 1.1,
kCIInputSaturationKey: 0.0]
let blackAndWhite = beginImage.applyingFilter("CIColorControls", parameters: paramsColor)
let paramsExposure: [String : AnyObject] = [kCIInputEVKey: NSNumber(value: 0.7)]
let output = blackAndWhite.applyingFilter("CIExposureAdjust", parameters: paramsExposure)
guard let processedCGImage = CIContext().createCGImage(output, from: output.extent) else {
return nil
}
return UIImage(cgImage: processedCGImage, scale: self.scale, orientation: self.imageOrientation)
}
return nil
}
Dies ist möglicherweise der einfachste und schnellste Weg, dies zu tun:
Wenden Sie einen CoreImage-Filter auf die Szene an:
https://developer.apple.com/documentation/scenekit/scnnode/1407949-filters
Dieser Filter vermittelt einen sehr guten Eindruck von einem Schwarzweißfoto mit guten Übergängen durch Grautöne: https://developer.apple.com/library/content/documentation/GraphicsImaging/Reference/CoreImageFilterReference/index.html#//apple_/ doc / filter / ci / CIPhotoEffectMono
Sie können auch diesen verwenden und Ergebnisse erhalten, die sich leicht im Farbton verschieben lassen:
Und hier, auf Japanisch, ist der Beweis dafür, dass Filter und SceneKit ARKit zusammenarbeiten: http: //appleengine.hatenablog .com / entry / advent20171215
Neue Fragen
ios
iOS ist das mobile Betriebssystem, das auf dem Apple iPhone, iPod touch und iPad ausgeführt wird. Verwenden Sie dieses Tag [ios] für Fragen zur Programmierung auf der iOS-Plattform. Verwenden Sie die zugehörigen Tags [Objective-C] und [Swift] für Probleme, die für diese Programmiersprachen spezifisch sind.