Adding preliminary metal support (it sucks)

This commit is contained in:
Dynamitos
2025-02-25 05:10:35 +01:00
parent 98edc34a81
commit 65bc1c78c7
124 changed files with 29790 additions and 412 deletions
+8
View File
@@ -0,0 +1,8 @@
target_sources(RayTracer
PRIVATE
MetalRenderer.h
MetalRenderer.cpp
MetalScene.h
MetalScene.cpp
Compute.metal
PrivateImpl.mm)
+272
View File
@@ -0,0 +1,272 @@
#include <metal_stdlib>
#include <simd/simd.h>
#include <metal_numeric>
using namespace metal;
using namespace raytracing;
enum class MaterialType
{
BlinnPhong
};
struct GPUCamera
{
packed_float3 position;
float f;
packed_float3 forward;
float S_O;
packed_float3 fogEmm;
float ks;
float A;
float ka;
float2 sensorSize;
uint width;
uint height;
};
struct SampleParams
{
uint pass;
uint samplesPerPixel;
uint numDirectionalLights;
uint numPointLights;
};
struct Payload
{
float3 rnd01;
float3 accumulatedRadiance = float3(0);
float3 accumulatedMaterial = float3(1);
uint depth = 0;
float emissive = 1;
};
struct ModelReference
{
uint positionOffset = 0;
uint numPositions = 0;
uint indicesOffset = 0;
uint numIndices = 0;
};
struct HitInfo
{
float t = numeric_limits<float>::max();
float3 position;
float3 normal;
// not entirely sure what that does
// its the normal being flipped based on some dot product
float3 normalLight;
float2 texCoords;
};
struct BRDF
{
float3 albedo = float3(1, 1, 1);
float alpha = 1;
float3 specularColor = float3(1, 1, 1);
float shininess = 0.04;
float3 emissive = float3(0, 0, 0);
MaterialType materialType;
float3 evaluate(HitInfo hit, float3 viewDir, float3 lightDir, float3 lightColor)
{
float3 normal = hit.normal;
float diffuse = max(dot(normal, lightDir), 0.0f);
float3 h = normalize(lightDir + viewDir);
float specular = pow(min(max(dot(normal, h), 0.0f), 1.0f), shininess);
return (albedo * diffuse * lightColor) + float3(0.03, 0.03, 0.03);
}
};
struct PointLight
{
float3 position = float3(0, 0, 0);
packed_float3 color = float3(1, 1, 1);
float attenuation = 1;
};
struct DirectionalLight
{
float3 direction = float3(0, 1, 0);
float3 color = float3(1, 1, 1);
};
float3 rand01(uint3 x)
{ // pseudo-random number generator
for (int i = 3; i-- > 0;)
x = ((x >> 8U) ^ uint3(x.y, x.z, x.x)) * 1103515245U;
return float3(x) * (1.0f / float(0xffffffffU));
}
template<typename T, typename IndexType>
inline T interpolateVertexAttribute(constant T *attributes,
uint offset,
IndexType i0,
IndexType i1,
IndexType i2,
float2 uv) {
// Look up value for each vertex.
const T T0 = attributes[offset + i0];
const T T1 = attributes[offset + i1];
const T T2 = attributes[offset + i2];
// Compute the sum of the vertex attributes weighted by the barycentric coordinates.
// The barycentric coordinates sum to one.
return (1.0f - uv.x - uv.y) * T0 + uv.x * T1 + uv.y * T2;
}
kernel void computeKernel(
uint2 threadId [[thread_position_in_grid]],
constant GPUCamera& camera,
constant SampleParams& sample,
constant packed_uint3* indexBuffer [[buffer(0)]],
constant packed_float3* positions [[buffer(1)]],
constant packed_float2* texCoords [[buffer(2)]],
constant packed_float3* normals [[buffer(3)]],
constant ModelReference* modelRefs [[buffer(4)]],
constant DirectionalLight* directionalLights [[buffer(5)]],
constant PointLight* pointLights [[buffer(6)]],
constant MTLAccelerationStructureInstanceDescriptor* instances [[buffer(7)]],
instance_acceleration_structure accelerationStructure [[buffer(8)]],
device packed_float3* accumulator [[buffer(9)]],
device packed_float3* image [[buffer(10)]]
)
{
Payload payload;
ray cam;
cam.origin = camera.position;
cam.direction = normalize(camera.forward);
cam.max_distance = INFINITY;
float3 cx =
normalize(cross(cam.direction, abs(cam.direction.y) < 0.9 ? float3(0, 1, 0) : float3(0, 0, 1))),
cy = cross(cx, cam.direction);
const float2 sdim = camera.sensorSize; // sensor size (36 x 24 mm)
float S_I = (camera.S_O * camera.f) / (camera.S_O - camera.f);
//-- sample sensor
uint2 pix = threadId;
if(pix.x >= camera.width || pix.y >= camera.height)
return;
payload.rnd01 = rand01(uint3(pix, sample.pass));
float2 rnd2 = 2.0f * float2(payload.rnd01.xy); // vvv tent filter sample
float2 tent =
float2(rnd2.x < 1 ? sqrt(rnd2.x) - 1 : 1 - sqrt(2 - rnd2.x), rnd2.y < 1 ? sqrt(rnd2.y) - 1 : 1 - sqrt(2 - rnd2.y));
float2 s =
((float2(pix) + 0.5f * (0.5f + float2((sample.pass / 2) % 2, sample.pass % 2) + tent)) / float2(camera.width, camera.height) -
0.5f) *
sdim;
float3 spos = cam.origin + cx * s.x + cy * s.y, lc = cam.origin + cam.direction * 0.035f; // sample on 3d sensor plane
cam.origin = lc;
cam.direction = normalize(lc - spos); // construct ray
//-- setup lens
float3 lensP = lc;
float3 lensN = -cam.direction;
float3 lensX = cross(lensN, float3(0, 1, 0)); // the exact vector doesnt matter
float3 lensY = cross(lensN, lensX);
float3 lensSample = lensP + payload.rnd01.x * camera.A * lensX + payload.rnd01.y * camera.A * lensY;
float3 focalPoint = cam.origin + (camera.S_O + S_I) * cam.direction;
float t = dot(focalPoint - cam.origin, lensN) / dot(cam.direction, lensN);
float3 focus = cam.origin + t * cam.direction;
cam.origin = lensSample;
cam.direction = normalize(focus - lensSample); // TODO: Fix lens
intersector<triangle_data, instancing> i;
i.assume_geometry_type(geometry_type::triangle);
i.force_opacity(forced_opacity::opaque);
typename intersector<triangle_data, instancing>::result_type intersection;
while(payload.depth < 12) {
i.accept_any_intersection(false);
intersection = i.intersect(cam, accelerationStructure, 0xff);
if(intersection.type == intersection_type::none)
break;
uint instanceId = intersection.instance_id;
constant ModelReference& ref = modelRefs[instanceId];
HitInfo info;
info.t = intersection.distance;
const auto indices = indexBuffer[ref.indicesOffset + intersection.primitive_id];
info.position = interpolateVertexAttribute(positions, ref.positionOffset, indices.x, indices.y, indices.z, intersection.triangle_barycentric_coord);
info.texCoords = interpolateVertexAttribute(texCoords, ref.positionOffset, indices.x, indices.y, indices.z, intersection.triangle_barycentric_coord);
info.normal = interpolateVertexAttribute(normals, ref.positionOffset, indices.x, indices.y, indices.z, intersection.triangle_barycentric_coord);
info.normalLight = dot(info.normal, cam.direction) < 0 ? info.normal : -info.normal;
BRDF brdf;
brdf.albedo = float3(0, 1, 0);
float p = max(max(brdf.albedo.x, brdf.albedo.y), brdf.albedo.z);
if (payload.depth > 5)
{
if (payload.rnd01.z >= p)
return;
else
payload.accumulatedMaterial /= p;
}
// emissive
payload.accumulatedRadiance += payload.accumulatedMaterial * brdf.emissive * payload.emissive;
payload.accumulatedMaterial *= brdf.albedo;
// direct lighting
for (uint l = 0; l < sample.numDirectionalLights; ++l)
{
// if there is an intersection, the light is occluded so no lighting
ray shadowRay;
shadowRay.origin = info.position + info.normal * 1e-3f;
shadowRay.direction = -directionalLights[l].direction;
shadowRay.max_distance = INFINITY;
i.accept_any_intersection(true);
intersection = i.intersect(shadowRay, accelerationStructure, 0xff);
if(intersection.type != intersection_type::none)
{
payload.accumulatedRadiance += brdf.evaluate(info, -cam.direction, shadowRay.direction, directionalLights[l].color);
}
}
for (uint l = 0; l < sample.numPointLights; ++l)
{
float3 lightDir = pointLights[l].position - info.position;
ray shadowRay;
shadowRay.origin = info.position + info.normal * 1e-3f;
shadowRay.direction = lightDir;
shadowRay.max_distance = 1;
i.accept_any_intersection(true);
intersection = i.intersect(shadowRay, accelerationStructure, 0xff);
if (intersection.type != intersection_type::none)
{
float d = length(lightDir);
float illuminance = max(1 - d / pointLights[l].attenuation, 0.0f);
payload.accumulatedRadiance += illuminance * brdf.evaluate(info, -cam.direction, normalize(lightDir), pointLights[l].color);
}
}
// TODO: Next Event Estimation for mesh lights
// indirect lighting
float r1 = 2 * M_PI_F * payload.rnd01.x;
float r2 = payload.rnd01.y;
float r2s = sqrt(r2);
float3 w = info.normalLight;
float3 u = normalize(cross(abs(w.x) > 0.1 ? float3(0, 1, 0) : float3(1, 0, 0), w));
float3 v = cross(w, u);
cam.origin = info.position;
cam.direction = normalize(u * cos(r1) * r2s + v * sin(r1) * r2s + w * sqrt(1 - r2));
payload.emissive = 0;
payload.depth++;
}
float resolver = float(sample.samplesPerPixel) / float(sample.pass+1);
accumulator[threadId.x + threadId.y * camera.width] += payload.accumulatedRadiance / float(sample.samplesPerPixel);
image[threadId.x + threadId.y * camera.width] = pow(max(accumulator[threadId.x + threadId.y * camera.width] * resolver, 0), float3(0.45f));
}
+96
View File
@@ -0,0 +1,96 @@
#include "MetalRenderer.h"
#include "metal/MetalScene.h"
#include "scene/Renderer.h"
#include "util/Camera.h"
#include <Foundation/Foundation.hpp>
#include <Metal/Metal.hpp>
#include <QuartzCore/QuartzCore.hpp>
MetalRenderer::MetalRenderer()
{
device = MTL::CreateSystemDefaultDevice();
library = device->newDefaultLibrary();
queue = device->newCommandQueue();
scene = new MetalScene(device, queue);
function = library->newFunction(NS::String::string("computeKernel", NS::ASCIIStringEncoding));
NS::Error* error;
computePipeline = device->newComputePipelineState(function, &error);
}
MetalRenderer::~MetalRenderer() {}
void MetalRenderer::render(Camera camera, RenderParameter parameter)
{
MTL::Buffer* cameraBuffer = device->newBuffer(sizeof(GPUCamera), 0);
*(GPUCamera*)cameraBuffer->contents() = GPUCamera {
.cameraPosition = camera.position,
.A = camera.A,
.cameraForward = camera.target - camera.position,
.f = camera.f,
.S_O = camera.S_O,
.sensorSize = camera.sensorSize,
.width = parameter.width,
.height = parameter.height,
};
SampleParams sample = {
.samplesPerPixel = parameter.numSamples,
.numDirectionalLights = scene->getNumDirLights(),
.numPointLights = scene->getNumPointLights(),
};
accumulator = device->newBuffer(parameter.width * parameter.height * sizeof(glm::vec3), 0);
resultTexture = device->newBuffer(parameter.width * parameter.height * sizeof(glm::vec3), 0);
for (uint i = 0; i < parameter.numSamples; ++i)
{
MTL::CommandBuffer* cmdBuffer = queue->commandBuffer();
MTL::ComputeCommandEncoder* encoder = cmdBuffer->computeCommandEncoder();
//cmdBuffer->addCompletedHandler([this](MTL::CommandBuffer* cmdBuffer)
// { std::memcpy(image.data(), resultTexture->buffer(), image.size() * sizeof(glm::vec3)); });
sample.pass = i;
MTL::Buffer* sampleBuffer = device->newBuffer(sizeof(SampleParams), 0);
*(SampleParams*)sampleBuffer->contents() = sample;
encoder->setComputePipelineState(computePipeline);
encoder->setBuffer(scene->indicesBuffer, 0, 0);
encoder->setBuffer(scene->positionBuffer, 0, 1);
encoder->setBuffer(scene->texCoordsBuffer, 0, 2);
encoder->setBuffer(scene->normalBuffer, 0, 3);
encoder->setBuffer(scene->modelRefsBuffer, 0, 4);
encoder->setBuffer(scene->directionalLightBuffer, 0, 5);
encoder->setBuffer(scene->pointLightBuffer, 0, 6);
encoder->setBuffer(scene->instanceBuffer, 0, 7);
encoder->setAccelerationStructure(scene->accelerationStructure, 8);
encoder->setBuffer(accumulator, 0, 9);
encoder->setBuffer(resultTexture, 0, 10);
encoder->setBuffer(cameraBuffer, 0, 11);
encoder->setBuffer(sampleBuffer, 0, 12);
encoder->useResource(scene->instanceBuffer, MTL::ResourceUsageRead);
encoder->useResource(scene->positionBuffer, MTL::ResourceUsageRead);
encoder->useResource(scene->texCoordsBuffer, MTL::ResourceUsageRead);
encoder->useResource(scene->normalBuffer, MTL::ResourceUsageRead);
encoder->useResource(scene->modelRefsBuffer, MTL::ResourceUsageRead);
if(scene->getNumDirLights() > 0)
{
encoder->useResource(scene->directionalLightBuffer, MTL::ResourceUsageRead);}
if(scene->getNumPointLights() > 0)
{encoder->useResource(scene->pointLightBuffer, MTL::ResourceUsageRead);}
encoder->useResource(scene->instanceBuffer, MTL::ResourceUsageRead);
encoder->useResource(scene->accelerationStructure, MTL::ResourceUsageRead);
encoder->useResource(accumulator, MTL::ResourceUsageWrite);
encoder->useResource(resultTexture, MTL::ResourceUsageWrite);
NS::UInteger width = (NS::UInteger)parameter.width;
NS::UInteger height = (NS::UInteger)parameter.height;
MTL::Size threadsPerThreadgroup = MTL::Size(8, 8, 1);
MTL::Size threadgroups = MTL::Size((width + threadsPerThreadgroup.width - 1) / threadsPerThreadgroup.width,
(height + threadsPerThreadgroup.height - 1) / threadsPerThreadgroup.height, 1);
encoder->dispatchThreadgroups(threadgroups, threadsPerThreadgroup);
encoder->endEncoding();
cmdBuffer->commit();
cmdBuffer->waitUntilCompleted();
std::memcpy(image.data(), resultTexture->contents(), image.size() * sizeof(glm::vec3));
}
}
+54
View File
@@ -0,0 +1,54 @@
#pragma once
#include "metal/MetalScene.h"
#include "scene/Renderer.h"
#include "util/Camera.h"
#include <Foundation/Foundation.hpp>
#include <Metal/Metal.hpp>
#include <QuartzCore/QuartzCore.hpp>
struct GPUCamera
{
glm::vec3 cameraPosition;
float f;
glm::vec3 cameraForward;
float S_O;
glm::vec3 fogEmm;
float ks;
float A;
float ka;
glm::vec2 sensorSize;
uint width;
uint height;
};
struct SampleParams
{
uint pass;
uint samplesPerPixel;
uint numDirectionalLights;
uint numPointLights;
};
class MetalRenderer : public Renderer
{
public:
MetalRenderer();
virtual ~MetalRenderer();
virtual void addPointLight(PointLight point) override { scene->addPointLight(point); }
virtual void addDirectionalLight(DirectionalLight dir) override { scene->addDirectionalLight(dir); }
virtual void addModel(PModel model, glm::mat4 transform) override { scene->addModel(std::move(model), transform); }
virtual void addModels(std::vector<PModel> models, glm::mat4 transform) override { scene->addModels(std::move(models), transform); }
virtual void generate() override { scene->generate(); }
virtual void render(Camera camera, RenderParameter params) override;
private:
MTL::Device* device;
MTL::Library* library;
MTL::CommandQueue* queue;
MTL::Function* function;
MTL::ComputePipelineState* computePipeline;
MTL::Buffer* accumulator;
MTL::Buffer* resultTexture;
MetalScene* scene;
};
+110
View File
@@ -0,0 +1,110 @@
#include "MetalScene.h"
MetalScene::MetalScene(MTL::Device* device, MTL::CommandQueue* queue)
: device(device), queue(queue)
{
}
MetalScene::~MetalScene()
{
}
void MetalScene::createRayTracingHierarchy()
{
indicesBuffer = device->newBuffer(indicesPool.size() * sizeof(decltype(indicesPool)::value_type), MTL::ResourceStorageModeShared);
positionBuffer = device->newBuffer(positionPool.size() * sizeof(decltype(positionPool)::value_type), MTL::ResourceStorageModeShared);
texCoordsBuffer = device->newBuffer(texCoordsPool.size() * sizeof(decltype(texCoordsPool)::value_type), MTL::ResourceStorageModeShared);
normalBuffer = device->newBuffer(normalsPool.size() * sizeof(decltype(normalsPool)::value_type), MTL::ResourceStorageModeShared);
modelRefsBuffer = device->newBuffer(refs.size() * sizeof(decltype(refs)::value_type), MTL::ResourceStorageModeShared);
if(directionalLights.size() > 0)
{
directionalLightBuffer = device->newBuffer(directionalLights.size() * sizeof(decltype(directionalLights)::value_type), MTL::ResourceStorageModeShared);
std::memcpy(directionalLightBuffer->contents(), directionalLights.data(), directionalLights.size() * sizeof(decltype(directionalLights)::value_type));
}
if(pointLights.size() > 0)
{
pointLightBuffer = device->newBuffer(pointLights.size() * sizeof(decltype(pointLights)::value_type), MTL::ResourceStorageModeShared);
std::memcpy(pointLightBuffer->contents(), pointLights.data(), pointLights.size() * sizeof(decltype(pointLights)::value_type));
}
std::memcpy(indicesBuffer->contents(), indicesPool.data(), indicesPool.size() * sizeof(decltype(indicesPool)::value_type));
std::memcpy(positionBuffer->contents(), positionPool.data(), positionPool.size() * sizeof(decltype(positionPool)::value_type));
std::memcpy(texCoordsBuffer->contents(), texCoordsPool.data(), texCoordsPool.size() * sizeof(decltype(texCoordsPool)::value_type));
std::memcpy(normalBuffer->contents(), normalsPool.data(), normalsPool.size() * sizeof(decltype(normalsPool)::value_type));
std::memcpy(modelRefsBuffer->contents(), refs.data(), refs.size() * sizeof(decltype(refs)::value_type));
MTL::AccelerationStructure** primitiveAccelerationStructures = new MTL::AccelerationStructure*[refs.size()];
for(uint i = 0; i < refs.size(); ++i)
{
MTL::AccelerationStructureTriangleGeometryDescriptor* descriptor = MTL::AccelerationStructureTriangleGeometryDescriptor::descriptor();
descriptor->setTriangleCount(refs[i].numIndices / 3);
descriptor->setIndexBuffer(indicesBuffer);
descriptor->setIndexBufferOffset(refs[i].indicesOffset);
descriptor->setVertexBufferOffset(refs[i].positionOffset);
descriptor->setVertexBuffer(positionBuffer);
descriptor->setIndexType(MTL::IndexTypeUInt32);
MTL::PrimitiveAccelerationStructureDescriptor* primitiveDescriptor = MTL::PrimitiveAccelerationStructureDescriptor::descriptor();
primitiveDescriptor->setGeometryDescriptors(NS::Array::array(descriptor));
primitiveAccelerationStructures[i] = device->newAccelerationStructure(primitiveDescriptor);
primitiveAccelerationStructures[i]->setLabel(NS::String::string("Primitive Structure", NS::ASCIIStringEncoding));
std::cout << primitiveAccelerationStructures[i]->debugDescription()->cString(NS::ASCIIStringEncoding) << std::endl;
}
NS::Array* primitiveArray = NS::Array::array((const NS::Object* const*)primitiveAccelerationStructures, refs.size());
std::cout << primitiveArray->debugDescription()->cString(NS::ASCIIStringEncoding) << std::endl;
instanceBuffer = device->newBuffer(sizeof(MTL::AccelerationStructureInstanceDescriptor) * refs.size(), MTL::ResourceOptionCPUCacheModeDefault);
MTL::AccelerationStructureInstanceDescriptor* instanceDescriptors = (MTL::AccelerationStructureInstanceDescriptor*)instanceBuffer->contents();
for(uint i = 0; i < refs.size(); ++i)
{
instanceDescriptors[i].transformationMatrix[0][0] = 1.0f;
instanceDescriptors[i].transformationMatrix[1][0] = 0.0f;
instanceDescriptors[i].transformationMatrix[2][0] = 0.0f;
instanceDescriptors[i].transformationMatrix[3][0] = 0.0f;
instanceDescriptors[i].transformationMatrix[0][1] = 0.0f;
instanceDescriptors[i].transformationMatrix[1][1] = 1.0f;
instanceDescriptors[i].transformationMatrix[2][1] = 0.0f;
instanceDescriptors[i].transformationMatrix[3][1] = 0.0f;
instanceDescriptors[i].transformationMatrix[0][2] = 0.0f;
instanceDescriptors[i].transformationMatrix[1][2] = 0.0f;
instanceDescriptors[i].transformationMatrix[2][2] = 1.0f;
instanceDescriptors[i].transformationMatrix[3][2] = 0.0f;
instanceDescriptors[i].accelerationStructureIndex = i;
instanceDescriptors[i].options = MTL::AccelerationStructureInstanceOptionOpaque;
instanceDescriptors[i].mask = 0xff;
}
MTL::InstanceAccelerationStructureDescriptor* accelDesc = MTL::InstanceAccelerationStructureDescriptor::descriptor();
accelDesc->setInstancedAccelerationStructures(primitiveArray);
accelDesc->setInstanceDescriptorBuffer(instanceBuffer);
accelDesc->setInstanceCount(refs.size());
accelerationStructure = device->newAccelerationStructure(accelDesc);
accelerationStructure->setLabel(NS::String::string("InstanceAccelerationStructure", NS::ASCIIStringEncoding));
std::cout << accelerationStructure->debugDescription()->cString(NS::ASCIIStringEncoding) << std::endl;
/*MTL::AccelerationStructureSizes accelSizes = device->accelerationStructureSizes(accelDesc);
MTL::AccelerationStructure* tempStructure = device->newAccelerationStructure(accelSizes.accelerationStructureSize);
MTL::Buffer* scratchBuffer = device->newBuffer(accelSizes.buildScratchBufferSize, MTL::StorageModeManaged);
MTL::CommandBuffer* cmdBuffer = queue->commandBuffer();
MTL::AccelerationStructureCommandEncoder* encoder = cmdBuffer->accelerationStructureCommandEncoder();
MTL::Buffer* compactedBuffer = device->newBuffer(sizeof(uint), MTL::ResourceOptionCPUCacheModeDefault);
encoder->buildAccelerationStructure(tempStructure, accelDesc, scratchBuffer, 0);
encoder->writeCompactedAccelerationStructureSize(tempStructure, compactedBuffer, 0);
encoder->endEncoding();
cmdBuffer->commit();
cmdBuffer->waitUntilCompleted();
uint compactedSize = *(uint*)compactedBuffer->contents();
accelerationStructure = device->newAccelerationStructure(compactedSize);
cmdBuffer = queue->commandBuffer();
encoder = cmdBuffer->accelerationStructureCommandEncoder();
encoder->copyAndCompactAccelerationStructure(tempStructure, accelerationStructure);
encoder->endEncoding();
cmdBuffer->commit();
cmdBuffer->waitUntilCompleted();*/
}
+28
View File
@@ -0,0 +1,28 @@
#pragma once
#include "scene/Scene.h"
#include <Foundation/Foundation.hpp>
#include <Metal/Metal.hpp>
#include <QuartzCore/QuartzCore.hpp>
class MetalScene : public Scene
{
public:
MetalScene(MTL::Device* device, MTL::CommandQueue* queue);
virtual ~MetalScene();
virtual void createRayTracingHierarchy() override;
MTL::Device* device;
MTL::CommandQueue* queue;
MTL::Buffer* indicesBuffer;
MTL::Buffer* positionBuffer;
MTL::Buffer* texCoordsBuffer;
MTL::Buffer* normalBuffer;
MTL::Buffer* modelRefsBuffer;
MTL::Buffer* directionalLightBuffer;
MTL::Buffer* pointLightBuffer;
MTL::Buffer* instanceBuffer;
MTL::AccelerationStructure* accelerationStructure;
};
+6
View File
@@ -0,0 +1,6 @@
#define NS_PRIVATE_IMPLEMENTATION
#define CA_PRIVATE_IMPLEMENTATION
#define MTL_PRIVATE_IMPLEMENTATION
#include <Foundation/Foundation.hpp>
#include <Metal/Metal.hpp>
#include <QuartzCore/QuartzCore.hpp>