title: SceneKit and with GLSL - how to add shader (GLSL) to a geometry
body: <p>I'm learning SceneKit and while using GLSL. I'm having hard time understanding using glsl with SceneKit, e.g., how to load glsl shaders in SceneKit and apply it to a geometry.</p>
  
  <p>lets say we have:</p>
  
  <pre><code>SCNBox *box = [SCNBox boxWithWidth:50 height:50 length:50 chamferRadius:0];
  SCNNode *bNode = [SCNNode nodeWithGeometry:box];
  
  SCNMaterial *redMaterial                = [SCNMaterial material];
  redMaterial.diffuse.contents            = [UIColor redColor];
  redMaterial.locksAmbientWithDiffuse     = YES;
  box.materials =  @[redMaterial];
  
  [scene.rootNode addChildNode:bNode];
  </code></pre>
  
  <p><a href="https://developer.apple.com/library/content/samplecode/GLSLShowpiece/Introduction/Intro.html#//apple_ref/doc/uid/DTS10003935-Intro-DontLinkElementID_2" rel="nofollow noreferrer">using the glass glsl example codes by apple from year 2006</a> how one can add this effect to a geometry. do one needs to bind those parameters from Glass.vert to SceneKit geometry? Originally, I'm trying to achieve glass effect and water effect.</p>
  
  <p>Glass effect has 2 files:
  1. file Glass.vert</p>
  
  <pre><code>varying vec3  Normal;
  varying vec3  EyeDir;
  varying vec4  EyePos;
  varying float LightIntensity;
  
  uniform vec3  LightPos;
  
  void main(void) 
  {
      gl_Position    = ftransform();
      Normal         = normalize(gl_NormalMatrix * gl_Normal);
      vec4 pos       = gl_ModelViewMatrix * gl_Vertex;
      EyeDir         = pos.xyz;
      EyePos         = gl_ModelViewProjectionMatrix * gl_Vertex;
      LightIntensity = max(dot(normalize(LightPos - EyeDir), Normal), 0.0);
  }
  </code></pre>
  
  <p>and the 2nd file: Glass.frag</p>
  
  <pre><code>const vec3 Xunitvec = vec3 (1.0, 0.0, 0.0);
  const vec3 Yunitvec = vec3 (0.0, 1.0, 0.0);
  
  uniform vec3  BaseColor;
  uniform float Depth;
  uniform float MixRatio;
  
  // need to scale our framebuffer - it has a fixed width/height of 2048
  uniform float FrameWidth;
  uniform float FrameHeight;
  uniform float textureWidth;
  uniform float textureHeight;
  
  uniform sampler2D EnvMap;
  uniform sampler2D RefractionMap;
  
  varying vec3  Normal;
  varying vec3  EyeDir;
  varying vec4  EyePos;
  varying float LightIntensity;
  
  void main (void)
  {
      // Compute reflection vector
      vec3 reflectDir = reflect(EyeDir, Normal);
  
      // Compute altitude and azimuth angles
  
      vec2 index;
  
      index.y = dot(normalize(reflectDir), Yunitvec);
      reflectDir.y = 0.0;
      index.x = dot(normalize(reflectDir), Xunitvec) * 0.5;
  
      // Translate index values into proper range
  
      if (reflectDir.z &gt;= 0.0)
          index = (index + 1.0) * 0.5;
      else
      {
          index.t = (index.t + 1.0) * 0.5;
          index.s = (-index.s) * 0.5 + 1.0;
      }
  
      // if reflectDir.z &gt;= 0.0, s will go from 0.25 to 0.75
      // if reflectDir.z &lt;  0.0, s will go from 0.75 to 1.25, and
      // that's OK, because we've set the texture to wrap.
  
      // Do a lookup into the environment map.
  
      vec3 envColor = vec3 (texture2D(EnvMap, index));
  
      // calc fresnels term.  This allows a view dependant blend of reflection/refraction
      float fresnel = abs(dot(normalize(EyeDir), Normal));
      fresnel *= MixRatio;
      fresnel = clamp(fresnel, 0.1, 0.9);
  
      // calc refraction
      vec3 refractionDir = normalize(EyeDir) - normalize(Normal);
  
      // Scale the refraction so the z element is equal to depth
      float depthVal = Depth / -refractionDir.z;
  
      // perform the div by w
      float recipW = 1.0 / EyePos.w;
      vec2 eye = EyePos.xy * vec2(recipW);
  
      // calc the refraction lookup
      index.s = (eye.x + refractionDir.x * depthVal);
      index.t = (eye.y + refractionDir.y * depthVal);
  
      // scale and shift so we're in the range 0-1
      index.s = index.s / 2.0 + 0.5;
      index.t = index.t / 2.0 + 0.5;
  
      // as we're looking at the framebuffer, we want it clamping at the edge of the rendered scene, not the edge of the texture,
      // so we clamp before scaling to fit
      float recipTextureWidth = 1.0 / textureWidth;
      float recipTextureHeight = 1.0 / textureHeight;
      index.s = clamp(index.s, 0.0, 1.0 - recipTextureWidth);
      index.t = clamp(index.t, 0.0, 1.0 - recipTextureHeight);
  
      // scale the texture so we just see the rendered framebuffer
      index.s = index.s * FrameWidth * recipTextureWidth;
      index.t = index.t * FrameHeight * recipTextureHeight;
  
      vec3 RefractionColor = vec3 (texture2D(RefractionMap, index));
  
      // Add lighting to base color and mix
      vec3 base = LightIntensity * BaseColor;
      envColor = mix(envColor, RefractionColor, fresnel);
      envColor = mix(envColor, base, 0.2);
  
      gl_FragColor = vec4 (envColor, 1.0);
  }
  </code></pre>
  
  <p><strong>Edit:</strong></p>
  
  <p>I made it to the point to load those shaders in SceneKit:</p>
  
  <pre><code>NSURL *vertexShaderURL   = [[NSBundle mainBundle] URLForResource:@"Glass" withExtension:@"vert"];
  NSURL *fragmentShaderURL = [[NSBundle mainBundle] URLForResource:@"Glass" withExtension:@"frag"];
  NSString *vertexShader = [[NSString alloc] initWithContentsOfURL:vertexShaderURL
                                                          encoding:NSUTF8StringEncoding
                                                             error:NULL];
  NSString *fragmentShader = [[NSString alloc] initWithContentsOfURL:fragmentShaderURL
                                                            encoding:NSUTF8StringEncoding
                                                               error:NULL];
  SCNProgram *program = [SCNProgram program];
  program.delegate = self;
  program.vertexShader   = vertexShader;
  program.fragmentShader = fragmentShader;
  
  SCNMaterial *redMaterial                = [SCNMaterial material];
  redMaterial.diffuse.contents            = [UIColor redColor];
  redMaterial.locksAmbientWithDiffuse     = YES;
  
  redMaterial.program = program;
  box.materials =  @[redMaterial];
  </code></pre>
  
  <p>And Additionally, I've initialised these in shader files:</p>
  
  <pre><code>//frag file
  BaseColor = vec3 (0.4, 0.4, 1.0)
  Depth = 0.1;
  MixRatio = 1;
  EnvMap = 0;
  RefractionMap = 1;
  //vert file
  LightPos = vec3 (0.0, 140.0, 0.0);
  </code></pre>
  
  <p>The Box now appears pink without glass effect. Removing program from redMaterial, the box appears red as expected without glass effect. so I'm still unable to achieve the desired effect. any help is much appreciated.</p>
  
  <p><strong>Edit 2:</strong></p>
  
  <p>xcode logs:</p>
  
  <pre><code>2016-11-21 08:08:26.758244 testGame[7837:3366037] [DYMTLInitPlatform] platform initialization successful
  2016-11-21 08:08:27.196142 testGame[7837:3365880] Metal GPU Frame Capture Enabled
  2016-11-21 08:08:27.196975 testGame[7837:3365880] Metal API Validation Enabled
  </code></pre>
  
  <p><a href="https://i.stack.imgur.com/VZnNg.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/VZnNg.png" alt="results on iPhone 6"></a></p>