【问题标题】:Using existing opengl texture with scenekit在 scenekit 中使用现有的 opengl 纹理
【发布时间】:2013-09-09 14:08:41
【问题描述】:

根据 Apple 的文档,您可以使用 NSColor、NSImage 和 CALayer 为 SCNMaterialProperty 提供纹理。 SCNMaterialProperty

不过,我想知道是否有办法提供现有的 OpenGL 纹理,该纹理已使用 glGenTextures 创建并单独渲染。

我当然可以读取纹理的缓冲区,设置一个 NSImage,然后将它提供给 SCNMaterialProperty;但出于性能原因,这显然不是最佳的。

我猜想通过覆盖材质的着色器程序来实现上述内容是有意义的,但这样做的文档似乎不存在。

【问题讨论】:

    标签: objective-c opengl scenekit


    【解决方案1】:

    您可以通过创建将 SCNProgram 分配给材质来覆盖着色器程序。然后在SCNProgramDelegate 的委托方法之一中,您可以为纹理(和其他制服)绑定您自己的值。但是,您将不得不编写自己的着色器。

    如果你习惯了 Objective-C 的话,有一点设置,但当你想到相应的 OpenGL 代码时,真的没有那么多。

    以下是将纹理绑定到几何对象表面的示例着色器程序。为简单起见,它不对法线和光源进行任何着色。

    请注意,我不知道您想如何绑定特定纹理,因此在下面的代码中,我使用 GLKit 读取了一个 png 并使用该纹理。

    // 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];
    

    在这个例子中,着色器被写成

    // 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;
    }
    

    // yourShader.frag
    uniform sampler2D yourTexture;
    varying vec2 texCoord;
    
    void main(void) {
        gl_FragColor = texture2D(yourTexture, texCoord);
    }
    

    最后实现...bindValueForSymbol:... 方法将纹理绑定到“yourTexture”制服。

    - (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.
    } 
    

    此外,出于调试目的,实现program:handleError: 以打印出任何着色器编译错误非常有帮助

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

    【讨论】:

    • 终于有时间玩这个了,只想再次说声谢谢!除了一个小错字(顶点着色器中的 a_textureCoordinate)外,它运行良好,并且比我之前一直使用新的 NSImage 替换纹理的实现要快得多。只是一个小问题;你是怎么想出来的?到目前为止,SceneKit 的文档似乎非常有限。
    【解决方案2】:

    如果您需要直接处理 OpenGL 并绑定您的自定义纹理,那么 SCNProgram 或 SCNNode 的委托是可行的方法(MountainLion 或更高版本)。如果您有 Apple 开发者帐户,您将在此处找到示例代码: http://developer.apple.com/downloads/ 在“WWDC 2013 示例代码”下。寻找“OS_X_SceneKit_Slides_WWDC2013”​​

    【讨论】:

    • 不幸的是,您将不得不提供自己的着色器。此外,SceneKit 本身就是 Mountain Lion 和更高版本;)
    • @Toyos 你的电子邮件地址是什么?如果您不介意,想通过电子邮件向您发送有关项目的信息。
    猜你喜欢
    • 2015-08-29
    • 2016-09-05
    • 2014-11-25
    • 2015-06-30
    • 2015-02-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-30
    相关资源
    最近更新 更多