Modeling refraction in SceneKit

I am trying to create an ios 9 application for a project that will render 3d scenes using these special types of theoretical lenses called glenses .

A ray tracing program called TIM has already been written from scratch to simulate these glenses and more, but it cannot just port it to ios.

My understanding of site search (i.e. this answer and many others about shaders) is that it should be possible, but it's hard for me to get the desired effect.

I decided that I would start by implementing a simpler form of refraction before moving on to more complex refraction like glens:

I managed to get the barrel distortion effect (fisheye) affecting the camera using SCNTechnique , however, it seems that you can only use methods on the camera or the entire scene, and not individual parts of the geometry.

After that, I tried to get the barrel distortion effect applied to the geometry by entering the opengl code using the SCNMaterial shaderModifiers property:

  var shaders = [SCNShaderModifierEntryPoint: String]() try! shaders[SCNShaderModifierEntryPoint.fragment] = String(contentsOfFile: Bundle.main.path(forResource: "FishEye", ofType: "fsh")!, encoding: String.Encoding.utf8) try! shaders[SCNShaderModifierEntryPoint.geometry] = String(contentsOfFile: Bundle.main.path(forResource: "FishEye", ofType: "vsh")!, encoding: String.Encoding.utf8) let material = SCNMaterial() material.shaderModifiers = shaders object.geometry?.materials = [material] 

I used the slightly modified shaders found here :

fisheye.vsh

 varying vec2 uv; #pragma body gl_Position = a_position; uv = a_position.xy; 

fisheye.fsh

 uniform sampler2D colorSampler; const float PI = 3.1415926535; const float barrelPower = 0.5; uniform vec2 rg; uniform vec2 uv2; varying vec2 uv; uniform float d; uniform vec2 xy; uniform vec2 Vertex_UV; vec2 Distort(vec2 p) { float theta = atan(py, px); float radius = length(p); radius = pow(radius, barrelPower); px = radius * cos(theta); py = radius * sin(theta); return 0.5 * (p + 1.0); } #pragma body #pragma transparent vec2 xy = 2.0 * Vertex_UV.xy - 1.0; vec2 rg = 2.0 * uv.xy - 1.0; vec2 uv2; float d = length(xy); if (d < 1.0){ uv2 = Distort(xy); }else{ uv2 = uv.xy; } gl_FragColor = texture2D(colorSampler, uv2); 

These shaders are compiled and loaded onto my object in the scene, but do nothing; an object is opaque and white when it is almost transparent without injected shaders, and the #pragma transparent should make it transparent anyway.

To make it clear what I'm trying to achieve here is to have a three-dimensional lens in the scene, which you can view and see the refracted image of what is on the other side.

Any help would be greatly appreciated!

+6
source share
1 answer

If you want to use your own vertex and fragment shader instead of the standard SceneKit shader program, you also use SCNProgram instead of SCNShaderModifierEntryPoint.

SCNShaderModifierEntryPoints allow you to modify the standard Swift shader program.

 let material = SCNMaterial() let program:SCNProgram = SCNProgram() do { program.vertexShader = try String(contentsOfFile: Bundle.main.path(forResource: "fisheye", ofType: "vsh")!, encoding: String.Encoding.utf8) } catch let error { print("shaderReadingError:\(error)") } do { program.fragmentShader = try String(contentsOfFile: Bundle.main.path(forResource: "fisheye", ofType: "fsh")!, encoding: String.Encoding.utf8) } catch let error { print("shaderReadingError:\(error)") } // and also your vertex shader has lack. // You have to add some geometry source and transformation matrix to the vertex shader first with setSemantic method. program.setSemantic(SCNGeometrySource.Semantic.vertex.rawValue, forSymbol: "vPosition", options: nil) program.setSemantic(SCNGeometrySource.Semantic.texcoord.rawValue, forSymbol: "uv", options: nil) program.setSemantic(SCNModelViewProjectionTransform, forSymbol: "uMVPMatrix", options: nil) // and also your fragment shader expect some realtime data like // colorSampler, rg, uv2, d, xy, Vertex_UV // you have to use handleBinding block to update this values before rendering the object. material.handleBinding(ofSymbol: "resolution", handler: { (programId:UInt32, location:UInt32, node:SCNNode?, renderer:SCNRenderer) in }) material.program = program yourNode.geometry.firstMaterial = material 
+2
source

Source: https://habr.com/ru/post/1011944/


All Articles