Ios 在场景中折射是可能的吗?

Ios 在场景中折射是可能的吗?,ios,scenekit,Ios,Scenekit,是否可以制作一个穿过光线的形状,这样你们就可以在光线因折射而弯曲的情况下看到它?就像一个镜头或一个玻璃杯(或水)?是的,一切都是可能的,有着惊人的物理力量!不过,您需要创建自己的着色器。发件人: 在计算机图形学领域,着色器是一种计算机程序 是用来做明暗处理的:制作适当的色彩等级 在一个形象之内,或者,在现代时代,也要产生特殊的影响 效果或做视频后处理。外行术语的定义 可能被称为“告诉计算机如何画画的程序” 以特定和独特的方式做某事” 如果您感兴趣,objc.io在SceneKit上有一个应用程序

是否可以制作一个穿过光线的形状,这样你们就可以在光线因折射而弯曲的情况下看到它?就像一个镜头或一个玻璃杯(或水)?

是的,一切都是可能的,有着惊人的物理力量!不过,您需要创建自己的着色器。发件人:

在计算机图形学领域,着色器是一种计算机程序 是用来做明暗处理的:制作适当的色彩等级 在一个形象之内,或者,在现代时代,也要产生特殊的影响 效果或做视频后处理。外行术语的定义 可能被称为“告诉计算机如何画画的程序” 以特定和独特的方式做某事”


如果您感兴趣,objc.io在SceneKit上有一个应用程序。

要使用SceneKit实现折射,您需要一个SCN程序。内置着色器无法对折射执行任何操作

根据本文()中的答案,可以像这样使用SceneKit实现折射效果。(此示例基于ARKit)

你需要:

  • 迅捷的
  • SceneKit/ARKit
  • 天空盒(我们需要折射和反射的东西)
  • SCN程序
  • 金属着色器
  • 物理设备(推荐)
创建一个新的ARKit(SceneKit)项目,创建或加载一个类似球体的几何体对象,并将其放置在空间中。(这里没问题)

实现skybox。确保skybox由6个单独的图像组成(立方体贴图)-不要使用2:1的球体贴图,它们似乎不适用于金属着色器中的采样器。这里有一个很好的Skybox链接:()

在保存skybox的各个图片所需的六个UIImages中创建一个图标,如下所示:

let skybox1 = UIImage.init(named: "art.scnassets/subdir/image-PX.png")
let skybox2 = UIImage.init(named: "art.scnassets/subdir/image-NX.png")
let skybox3 = UIImage.init(named: "art.scnassets/subdir/image-PY.png")
let skybox4 = UIImage.init(named: "art.scnassets/subdir/image-NY.png")
let skybox5 = UIImage.init(named: "art.scnassets/subdir/image-PZ.png")
let skybox6 = UIImage.init(named: "art.scnassets/subdir/image-NZ.png")
图像必须是正方形,并且应该是2的幂次方(用于最佳mipmapping目的)。512x512就可以了,1024x1024已经需要很多内存了

创建一个SCNMaterialProperty,用于保存skybox的单个图像数组,如下所示:

// Cube-Map Structure:
//      PY
//  NX  PZ  PX  NZ
//      NY

// Array Order:
// PX, NX, PY, NY, PZ, NZ

let envMapSkyboxMaterialProperty = SCNMaterialProperty(contents: [skybox1!,skybox2!,skybox3!,skybox4!,skybox5!,skybox6!])
(p=正,N=负)

然后像这样设置天空盒:(我们需要这个用于场景的反射、折射背景和照明*)

同时设置照明环境**

myScene.lightingEnvironment.contents = envMapSkyboxMaterialProperty?.contents
假设到现在为止,您可以使用默认材质将几何体对象放置在空间中-我们现在准备好将SCN程序与用于光线折射的特殊金属着色器对齐

制作SCN程序并进行如下配置:

let sceneProgramRefract = SCNProgram()
sceneProgramRefract.vertexFunctionName   = "myVertexRefract" // (myVertexRefract is the Keyword used in the shader)
sceneProgramRefract.fragmentFunctionName = "myFragmentRefract" // (myFragmentRefract is the Keyword used in the shader)
在目标几何体节点的材质上,按如下方式附着SCN程序:

firstMaterial.program = sceneProgramRefract // doing this will replace the entire built-in SceneKit shaders for that object.
firstMaterial.setValue(envMapSkyboxMaterialProperty, forKey: "cubeTexture") // (cubeTexture is the Keyword used in the shader to access the Skybox)
将新的金属文件添加到项目中,并将其命名为“shaders.Metal”

用以下内容替换金属文件中的任何内容:

// Default Metal Header for SCNProgram
#include <metal_stdlib>
using namespace metal;
#include <SceneKit/scn_metal>

// Default Sampler for the Skybox
constexpr sampler cubeSampler;


// Nodebuffer (you only need the enabled Matrix floats)
struct MyNodeBuffer {
    // float4x4 modelTransform;
    // float4x4 inverseModelTransform;
    float4x4 modelViewTransform; // required
    // float4x4 inverseModelViewTransform;
    float4x4 normalTransform; // required
    // float4x4 modelViewProjectionTransform;
    // float4x4 inverseModelViewProjectionTransform;
};

// Input Struct
typedef struct {
    float3 position [[ attribute(SCNVertexSemanticPosition) ]];
    float3 normal   [[ attribute(SCNVertexSemanticNormal)   ]];
} MyVertexInput;

// Struct filled by the Vertex Shader
struct SimpleVertexRefract
{
    float4 position [[position]];
    float  k;
    float3 worldSpaceReflection;
    float3 worldSpaceRefraction;
};

// VERTEX SHADER
vertex SimpleVertexRefract myVertexRefract(MyVertexInput in [[stage_in]],
                                          constant SCNSceneBuffer& scn_frame [[buffer(0)]],
                                          constant MyNodeBuffer& scn_node [[buffer(1)]])
{
float4 modelSpacePosition(in.position, 1.0f);
float4 modelSpaceNormal(in.normal, 0.0f);

// We'll be computing the reflection in eye space, so first we find the eye-space
// position. This is also used to compute the clip-space position below.
float4 eyeSpacePosition         = scn_node.modelViewTransform * modelSpacePosition;

// We compute the eye-space normal in the usual way.
float3 eyeSpaceNormal           = (scn_node.normalTransform * modelSpaceNormal).xyz;

// The view vector in eye space is just the vector from the eye-space position.
float3 eyeSpaceViewVector       = normalize(-eyeSpacePosition.xyz);

float3 view_vec                 = normalize(eyeSpaceViewVector);
float3 normal                   = normalize(eyeSpaceNormal);

const float ETA                 = 1.12f; // (this defines the intensity of the refraction. 1.0 will be no refraction)
float c                         = dot(view_vec, normal);
float d                         = ETA * c;
float k                         = clamp(d * d + (1.0f - ETA * ETA), 0.0f, 1.0f); // k is used in the fragment shader

// for Reflection / Refraction
// To find the reflection/refraction vector, we reflect/refract the (inbound) view vector about the normal.
float4 eyeSpaceReflection       = float4(reflect(-eyeSpaceViewVector, eyeSpaceNormal), 0.0f);
float4 eyeSpaceRefraction       = float4(refract(-eyeSpaceViewVector, eyeSpaceNormal, ETA), 0.0f);

// To sample the cube-map, we want a world-space reflection vector, so multiply
// by the inverse view transform to go back from eye space to world space.
float3 worldSpaceReflection     = (scn_frame.inverseViewTransform * eyeSpaceReflection).xyz;
float3 worldSpaceRefraction     = (scn_frame.inverseViewTransform * eyeSpaceRefraction).xyz;

// Fill the Out-Struct
SimpleVertexRefract out;
out.position                    = scn_frame.projectionTransform * eyeSpacePosition;
out.k                           = k;
out.worldSpaceReflection        = worldSpaceReflection; //
out.worldSpaceRefraction        = worldSpaceRefraction; //
return out;
}

// FRAGMENT SHADER
fragment float4 myFragmentRefract(SimpleVertexRefract in [[stage_in]],
                                  texturecube<float, access::sample> cubeTexture [[texture(0)]])
{
// Since the reflection vector's length will vary under interpolation, we normalize it
// and flip it from the assumed right-hand space of the world to the left-hand space
// of the interior of the cubemap.
float3 worldSpaceReflection     = normalize(in.worldSpaceReflection) * float3(1.0f, 1.0f, -1.0f);
float3 worldSpaceRefraction     = normalize(in.worldSpaceRefraction) * float3(1.0f, 1.0f, -1.0f);

float3 reflection               = cubeTexture.sample(cubeSampler, worldSpaceReflection).rgb;
float3 refraction               = cubeTexture.sample(cubeSampler, worldSpaceRefraction).rgb;

float4 color;
color.rgb                       = mix(reflection, refraction, float3(in.k)); // this is where k is finally used
color.a                         = 1.0f;
return color;
}
通过将myScene.background.contents设置回originalARSource,可以跳回AR提要

**在ARKit中,确保将跟踪配置设置为。skybox处于活动状态期间,无:

configuration.environmentTexturing = .none

您需要创建自己的着色器。物理没那么难,谢谢!请给出一个答案——物理学是微不足道的——制作着色器有多难?我是一个3D和SceneKit ultra-noob。对于2D场景,它应该是微不足道的。不过我不确定3D场景。我已经回答了你的问题(tl;dr:是的)。从个人经验来看,我发现像这样的问题并不能真正得到想要的答案(是/否永远不够!)。例如,这个问题最好命名为“如何在SceneKit中实现折射?”。记住,如果不可能,人们会让你知道;)你能达到玻璃或水的效果吗?如果是的话,请发布一个答案。谢谢参考——我买了David Rönnqvist的书,但我还没有达到有关着色器的章节(刚刚完成的材质,认为它可能在那里,但没有)
var originalARSource : Any? = nil // screen Scene Backup
originalARSource = myScene.background.contents
configuration.environmentTexturing = .none