SceneKit and with GLSL - how to add shader (GLSL) to a geometry

谁说胖子不能爱 提交于 2019-12-24 06:45:57

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!