Пользовательский шейдер SCNProgram iOS 9 Scenekit

Я пытаюсь запутаться в SceneKit и научиться этому. В принципе, я создаю квадроцикл с тремя прямоугольными сторонами и одним наклонным слайдом.

Я хочу, чтобы текстура на нем растягивалась и деформировалась по поверхности.

Считывая некоторые вещи в Интернете, мне кажется, что мне нужно сделать SCNProgram с настраиваемыми шейками вершин и фрагментов, чтобы получить эффект. Но я не могу заставить текстуру распространиться по поверхности. Нужна помощь, пожалуйста. (Я новичок в графическом программировании, поэтому стараюсь научить его себе).

Мой код Swift для создания геометрии и текстуры выглядит следующим образом:

func geometryCreate() -> SCNNode {  

    let verticesPosition = [  
        SCNVector3Make(0.0, 0.0, 0.0),  
        SCNVector3Make(5.0, 0.0, 0.0),  
        SCNVector3Make(5.0, 5.0, 0.0),  
        SCNVector3Make(0.0, 3.0, 0.0)  
    ]  
    let textureCord =    [CGPoint (x: 0.0,y: 0.0), CGPoint(x: 1.0,y: 0.0), CGPoint(x: 1.0,y: 1.0), CGPoint(x: 0.0,y: 1.0)]  

    let indices: [CInt] = [  
    0, 2, 3,  
    0, 1, 2  
    ]  

    let vertexSource = SCNGeometrySource(vertices: verticesPosition, count: 4)  
    let srcTex = SCNGeometrySource(textureCoordinates: textureCord, count: 4)  
    let date = NSData(bytes: indices, length: sizeof(CInt) * indices.count)  

    let scngeometry = SCNGeometryElement(data: date, primitiveType: SCNGeometryPrimitiveType.Triangles, primitiveCount: 2, bytesPerIndex: sizeof(CInt))  

    let geometry = SCNGeometry(sources: [vertexSource,srcTex], elements: [scngeometry])  
    let program = SCNProgram()  

    if let filepath = NSBundle.mainBundle().pathForResource("vertexshadertry", ofType: "vert") {  
        do {  
            let contents = try NSString(contentsOfFile: filepath, encoding: NSUTF8StringEncoding) as String  
            program.vertexShader = contents  
        } catch {  
            print("**** happened loading vertex shader")  
        }  
    }  


    if let fragmentShaderPath = NSBundle.mainBundle().pathForResource("fragshadertry", ofType:"frag")  
    {  
        do {  
        let fragmentShaderAsAString = try NSString(contentsOfFile: fragmentShaderPath, encoding: NSUTF8StringEncoding)  
        program.fragmentShader = fragmentShaderAsAString as String  
        } catch {  
            print("**** happened loading frag shader")  
        }  
    }  
    program.setSemantic(SCNGeometrySourceSemanticVertex, forSymbol: "position", options: nil)  
    program.setSemantic(SCNGeometrySourceSemanticTexcoord, forSymbol: "textureCoordinate", options: nil)  
    program.setSemantic(SCNModelViewProjectionTransform, forSymbol: "modelViewProjection", options: nil)  
    do {  
        let texture = try GLKTextureLoader.textureWithCGImage(UIImage(named: "stripes")!.CGImage!, options: nil)  

        geometry.firstMaterial?.handleBindingOfSymbol("yourTexture", usingBlock: { (programId:UInt32, location:UInt32, node:SCNNode!, renderer:SCNRenderer!) -> Void in  
                glTexParameterf(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_WRAP_S), Float(GL_CLAMP_TO_EDGE) )  
                glTexParameterf(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_WRAP_T), Float(GL_CLAMP_TO_EDGE) )  
                glTexParameterf(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_MAG_FILTER), Float(GL_LINEAR) )  
                glTexParameterf(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_MIN_FILTER), Float(GL_LINEAR) )  
                glBindTexture(GLenum(GL_TEXTURE_2D), texture.name)  
        })  
    } catch {  
        print("Texture not loaded")  
    }  

    geometry.firstMaterial?.program = program  
    let scnnode = SCNNode(geometry: geometry)  
    return scnnode  

}

Мой вершинный шейдер:

attribute vec4 position;  
attribute vec2 textureCoordinate;  
uniform mat4 modelViewProjection;  
varying highp vec2 pos;  
varying vec2 texCoord;  
void main() {  
    texCoord = vec2(textureCoordinate.s, 1.0 - textureCoordinate.t) ;  
    gl_Position = modelViewProjection * position;  
    pos = vec2(position.x, 1.0 - position.y);  
}  

Мой фрагментарный шейдер:

precision highp float;  
uniform sampler2D yourTexture;  
varying highp vec2 texCoord;  
varying highp vec2 pos;  
void main() {  
    gl_FragColor = texture2D(yourTexture, vec2(pos.x, pos.y));  
} 

Я просто не могу получить текстуру в левом нижнем углу, распространяющуюся по поверхности. Не могли бы вы помочь?

Это то, что отображается при запуске кода

Выполняя некоторые ручные вершинные и флеш-шейдерные манипуляции, я могу получить результат, но он чувствует себя очень неэлегантно, и я уверен, что он не должен писать конкретный код, подобный этому.

attribute vec4 position;
attribute vec2 textureCoordinate;
uniform mat4 modelViewProjection;
varying highp vec2 pos;

varying vec2 texCoord;

void main() {
    // Pass along to the fragment shader
    texCoord = vec2(textureCoordinate.s, 1.0 - textureCoordinate.t) ;

    // output the projected position
    gl_Position = modelViewProjection * position;
    pos = vec2(position.x, position.y);
}

Изменения в шейдере фрагмента (где 0.4 - наклон верхней части квадроцикла):

precision highp float;
uniform sampler2D yourTexture;
varying highp vec2 texCoord;
varying highp vec2 pos;


void main() {

    gl_FragColor = texture2D(yourTexture, vec2(pos.x/5.0, 1.0 - pos.y/(3.0+0.4*pos.x)));
//    gl_FragColor = vec4 (0.0, pos.y/5.0, 0.0, 1.0);
}

Который дает мне именно то, что я ищу, но он чувствует себя очень неправильно, делая что-то.

введите описание изображения здесь

EDIT: Я использую переменную pos вместо texCoord, поскольку texCoord дает мне странные результаты, которые я не могу понять: (.

Если бы мне пришлось модифицировать шейдер фрагмента как:

precision highp float;
uniform sampler2D yourTexture;
varying highp vec2 texCoord;
varying highp vec2 pos;

void main() {

//    gl_FragColor = texture2D(yourTexture, vec2(pos.x/5.0, 1.0 - pos.y/(3.0+0.4*pos.x)));
    gl_FragColor = texture2D(yourTexture, texCoord);

//    gl_FragColor = vec4 (0.0, pos.y/5.0, 0.0, 1.0);
}

Я получаю что-то вроде рисунка ниже: введите описание изображения здесь

Что говорит мне, что что-то не так с моим определением координат текстур, но я не могу понять, что?

EDIT2: прогресс ОК. Основываясь на ответе, который Lock дал в связанном потоке, я переопределил свои uvs, используя следующий метод:

let uvSource = SCNGeometrySource(data: uvData,
            semantic: SCNGeometrySourceSemanticTexcoord,
            vectorCount: textureCord.count,
            floatComponents: true,
            componentsPerVector: 3,
            bytesPerComponent: sizeof(Float),
            dataOffset: 0,
            dataStride: sizeof(vector_float2))

Теперь он дает мне такой результат, когда я использую texCoord в моем флеш-шейдере:

введите описание изображения здесь

Его не так сильно, как изогнутые деформации, которые я получаю в текстуре выше. Но его прогресс. Любые идеи, как я могу заставить его сглаживаться, как pic 2 в этом массовом вопросе?

Просьба помочь.

Ответ 1

в шейдере фрагмента вы должны использовать texCoord вместо pos, чтобы пробовать текстуру.

Также обратите внимание, что вам не нужна программа для текстурирования произвольной геометрии. Вы также можете использовать обычный материал для пользовательских геометрий. Если вы хотите сделать что-то невозможное с помощью обычного материала, вы также можете взглянуть на модификаторы шейдеров, они проще в использовании, чем программы, и не требуют, чтобы вы вручную обрабатывали свет, например.