SceneKit和GLSL-如何将着色器(GLSL)添加到几何 [英] SceneKit and with GLSL - how to add shader (GLSL) to a geometry

查看:95
本文介绍了SceneKit和GLSL-如何将着色器(GLSL)添加到几何的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在学习SceneKit,并且正在使用GLSL.我很难理解如何将glsl与SceneKit一起使用,例如,如何在SceneKit中加载glsl着色器并将其应用于几何体.

让我们说:

  SCNBox * box = [SCNBox boxWithWidth:50 height:50 length:50 chamferRadius:0];SCNNode * bNode = [SCNNode nodeWithGeometry:box];SCNMaterial * redMaterial = [SCNMaterial材料];redMaterial.diffuse.contents = [UIColor redColor];redMaterial.locksAmbientWithDiffuse = YES;box.materials = @ [redMaterial];[scene.rootNode addChildNode:bNode]; 

解决方案

您正在看到后备着色器.确保使用说明符创建渲染器,使其更喜欢OpenGL,而不是Metal.例如,使用SCNView:

  _sceneView = [[SCNView分配] initWithFrame:[UIScreen mainScreen] .bounds选项:@ {SCNPreferredRenderingAPIKey:@(SCNRenderingAPIOpenGLES2)}]; 

此外,您的着色器可能会引发您没有看到的错误.将程序的 delegate 属性设置为可实现此目的的东西:

 -(void)程序:(SCNProgram *)程序handleError:(NSError *)错误{NSLog(@"SCNProgram错误%@",错误);} 

,您将获得有关为何着色器无法编译的一些调试信息.

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

解决方案

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.

这篇关于SceneKit和GLSL-如何将着色器(GLSL)添加到几何的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆