草庐IT

ios - Metal - `include` 或 `import` 函数

coder 2023-09-22 原文

是否可以将 metal 文件导入或包含到另一个 metal 文件中?假设我有一个包含所有数学函数的 Metal 文件,我只会在我的 Metal 项目中需要时包含或导入它。可能吗?

我试过:

#include "sdf.metal"

我得到了错误:

metallib: Multiply defined symbols _Z4vmaxDv2_f Command/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/usr/bin/metallib failed with exit code 1

更新:

这是我的两个着色器文件:

SDF.metal:

#ifndef MYAPP_METAL_CONSTANTS
#define MYAPP_METAL_CONSTANTS


#include <metal_stdlib>

namespace metal {

float kk(float2 v) {
    return max(v.x, v.y);
}

float kkk(float3 v) {
    return max(max(v.x, v.y), v.z);
}

}

#endif

和Shaders.metal:

#include <metal_stdlib>
#include "SDF.metal"
using namespace metal;


float fBoxCheap(float3 p, float3 b) { //cheap box
    return kkk(abs(p) - b);
}


float map( float3 p )
{
    float box2 = fBoxCheap(p-float3(0.0,3.0,0.0),float3(4.0,3.0,1.0));



    return box2;
}

float3 getNormal( float3 p )
{
    float3 e = float3( 0.001, 0.00, 0.00 );

    float deltaX = map( p + e.xyy ) - map( p - e.xyy );
    float deltaY = map( p + e.yxy ) - map( p - e.yxy );
    float deltaZ = map( p + e.yyx ) - map( p - e.yyx );

    return normalize( float3( deltaX, deltaY, deltaZ ) );
}

float trace( float3 origin, float3 direction, thread float3 &p )
{
    float totalDistanceTraveled = 0.0;

    for( int i=0; i <64; ++i)
    {
        p = origin + direction * totalDistanceTraveled;

        float distanceFromPointOnRayToClosestObjectInScene = map( p );
        totalDistanceTraveled += distanceFromPointOnRayToClosestObjectInScene;

        if( distanceFromPointOnRayToClosestObjectInScene < 0.0001 )
        {
            break;
        }

        if( totalDistanceTraveled > 10000.0 )
        {
            totalDistanceTraveled = 0.0000;
            break;
        }
    }

    return totalDistanceTraveled;
}

float3 calculateLighting(float3 pointOnSurface, float3 surfaceNormal, float3 lightPosition, float3 cameraPosition)
{
    float3 fromPointToLight = normalize(lightPosition - pointOnSurface);
    float diffuseStrength = clamp( dot( surfaceNormal, fromPointToLight ), 0.0, 1.0 );

    float3 diffuseColor = diffuseStrength * float3( 1.0, 0.0, 0.0 );
    float3 reflectedLightVector = normalize( reflect( -fromPointToLight, surfaceNormal ) );

    float3 fromPointToCamera = normalize( cameraPosition - pointOnSurface );
    float specularStrength = pow( clamp( dot(reflectedLightVector, fromPointToCamera), 0.0, 1.0 ), 10.0 );

    // Ensure that there is no specular lighting when there is no diffuse lighting.
    specularStrength = min( diffuseStrength, specularStrength );
    float3 specularColor = specularStrength * float3( 1.0 );

    float3 finalColor = diffuseColor + specularColor;

    return finalColor;
}

kernel void compute(texture2d<float, access::write> output [[texture(0)]],
                    constant float &timer [[buffer(1)]],
                    constant float &mousex [[buffer(2)]],
                    constant float &mousey [[buffer(3)]],
                    uint2 gid [[thread_position_in_grid]])
{
    int width = output.get_width();
    int height = output.get_height();
    float2 uv = float2(gid) / float2(width, height);
    uv = uv * 2.0 - 1.0;
    // scale proportionately.
    if(width > height) uv.x *= float(width)/float(height);
    if(width < height) uv.y *= float(height)/float(width);


    float posx = mousex * 2.0 - 1.0;
    float posy = mousey * 2.0 - 1.0;

    float3 cameraPosition = float3( posx * 0.01,posy * 0.01, -10.0 );


    float3 cameraDirection = normalize( float3( uv.x, uv.y, 1.0) );

    float3 pointOnSurface;
    float distanceToClosestPointInScene = trace( cameraPosition, cameraDirection, pointOnSurface );

    float3 finalColor = float3(1.0);
    if( distanceToClosestPointInScene > 0.0 )
    {
        float3 lightPosition = float3( 5.0, 2.0, -10.0 );
        float3 surfaceNormal = getNormal( pointOnSurface );
        finalColor = calculateLighting( pointOnSurface, surfaceNormal, lightPosition, cameraPosition );
    }
    output.write(float4(float3(finalColor), 1), gid);

}

更新2:

和我的MetalView.swift:

import MetalKit

public class MetalView: MTKView, NSWindowDelegate {

    var queue: MTLCommandQueue! = nil
    var cps: MTLComputePipelineState! = nil

    var timer: Float = 0
    var timerBuffer: MTLBuffer!

    var mousexBuffer: MTLBuffer!
    var mouseyBuffer: MTLBuffer!
    var pos: NSPoint!
    var floatx: Float!
    var floaty: Float!

    required public init(coder: NSCoder) {
        super.init(coder: coder)
        self.framebufferOnly = false
        device = MTLCreateSystemDefaultDevice()
        registerShaders()
    }


    override public func drawRect(dirtyRect: NSRect) {
        super.drawRect(dirtyRect)
        if let drawable = currentDrawable {
            let command_buffer = queue.commandBuffer()
            let command_encoder = command_buffer.computeCommandEncoder()
            command_encoder.setComputePipelineState(cps)
            command_encoder.setTexture(drawable.texture, atIndex: 0)
            command_encoder.setBuffer(timerBuffer, offset: 0, atIndex: 1)
            command_encoder.setBuffer(mousexBuffer, offset: 0, atIndex: 2)
            command_encoder.setBuffer(mouseyBuffer, offset: 0, atIndex: 3)
            update()
            let threadGroupCount = MTLSizeMake(8, 8, 1)
            let threadGroups = MTLSizeMake(drawable.texture.width / threadGroupCount.width, drawable.texture.height / threadGroupCount.height, 1)
            command_encoder.dispatchThreadgroups(threadGroups, threadsPerThreadgroup: threadGroupCount)
            command_encoder.endEncoding()
            command_buffer.presentDrawable(drawable)
            command_buffer.commit()
        }
    }

    func registerShaders() {
        queue = device!.newCommandQueue()
        do {
            let library = device!.newDefaultLibrary()!
            let kernel = library.newFunctionWithName("compute")!
            timerBuffer = device!.newBufferWithLength(sizeof(Float), options: [])
            mousexBuffer = device!.newBufferWithLength(sizeof(Float), options: [])
            mouseyBuffer = device!.newBufferWithLength(sizeof(Float), options: [])
            cps = try device!.newComputePipelineStateWithFunction(kernel)
        } catch let e {
            Swift.print("\(e)")
        }
    }

    func update() {
        timer += 0.01
        var bufferPointer = timerBuffer.contents()
        memcpy(bufferPointer, &timer, sizeof(Float))
        bufferPointer = mousexBuffer.contents()
        memcpy(bufferPointer, &floatx, sizeof(NSPoint))
        bufferPointer = mouseyBuffer.contents()
        memcpy(bufferPointer, &floaty, sizeof(NSPoint))
    }

    override public func mouseDragged(event: NSEvent) {
        pos = convertPointToLayer(convertPoint(event.locationInWindow, fromView: nil))
        let scale = layer!.contentsScale
        pos.x *= scale
        pos.y *= scale
        floatx = Float(pos.x)
        floaty = Float(pos.y)
        debugPrint("Hello",pos.x,pos.y)
    }
}

更新 3

按照 KickimusButticus 的解决方案实现后,着色器确实可以编译。但是我有另一个错误:

最佳答案

您的设置不正确(编辑:我在其他答案和此答案的先前版本中的设置也是如此。)

您可以像在 C++ 中一样使用 header (Metal is based on C++11, after all...)。您只需要一个文件,我将其命名为 SDF.h。该文件包含没有命名空间声明的函数原型(prototype)声明。您需要在 using namespace metal; 其他文件中的声明之后 #include 它。确保头文件不是 .metal 文件并且它不是Compile Sources 列表中你的构建阶段。如果 header 被视为已编译的源代码,这很可能是导致 CompilerError 的原因。

SDF.h:

// SDFHeaders.metal
#ifndef SDF_HEADERS
#define SDF_HEADERS

float kk(float2 v);
float kkk(float3 v);

#endif

SDF.metal:

#include <metal_stdlib>

using namespace metal;
#include "SDF.h"

float kk(float2 v) {
    return max(v.x, v.y);
}

float kkk(float3 v) {
    return max(max(v.x, v.y), v.z);
}

Shaders.metal:

这里是你在包含SDF.h之后使用函数的地方。

// Shaders.metal

#include <metal_stdlib>

using namespace metal;
#include "SDF.h"    

float fBoxCheap(float3 p, float3 b) { //cheap box
    return kkk(abs(p) - b);
}

// ...

当然,清理后构建。祝你好运!

关于ios - Metal - `include` 或 `import` 函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39283565/

有关ios - Metal - `include` 或 `import` 函数的更多相关文章

  1. ruby - 在没有 sass 引擎的情况下使用 sass 颜色函数 - 2

    我想在一个没有Sass引擎的类中使用Sass颜色函数。我已经在项目中使用了sassgem,所以我认为搭载会像以下一样简单:classRectangleincludeSass::Script::FunctionsdefcolorSass::Script::Color.new([0x82,0x39,0x06])enddefrender#hamlengineexecutedwithcontextofself#sothatwithintemlateicouldcall#%stop{offset:'0%',stop:{color:lighten(color)}}endend更新:参见上面的#re

  2. ruby-on-rails - 在 ruby​​ 中使用 gsub 函数替换单词 - 2

    我正在尝试用ruby​​中的gsub函数替换字符串中的某些单词,但有时效果很好,在某些情况下会出现此错误?这种格式有什么问题吗NoMethodError(undefinedmethod`gsub!'fornil:NilClass):模型.rbclassTest"replacethisID1",WAY=>"replacethisID2andID3",DELTA=>"replacethisID4"}end另一个模型.rbclassCheck 最佳答案 啊,我找到了!gsub!是一个非常奇怪的方法。首先,它替换了字符串,所以它实际上修改了

  3. ruby - 在 Ruby 中有条件地定义函数 - 2

    我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin

  4. ruby - 如何验证 IO.copy_stream 是否成功 - 2

    这里有一个很好的答案解释了如何在Ruby中下载文件而不将其加载到内存中:https://stackoverflow.com/a/29743394/4852737require'open-uri'download=open('http://example.com/image.png')IO.copy_stream(download,'~/image.png')我如何验证下载文件的IO.copy_stream调用是否真的成功——这意味着下载的文件与我打算下载的文件完全相同,而不是下载一半的损坏文件?documentation说IO.copy_stream返回它复制的字节数,但是当我还没有下

  5. Ruby 文件 IO 定界符? - 2

    我正在尝试解析一个文本文件,该文件每行包含可变数量的单词和数字,如下所示:foo4.500bar3.001.33foobar如何读取由空格而不是换行符分隔的文件?有什么方法可以设置File("file.txt").foreach方法以使用空格而不是换行符作为分隔符? 最佳答案 接受的答案将slurp文件,这可能是大文本文件的问题。更好的解决方案是IO.foreach.它是惯用的,将按字符流式传输文件:File.foreach(filename,""){|string|putsstring}包含“thisisanexample”结果的

  6. ruby - 在 Ruby 中按名称传递函数 - 2

    如何在Ruby中按名称传递函数?(我使用Ruby才几个小时,所以我还在想办法。)nums=[1,2,3,4]#Thisworks,butismoreverbosethanI'dlikenums.eachdo|i|putsiend#InJS,Icouldjustdosomethinglike:#nums.forEach(console.log)#InF#,itwouldbesomethinglike:#List.iternums(printf"%A")#InRuby,IwishIcoulddosomethinglike:nums.eachputs在Ruby中能不能做到类似的简洁?我可以只

  7. Get https://registry-1.docker.io/v2/: net/http: request canceled while waiting - 2

    1.错误信息:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:requestcanceledwhilewaitingforconnection(Client.Timeoutexceededwhileawaitingheaders)或者:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:TLShandshaketimeout2.报错原因:docker使用的镜像网址默认为国外,下载容易超时,需要修改成国内镜像地址(首先阿里

  8. C51单片机——实现用独立按键控制LED亮灭(调用函数篇) - 2

    说在前面这部分我本来是合为一篇来写的,因为目的是一样的,都是通过独立按键来控制LED闪灭本质上是起到开关的作用,即调用函数和中断函数。但是写一篇太累了,我还是决定分为两篇写,这篇是调用函数篇。在本篇中你主要看到这些东西!!!1.调用函数的方法(主要讲语法和格式)2.独立按键如何控制LED亮灭3.程序中的一些细节(软件消抖等)1.调用函数的方法思路还是比较清晰地,就是通过按下按键来控制LED闪灭,即每按下一次,LED取反一次。重要的是,把按键与LED联系在一起。我打算用K1来作为开关,看了一下开发板原理图,K1连接的是单片机的P31口,当按下K1时,P31是与GND相连的,也就是说,当我按下去时

  9. spring.profiles.active和spring.profiles.include的使用及区别说明 - 2

    转自:spring.profiles.active和spring.profiles.include的使用及区别说明下文笔者讲述spring.profiles.active和spring.profiles.include的区别简介说明,如下所示我们都知道,在日常开发中,开发|测试|生产环境都拥有不同的配置信息如:jdbc地址、ip、端口等此时为了避免每次都修改全部信息,我们则可以采用以上的属性处理此类异常spring.profiles.active属性例:配置文件,可使用以下方式定义application-${profile}.properties开发环境配置文件:application-dev

  10. ruby-on-rails - 将字符串转换为 ruby​​-on-rails 中的函数 - 2

    我需要一个通过输入字符串进行计算的方法,像这样function="(a/b)*100"a=25b=50function.something>>50有什么方法吗? 最佳答案 您可以使用instance_eval:function="(a/b)*100"a=25.0b=50instance_evalfunction#=>50.0请注意,使用eval本质上是不安全的,尤其是当您使用外部输入时,因为它可能包含注入(inject)的恶意代码。另请注意,a设置为25.0而不是25,因为如果它是整数a/b将导致0(整数)。

随机推荐