问题
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.
lets say we have:
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];
using the glass glsl example codes by apple from year 2006 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.
Glass effect has 2 files: 1. file Glass.vert
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);
}
and the 2nd file: Glass.frag
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 >= 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 >= 0.0, s will go from 0.25 to 0.75
// if reflectDir.z < 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);
}
Edit:
I made it to the point to load those shaders in SceneKit:
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];
And Additionally, I've initialised these in shader files:
//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);
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.
Edit 2:
xcode logs:
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
回答1:
You're seeing the fallback shader. Make sure you're creating your renderer with the specifier to prefer OpenGL, rather than Metal. For example, with SCNView:
_sceneView = [[SCNView alloc] initWithFrame:[UIScreen mainScreen].bounds
options:@{ SCNPreferredRenderingAPIKey: @(SCNRenderingAPIOpenGLES2) }];
Also, your shader might be raising an error that you're not seeing. Set the delegate
property of the program to something that implements this:
- (void)program:(SCNProgram *)program handleError:(NSError *)error
{
NSLog(@"SCNProgram error %@", error);
}
and you'll get some debug info about why the shader isn't compiling.
来源:https://stackoverflow.com/questions/40693242/scenekit-and-with-glsl-how-to-add-shader-glsl-to-a-geometry