SceneKit和GLSL-如何将着色器(GLSL)添加到几何 [英] SceneKit and with GLSL - how to add shader (GLSL) to a geometry
问题描述
我正在学习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屋!