2013-09-05 7 views
6

Gemäß der Dokumentation von Apple können Sie NSColor, NSImage und CALayer verwenden, um einer SCNMaterialProperty eine Textur bereitzustellen. SCNMaterialPropertyVorhandene OpenGL-Textur mit Scenekit verwenden

Ich frage mich jedoch, ob es eine Möglichkeit gibt, eine vorhandene OpenGL-Textur bereitzustellen, die mit glGenTextures erstellt und separat gerendert wurde.

Ich kann natürlich den Puffer der Textur lesen, einen NSImage einrichten und diesen an die SCNMaterialProperty übergeben; aber aus Performance-Gründen ist das offensichtlich nicht optimal.

Es wäre sinnvoll, das obige zu implementieren, indem ich das/die Shaderprogramm (e) des Materials überschreibe, das ich vermute, aber die Dokumentation dafür scheint nicht existent zu sein.

Antwort

8

Sie können die Shader-Programme überschreiben, indem Sie dem Material ein SCNProgram zuweisen. Dann können Sie in einer der Delegate-Methoden für SCNProgramDelegate Ihre eigenen Werte für die Textur (und andere Uniformen) binden. Sie werden jedoch Ihre eigenen Shader schreiben müssen.

Es gibt ein wenig Setup, wenn Sie an Objective-C gewöhnt sind, aber es ist wirklich nicht so viel, wenn Sie an den entsprechenden OpenGL-Code denken.

Das folgende Beispiel zeigt ein Shaderprogramm, das eine Textur an die Oberfläche eines Geometrieobjekts bindet. Der Einfachheit halber erfolgt keine Schattierung mit Normalen und Lichtquellen.

Beachten Sie, dass ich nicht weiß, wie Sie Ihre spezifische Textur binden möchten, so dass ich im folgenden Code ein PNG mit GLKit lesen und diese Textur verwenden.

// Create a material 
SCNMaterial *material = [SCNMaterial material]; 

// Create a program 
SCNProgram *program = [SCNProgram program]; 

// Read the shader files from your bundle 
NSURL *vertexShaderURL = [[NSBundle mainBundle] URLForResource:@"yourShader" withExtension:@"vert"]; 
NSURL *fragmentShaderURL = [[NSBundle mainBundle] URLForResource:@"yourShader" withExtension:@"frag"]; 
NSString *vertexShader = [[NSString alloc] initWithContentsOfURL:vertexShaderURL 
                 encoding:NSUTF8StringEncoding 
                  error:NULL]; 
NSString *fragmentShader = [[NSString alloc] initWithContentsOfURL:fragmentShaderURL 
                  encoding:NSUTF8StringEncoding 
                  error:NULL]; 
// Assign the shades 
program.vertexShader = vertexShader; 
program.fragmentShader = fragmentShader; 

// Bind the position of the geometry and the model view projection 
// you would do the same for other geometry properties like normals 
// and other geometry properties/transforms. 
// 
// The attributes and uniforms in the shaders are defined as: 
// attribute vec4 position; 
// attribute vec2 textureCoordinate; 
// uniform mat4 modelViewProjection;  
[program setSemantic:SCNGeometrySourceSemanticVertex 
      forSymbol:@"position" 
      options:nil]; 
[program setSemantic:SCNGeometrySourceSemanticTexcoord 
      forSymbol:@"textureCoordinate" 
      options:nil]; 
[program setSemantic:SCNModelViewProjectionTransform 
      forSymbol:@"modelViewProjection" 
      options:nil]; 


// Become the program delegate so that you get the binding callback 
program.delegate = self; 

// Set program on geometry 
material.program = program; 
yourGeometry.materials = @[material]; 

In diesem Beispiel werden die Shadern geschrieben werden als

// yourShader.vert 
attribute vec4 position; 
attribute vec2 textureCoordinate; 
uniform mat4 modelViewProjection; 

varying vec2 texCoord; 

void main(void) { 
    // Pass along to the fragment shader 
    texCoord = textureCoordinate; 

    // output the projected position 
    gl_Position = modelViewProjection * position; 
} 

Und

// yourShader.frag 
uniform sampler2D yourTexture; 
varying vec2 texCoord; 

void main(void) { 
    gl_FragColor = texture2D(yourTexture, texCoord); 
} 

Schließlich wird die Implementierung für die ...bindValueForSymbol:... Verfahren eine Textur auf die "yourTexture" Uniform zu binden.

- (BOOL) program:(SCNProgram *)program 
bindValueForSymbol:(NSString *)symbol 
     atLocation:(unsigned int)location 
      programID:(unsigned int)programID 
      renderer:(SCNRenderer *)renderer 
{ 
    if ([symbol isEqualToString:@"yourTexture"]) { 
     // I'm loading a png with GLKit but you can do your very own thing 
     // here to bind your own texture. 
     NSError *error = nil; 
     NSString *imagePath = [[NSBundle mainBundle] pathForResource:@"sampleImage" ofType:@"png"]; 
     GLKTextureInfo *texture = [GLKTextureLoader textureWithContentsOfFile:imagePath options:nil error:&error]; 
     if(!texture) { 
      NSLog(@"Error loading file: %@", [error localizedDescription]); 
     } 

     glBindTexture(GL_TEXTURE_2D, texture.name); 

     return YES; // indicate that the symbol was bound successfully. 
    } 

    return NO; // no symbol was bound. 
} 

Auch für Debugging-Zwecke ist es sehr hilfreich program:handleError: zu implementieren alle Shader Kompilierungsfehlern auszudrucken

- (void)program:(SCNProgram*)program handleError:(NSError*)error { 
    // Log the shader compilation error 
    NSLog(@"%@", error); 
} 
+0

wow, vielen Dank! – simonfi

+2

Endlich hatte ich etwas Zeit, um damit zu spielen, und wollte nur nochmal Danke sagen! Abgesehen von einem kleinen Tippfehler (a_textureCoordinate im Vertex-Shader) funktionierte das perfekt und ist viel schneller als meine vorherige Implementierung, die die Textur immer mit einem neuen NSMageitor ersetzte. Nur eine kleine Frage; Wie hast du das herausgefunden? Die Dokumentation für SceneKit scheint bisher sehr begrenzt zu sein. – simonfi

1

Wenn Sie direkt mit OpenGL arbeiten und Ihre benutzerdefinierten Texturen binden müssen, dann sind SCNProgram oder SCNNode's Delegate die richtigen Methoden (MountainLion oder höher). Wenn Sie ein Apple-Entwicklerkonto haben, finden Sie hier einen Beispielcode: http://developer.apple.com/downloads/ unter "WWDC 2013 Sample Code". Suchen Sie nach "OS_X_SceneKit_Slides_WWDC2013"

+0

Leider müssen Sie Ihre eigene Shadern liefern. SceneKit an sich ist auch Mountain Lion und höher;) –

+0

@Toyos Wie lautet Ihre E-Mail-Adresse? Ich möchte dir gerne ein Projekt mailen, wenn es dir nichts ausmacht. – Crashalot