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

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

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

Читая некоторые вещи в Интернете, кажется, что мне нужно сделать 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);
}

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

РЕДАКТИРОВАТЬ: я использую 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);
}

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

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

РЕДАКТИРОВАТЬ 2: ОК, прогресс. Основываясь на ответе, который Лок дал в связанной теме, я переопределил свои 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 в моем фрагментном шейдере:

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

Пожалуйста, помогите.

1 ответ

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

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

Другие вопросы по тегам