2016-11-19 88 views
1

我正在學習SceneKit並使用GLSL。我很難理解在SceneKit中使用glsl,例如,如何在SceneKit中加載glsl着色器並將其應用於幾何。SceneKit和GLSL - 如何將着色器(GLSL)添加到幾何圖形

可以說,我們有:

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怎麼能夠這個效果添加到幾何。是否需要將這些參數從Glass.vert綁定到SceneKit幾何?最初,我試圖達到玻璃效果和水效果。

玻璃效果有2個文件: 1.文件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); 
} 

和2號文件: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); 
} 

編輯:

我做到了以在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]; 

,另外,我在着色器文件初始化這些:

//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); 

盒子現在沒有出現玻璃效果的粉紅色。從redMaterial中刪除程序,該框顯示爲紅色,但沒有玻璃效果。所以我仍然無法達到預期的效果。任何幫助深表感謝。

編輯2:

Xcode的日誌:

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 

results on iPhone 6

+0

截圖可能嗎? –

+0

我添加了一個截圖,立方體有紅色,添加垂直和碎片後,它變成粉紅色,沒有玻璃效果。 –

回答

2

你看到的後備着色器。確保你正在使用說明符來創建渲染器,以使用OpenGL而不是Metal。例如,使用SCNView:

_sceneView = [[SCNView alloc] initWithFrame:[UIScreen mainScreen].bounds 
            options:@{ SCNPreferredRenderingAPIKey: @(SCNRenderingAPIOpenGLES2) }]; 

此外,您的着色器可能會引發一個您沒有看到的錯誤。該方案的delegate屬性設置的東西,實現了這個:

- (void)program:(SCNProgram *)program handleError:(NSError *)error 
{ 
    NSLog(@"SCNProgram error %@", error); 
} 

,你會得到關於爲什麼着色器編譯沒有一些調試信息。