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