本文代码地址node
ARKit系列文章目录c++
上一篇咱们已经初步示范了:如何读取着色器String,并经过shaderModifiers加载.
Shader类型SCNShaderModifierEntryPoint有geometry,surface,lightingModel,fragment.git
咱们以surface类型为例.swift
如何给着色器传参呢??直接使用KVC...github
private func setupShader() {
guard let path = Bundle.main.path(forResource: "skin", ofType: "shaderModifier", inDirectory: "art.scnassets"),
let shader = try? String(contentsOfFile: path, encoding: String.Encoding.utf8) else {
return
}
skin.shaderModifiers = [SCNShaderModifierEntryPoint.surface: shader]
skin.setValue(Double(0), forKey: "blendFactor")
skin.setValue(NSValue(scnVector3: SCNVector3Zero), forKey: "skinColorFromEnvironment")
let sparseTexture = SCNMaterialProperty(contents: UIImage(named: "art.scnassets/textures/chameleon_DIFFUSE_BASE.png")!)
skin.setValue(sparseTexture, forKey: "sparseTexture")
}
复制代码
OCswift
NSURL *url = [[NSBundle mainBundle] URLForResource:@"skin" withExtension:@"shaderModifier"];
NSError *error;
NSString *shaderSource = [[NSString alloc] initWithContentsOfURL:url
encoding:NSUTF8StringEncoding
error:&error];
if (!shaderSource) {
// Handle the error
NSLog(@"Failed to load shader source code, with error: %@", [error localizedDescription]);
} else {
skin.shaderModifiers = @{ SCNShaderModifierEntryPointSurface : shader };
}
[skin setValue:@(0) forKey:@"blendFactor"];
[skin setValue:[NSValue valueWithSCNVector3:SCNVector3Zero] forKey:@"skinColorFromEnvironment"];
SCNMaterialProperty *sparseTexture = [SCNMaterialProperty materialPropertyWithContents:[NSImage imageNamed:@"art.scnassets/textures/chameleon_DIFFUSE_BASE.png"]];
[skin setValue:sparseTexture forKey:@"sparseTexture"];
复制代码
输入为结构体geometry:bash
struct SCNShaderGeometry {
float4 position;
float3 normal;
float4 tangent;
float4 color;
float2 texcoords[kSCNTexcoordCount];
} _geometry;
Access: ReadWrite 访问权限:读写
Stages: Vertex shader only 只限于顶点着色器中
复制代码
kSCNTexcoordCount
是个整型常量,表示用到的顶点坐标数.
其中的向量及坐标(position, normal and tangent)为模型空间.
着色器示例,正弦变形:函数
GLSL
uniform float Amplitude = 0.1;
_geometry.position.xyz += _geometry.normal * (Amplitude * _geometry.position.y * _geometry.position.x) * sin(u_time);
Metal Shading Language
#pragma arguments
float Amplitude;
#pragma body
_geometry.position.xyz += _geometry.normal * (Amplitude * _geometry.position.y * _geometry.position.x) * sin(scn_frame.time);
复制代码
输入为结构体surface:post
struct SCNShaderSurface {
float3 view; // Direction from the point on the surface toward the camera (V)
float3 position; // Position of the fragment
float3 normal; // Normal of the fragment (N)
float3 geometryNormal; // Geometric normal of the fragment (normal map is ignored)
float3 tangent; // Tangent of the fragment
float3 bitangent; // Bitangent of the fragment
float4 ambient; // Ambient property of the fragment
float2 ambientTexcoord; // Ambient texture coordinates
float4 diffuse; // Diffuse property of the fragment. Alpha contains the opacity.
float2 diffuseTexcoord; // Diffuse texture coordinates
float4 specular; // Specular property of the fragment
float2 specularTexcoord; // Specular texture coordinates
float4 emission; // Emission property of the fragment
float2 emissionTexcoord; // Emission texture coordinates
float4 multiply; // Multiply property of the fragment
float2 multiplyTexcoord; // Multiply texture coordinates
float4 transparent; // Transparent property of the fragment
float2 transparentTexcoord; // Transparent texture coordinates
float4 reflective; // Reflective property of the fragment
float metalness; // Metalness property of the fragment
float2 metalnessTexcoord; // Metalness texture coordinates
float roughness; // Roughness property of the fragment
float2 roughnessTexcoord; // Roughness texture coordinates
float4 selfIllumination; // Self Illumination property of the fragment. Available since macOS 10.13, iOS 11, tvOS 11 and watchOS 4. Available as `emission` in previous versions.
float2 selfIlluminationTexcoord; // Self Illumination texture coordinates. Available since macOS 10.13, iOS 11, tvOS 11 and watchOS 4. Available as `emissionTexcoord` in previous versions.
float ambientOcclusion; // Ambient Occlusion property of the fragment. Available macOS 10.13, iOS 11, tvOS 11 and watchOS 4. Available as `multiply` in previous versions.
float2 ambientOcclusionTexcoord; // Ambient Occlusion texture coordinates. Available since macOS 10.13, iOS 11, tvOS 11 and watchOS 4. Available as `multiplyTexcoord` in previous versions.
float shininess; // Shininess property of the fragment
float fresnel; // Fresnel property of the fragment
} _surface;
Access: ReadWrite 访问权限:读写
Stages: Fragment shader only 只限于片断着色器中
复制代码
其中的向量及坐标为视图空间.
着色器示例,产生黑白条纹:动画
GLSL
uniform float Scale = 12.0;
uniform float Width = 0.25;
uniform float Blend = 0.3;
vec2 position = fract(_surface.diffuseTexcoord * Scale);
float f1 = clamp(position.y / Blend, 0.0, 1.0);
float f2 = clamp((position.y - Width) / Blend, 0.0, 1.0);
f1 = f1 * (1.0 - f2);
f1 = f1 * f1 * 2.0 * (3. * 2. * f1);
_surface.diffuse = mix(vec4(1.0), vec4(vec3(0.0),1.0), f1);
Metal Shading Language
#pragma arguments
float Scale;
float Width;
float Blend;
#pragma body
float2 position = fract(_surface.diffuseTexcoord * Scale);
float f1 = clamp(position.y / Blend, 0.0, 1.0);
float f2 = clamp((position.y - Width) / Blend, 0.0, 1.0);
f1 = f1 * (1.0 - f2);
f1 = f1 * f1 * 2.0 * (3. * 2. * f1);
_surface.diffuse = mix(float4(1.0), float4(float3(0.0),1.0), f1);
复制代码
输入有多个,SCNShaderModifierEntryPointSurface中的全部结构体,结构体lightingContribution,结构体light:ui
All the structures available from the SCNShaderModifierEntryPointSurface entry point
SCNShaderModifierEntryPointSurface中的全部结构体
Access: ReadOnly 访问权限:只读
Stages: Vertex shader and fragment shader 顶点着色器及片断着色器中
struct SCNShaderLightingContribution {
float3 ambient;
float3 diffuse;
float3 specular;
} _lightingContribution;
Access: ReadWrite 访问权限:读写
Stages: Vertex shader and fragment shader 顶点着色器及片断着色器中
struct SCNShaderLight {
float4 intensity;
float3 direction; // Direction from the point on the surface toward the light (L)
} _light;
Access: ReadOnly 访问权限:只读
Stages: Vertex shader and fragment shader 顶点着色器及片断着色器中
复制代码
着色器示例,漫反射光照:
GLSL
uniform float WrapFactor = 0.5;
float dotProduct = (WrapFactor + max(0.0, dot(_surface.normal,_light.direction))) / (1 + WrapFactor);
_lightingContribution.diffuse += (dotProduct * _light.intensity.rgb);
vec3 halfVector = normalize(_light.direction + _surface.view);
dotProduct = max(0.0, pow(max(0.0, dot(_surface.normal, halfVector)), _surface.shininess));
_lightingContribution.specular += (dotProduct * _light.intensity.rgb);
Metal Shading Language
#pragma arguments
float WrapFactor;
#pragma body
float dotProduct = (WrapFactor + max(0.0, dot(_surface.normal,_light.direction))) / (1 + WrapFactor);
_lightingContribution.diffuse += (dotProduct * _light.intensity.rgb);
float3 halfVector = normalize(_light.direction + _surface.view);
dotProduct = max(0.0, pow(max(0.0, dot(_surface.normal, halfVector)), _surface.shininess));
_lightingContribution.specular += (dotProduct * _light.intensity.rgb);
复制代码
输入为SCNShaderModifierEntryPointSurface中的结构体,及结构体output:
All the structures available from the SCNShaderModifierEntryPointSurface entry point
SCNShaderModifierEntryPointSurface中的全部结构体
Access: ReadOnly 访问权限:只读
Stages: Fragment shader only 只限于片断着色器中
struct SCNShaderOutput {
float4 color;
} _output;
Access: ReadWrite 访问权限:读写
Stages: Fragment shader only 只限于片断着色器中
复制代码
着色器示例,反转最终的颜色:
GLSL
_output.color.rgb = vec3(1.0) - _output.color.rgb;
Metal Shading Language
_output.color.rgb = 1.0 - _output.color.rgb;
复制代码
shaderModifiers相关说明:
// Custom GLSL uniforms declarations are of the form: [uniform type uniformName [= defaultValue]]
// 自定义GLSL uniforms声明格式:[uniform 类型 全局变量名 [= 默认值]]
uniform float myGrayAmount = 3.0;
// Custom Metal uniforms declarations require a #pragma and are of the form: [type name]
// 自定义Metal uniforms声明格式要求#pragma,格式:[类型 全局变量名]
#pragma arguments
float myGrayAmount;
// In Metal, you can also transfer varying values from the vertex shader (geometry modifier) to the fragment shader (surface/fragment modifier)
// 在Metal中,你将变量值从顶点着色器(geometry modifier)传递到片断着色器(surface/fragment modifier)中
// In one (or both) of the modifier, declare the varying values
// 在一个(或所有)的modifier中,声明变量值
#pragma varyings
half3 myVec;
// Output varying values in the geometry modifier
// 在geometry modifier中输出变量值
out.myVec = _geometry.normal.xyz * 0.5h + 0.5h;
// And use them in the fragment modifier
// 在fragment modifier中使用这些值
_output.color.rgb = saturate(in.myVec);
// Optional global function definitions (for Metal: references to uniforms in global functions are not supported).
// 可选的全局函数定义(Metal中不支持在全局函数中对uniforms的引用)
float mySin(float t) {
return sin(t);
}
[#pragma transparent | opaque]
[#pragma body]
// the shader modifier code snippet itself
//
float3 myColor = myGrayAmount;
_output.color.rgb += myColor;
复制代码
#pragma body
指令:当声明函数不在着色器代码中时,必须使用这个指令.
#pragma transparent
指令:强制使用公式_output.color.rgb + (1 - _output.color.a) * dst.rgb;
来渲染混合模式;其中dst
表示当前片断颜色,rgb份量必须是自左乘的.
#pragma opaque
指令:强制渲染为不透明的.忽略片断的alpha份量.
SCNGeometry和SCNMaterial类是兼容key-value编码的类,就是说你能够用KVC来赋值,哪怕key myAmplitude
在当前类中并无被声明,仍然能够赋值.
当在shader modifier中声明myAmplitude
uniform后,SceneKit会监听接收者的myAmplitude
key.当该值改变时,SceneKit会给uniform绑定一个新的值.
普通用NSValue包装的标题类型都是支持的.
对于Metal:
自定义uniforms能够使用显式动画.
在声明或绑定自定义uniforms时,可用到的对应类型以下:
GLSL | Metal Shading Language | Objective-C |
---|---|---|
int | int | NSNumber, NSInteger, int |
float | float | NSNumber, CGFloat, float, double |
vec2 | float2 | CGPoint |
vec3 | float3 | SCNVector3 |
vec4 | float4 | SCNVector4 |
mat4, mat44 | float4x4 | SCNMatrix4 |
sampler2D | texture2d | SCNMaterialProperty |
samplerCube | texturecube | SCNMaterialProperty (with a cube map) |
下列前缀是SceneKit默认保留的,在自定义变量命名中不能使用:
u_
a_
v_
SceneKit声明的内置uniforms共有如下这些:
GLSL | Metal Shading Language | 说明 |
---|---|---|
float u_time | float scn_frame.time | The current time, in seconds |
vec2 u_inverseResolution | float2 scn_frame.inverseResolution | 1.0 / screen size |
mat4 u_viewTransform | float4x4 scn_frame.viewTransform | See SCNViewTransform |
mat4 u_inverseViewTransform | float4x4 scn_frame.inverseViewTransform | |
mat4 u_projectionTransform | float4x4 scn_frame.projectionTransform | See SCNProjectionTransform |
mat4 u_inverseProjectionTransform | float4x4 scn_frame.inverseProjectionTransform | |
mat4 u_normalTransform | float4x4 scn_node.normalTransform | See SCNNormalTransform |
mat4 u_modelTransform | float4x4 scn_node.modelTransform | See SCNModelTransform |
mat4 u_inverseModelTransform | float4x4 scn_node.inverseModelTransform | |
mat4 u_modelViewTransform | float4x4 scn_node.modelViewTransform | See SCNModelViewTransform |
mat4 u_inverseModelViewTransform | float4x4 scn_node.inverseModelViewTransform | |
mat4 u_modelViewProjectionTransform | float4x4 scn_node.modelViewProjectionTransform | See SCNModelViewProjectionTransform |
mat4 u_inverseModelViewProjectionTransform | float4x4 scn_node.inverseModelViewProjectionTransform | |
mat2x3 u_boundingBox; | float2x3 scn_node.boundingBox | The bounding box of the current geometry, in model space, u_boundingBox[0].xyz and u_boundingBox[1].xyz being respectively the minimum and maximum corner of the box. |
首先,咱们新建个AR项目,Xcode会自动生成默认的项目,显示一个小飞机. 咱们对界面稍加改动,添加几个开关,用来控制shader.
而后实现setupShader()
方法来加载四个shader,并在viewDidLoad()
中调用.这里咱们对着色器稍作修改,添加一个效果因子factor
来控制效果是否显示:
private func setupShader() {
let shipNode = sceneView.scene.rootNode.childNode(withName: "shipMesh", recursively: true)
let skin = shipNode?.geometry?.firstMaterial;
// 为了方便观察混合效果,我在各个示例shader中添加了一个因子factor,分别设置为0.0和1.0能够控制效果的开启和关闭;默认为0.0--关闭;
let geometryShader = """
uniform float Amplitude = 0.1;
uniform float GeometryFactor = 0.0;
_geometry.position.xyz += _geometry.normal * (Amplitude * _geometry.position.y * _geometry.position.x) * sin(u_time) * GeometryFactor;
"""
let surfaceShader = """
uniform float Scale = 12.0;
uniform float Width = 0.25;
uniform float Blend = 0.3;
uniform float SurfaceFactor = 0.0;
vec2 position = fract(_surface.diffuseTexcoord * Scale);
float f1 = clamp(position.y / Blend, 0.0, 1.0);
float f2 = clamp((position.y - Width) / Blend, 0.0, 1.0);
f1 = f1 * (1.0 - f2);
f1 = f1 * f1 * 2.0 * (3. * 2. * f1);
_surface.diffuse = _surface.diffuse * (1-SurfaceFactor) + mix(vec4(1.0), vec4(vec3(0.0),1.0), f1) * SurfaceFactor;
"""
let lightShader = """
uniform float WrapFactor = 0.5;
uniform float LightFactor = 0.0;
float dotProduct = (WrapFactor + max(0.0, dot(_surface.normal,_light.direction))) / (1 + WrapFactor);
_lightingContribution.diffuse += (dotProduct * _light.intensity.rgb) * LightFactor;
vec3 halfVector = normalize(_light.direction + _surface.view);
dotProduct = max(0.0, pow(max(0.0, dot(_surface.normal, halfVector)), _surface.shininess));
_lightingContribution.specular += (dotProduct * _light.intensity.rgb) * LightFactor;
"""
let fragmentShader = """
uniform float FragmentFactor = 0.0;
_output.color.rgb = (vec3(1.0) - _output.color.rgb) * FragmentFactor + (1-FragmentFactor) * _output.color.rgb;
"""
skin?.shaderModifiers = [SCNShaderModifierEntryPoint.geometry: geometryShader,
SCNShaderModifierEntryPoint.surface: surfaceShader,
SCNShaderModifierEntryPoint.lightingModel: lightShader,
SCNShaderModifierEntryPoint.fragment: fragmentShader
]
}
复制代码
最后,只要在开关改变时,用KVC设置对应shader的因子值就能够了:
@IBAction func switchChange(_ sender: UISwitch) {
let shipNode = sceneView.scene.rootNode.childNode(withName: "shipMesh", recursively: true)
let skin = shipNode?.geometry?.firstMaterial;
let factor = sender.isOn ? 1.0 : 0.0
switch sender.tag {
case 10:
skin?.setValue(Double(factor), forKey: "GeometryFactor")
case 11:
skin?.setValue(Double(factor), forKey: "SurfaceFactor")
case 12:
skin?.setValue(Double(factor), forKey: "LightFactor")
case 13:
skin?.setValue(Double(factor), forKey: "FragmentFactor")
default:
print("switch")
}
}
复制代码
效果如图: