微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

我在使用 Vulkan Ray-Tracing 在同一个 blas/tlas 上看到多个网格时遇到了一些问题

如何解决我在使用 Vulkan Ray-Tracing 在同一个 blas/tlas 上看到多个网格时遇到了一些问题

我正在尝试使用相同的 blas/tlas 加载多个网格,但看起来似乎不太正确。当它只有网格时它加载得很好,但我尝试了多个网格,它看起来像这样。我不确定使用 blas 和 tlas 中的设置的方式是否有问题,或者是否有多个网格改变了我在着色器中解压缩顶点的方式。

我也试过查看 nvidia 光线追踪教程和 sacha williams 光线追踪代码,看起来它们都只使用一个网格,每个 blas/tlas 在场景中加载为一个网格。对于样本来说,这看起来不错,但是当尝试通过变换、旋转或缩放来操纵每个网格个体时,这会不会导致问题?

enter image description here

这是blas和tlas代码

void RayTraceRenderer::createBottomLevelaccelerationStructure(VulkanEngine& engine)
{
std::shared_ptr<TextureManager> manager = std::make_shared<TextureManager>(engine);
std::shared_ptr<Texture> texture = std::make_shared<Texture>();

model = Model(engine,manager,"C:/Users/dotha/source/repos/Vulkangraphics/Models/vulkanscene_shadow.obj",RayTraceDescriptorSetLayout,1,texture);


texture2D = Texture2D(engine,VK_FORMAT_R8G8B8A8_UnorM,"C:/Users/dotha/source/repos/Vulkangraphics/texture/Brick_diffuSEOriginal.bmp",1);
normalMap = Texture2D(engine,"C:/Users/dotha/source/repos/Vulkangraphics/texture/Brick_normal.bmp",1);

std::vector<VkaccelerationStructureGeometryKHR> GeometryaccelerationStructureList;


for (int x = 0; x < 11; x++)
{
    auto mesh = model.SubMeshList[x];

    glm::mat4 transformMatrix = glm::mat4(1.0f);
    transformMatrix = glm::rotate(transformMatrix,glm::radians(180.0f),glm::vec3(0.0f,0.0f,1.0f));

    vertexBuffer.CreateBuffer(engine,mesh.VertexList.size() * sizeof(Vertex),VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_acceleration_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR,VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,mesh.VertexList.data());
    indexBuffer.CreateBuffer(engine,mesh.IndexList.size() * sizeof(uint32_t),mesh.IndexList.data());
    transformBuffer.CreateBuffer(engine,sizeof(glm::mat4),VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_acceleration_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR,&transformMatrix);

    VkDeviceOrHostAddressConstKHR vertexBufferDeviceAddress = engine.BufferToDeviceAddress(vertexBuffer.Buffer);
    VkDeviceOrHostAddressConstKHR indexBufferDeviceAddress = engine.BufferToDeviceAddress(indexBuffer.Buffer);
    VkDeviceOrHostAddressConstKHR transformBufferDeviceAddress = engine.BufferToDeviceAddress(transformBuffer.Buffer);

    VkaccelerationStructureGeometryKHR GeometryaccelerationStructure = {};
    GeometryaccelerationStructure.sType = VK_STRUCTURE_TYPE_acceleration_STRUCTURE_GEOMETRY_KHR;
    GeometryaccelerationStructure.flags = VK_GEOMETRY_OPAQUE_BIT_KHR;
    GeometryaccelerationStructure.geometryType = VK_GEOMETRY_TYPE_TRIANGLES_KHR;
    GeometryaccelerationStructure.geometry.triangles.sType = VK_STRUCTURE_TYPE_acceleration_STRUCTURE_GEOMETRY_TRIANGLES_DATA_KHR;
    GeometryaccelerationStructure.geometry.triangles.vertexFormat = VK_FORMAT_R32G32B32_SFLOAT;
    GeometryaccelerationStructure.geometry.triangles.vertexData = vertexBufferDeviceAddress;
    GeometryaccelerationStructure.geometry.triangles.maxVertex = mesh.VertexList.size() * sizeof(Vertex);
    GeometryaccelerationStructure.geometry.triangles.vertexStride = sizeof(Vertex);
    GeometryaccelerationStructure.geometry.triangles.indexType = VK_INDEX_TYPE_UINT32;
    GeometryaccelerationStructure.geometry.triangles.indexData = indexBufferDeviceAddress;
    GeometryaccelerationStructure.geometry.triangles.transformData.deviceAddress = 0;
    GeometryaccelerationStructure.geometry.triangles.transformData.hostAddress = nullptr;
    GeometryaccelerationStructure.geometry.triangles.transformData = transformBufferDeviceAddress;
    GeometryaccelerationStructureList.emplace_back(GeometryaccelerationStructure);

    VkaccelerationStructurebuildrangeInfoKHR Acclerationbuildrange = {};
    Acclerationbuildrange.primitiveCount = static_cast<uint32_t>(mesh.IndexList.size()) / 3;
    Acclerationbuildrange.primitiveOffset = x;
    Acclerationbuildrange.firstVertex = 0;
    Acclerationbuildrange.transformOffset = 0;
    AcclerationbuildrangeList.emplace_back(Acclerationbuildrange);
}

VkaccelerationStructureBuildGeometryInfoKHR accelerationBuildGeometry = {};
accelerationBuildGeometry.sType = VK_STRUCTURE_TYPE_acceleration_STRUCTURE_BUILD_GEOMETRY_INFO_KHR;
accelerationBuildGeometry.type = VK_acceleration_STRUCTURE_TYPE_BottOM_LEVEL_KHR;
accelerationBuildGeometry.flags = VK_BUILD_acceleration_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR;
accelerationBuildGeometry.geometryCount = static_cast<uint32_t>(GeometryaccelerationStructureList.size());
accelerationBuildGeometry.pGeometries = GeometryaccelerationStructureList.data();


maxPrimCount.resize(AcclerationbuildrangeList.size());
for (auto x = 0; x < AcclerationbuildrangeList.size(); x++)
{
    maxPrimCount[x] = AcclerationbuildrangeList[x].primitiveCount;
}

VkaccelerationStructureBuildSizesInfoKHR accelerationBuildInfo = {};
accelerationBuildInfo.sType = VK_STRUCTURE_TYPE_acceleration_STRUCTURE_BUILD_SIZES_INFO_KHR;
vkGetaccelerationStructureBuildSizesKHR(engine.Device,VK_acceleration_STRUCTURE_BUILD_TYPE_DEVICE_KHR,&accelerationBuildGeometry,maxPrimCount.data(),&accelerationBuildInfo);

bottomLevelAS.CreateBuffer(engine,accelerationBuildInfo.accelerationStructureSize,VK_BUFFER_USAGE_acceleration_STRUCTURE_STORAGE_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);

VkaccelerationStructureCreateInfoKHR accelerationStructureInfo = {};
accelerationStructureInfo.sType = VK_STRUCTURE_TYPE_acceleration_STRUCTURE_CREATE_INFO_KHR;
accelerationStructureInfo.buffer = bottomLevelAS.Buffer;
accelerationStructureInfo.size = accelerationBuildInfo.accelerationStructureSize;
accelerationStructureInfo.type = VK_acceleration_STRUCTURE_TYPE_BottOM_LEVEL_KHR;
VkResult result = vkCreateaccelerationStructureKHR(engine.Device,&accelerationStructureInfo,nullptr,&bottomLevelAS.BufferHandle);

VulkanBuffer ScratchBuffer = VulkanBuffer();
ScratchBuffer.CreateBuffer(engine,accelerationBuildInfo.buildScratchSize,VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
VkDeviceOrHostAddressConstKHR ScratchBufferDeviceAddress = engine.BufferToDeviceAddress(ScratchBuffer.GetBuffer());

VkaccelerationStructureBuildGeometryInfoKHR accelerationBuildGeometryInfo = {};
accelerationBuildGeometryInfo.sType = VK_STRUCTURE_TYPE_acceleration_STRUCTURE_BUILD_GEOMETRY_INFO_KHR;
accelerationBuildGeometryInfo.type = VK_acceleration_STRUCTURE_TYPE_BottOM_LEVEL_KHR;
accelerationBuildGeometryInfo.flags = VK_BUILD_acceleration_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR;
accelerationBuildGeometryInfo.mode = VK_BUILD_acceleration_STRUCTURE_MODE_BUILD_KHR;
accelerationBuildGeometryInfo.dstaccelerationStructure = bottomLevelAS.BufferHandle;
accelerationBuildGeometryInfo.geometryCount = static_cast<uint32_t>(GeometryaccelerationStructureList.size());
accelerationBuildGeometryInfo.pGeometries = GeometryaccelerationStructureList.data();
accelerationBuildGeometryInfo.scratchData.deviceAddress = ScratchBufferDeviceAddress.deviceAddress;

VkCommandBufferAllocateInfo commandBufferAllocateInfo{};
commandBufferAllocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
commandBufferAllocateInfo.commandPool = engine.GetRenderCommandPool();
commandBufferAllocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
commandBufferAllocateInfo.commandBufferCount = 1;

VkCommandBuffer cmdBuffer;
vkAllocateCommandBuffers(engine.Device,&commandBufferAllocateInfo,&cmdBuffer);

VkCommandBufferBeginInfo cmdBufferBeginInfo{};
cmdBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;

auto asdf = AcclerationbuildrangeList.data();
vkBeginCommandBuffer(cmdBuffer,&cmdBufferBeginInfo);
vkCmdBuildaccelerationStructuresKHR(cmdBuffer,&accelerationBuildGeometryInfo,&asdf);
vkEndCommandBuffer(cmdBuffer);

VkSubmitInfo submitInfo{};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &cmdBuffer;
VkFenceCreateInfo fenceCreateInfo{};
fenceCreateInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
fenceCreateInfo.flags = 0;

VkFence fence;
vkCreateFence(engine.Device,&fenceCreateInfo,&fence);
vkQueueSubmit(engine.GraphicsQueue,&submitInfo,fence);
vkWaitForFences(engine.Device,&fence,VK_TRUE,100000000000);
vkDestroyFence(engine.Device,fence,nullptr);
vkFreeCommandBuffers(engine.Device,engine.GetRenderCommandPool(),&cmdBuffer);


VkaccelerationStructureDeviceAddressInfoKHR accelerationDeviceAddressInfo{};
accelerationDeviceAddressInfo.sType = VK_STRUCTURE_TYPE_acceleration_STRUCTURE_DEVICE_ADDRESS_INFO_KHR;
accelerationDeviceAddressInfo.accelerationStructure = bottomLevelAS.BufferHandle;
bottomLevelAS.BufferDeviceAddress = vkGetaccelerationStructureDeviceAddressKHR(engine.Device,&accelerationDeviceAddressInfo);

ScratchBuffer.DestoryBuffer(engine);
}
void RayTraceRenderer::createtopLevelaccelerationStructure(VulkanEngine& engine)
{
VkTransformMatrixKHR transformMatrix = {
        1.0f,1.0f,0.0f
};

VkaccelerationStructureInstanceKHR accelerationInstance = {};
accelerationInstance.transform = transformMatrix;
accelerationInstance.instanceCustomIndex = 0;
accelerationInstance.mask = 0xFF;
accelerationInstance.instanceShaderBindingTableRecordOffset = 0;
accelerationInstance.flags = VK_GEOMETRY_INSTANCE_TRIANGLE_FACING_CULL_disABLE_BIT_KHR;
accelerationInstance.accelerationStructureReference = bottomLevelAS.BufferDeviceAddress;

VulkanBuffer instancesBuffer;
instancesBuffer.CreateBuffer(engine,sizeof(VkaccelerationStructureInstanceKHR),&accelerationInstance);

VkDeviceOrHostAddressConstKHR TopLevelaccelerationInstanceBufferDeviceAddress{};
TopLevelaccelerationInstanceBufferDeviceAddress.deviceAddress = engine.BufferToDeviceAddress(instancesBuffer.Buffer).deviceAddress;

VkaccelerationStructureGeometryKHR accelerationGeometry = {};
accelerationGeometry.sType = VK_STRUCTURE_TYPE_acceleration_STRUCTURE_GEOMETRY_KHR;
accelerationGeometry.geometryType = VK_GEOMETRY_TYPE_INSTANCES_KHR;
accelerationGeometry.flags = VK_GEOMETRY_OPAQUE_BIT_KHR;
accelerationGeometry.geometry.instances.sType = VK_STRUCTURE_TYPE_acceleration_STRUCTURE_GEOMETRY_INSTANCES_DATA_KHR;
accelerationGeometry.geometry.instances.arrayOfPointers = VK_FALSE;
accelerationGeometry.geometry.instances.data = TopLevelaccelerationInstanceBufferDeviceAddress;

VkaccelerationStructureBuildGeometryInfoKHR accelerationStructureBuildGeometry = {};
accelerationStructureBuildGeometry.sType = VK_STRUCTURE_TYPE_acceleration_STRUCTURE_BUILD_GEOMETRY_INFO_KHR;
accelerationStructureBuildGeometry.type = VK_acceleration_STRUCTURE_TYPE_TOP_LEVEL_KHR;
accelerationStructureBuildGeometry.flags = VK_BUILD_acceleration_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR;
accelerationStructureBuildGeometry.geometryCount = 1;
accelerationStructureBuildGeometry.pGeometries = &accelerationGeometry;


VkaccelerationStructureBuildSizesInfoKHR accelerationBuildInfo = {};
accelerationBuildInfo.sType = VK_STRUCTURE_TYPE_acceleration_STRUCTURE_BUILD_SIZES_INFO_KHR;
vkGetaccelerationStructureBuildSizesKHR(engine.Device,&accelerationStructureBuildGeometry,&accelerationBuildInfo);

topLevelAS.CreateBuffer(engine,VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);

VkaccelerationStructureCreateInfoKHR accelerationStructureInfo = {};
accelerationStructureInfo.sType = VK_STRUCTURE_TYPE_acceleration_STRUCTURE_CREATE_INFO_KHR;
accelerationStructureInfo.buffer = topLevelAS.Buffer;
accelerationStructureInfo.size = accelerationBuildInfo.accelerationStructureSize;
accelerationStructureInfo.type = VK_acceleration_STRUCTURE_TYPE_TOP_LEVEL_KHR;
VkResult result = vkCreateaccelerationStructureKHR(engine.Device,&topLevelAS.BufferHandle);

VulkanBuffer ScratchBuffer = VulkanBuffer();
ScratchBuffer.CreateBuffer(engine,VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
VkDeviceOrHostAddressConstKHR ScratchBufferDeviceAddress = engine.BufferToDeviceAddress(ScratchBuffer.GetBuffer());

VkaccelerationStructureBuildGeometryInfoKHR accelerationBuildGeometryInfo = {};
accelerationBuildGeometryInfo.sType = VK_STRUCTURE_TYPE_acceleration_STRUCTURE_BUILD_GEOMETRY_INFO_KHR;
accelerationBuildGeometryInfo.type = VK_acceleration_STRUCTURE_TYPE_TOP_LEVEL_KHR;
accelerationBuildGeometryInfo.flags = VK_BUILD_acceleration_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR;
accelerationBuildGeometryInfo.mode = VK_BUILD_acceleration_STRUCTURE_MODE_BUILD_KHR;
accelerationBuildGeometryInfo.dstaccelerationStructure = topLevelAS.BufferHandle;
accelerationBuildGeometryInfo.geometryCount = 1;
accelerationBuildGeometryInfo.pGeometries = &accelerationGeometry;
accelerationBuildGeometryInfo.scratchData.deviceAddress = ScratchBufferDeviceAddress.deviceAddress;

std::vector<VkaccelerationStructurebuildrangeInfoKHR*> AcclerationbuildrangeList;
VkaccelerationStructurebuildrangeInfoKHR Acclerationbuildrange = {};
Acclerationbuildrange.primitiveCount = static_cast<uint32_t>(maxPrimCount.size());
Acclerationbuildrange.primitiveOffset = 0;
Acclerationbuildrange.firstVertex = 0;
Acclerationbuildrange.transformOffset = 0;
AcclerationbuildrangeList.emplace_back(&Acclerationbuildrange);

VkCommandBufferAllocateInfo commandBufferAllocateInfo{};
commandBufferAllocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
commandBufferAllocateInfo.commandPool = engine.GetRenderCommandPool();
commandBufferAllocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
commandBufferAllocateInfo.commandBufferCount = 1;

VkCommandBuffer cmdBuffer;
vkAllocateCommandBuffers(engine.Device,&cmdBuffer);

VkCommandBufferBeginInfo cmdBufferBeginInfo{};
cmdBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;

vkBeginCommandBuffer(cmdBuffer,AcclerationbuildrangeList.data());
vkEndCommandBuffer(cmdBuffer);

VkSubmitInfo submitInfo{};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &cmdBuffer;
VkFenceCreateInfo fenceCreateInfo{};
fenceCreateInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
fenceCreateInfo.flags = 0;

VkFence fence;
vkCreateFence(engine.Device,&cmdBuffer);

VkaccelerationStructureDeviceAddressInfoKHR accelerationDeviceAddressInfo{};
accelerationDeviceAddressInfo.sType = VK_STRUCTURE_TYPE_acceleration_STRUCTURE_DEVICE_ADDRESS_INFO_KHR;
accelerationDeviceAddressInfo.accelerationStructure = topLevelAS.BufferHandle;
topLevelAS.BufferDeviceAddress = vkGetaccelerationStructureDeviceAddressKHR(engine.Device,&accelerationDeviceAddressInfo);

ScratchBuffer.DestoryBuffer(engine);

}

和命中着色器代码

#version 460
#extension GL_EXT_ray_tracing : require
#extension GL_EXT_nonuniform_qualifier : enable

layout(location = 0) rayPayloadInEXT vec3 hitValue;
layout(location = 2) rayPayloadEXT bool shadowed;
hitAttributeEXT vec3 attribs;

layout(binding = 0,set = 0) uniform accelerationStructureEXT topLevelAS;
layout(binding = 2,set = 0) uniform UBO 
{
    mat4 viewInverse;
    mat4 projInverse;
    mat4 modelInverse;
    vec4 lightPos;
    vec4 viewPos;
    int vertexSize;
} ubo;
layout(binding = 3,set = 0) buffer Vertices { vec4 v[]; } vertices;
layout(binding = 4,set = 0) buffer Indices { uint i[]; } indices;
layout(binding = 5,set = 0) uniform sampler2D DiffuseMap;
layout(binding = 6,set = 0) uniform sampler2D normalMap;

struct Vertex
{
  vec3 pos;
  vec3 normal;
  vec2 uv;
  vec4 tangent;
  vec4 BiTangant;
  vec4 Color;
  vec4 BoneID;
  vec4 BoneWeights;
};

Vertex unpack(uint index)
{
// Unpack the vertices from the SSBO using the glTF vertex structure
// The multiplier is the size of the vertex divided by four float components (=16 bytes)
const int m = ubo.vertexSize / 16;

vec4 d0 = vertices.v[m * index + 0];
vec4 d1 = vertices.v[m * index + 1];
vec4 d2 = vertices.v[m * index + 2];

Vertex v;
v.pos = d0.xyz;
v.normal = vec3(d0.w,d1.x,d1.y);
v.Color = vec4(d2.x,d2.y,d2.z,1.0);
v.uv = vec2(d0.x,d0.y);
v.tangent = vec4(d0.w,d1.y,0.0f);

return v;
}

void main()
{
ivec3 index = ivec3(indices.i[3 * gl_PrimitiveID],indices.i[3 * gl_PrimitiveID + 1],indices.i[3 * gl_PrimitiveID + 2]);

Vertex v0 = unpack(index.x);
Vertex v1 = unpack(index.y);
Vertex v2 = unpack(index.z);

const vec3 barycentricCoords = vec3(1.0f - attribs.x - attribs.y,attribs.x,attribs.y);
vec3 worldPos = v0.pos * barycentricCoords.x + v1.pos * barycentricCoords.y + v2.pos * barycentricCoords.z;
vec3 normal2 = normalize(v0.normal * barycentricCoords.x + v1.normal * barycentricCoords.y + v2.normal * barycentricCoords.z);
vec2 texCoord = v0.uv * barycentricCoords.x + v1.uv * barycentricCoords.y + v2.uv * barycentricCoords.z;
vec3 tangent = v0.tangent.xyz * barycentricCoords.x + v1.tangent.xyz * barycentricCoords.y + v2.tangent.xyz * barycentricCoords.z;

mat3 normalMatrix = transpose(inverse(mat3(ubo.modelInverse)));
vec3 T = normalize(normalMatrix * tangent);
vec3 N = normalize(normalMatrix * normal2);
T = normalize(T - dot(T,N) * N);
vec3 B = cross(N,T);

mat3 TBN = transpose(mat3(T,B,N));    
vec3 TangentLightPos = TBN * ubo.lightPos.xyz;
vec3 TangentViewPos  = TBN * ubo.viewPos.xyz;
vec3 TangentFragPos  = TBN * worldPos;

vec3 normal = texture(normalMap,texCoord).rgb;
normal = normalize(normal * 2.0 - 1.0);  


vec3 color = texture(DiffuseMap,texCoord).rgb;
vec3 ambient = 0.1 * color;

vec3 lightDir = normalize(TangentLightPos - TangentFragPos);
float diff = max(dot(lightDir,normal),0.0);
vec3 diffuse = diff * color;

//vec3 lightVector = normalize(ubo.lightPos.xyz);
//float dot_product = max(dot(lightVector,0.2);
hitValue = ambient + diffuse;

// Shadow casting
float tmin = 0.001;
float tmax = 10000.0;
vec3 origin = gl_WorldRayOriginEXT + gl_WorldRayDirectionEXT * gl_HitTEXT;
shadowed = true;  
// Trace shadow ray and offset indices to match shadow hit/miss shader group indices
traceRayEXT(topLevelAS,gl_RayFlagsTerminateOnFirstHitEXT | gl_RayFlagsOpaqueEXT | gl_RayFlagsSkipClosestHitShaderEXT,0xFF,origin,tmin,lightDir,tmax,2);
if (shadowed) {
    hitValue *= 0.3;
}
else
{
    vec3 viewDir = normalize(TangentLightPos - TangentFragPos);
    vec3 reflectDir = reflect(-lightDir,normal);
    vec3 halfwayDir = normalize(lightDir + viewDir);  
    float spec = pow(max(dot(normal,halfwayDir),0.0),32.0);

    vec3 specular = vec3(0.2) * spec;

    hitValue += specular;
}

}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。