@meimu/mutracer 0.1.4 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/bvh.worker.js +1 -1
- package/dist/mutracer.umd.js +1425 -1421
- package/docs/api.md +2 -1
- package/docs/materials.md +17 -26
- package/docs/threejs.md +5 -5
- package/docs/viewer.md +36 -3
- package/lib/oidn.d.ts +1 -1
- package/lib/oidnBundledWeights.cdn.d.ts +2 -2
- package/lib/renderer/WGPUFullQuadPass.js +3 -3
- package/lib/renderer/WGPUJNLMDenoiser.js +35 -35
- package/lib/renderer/WGPURayTraceOutput.js +4 -4
- package/lib/renderer/WGPURayTracePass.js +5 -3
- package/lib/renderer/WGPURayTracePass.js.map +1 -1
- package/lib/renderer/WGPURenderingPipeline.js +7 -7
- package/lib/renderer/WebGLJNLMDenoiser.js +36 -36
- package/lib/renderer/WebGLRayTracePass.js +1 -0
- package/lib/renderer/WebGLRayTracePass.js.map +1 -1
- package/lib/renderer/glsl/chunks/cameraRay.glsl.js +8 -8
- package/lib/renderer/glsl/chunks/envMap.glsl.js +26 -26
- package/lib/renderer/glsl/chunks/intersect.glsl.js +141 -139
- package/lib/renderer/glsl/chunks/intersect.glsl.js.map +1 -1
- package/lib/renderer/glsl/chunks/materialBuffer.glsl.js +56 -55
- package/lib/renderer/glsl/chunks/materialBuffer.glsl.js.map +1 -1
- package/lib/renderer/glsl/chunks/openPbrBsdf.glsl.js +144 -144
- package/lib/renderer/glsl/chunks/openPbrBsdf.glsl.js.map +1 -1
- package/lib/renderer/glsl/chunks/random.glsl.js +1 -1
- package/lib/renderer/glsl/chunks/rayTraceCore.glsl.js +14 -14
- package/lib/renderer/glsl/chunks/rayTraceCore.glsl.js.map +1 -1
- package/lib/renderer/glsl/chunks/sampleLight.glsl.js +19 -19
- package/lib/renderer/glsl/chunks/sampleMaterial.glsl.js +46 -46
- package/lib/renderer/glsl/chunks/sampleMaterial.glsl.js.map +1 -1
- package/lib/renderer/glsl/chunks/sampleShadowCatcher.glsl.js +6 -6
- package/lib/renderer/glsl/chunks/sampling.glsl.js +33 -33
- package/lib/renderer/glsl/chunks/sobol.glsl.js +7 -7
- package/lib/renderer/glsl/chunks/uvProjection.glsl.js +1 -1
- package/lib/renderer/glsl/depthQuery.frag.js +2 -2
- package/lib/renderer/glsl/rayTrace.frag.js +67 -67
- package/lib/renderer/glsl/shadowCatcher.frag.js +3 -3
- package/lib/renderer/glsl/webglPipeline.frag.js +26 -26
- package/lib/renderer/scratchSampling.js +16 -16
- package/lib/renderer/wgsl/chunks/envMap.wgsl.js +14 -14
- package/lib/renderer/wgsl/chunks/intersect.wgsl.js +178 -178
- package/lib/renderer/wgsl/chunks/intersect.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/chunks/intersectionAlpha.wgsl.js +3 -3
- package/lib/renderer/wgsl/chunks/materialBuffer.wgsl.js +24 -23
- package/lib/renderer/wgsl/chunks/materialBuffer.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/chunks/openPbrBsdf.wgsl.js +160 -160
- package/lib/renderer/wgsl/chunks/pdm_displacement.wgsl.js +12 -12
- package/lib/renderer/wgsl/chunks/rayTraceCore.wgsl.js +5 -5
- package/lib/renderer/wgsl/chunks/sampleLight.wgsl.js +44 -44
- package/lib/renderer/wgsl/chunks/sampleMaterial.wgsl.js +69 -69
- package/lib/renderer/wgsl/chunks/sampleShadowCatcher.wgsl.js +3 -3
- package/lib/renderer/wgsl/chunks/sampling.wgsl.js +33 -33
- package/lib/renderer/wgsl/chunks/sobol.wgsl.js +7 -7
- package/lib/renderer/wgsl/chunks/uvProjection.wgsl.js +1 -1
- package/lib/renderer/wgsl/compose.frag.wgsl.js +54 -54
- package/lib/renderer/wgsl/customDenoise.wgsl.js +6 -6
- package/lib/renderer/wgsl/depthQuery.wgsl.js +2 -2
- package/lib/renderer/wgsl/offlineAdaptive.compute.js +5 -5
- package/lib/renderer/wgsl/rayTrace.compute.js +103 -103
- package/lib/scene/OpenPBRMaterial.d.ts +1 -1
- package/lib/scene/OpenPBRMaterial.js +1 -1
- package/lib/scene/OpenPBRMaterial.js.map +1 -1
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/lib/version.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"intersect.glsl.js","sourceRoot":"","sources":["../../../../src/renderer/glsl/chunks/intersect.glsl.ts"],"names":[],"mappings":"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","sourcesContent":["import { mapAlphaUsageConstants } from '../../materialConstants.js';\n// Portions adapted from ray-tracing-renderer@0.10.15\n// (https://github.com/hoverinc/ray-tracing-renderer).\n// Copyright (c) 2019 HOVER, MIT license; see LICENSES/RayTracingRenderer-MIT.txt.\n\nimport { INSTANCE_VEC4_STRIDE } from '../../bvhAccel.js';\nimport { createShaderMixin, glsl, Shader } from '../../shader.js';\n\nfunction getBVHTraverseCode(\n handleHitCode: string,\n skipTriangleCode = '',\n initialTMax = 'RAY_MAX_DISTANCE'\n) {\n const intersectTriangles = glsl`\n ivec3 vertexOffset = ivec3(floatBitsToInt(fetchIns(insIdx * ${INSTANCE_VEC4_STRIDE}).y));\n for (int k = 0; k < primitiveCount; k++) {\n int triangleIndex = primitiveOffset + k;\n ${skipTriangleCode}\n vec4 r3 = fetchBVH(triangleIndex);\n ivec3 index = floatBitsToInt(r3.xyz) + vertexOffset;\n Triangle tri = Triangle(\n fetchData(positionBuffer, index.x, vertexColumns).xyz,\n fetchData(positionBuffer, index.y, vertexColumns).xyz,\n fetchData(positionBuffer, index.z, vertexColumns).xyz\n );\n TriangleIntersect hit = intersectTriangle(o, d, tri, maxDim, shear, tMax);\n\n if (hit.t > 0.0) {\n tMax = hit.t;\n vec3 e1 = tri.p1 - tri.p0;\n vec3 e2 = tri.p2 - tri.p0;\n vec3 faceNormal = normalize(cross(e1, e2));\n int sideMaterialIndex = int(unpackMaterialIndex(\n floatBitsToInt(fetchIns(insIdx * ${INSTANCE_VEC4_STRIDE}).z)\n ).x);\n bool materialDoubleSided = fetchMatData(\n sideMaterialIndex * MAT_DATA_SIZE + MAT_ROW_ALPHA\n ).z > 0.5;\n if (!doubleSided && !materialDoubleSided && dot(faceNormal, d) > 0.0) {\n continue;\n }\n\n ${handleHitCode}\n }\n }\n`;\n return glsl`\n ivec2 stack[STACK_SIZE];\n int top = -1;\n int prevInsIdx = -1;\n float tMax = ${initialTMax};\n\n vec3 o = ray.o;\n vec3 d = ray.d;\n\n bool isHit = false;\n vec3 hitFaceNormal;\n vec3 hitBarycentric;\n ivec3 hitIndex;\n\n Triangle hitTri;\n\n // TODO not sure why. but do root node check here will cause rendering wrong in windows.\n top = 0;\n stack[0] = ivec2(0, -1);\n\n int iter = 0;\n while(top >= 0 && iter < 5000) {\n iter++;\n ivec2 node = stack[top--];\n\n int insIdx = node.y;\n\n if (insIdx != prevInsIdx) {\n if (insIdx < 0) {\n o = ray.o;\n d = ray.d;\n }\n else {\n mat4 w2l = mat4(\n fetchIns(insIdx * ${INSTANCE_VEC4_STRIDE} + 1),\n fetchIns(insIdx * ${INSTANCE_VEC4_STRIDE} + 2),\n fetchIns(insIdx * ${INSTANCE_VEC4_STRIDE} + 3),\n fetchIns(insIdx * ${INSTANCE_VEC4_STRIDE} + 4)\n );\n o = (w2l * vec4(ray.o, 1.0)).xyz;\n d = (w2l * vec4(ray.d, 0.0)).xyz;\n }\n maxDim = maxDimension(abs(d));\n shear = getShear(d, maxDim);\n prevInsIdx = insIdx;\n }\n\n int bufferOff = insIdx < 0 ? 0 : blasOffset;\n uint encodedBlasOffset = insIdx < 0 ? 0u : floatBitsToUint(fetchIns(insIdx * ${INSTANCE_VEC4_STRIDE}).x);\n int blasLayout = int(encodedBlasOffset >> 30u);\n int blasInsOffset = int(encodedBlasOffset & 0x3fffffffu);\n int primitiveCount = 0;\n int primitiveOffset = 0;\n\n if (blasLayout == 3) {\n uint nodeInfo = uint(node.x);\n if ((nodeInfo & 0x80000000u) != 0u) {\n primitiveCount = int((nodeInfo >> 23u) & 0xffu);\n primitiveOffset = bufferOff + blasInsOffset + int(nodeInfo & 0x007fffffu);\n }\n else {\n int i = node.x + bufferOff;\n vec4 compressed0 = fetchBVH(i);\n vec4 compressed1 = fetchBVH(i + 1);\n vec4 compressed2 = fetchBVH(i + 2);\n vec4 compressed3 = fetchBVH(i + 3);\n vec4 compressed4 = fetchBVH(i + 4);\n uint header = floatBitsToUint(compressed0.w);\n int exponentX = int(header << 24u) >> 24;\n int exponentY = int((header >> 8u) << 24u) >> 24;\n int exponentZ = int((header >> 16u) << 24u) >> 24;\n vec3 quantizationScale = vec3(\n uintBitsToFloat(uint(exponentX + 127) << 23u),\n uintBitsToFloat(uint(exponentY + 127) << 23u),\n uintBitsToFloat(uint(exponentZ + 127) << 23u)\n );\n uint childBase = floatBitsToUint(compressed1.x);\n uint triangleBase = floatBitsToUint(compressed1.y);\n uint internalMask = header >> 24u;\n uint octant = (d.x < 0.0 ? 4u : 0u) |\n (d.y < 0.0 ? 2u : 0u) |\n (d.z < 0.0 ? 1u : 0u);\n uint quantizedWords[12] = uint[12](\n floatBitsToUint(compressed2.x), floatBitsToUint(compressed2.y),\n floatBitsToUint(compressed2.z), floatBitsToUint(compressed2.w),\n floatBitsToUint(compressed3.x), floatBitsToUint(compressed3.y),\n floatBitsToUint(compressed3.z), floatBitsToUint(compressed3.w),\n floatBitsToUint(compressed4.x), floatBitsToUint(compressed4.y),\n floatBitsToUint(compressed4.z), floatBitsToUint(compressed4.w)\n );\n\n for (int order = 7; order >= 0; order--) {\n uint slot = uint(order) ^ octant;\n uint metaWord = slot >= 4u\n ? floatBitsToUint(compressed1.w)\n : floatBitsToUint(compressed1.z);\n uint meta = (metaWord >> ((slot & 3u) * 8u)) & 0xffu;\n if (meta == 0u) continue;\n uint byteShift = (slot & 3u) * 8u;\n uint wordOffset = slot >> 2u;\n vec3 quantizedMin = vec3(\n float((quantizedWords[wordOffset] >> byteShift) & 0xffu),\n float((quantizedWords[2u + wordOffset] >> byteShift) & 0xffu),\n float((quantizedWords[4u + wordOffset] >> byteShift) & 0xffu)\n );\n vec3 quantizedMax = vec3(\n float((quantizedWords[6u + wordOffset] >> byteShift) & 0xffu),\n float((quantizedWords[8u + wordOffset] >> byteShift) & 0xffu),\n float((quantizedWords[10u + wordOffset] >> byteShift) & 0xffu)\n );\n vec3 childMin = compressed0.xyz + quantizedMin * quantizationScale;\n vec3 childMax = compressed0.xyz + quantizedMax * quantizationScale;\n if (intersectBox(o, d, childMin, childMax, tMax) <= 0.0) continue;\n uint localOffset = meta & 0x1fu;\n if (localOffset >= 24u) {\n uint precedingMask = internalMask & ((1u << slot) - 1u);\n uint internalIndex = childBase + countBvhBits(precedingMask);\n stack[++top] = ivec2(\n blasInsOffset + int(internalIndex * 5u), insIdx\n );\n }\n else {\n uint triangleCount = countBvhBits(meta >> 5u);\n uint primitiveAddress = triangleBase + localOffset;\n uint leafInfo = 0x80000000u |\n (triangleCount << 23u) |\n (primitiveAddress & 0x007fffffu);\n stack[++top] = ivec2(int(leafInfo), insIdx);\n }\n }\n }\n }\n else if (blasLayout == 2) {\n int i = node.x + bufferOff;\n vec4 gpu0 = fetchBVH(i);\n vec4 gpu1 = fetchBVH(i + 1);\n vec4 gpu2 = fetchBVH(i + 2);\n vec4 gpu3 = fetchBVH(i + 3);\n primitiveCount = int(floatBitsToUint(gpu2.w));\n if (primitiveCount > 0) {\n primitiveOffset = bufferOff + blasInsOffset + int(floatBitsToUint(gpu3.w));\n }\n else {\n int left = blasInsOffset + int(floatBitsToUint(gpu0.w) * 4u);\n int right = blasInsOffset + int(floatBitsToUint(gpu1.w) * 4u);\n float d0 = intersectBox(o, d, gpu0.xyz, gpu1.xyz, tMax);\n float d1 = intersectBox(o, d, gpu2.xyz, gpu3.xyz, tMax);\n if (d0 > d1) {\n if (d0 > 0.0) stack[++top] = ivec2(left, insIdx);\n if (d1 > 0.0) stack[++top] = ivec2(right, insIdx);\n }\n else {\n if (d1 > 0.0) stack[++top] = ivec2(right, insIdx);\n if (d0 > 0.0) stack[++top] = ivec2(left, insIdx);\n }\n }\n }\n else if (blasLayout == 1) {\n uint nodeInfo = uint(node.x);\n if ((nodeInfo & 0x80000000u) != 0u) {\n primitiveCount = int((nodeInfo >> 23u) & 0xffu);\n primitiveOffset = bufferOff + blasInsOffset + int(nodeInfo & 0x007fffffu);\n }\n else {\n int i = node.x + bufferOff;\n vec4 wide0 = fetchBVH(i);\n vec4 wide1 = fetchBVH(i + 1);\n vec4 wide2 = fetchBVH(i + 2);\n vec4 wide3 = fetchBVH(i + 3);\n vec3 nodeMin = wide0.xyz;\n vec3 nodeExtent = vec3(wide0.w, wide1.x, wide1.y);\n uint childInfos[4] = uint[4](\n floatBitsToUint(wide1.z), floatBitsToUint(wide1.w),\n floatBitsToUint(wide2.x), floatBitsToUint(wide2.y)\n );\n vec4 childMinX = unpackBvh4Bounds(floatBitsToUint(wide2.z));\n vec4 childMinY = unpackBvh4Bounds(floatBitsToUint(wide2.w));\n vec4 childMinZ = unpackBvh4Bounds(floatBitsToUint(wide3.x));\n vec4 childMaxX = unpackBvh4Bounds(floatBitsToUint(wide3.y));\n vec4 childMaxY = unpackBvh4Bounds(floatBitsToUint(wide3.z));\n vec4 childMaxZ = unpackBvh4Bounds(floatBitsToUint(wide3.w));\n vec3 invD = traversalInverse(d);\n vec3 padding = depthSlabPadding(invD, o);\n vec4 tx0 = (nodeMin.x + childMinX * nodeExtent.x - o.x) * invD.x;\n vec4 tx1 = (nodeMin.x + childMaxX * nodeExtent.x - o.x) * invD.x;\n vec4 ty0 = (nodeMin.y + childMinY * nodeExtent.y - o.y) * invD.y;\n vec4 ty1 = (nodeMin.y + childMaxY * nodeExtent.y - o.y) * invD.y;\n vec4 tz0 = (nodeMin.z + childMinZ * nodeExtent.z - o.z) * invD.z;\n vec4 tz1 = (nodeMin.z + childMaxZ * nodeExtent.z - o.z) * invD.z;\n vec4 tNear = max(max((min(tx0, tx1) - vec4(padding.x)), (min(ty0, ty1) - vec4(padding.y))), (min(tz0, tz1) - vec4(padding.z)));\n vec4 tFar = min(min((max(tx0, tx1) + vec4(padding.x)), (max(ty0, ty1) + vec4(padding.y))), (max(tz0, tz1) + vec4(padding.z)));\n vec4 distance = mix(tFar, tNear, greaterThan(tNear, vec4(0.0)));\n distance = mix(distance, vec4(-1.0), greaterThan(tNear, min(tFar, vec4(tMax))));\n float childDistances[4] = float[4](distance.x, distance.y, distance.z, distance.w);\n\n // Push far-to-near so that the closest child is popped first.\n for (int order = 0; order < 4; order++) {\n float farthestDistance = -1.0;\n int farthestChild = -1;\n for (int child = 0; child < 4; child++) {\n if (childInfos[child] != 0u && childDistances[child] > farthestDistance) {\n farthestDistance = childDistances[child];\n farthestChild = child;\n }\n }\n if (farthestChild >= 0 && farthestDistance > 0.0) {\n uint childInfo = childInfos[farthestChild];\n int childAddress = int(childInfo);\n if ((childInfo & 0x80000000u) == 0u) {\n childAddress += blasInsOffset;\n }\n stack[++top] = ivec2(childAddress, insIdx);\n childDistances[farthestChild] = -1.0;\n }\n }\n }\n }\n else {\n int i = node.x + bufferOff;\n vec4 r1 = fetchBVH(i);\n int pointerToRightOrNumPrims = floatBitsToInt(r1.w);\n\n if (pointerToRightOrNumPrims >= 0) {\n int left = node.x + 2;\n int right = pointerToRightOrNumPrims + blasInsOffset;\n\n float d0 = intersectBox(o, d, fetchBVH(left + bufferOff).xyz, fetchBVH(left + 1 + bufferOff).xyz, tMax);\n float d1 = intersectBox(o, d, fetchBVH(right + bufferOff).xyz, fetchBVH(right + 1 + bufferOff).xyz, tMax);\n if (d0 > d1) {\n if (d0 > 0.0) {\n stack[++top] = ivec2(left, insIdx);\n }\n if (d1 > 0.0) {\n stack[++top] = ivec2(right, insIdx);\n }\n }\n else {\n if (d1 > 0.0) {\n stack[++top] = ivec2(right, insIdx);\n }\n if (d0 > 0.0) {\n stack[++top] = ivec2(left, insIdx);\n }\n }\n }\n else {\n if (insIdx < 0) {\n for (int k = 0; k < -pointerToRightOrNumPrims; k++) {\n vec4 r3 = fetchBVH(i + 2 + k);\n stack[++top] = ivec2(floatBitsToInt(r3.y), floatBitsToInt(r3.x));\n }\n }\n else {\n primitiveCount = -pointerToRightOrNumPrims;\n primitiveOffset = i + 2;\n }\n }\n }\n if (primitiveCount > 0) {\n ${intersectTriangles}\n }\n }\n`;\n}\n\nexport default createShaderMixin({\n uniforms: {\n positionBuffer: Shader.uniform('sampler2D'),\n normalBuffer: Shader.uniform('sampler2D'),\n tangentBuffer: Shader.uniform('sampler2D'),\n uvBuffer: Shader.uniform('sampler2D'),\n uv1Buffer: Shader.uniform('sampler2D'),\n colorBuffer: Shader.uniform('sampler2D'),\n materialIndexBuffer: Shader.uniform('sampler2D'),\n\n bvhBuffer: Shader.uniform('sampler2D'),\n instancesBuffer: Shader.uniform('sampler2D'),\n\n bvhColumns: Shader.uniform('int', 1),\n vertexColumns: Shader.uniform('int', 1),\n\n instancesColumns: Shader.uniform('int', 1),\n\n blasOffset: Shader.uniform('int'),\n\n isEmptyScene: Shader.uniform('bool')\n },\n main: glsl`\n\nstruct Triangle {\n vec3 p0;\n vec3 p1;\n vec3 p2;\n};\n#ifdef NUM_LIGHTS\nfloat intersectSphere(float rad, vec3 pos, Ray r) {\n vec3 op = pos - r.o;\n float eps = 0.001;\n float b = dot(op, r.d);\n float det = b * b - dot(op, op) + rad * rad;\n if (det < 0.0)\n return INF;\n\n det = sqrt(det);\n float t1 = b - det;\n if (t1 > eps)\n return t1;\n\n float t2 = b + det;\n if (t2 > eps)\n return t2;\n\n return INF;\n}\n\nfloat intersectRect(vec3 pos, vec3 u, vec3 v, vec3 n, Ray r) {\n float dt = dot(r.d, n);\n float t = (dot(n, pos) - dot(n, r.o)) / dt;\n\n if (t > EPS) {\n vec3 p = r.o + r.d * t;\n vec3 vi = p - pos;\n float a1 = dot(u / dot(u, u), vi);\n if (a1 >= 0.0 && a1 <= 1.0) {\n float a2 = dot(v / dot(v, v), vi);\n if (a2 >= 0.0 && a2 <= 1.0) {\n return t;\n }\n }\n }\n\n return INF;\n}\n\nfloat intersectLight(Ray r, Light light, out LightSampleRec lightSample) {\n // Directional lights are delta sources without finite geometry. Start at\n // INF so they cannot accidentally terminate a BSDF continuation as a\n // geometric light hit; rect and sphere branches overwrite this value.\n float d = INF;\n vec3 u = light.u;\n vec3 v = light.v;\n int type = int(light.type);\n\n#ifdef HAS_RECT_LIGHTS\n if (type == RECT_LIGHT) {\n // Adjust the position from center to start corner\n vec3 pos = light.position-u*0.5-v*0.5;\n vec3 normal = normalize(cross(u, v));\n if (dot(normal, r.d) > 0.) // Hide backfacing quad light\n return INF;\n\n d = intersectRect(pos, u, v, normal, r);\n if (d < 0.) {\n d = INF;\n }\n else {\n float cosTheta = dot(-r.d, normal);\n float spread = light.spread;\n if (cosTheta >= cos(spread * PI / 2.0)) {\n lightSample.pdf = (d * d) / (light.area * cosTheta);\n lightSample.emission = light.emission;\n }\n }\n }\n#endif\n#ifdef HAS_SPHERE_LIGHTS\n if (type == SPHERE_LIGHT) {\n if (light.radius <= 0.0) {\n vec3 toPoint = light.position - r.o;\n float projection = dot(toPoint, r.d);\n vec3 perpendicular = toPoint - projection * r.d;\n if (projection <= EPS || dot(perpendicular, perpendicular) > 1e-8) return INF;\n d = projection;\n lightSample.direction = r.d;\n lightSample.normal = -r.d;\n lightSample.dist = d;\n lightSample.pdf = 1.0;\n lightSample.isDelta = true;\n } else {\n d = intersectSphere(light.radius, light.position, r);\n lightSample.isDelta = false;\n }\n if (d < 0.) {\n d = INF;\n }\n else {\n float centerDistance = distance(r.o, light.position);\n float cosThetaMax = centerDistance <= light.radius\n ? -1.0\n : sqrt(max(0.0, 1.0 - light.radius * light.radius / (centerDistance * centerDistance)));\n if (light.radius > 0.0) {\n lightSample.pdf = centerDistance <= light.radius\n ? INV4PI\n : 1.0 / max(TWOPI * (1.0 - cosThetaMax), 1e-8);\n }\n lightSample.emission = light.radius <= 0.0\n ? light.emission / max(d * d, 1e-8)\n : light.emission;\n }\n }\n#endif\n#ifdef HAS_SPOT_LIGHTS\n if (type == SPOT_LIGHT) {\n if (light.radius <= 0.0) {\n vec3 toPoint = light.position - r.o;\n float projection = dot(toPoint, r.d);\n vec3 perpendicular = toPoint - projection * r.d;\n if (projection <= EPS || dot(perpendicular, perpendicular) > 1e-8) return INF;\n d = projection;\n lightSample.direction = r.d;\n lightSample.normal = -r.d;\n lightSample.dist = d;\n lightSample.pdf = 1.0;\n lightSample.isDelta = true;\n } else {\n d = intersectSphere(light.radius, light.position, r);\n lightSample.isDelta = false;\n }\n if (d != INF) {\n vec3 hitPt = r.o + d * r.d;\n float multiplier = evaluateSpotEmissionMultiplier(light, normalize(r.o - hitPt));\n if (multiplier <= 0.0) return INF;\n float centerDistance = distance(r.o, light.position);\n float cosThetaMax = centerDistance <= light.radius\n ? -1.0\n : sqrt(max(0.0, 1.0 - light.radius * light.radius / (centerDistance * centerDistance)));\n if (light.radius > 0.0) {\n lightSample.pdf = centerDistance <= light.radius\n ? INV4PI\n : 1.0 / max(TWOPI * (1.0 - cosThetaMax), 1e-8);\n }\n lightSample.emission = light.emission * multiplier * (light.radius <= 0.0\n ? 1.0 / max(d * d, 1e-8)\n : 1.0);\n }\n }\n#endif\n\n return d;\n}\n#endif\n\nint randomPickMaterial(ivec4 m0, ivec4 m1, ivec4 m2, vec3 bary) {\n vec2 m00 = unpackMaterialIndex(m0.x);\n vec2 m01 = unpackMaterialIndex(m0.y);\n vec2 m02 = unpackMaterialIndex(m0.z);\n vec2 m10 = unpackMaterialIndex(m1.x);\n vec2 m11 = unpackMaterialIndex(m1.y);\n vec2 m12 = unpackMaterialIndex(m1.z);\n vec2 m20 = unpackMaterialIndex(m2.x);\n vec2 m21 = unpackMaterialIndex(m2.y);\n vec2 m22 = unpackMaterialIndex(m2.z);\n float w00 = m00.y * bary.x;\n float w01 = m01.y * bary.x;\n float w02 = m02.y * bary.x;\n float w10 = m10.y * bary.y;\n float w11 = m11.y * bary.y;\n float w12 = m12.y * bary.y;\n float w20 = m20.y * bary.z;\n float w21 = m21.y * bary.z;\n float w22 = m22.y * bary.z;\n\n float wTotal = w00 + w01 + w02 + w10 + w11 + w12 + w20 + w21 + w22;\n // Accumulate weights\n w00 = w00 / wTotal;\n w01 = w01 / wTotal + w00;\n w02 = w02 / wTotal + w01;\n w10 = w10 / wTotal + w02;\n w11 = w11 / wTotal + w10;\n w12 = w12 / wTotal + w11;\n w20 = w20 / wTotal + w12;\n w21 = w21 / wTotal + w20;\n w22 = w22 / wTotal + w21;\n\n float rand = hashedSample(0u);\n float mIdx;\n if (rand < w00) {\n mIdx = m00.x;\n } else if (rand < w01) {\n mIdx = m01.x;\n } else if (rand < w02) {\n mIdx = m02.x;\n } else if (rand < w10) {\n mIdx = m10.x;\n } else if (rand < w11) {\n mIdx = m11.x;\n } else if (rand < w12) {\n mIdx = m12.x;\n } else if (rand < w20) {\n mIdx = m20.x;\n } else if (rand < w21) {\n mIdx = m21.x;\n } else {\n mIdx = m22.x;\n }\n return int(mIdx);\n}\n\n\nvec4 fetchIns(int i);\n\n\nmat2x3 anisotropy_axes(vec3 normal, vec3 dp1, vec3 dp2, vec2 duv1, vec2 duv2, vec4 inputTangent) {\n float determinant = duv1.x * duv2.y - duv1.y * duv2.x;\n vec3 t = inputTangent.xyz;\n vec3 b = vec3(0.0);\n // Internal W=2 marks an authored XYZ tangent whose sign is derived from UVs.\n float handedness = abs(inputTangent.w) > 1.5 ? 0.0 : inputTangent.w;\n if (abs(determinant) > 1e-12) {\n vec3 dt = (dp1 * duv2.y - dp2 * duv1.y) / determinant;\n vec3 db = (dp2 * duv1.x - dp1 * duv2.x) / determinant;\n if (dot(t, t) < 1e-8) { t = dt; }\n if (handedness == 0.0) { handedness = (dot(cross(normal, dt), db) < 0.0 ? -1.0 : 1.0); }\n }\n t -= normal * dot(normal, t);\n if (dot(t, t) < 1e-8) {\n float signZ = (normal.z >= 0.0 ? 1.0 : -1.0);\n float factor = -1.0 / (signZ + normal.z);\n float xy = normal.x * normal.y * factor;\n t = vec3(1.0 + signZ * normal.x * normal.x * factor, signZ * xy, -signZ * normal.x);\n b = vec3(xy, signZ + normal.y * normal.y * factor, -normal.y);\n }\n else { t = normalize(t); b = normalize(cross(normal, t)); }\n return mat2x3(t, b * (handedness < 0.0 ? -1.0 : 1.0));\n}\n\nvoid fetchAttributes(inout SurfaceInteraction si, Triangle tri, vec3 barycentric, ivec3 index) {\n si.hit = true;\n si.position = barycentric.x * tri.p0 + barycentric.y * tri.p1 + barycentric.z * tri.p2;\n ivec2 i0 = unpackTexel(index.x, vertexColumns);\n ivec2 i1 = unpackTexel(index.y, vertexColumns);\n ivec2 i2 = unpackTexel(index.z, vertexColumns);\n si.vertexColor = vec3(1.0);\n#ifdef USE_VERTEX_COLOR\n si.vertexColor = barycentric.x * texelFetch(colorBuffer, i0, 0).rgb\n + barycentric.y * texelFetch(colorBuffer, i1, 0).rgb\n + barycentric.z * texelFetch(colorBuffer, i2, 0).rgb;\n#endif\n\n vec3 n0 = texelFetch(normalBuffer, i0, 0).xyz;\n vec3 n1 = texelFetch(normalBuffer, i1, 0).xyz;\n vec3 n2 = texelFetch(normalBuffer, i2, 0).xyz;\n vec3 normal = normalize(barycentric.x * n0 + barycentric.y * n1 + barycentric.z * n2);\n si.vertexNormal = normal;\n\n // Approximate the smooth limit surface at this triangle hit. Direct-light\n // rays use this geometric offset only near the shadow terminator, avoiding\n // false self-shadowing by adjacent facets without moving ordinary hits.\n // Architecture follows Cycles' shadow_ray_smooth_surface_offset.\n vec3 geometricNormal = normalize(cross(tri.p1 - tri.p0, tri.p2 - tri.p0));\n vec3 smoothNormal = barycentric.x * n0 + barycentric.y * n1 + barycentric.z * n2;\n float a = dot(n2 - n0, tri.p0 - tri.p2);\n float b = dot(n2 - n1, tri.p1 - tri.p2);\n float c = dot(n1 - n0, tri.p1 - tri.p0);\n float h = a * barycentric.x * (barycentric.x - 1.0)\n + (a + b + c) * barycentric.x * barycentric.y\n + b * barycentric.y * (barycentric.y - 1.0);\n if (dot(smoothNormal, geometricNormal) > 0.0) {\n float h0 = max(max(dot(tri.p1 - tri.p0, n0), dot(tri.p2 - tri.p0, n0)), 0.0);\n float h1 = max(max(dot(tri.p0 - tri.p1, n1), dot(tri.p2 - tri.p1, n1)), 0.0);\n float h2 = max(max(dot(tri.p0 - tri.p2, n2), dot(tri.p1 - tri.p2, n2)), 0.0);\n h0 = max(dot(tri.p0 - si.position, n0) + h0, 0.0);\n h1 = max(dot(tri.p1 - si.position, n1) + h1, 0.0);\n h2 = max(dot(tri.p2 - si.position, n2) + h2, 0.0);\n h = max(min(min(h0, h1), h2), h * 0.5);\n }\n else {\n float h0 = max(max(dot(tri.p0 - tri.p1, n0), dot(tri.p0 - tri.p2, n0)), 0.0);\n float h1 = max(max(dot(tri.p1 - tri.p0, n1), dot(tri.p1 - tri.p2, n1)), 0.0);\n float h2 = max(max(dot(tri.p2 - tri.p0, n2), dot(tri.p2 - tri.p1, n2)), 0.0);\n h0 = max(dot(si.position - tri.p0, n0) + h0, 0.0);\n h1 = max(dot(si.position - tri.p1, n1) + h1, 0.0);\n h2 = max(dot(si.position - tri.p2, n2) + h2, 0.0);\n h = min(-min(min(h0, h1), h2), h * 0.5);\n }\n si.shadowOffset = smoothNormal * h;\n\n // TODO currently isampler doesn't work\n ivec4 m0 = ivec4(texelFetch(materialIndexBuffer, i0, 0));\n ivec4 m1 = ivec4(texelFetch(materialIndexBuffer, i1, 0));\n ivec4 m2 = ivec4(texelFetch(materialIndexBuffer, i2, 0));\n \n if (m0.x > 0 || m1.x > 0 || m2.x > 0) {\n si.materialIndex = randomPickMaterial(m0, m1, m2, barycentric);\n }\n \n vec2 uv0 = texelFetch(uvBuffer, i0, 0).xy;\n vec2 uv1 = texelFetch(uvBuffer, i1, 0).xy;\n vec2 uv2 = texelFetch(uvBuffer, i2, 0).xy;\n #if defined(NUM_DIFFUSE_MAPS) || defined(NUM_NORMAL_MAPS) || defined(NUM_METALLIC_ROUGHNESS_MAPS) || defined(NUM_ROUGHNESS_MAPS) || defined(NUM_METALNESS_MAPS) || defined(NUM_EMISSIVE_MAPS) || defined(NUM_THIN_FILM_MAPS) || defined(NUM_ANISOTROPY_MAPS) || defined(NUM_COAT_NORMAL_MAPS)\n vec2 uv = barycentric.x * uv0 + barycentric.y * uv1 + barycentric.z * uv2;\n vec2 secondaryUv0 = texelFetch(uv1Buffer, i0, 0).xy;\n vec2 secondaryUv1 = texelFetch(uv1Buffer, i1, 0).xy;\n vec2 secondaryUv2 = texelFetch(uv1Buffer, i2, 0).xy;\n vec2 secondaryUv = barycentric.x * secondaryUv0\n + barycentric.y * secondaryUv1\n + barycentric.z * secondaryUv2;\n\n vec4 uvInfo = fetchMatData(si.materialIndex * MAT_DATA_SIZE + MAT_ROW_UV);\n int uvProjection = int(uvInfo.r);\n if (uvProjection > 0) {\n uv = projectUV(uvProjection, si.position, normal, uvInfo.zw);\n secondaryUv = uv;\n } else {\n uv *= uvInfo.zw;\n secondaryUv *= uvInfo.zw;\n }\n #else\n vec2 uv = vec2(0.0);\n vec2 secondaryUv = vec2(0.0);\n #endif\n\n#ifdef NUM_COAT_NORMAL_MAPS\n si.coatNormal = normal;\n float coatNormalIndex = fetchMatData(si.materialIndex * MAT_DATA_SIZE + MAT_ROW_LAYER_MAPS).z;\n#endif\n vec4 directionMaterial = fetchMatData(si.materialIndex * MAT_DATA_SIZE + MAT_ROW_DIRECTION_NORMAL);\n float coatRotation = fetchMatData(si.materialIndex * MAT_DATA_SIZE + MAT_ROW_FILM_TEXTURE).x;\n if (directionMaterial.x >= 0.0 || directionMaterial.y != 0.0 || coatRotation != 0.0\n#ifdef NUM_COAT_NORMAL_MAPS\n || coatNormalIndex >= 0.0\n#endif\n ) {\n vec3 dp1 = tri.p0 - tri.p2;\n vec3 dp2 = tri.p1 - tri.p2;\n vec4 inputTangent = vec4(0.0);\n#ifdef USE_TANGENT\n inputTangent = barycentric.x * texelFetch(tangentBuffer, i0, 0)\n + barycentric.y * texelFetch(tangentBuffer, i1, 0)\n + barycentric.z * texelFetch(tangentBuffer, i2, 0);\n#endif\n mat2x3 geometryAxes = anisotropy_axes(normal, dp1, dp2, uv0-uv2, uv1-uv2, inputTangent);\n si.geometryTangent = geometryAxes[0];\n si.geometryBitangent = geometryAxes[1];\n mat2x3 directionAxes = geometryAxes;\n#ifdef NUM_ANISOTROPY_MAPS\n if (directionMaterial.x >= 0.0 && mapUvChannel[int(directionMaterial.x)] > 0.5) {\n vec2 a = texelFetch(uv1Buffer, i0, 0).xy;\n vec2 b = texelFetch(uv1Buffer, i1, 0).xy;\n vec2 c = texelFetch(uv1Buffer, i2, 0).xy;\n directionAxes = anisotropy_axes(normal, dp1, dp2, a-c, b-c, (inputTangent.w == 0.0 ? vec4(0.0) : inputTangent));\n }\n#endif\n si.directionTangent = directionAxes[0];\n si.directionBitangent = directionAxes[1];\n#ifdef NUM_COAT_NORMAL_MAPS\n if (coatNormalIndex >= 0.0) {\n int coatIndex = int(coatNormalIndex);\n mat2x3 coatAxes = geometryAxes;\n if (mapUvChannel[coatIndex] > 0.5) {\n vec2 a = texelFetch(uv1Buffer, i0, 0).xy;\n vec2 b = texelFetch(uv1Buffer, i1, 0).xy;\n vec2 c = texelFetch(uv1Buffer, i2, 0).xy;\n coatAxes = anisotropy_axes(normal, dp1, dp2, a-c, b-c,\n inputTangent.w == 0.0 ? vec4(0.0) : inputTangent);\n }\n vec3 decoded = sampleMap(coatIndex, getMapUvSet(coatIndex, uv, secondaryUv)).rgb * 2.0 - 1.0;\n vec3 mapped = coatAxes[0] * decoded.x * directionMaterial.z\n + coatAxes[1] * decoded.y * directionMaterial.w + normal * decoded.z;\n if (dot(mapped, mapped) > 1e-12) { si.coatNormal = normalize(mapped); }\n }\n#endif\n }\n\n si.uv = uv;\n si.uv1 = secondaryUv;\n\n #ifdef USE_TANGENT\n vec3 t0 = texelFetch(tangentBuffer, i0, 0).xyz;\n vec3 t1 = texelFetch(tangentBuffer, i1, 0).xyz;\n vec3 t2 = texelFetch(tangentBuffer, i2, 0).xyz;\n si.tangent = normalize(barycentric.x * t0 + barycentric.y * t1 + barycentric.z * t2);\n #endif\n #if defined(NUM_NORMAL_MAPS)\n vec3 dp1 = tri.p0 - tri.p2;\n vec3 dp2 = tri.p1 - tri.p2;\n vec2 duv1 = uv0 - uv2;\n vec2 duv2 = uv1 - uv2;\n si.normal = getMatNormal(\n si.materialIndex, uv, normal, dp1, dp2, duv1, duv2\n );\n #else\n si.normal = normal;\n #endif\n}\n\nstruct TriangleIntersect {\n float t;\n vec3 barycentric;\n};\n\nvec4 fetchBVH(int i) {\n return fetchData(bvhBuffer, i, bvhColumns);\n}\n\nvec4 fetchIns(int i) {\n return fetchData(instancesBuffer, i, instancesColumns);\n}\n\n// GLSL ES 3.00 has no bitCount intrinsic. Count the CWBVH mask bits using\n// integer operations supported by WebGL 2.\nuint countBvhBits(uint value) {\n value -= (value >> 1u) & 0x55555555u;\n value = (value & 0x33333333u) + ((value >> 2u) & 0x33333333u);\n value = (value + (value >> 4u)) & 0x0f0f0f0fu;\n return (value * 0x01010101u) >> 24u;\n}\n\nvec4 unpackBvh4Bounds(uint packedBounds) {\n return vec4(\n float(packedBounds & 0xffu),\n float((packedBounds >> 8u) & 0xffu),\n float((packedBounds >> 16u) & 0xffu),\n float(packedBounds >> 24u)\n ) / 255.0;\n}\n\n// Triangle-ray intersection\n// Faster than the classic Möller–Trumbore intersection algorithm\n// http://www.pbr-book.org/3ed-2018/Shapes/Triangle_Meshes.html#TriangleIntersection\nTriangleIntersect intersectTriangle(vec3 o, vec3 d, Triangle tri, int maxDim, vec3 shear, float tMax) {\n TriangleIntersect ti;\n\n // translate vertices based on ray origin\n vec3 p0t = tri.p0 - o;\n vec3 p1t = tri.p1 - o;\n vec3 p2t = tri.p2 - o;\n\n // permute components of triangle vertices\n if (maxDim == 0) {\n p0t = p0t.yzx;\n p1t = p1t.yzx;\n p2t = p2t.yzx;\n } else if (maxDim == 1) {\n p0t = p0t.zxy;\n p1t = p1t.zxy;\n p2t = p2t.zxy;\n }\n\n // apply shear transformation to translated vertex positions\n p0t.xy += shear.xy * p0t.z;\n p1t.xy += shear.xy * p1t.z;\n p2t.xy += shear.xy * p2t.z;\n\n // compute edge function coefficients\n vec3 e = vec3(\n p1t.x * p2t.y - p1t.y * p2t.x,\n p2t.x * p0t.y - p2t.y * p0t.x,\n p0t.x * p1t.y - p0t.y * p1t.x\n );\n\n#ifdef DEPTH_QUERY_MODE\n vec3 edgeScale = abs(vec3(p1t.x * p2t.y, p2t.x * p0t.y, p0t.x * p1t.y))\n + abs(vec3(p1t.y * p2t.x, p2t.y * p0t.x, p0t.y * p1t.x));\n e = mix(e, vec3(0.0), lessThanEqual(abs(e), edgeScale * 1e-6));\n#endif\n\n // check if intersection is inside triangle\n if (any(lessThan(e, vec3(0))) && any(greaterThan(e, vec3(0)))) {\n return ti;\n }\n\n float det = e.x + e.y + e.z;\n#ifdef DEPTH_QUERY_MODE\n if (det == 0.0) return ti;\n#endif\n\n p0t.z *= shear.z;\n p1t.z *= shear.z;\n p2t.z *= shear.z;\n float tScaled = (e.x * p0t.z + e.y * p1t.z + e.z * p2t.z);\n\n // check if closer intersection already exists\n if (abs(tScaled) > abs(tMax * det)) {\n return ti;\n }\n\n float invDet = 1. / det;\n ti.t = tScaled * invDet;\n ti.barycentric = e * invDet;\n\n return ti;\n}\n\nstruct Box {\n vec3 min;\n vec3 max;\n};\n\n// Conservative slabs only for the unjittered depth query shader.\nvec3 traversalInverse(vec3 d) {\n#ifdef DEPTH_QUERY_MODE\n vec3 denominator = mix(d, mix(vec3(1e-20), vec3(-1e-20), lessThan(d, vec3(0.0))), lessThan(abs(d), vec3(1e-20)));\n return 1.0 / denominator;\n#else\n return 1.0 / d;\n#endif\n}\n\nvec3 depthSlabPadding(vec3 invD, vec3 o) {\n#ifdef DEPTH_QUERY_MODE\n return abs(invD) * max(vec3(1.0), abs(o)) * 1e-6;\n#else\n return vec3(0.0);\n#endif\n}\n\n// Branchless ray/box intersection\n// https://tavianator.com/fast-branchless-raybounding-box-intersections/\nfloat intersectBox(vec3 o, vec3 d, vec3 bmin, vec3 bmax, float tMax) {\n#ifdef DEPTH_QUERY_MODE\n vec3 invD = traversalInverse(d);\n vec3 tBot = (bmin - o) * invD;\n vec3 tTop = (bmax - o) * invD;\n vec3 padding = depthSlabPadding(invD, o);\n#else\n vec3 tBot = (bmin - o) / d;\n vec3 tTop = (bmax - o) / d;\n vec3 padding = vec3(0.0);\n#endif\n vec3 tNear = min(tBot, tTop) - padding;\n vec3 tFar = max(tBot, tTop) + padding;\n float t0 = max(tNear.x, max(tNear.y, tNear.z));\n float t1 = min(tFar.x, min(tFar.y, tFar.z));\n\n return (t0 > t1 || t0 > tMax) ? -1.0 : (t0 > 0.0 ? t0 : t1);\n}\n\nint maxDimension(vec3 v) {\n return v.x > v.y ? (v.x > v.z ? 0 : 2) : (v.y > v.z ? 1 : 2);\n}\n\nvec3 getShear(vec3 d, int maxDim) {\n // Permute space so that the z dimension is the one where the absolute value of the ray's direction is largest.\n // Then create a shear transformation that aligns ray direction with the +z axis\n if (maxDim == 0) {\n return vec3(-d.y, -d.z, 1.0) / d.x;\n } else if (maxDim == 1) {\n return vec3(-d.z, -d.x, 1.0) / d.y;\n } else {\n return vec3(-d.x, -d.y, 1.0) / d.z;\n }\n}\n\n// Traverse BVH, find closest triangle intersection, and return surface information\nvoid intersectScene(in Ray ray, inout SurfaceInteraction si, bool doubleSided) {\n si.hit = false;\n\n if (isEmptyScene) {\n return;\n }\n\n int maxDim = maxDimension(abs(ray.d));\n vec3 shear = getShear(ray.d, maxDim);\n int hitInsIdx;\n\n${getBVHTraverseCode(glsl`\n si.triangleIndex = triangleIndex;\n hitTri = tri;\n hitFaceNormal = faceNormal;\n hitBarycentric = hit.barycentric;\n hitIndex = index;\n isHit = true;\n hitInsIdx = insIdx;\n`)}\n\n if (isHit) {\n si.instanceIndex = hitInsIdx;\n si.materialIndex = int(unpackMaterialIndex(floatBitsToInt(fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE}).z)).x);\n // fetchAttributes before transform to world space.\n fetchAttributes(si, hitTri, hitBarycentric, hitIndex);\n mat4 l2w = mat4(\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 5),\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 6),\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 7),\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 8)\n );\n mat4 w2l = mat4(\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 1),\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 2),\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 3),\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 4)\n );\n mat3 normalTransform = transpose(mat3(w2l));\n si.faceNormal = normalize(normalTransform * hitFaceNormal);\n si.vertexNormal = normalize(normalTransform * si.vertexNormal);\n si.normal = normalize(normalTransform * si.normal);\n#ifdef NUM_COAT_NORMAL_MAPS\n si.coatNormal = normalize(normalTransform * si.coatNormal);\n#endif\n#ifdef USE_TANGENT\n if (dot(si.tangent, si.tangent) > 0.0) {\n si.tangent = normalize(mat3(l2w) * si.tangent);\n }\n#endif\n si.shadowOffset = (l2w * vec4(si.shadowOffset, 0.0)).xyz;\n si.directionTangent = mat3(l2w) * si.directionTangent;\n si.directionBitangent = mat3(l2w) * si.directionBitangent;\n si.geometryTangent = mat3(l2w) * si.geometryTangent;\n si.geometryBitangent = mat3(l2w) * si.geometryBitangent;\n si.position = (l2w * vec4(si.position, 1.0)).xyz;\n }\n}\n\n// Local intersection helper for shader bevel.\nvoid intersectSceneBevel(in Ray ray, inout SurfaceInteraction si, int instanceIndex, float maxDistance) {\n si.hit = false;\n\n if (isEmptyScene) {\n return;\n }\n\n bool doubleSided = true;\n int maxDim = maxDimension(abs(ray.d));\n vec3 shear = getShear(ray.d, maxDim);\n int hitInsIdx;\n\n${getBVHTraverseCode(\n glsl`\n si.triangleIndex = triangleIndex;\n hitTri = tri;\n hitFaceNormal = faceNormal;\n hitBarycentric = hit.barycentric;\n hitIndex = index;\n isHit = true;\n hitInsIdx = insIdx;\n`,\n glsl`\n if (insIdx != instanceIndex) {\n continue;\n }\n`,\n 'maxDistance'\n)}\n\n if (isHit) {\n si.instanceIndex = hitInsIdx;\n si.materialIndex = int(unpackMaterialIndex(floatBitsToInt(fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE}).z)).x);\n fetchAttributes(si, hitTri, hitBarycentric, hitIndex);\n mat4 l2w = mat4(\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 5),\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 6),\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 7),\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 8)\n );\n mat4 w2l = mat4(\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 1),\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 2),\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 3),\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 4)\n );\n mat3 normalTransform = transpose(mat3(w2l));\n si.faceNormal = normalize(normalTransform * hitFaceNormal);\n si.vertexNormal = normalize(normalTransform * si.vertexNormal);\n si.normal = normalize(normalTransform * si.normal);\n#ifdef NUM_COAT_NORMAL_MAPS\n si.coatNormal = normalize(normalTransform * si.coatNormal);\n#endif\n#ifdef USE_TANGENT\n if (dot(si.tangent, si.tangent) > 0.0) {\n si.tangent = normalize(mat3(l2w) * si.tangent);\n }\n#endif\n si.shadowOffset = (l2w * vec4(si.shadowOffset, 0.0)).xyz;\n si.directionTangent = mat3(l2w) * si.directionTangent;\n si.directionBitangent = mat3(l2w) * si.directionBitangent;\n si.geometryTangent = mat3(l2w) * si.geometryTangent;\n si.geometryBitangent = mat3(l2w) * si.geometryBitangent;\n si.position = (l2w * vec4(si.position, 1.0)).xyz;\n }\n}\n\nbool intersectSceneShadow(in Ray ray, float lightDistance, int currTriangleIndex) {\n\n if (isEmptyScene) {\n return false;\n }\n\n bool doubleSided = true;\n int maxDim = maxDimension(abs(ray.d));\n vec3 shear = getShear(ray.d, maxDim);\n\n${getBVHTraverseCode(\n glsl`\n // Traversal is clipped to lightDistance. Affine instance transforms keep\n // the ray parameter unchanged because local directions are not normalized.\n bool isOccluded = true;\n vec3 barycentric = hit.barycentric;\n vec3 position = barycentric.x * tri.p0 + barycentric.y * tri.p1 + barycentric.z * tri.p2;\n\n if (isOccluded) {\n#ifdef HAS_ALPHA\n int materialIndex = int(unpackMaterialIndex(floatBitsToInt(fetchIns(insIdx * ${INSTANCE_VEC4_STRIDE}).z)).x);\n ivec2 i0 = unpackTexel(index.x, vertexColumns);\n ivec2 i1 = unpackTexel(index.y, vertexColumns);\n ivec2 i2 = unpackTexel(index.z, vertexColumns);\n ivec4 m0 = ivec4(texelFetch(materialIndexBuffer, i0, 0));\n ivec4 m1 = ivec4(texelFetch(materialIndexBuffer, i1, 0));\n ivec4 m2 = ivec4(texelFetch(materialIndexBuffer, i2, 0));\n \n if (m0.x > 0 || m1.x > 0 || m2.x > 0) {\n materialIndex = randomPickMaterial(m0, m1, m2, barycentric);\n }\n \n int ii = materialIndex * MAT_DATA_SIZE;\n int mapAlphaUsage = int(fetchMatData(ii + MAT_ROW_MISC).a);\n float alpha = fetchMatData(ii + MAT_ROW_ALPHA_FILM).r;\n #ifdef NUM_DIFFUSE_MAPS\n if (mapAlphaUsage == ${mapAlphaUsageConstants.alpha + ''}) {\n int diffuseMapIndex = int(fetchMatData(ii + MAT_ROW_METALLIC_MAPS).z);\n if (diffuseMapIndex >= 0) {\n vec2 uv0 = texelFetch(uvBuffer, i0, 0).xy;\n vec2 uv1 = texelFetch(uvBuffer, i1, 0).xy;\n vec2 uv2 = texelFetch(uvBuffer, i2, 0).xy;\n vec2 uv = barycentric.x * uv0 + barycentric.y * uv1 + barycentric.z * uv2;\n vec4 uvInfo = fetchMatData(ii + MAT_ROW_UV);\n int uvProjection = int(uvInfo.r);\n if (uvProjection > 0) {\n uv = projectUV(uvProjection, position, hitFaceNormal, uvInfo.zw);\n } else {\n uv *= uvInfo.zw;\n }\n \n alpha *= sampleMap(diffuseMapIndex, uv).a;\n }\n }\n #endif\n #ifdef NUM_ALPHA_MAPS\n int alphaMapIndex = int(fetchMatData(ii + MAT_ROW_STANDALONE_MAPS).z);\n if (alphaMapIndex >= 0) {\n vec2 uv0 = texelFetch(uvBuffer, i0, 0).xy;\n vec2 uv1 = texelFetch(uvBuffer, i1, 0).xy;\n vec2 uv2 = texelFetch(uvBuffer, i2, 0).xy;\n vec2 uv = barycentric.x * uv0 + barycentric.y * uv1 + barycentric.z * uv2;\n vec4 uvInfo = fetchMatData(ii + MAT_ROW_UV);\n int uvProjection = int(uvInfo.r);\n if (uvProjection > 0) {\n uv = projectUV(uvProjection, position, hitFaceNormal, uvInfo.zw);\n } else {\n uv *= uvInfo.zw;\n }\n alpha *= sampleMap(alphaMapIndex, uv).g;\n }\n #endif\n float alphaCutoff = fetchMatData(ii + MAT_ROW_STANDALONE_MAPS).a;\n alpha = resolveMaterialAlpha(\n alpha, int(fetchMatData(ii + MAT_ROW_ALPHA).w), alphaCutoff\n );\n if (hashedSample(0u) > alpha) {\n // Continue after a transparent hit without traversing beyond the light.\n tMax = min(max(lightDistance, 0.0), RAY_MAX_DISTANCE);\n isOccluded = false;\n }\n#endif\n }\n if (isOccluded) {\n return true;\n }\n`,\n glsl`\n if (triangleIndex == currTriangleIndex) {\n continue;\n }\n`,\n 'min(max(lightDistance, 0.0), RAY_MAX_DISTANCE)'\n)}\n return false;\n}\n`\n});\n"]}
|
|
1
|
+
{"version":3,"file":"intersect.glsl.js","sourceRoot":"","sources":["../../../../src/renderer/glsl/chunks/intersect.glsl.ts"],"names":[],"mappings":"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","sourcesContent":["import { mapAlphaUsageConstants } from '../../materialConstants.js';\n// Portions adapted from ray-tracing-renderer@0.10.15\n// (https://github.com/hoverinc/ray-tracing-renderer).\n// Copyright (c) 2019 HOVER, MIT license; see LICENSES/RayTracingRenderer-MIT.txt.\n\nimport { INSTANCE_VEC4_STRIDE } from '../../bvhAccel.js';\nimport { createShaderMixin, glsl, Shader } from '../../shader.js';\n\nfunction getBVHTraverseCode(\n handleHitCode: string,\n skipTriangleCode = '',\n initialTMax = 'RAY_MAX_DISTANCE'\n) {\n const intersectTriangles = glsl`\n ivec3 vertexOffset = ivec3(floatBitsToInt(fetchIns(insIdx * ${INSTANCE_VEC4_STRIDE}).y));\n for (int k = 0; k < primitiveCount; k++) {\n int triangleIndex = primitiveOffset + k;\n ${skipTriangleCode}\n vec4 r3 = fetchBVH(triangleIndex);\n ivec3 index = floatBitsToInt(r3.xyz) + vertexOffset;\n Triangle tri = Triangle(\n fetchData(positionBuffer, index.x, vertexColumns).xyz,\n fetchData(positionBuffer, index.y, vertexColumns).xyz,\n fetchData(positionBuffer, index.z, vertexColumns).xyz\n );\n TriangleIntersect hit = intersectTriangle(o, d, tri, maxDim, shear, tMax);\n\n if (hit.t > 0.0) {\n tMax = hit.t;\n vec3 e1 = tri.p1 - tri.p0;\n vec3 e2 = tri.p2 - tri.p0;\n vec3 faceNormal = normalize(cross(e1, e2));\n int sideMaterialIndex = int(unpackMaterialIndex(\n floatBitsToInt(fetchIns(insIdx * ${INSTANCE_VEC4_STRIDE}).z)\n ).x);\n bool materialDoubleSided = fetchMatData(\n sideMaterialIndex * MAT_DATA_SIZE + MAT_ROW_ALPHA\n ).z > 0.5;\n if (!doubleSided && !materialDoubleSided && dot(faceNormal, d) > 0.0) {\n continue;\n }\n\n ${handleHitCode}\n }\n }\n`;\n return glsl`\n ivec2 stack[STACK_SIZE];\n int top = -1;\n int prevInsIdx = -1;\n float tMax = ${initialTMax};\n\n vec3 o = ray.o;\n vec3 d = ray.d;\n\n bool isHit = false;\n vec3 hitFaceNormal;\n vec3 hitBarycentric;\n ivec3 hitIndex;\n\n Triangle hitTri;\n\n // TODO not sure why. but do root node check here will cause rendering wrong in windows.\n top = 0;\n stack[0] = ivec2(0, -1);\n\n int iter = 0;\n while(top >= 0 && iter < 5000) {\n iter++;\n ivec2 node = stack[top--];\n\n int insIdx = node.y;\n\n if (insIdx != prevInsIdx) {\n if (insIdx < 0) {\n o = ray.o;\n d = ray.d;\n }\n else {\n mat4 w2l = mat4(\n fetchIns(insIdx * ${INSTANCE_VEC4_STRIDE} + 1),\n fetchIns(insIdx * ${INSTANCE_VEC4_STRIDE} + 2),\n fetchIns(insIdx * ${INSTANCE_VEC4_STRIDE} + 3),\n fetchIns(insIdx * ${INSTANCE_VEC4_STRIDE} + 4)\n );\n o = (w2l * vec4(ray.o, 1.0)).xyz;\n d = (w2l * vec4(ray.d, 0.0)).xyz;\n }\n maxDim = maxDimension(abs(d));\n shear = getShear(d, maxDim);\n prevInsIdx = insIdx;\n }\n\n int bufferOff = insIdx < 0 ? 0 : blasOffset;\n uint encodedBlasOffset = insIdx < 0 ? 0u : floatBitsToUint(fetchIns(insIdx * ${INSTANCE_VEC4_STRIDE}).x);\n int blasLayout = int(encodedBlasOffset >> 30u);\n int blasInsOffset = int(encodedBlasOffset & 0x3fffffffu);\n int primitiveCount = 0;\n int primitiveOffset = 0;\n\n if (blasLayout == 3) {\n uint nodeInfo = uint(node.x);\n if ((nodeInfo & 0x80000000u) != 0u) {\n primitiveCount = int((nodeInfo >> 23u) & 0xffu);\n primitiveOffset = bufferOff + blasInsOffset + int(nodeInfo & 0x007fffffu);\n }\n else {\n int i = node.x + bufferOff;\n vec4 compressed0 = fetchBVH(i);\n vec4 compressed1 = fetchBVH(i + 1);\n vec4 compressed2 = fetchBVH(i + 2);\n vec4 compressed3 = fetchBVH(i + 3);\n vec4 compressed4 = fetchBVH(i + 4);\n uint header = floatBitsToUint(compressed0.w);\n int exponentX = int(header << 24u) >> 24;\n int exponentY = int((header >> 8u) << 24u) >> 24;\n int exponentZ = int((header >> 16u) << 24u) >> 24;\n vec3 quantizationScale = vec3(\n uintBitsToFloat(uint(exponentX + 127) << 23u),\n uintBitsToFloat(uint(exponentY + 127) << 23u),\n uintBitsToFloat(uint(exponentZ + 127) << 23u)\n );\n uint childBase = floatBitsToUint(compressed1.x);\n uint triangleBase = floatBitsToUint(compressed1.y);\n uint internalMask = header >> 24u;\n uint octant = (d.x < 0.0 ? 4u : 0u) |\n (d.y < 0.0 ? 2u : 0u) |\n (d.z < 0.0 ? 1u : 0u);\n uint quantizedWords[12] = uint[12](\n floatBitsToUint(compressed2.x), floatBitsToUint(compressed2.y),\n floatBitsToUint(compressed2.z), floatBitsToUint(compressed2.w),\n floatBitsToUint(compressed3.x), floatBitsToUint(compressed3.y),\n floatBitsToUint(compressed3.z), floatBitsToUint(compressed3.w),\n floatBitsToUint(compressed4.x), floatBitsToUint(compressed4.y),\n floatBitsToUint(compressed4.z), floatBitsToUint(compressed4.w)\n );\n\n for (int order = 7; order >= 0; order--) {\n uint slot = uint(order) ^ octant;\n uint metaWord = slot >= 4u\n ? floatBitsToUint(compressed1.w)\n : floatBitsToUint(compressed1.z);\n uint meta = (metaWord >> ((slot & 3u) * 8u)) & 0xffu;\n if (meta == 0u) continue;\n uint byteShift = (slot & 3u) * 8u;\n uint wordOffset = slot >> 2u;\n vec3 quantizedMin = vec3(\n float((quantizedWords[wordOffset] >> byteShift) & 0xffu),\n float((quantizedWords[2u + wordOffset] >> byteShift) & 0xffu),\n float((quantizedWords[4u + wordOffset] >> byteShift) & 0xffu)\n );\n vec3 quantizedMax = vec3(\n float((quantizedWords[6u + wordOffset] >> byteShift) & 0xffu),\n float((quantizedWords[8u + wordOffset] >> byteShift) & 0xffu),\n float((quantizedWords[10u + wordOffset] >> byteShift) & 0xffu)\n );\n vec3 childMin = compressed0.xyz + quantizedMin * quantizationScale;\n vec3 childMax = compressed0.xyz + quantizedMax * quantizationScale;\n if (intersectBox(o, d, childMin, childMax, tMax) <= 0.0) continue;\n uint localOffset = meta & 0x1fu;\n if (localOffset >= 24u) {\n uint precedingMask = internalMask & ((1u << slot) - 1u);\n uint internalIndex = childBase + countBvhBits(precedingMask);\n stack[++top] = ivec2(\n blasInsOffset + int(internalIndex * 5u), insIdx\n );\n }\n else {\n uint triangleCount = countBvhBits(meta >> 5u);\n uint primitiveAddress = triangleBase + localOffset;\n uint leafInfo = 0x80000000u |\n (triangleCount << 23u) |\n (primitiveAddress & 0x007fffffu);\n stack[++top] = ivec2(int(leafInfo), insIdx);\n }\n }\n }\n }\n else if (blasLayout == 2) {\n int i = node.x + bufferOff;\n vec4 gpu0 = fetchBVH(i);\n vec4 gpu1 = fetchBVH(i + 1);\n vec4 gpu2 = fetchBVH(i + 2);\n vec4 gpu3 = fetchBVH(i + 3);\n primitiveCount = int(floatBitsToUint(gpu2.w));\n if (primitiveCount > 0) {\n primitiveOffset = bufferOff + blasInsOffset + int(floatBitsToUint(gpu3.w));\n }\n else {\n int left = blasInsOffset + int(floatBitsToUint(gpu0.w) * 4u);\n int right = blasInsOffset + int(floatBitsToUint(gpu1.w) * 4u);\n float d0 = intersectBox(o, d, gpu0.xyz, gpu1.xyz, tMax);\n float d1 = intersectBox(o, d, gpu2.xyz, gpu3.xyz, tMax);\n if (d0 > d1) {\n if (d0 > 0.0) stack[++top] = ivec2(left, insIdx);\n if (d1 > 0.0) stack[++top] = ivec2(right, insIdx);\n }\n else {\n if (d1 > 0.0) stack[++top] = ivec2(right, insIdx);\n if (d0 > 0.0) stack[++top] = ivec2(left, insIdx);\n }\n }\n }\n else if (blasLayout == 1) {\n uint nodeInfo = uint(node.x);\n if ((nodeInfo & 0x80000000u) != 0u) {\n primitiveCount = int((nodeInfo >> 23u) & 0xffu);\n primitiveOffset = bufferOff + blasInsOffset + int(nodeInfo & 0x007fffffu);\n }\n else {\n int i = node.x + bufferOff;\n vec4 wide0 = fetchBVH(i);\n vec4 wide1 = fetchBVH(i + 1);\n vec4 wide2 = fetchBVH(i + 2);\n vec4 wide3 = fetchBVH(i + 3);\n vec3 nodeMin = wide0.xyz;\n vec3 nodeExtent = vec3(wide0.w, wide1.x, wide1.y);\n uint childInfos[4] = uint[4](\n floatBitsToUint(wide1.z), floatBitsToUint(wide1.w),\n floatBitsToUint(wide2.x), floatBitsToUint(wide2.y)\n );\n vec4 childMinX = unpackBvh4Bounds(floatBitsToUint(wide2.z));\n vec4 childMinY = unpackBvh4Bounds(floatBitsToUint(wide2.w));\n vec4 childMinZ = unpackBvh4Bounds(floatBitsToUint(wide3.x));\n vec4 childMaxX = unpackBvh4Bounds(floatBitsToUint(wide3.y));\n vec4 childMaxY = unpackBvh4Bounds(floatBitsToUint(wide3.z));\n vec4 childMaxZ = unpackBvh4Bounds(floatBitsToUint(wide3.w));\n vec3 invD = traversalInverse(d);\n vec3 padding = depthSlabPadding(invD, o);\n vec4 tx0 = (nodeMin.x + childMinX * nodeExtent.x - o.x) * invD.x;\n vec4 tx1 = (nodeMin.x + childMaxX * nodeExtent.x - o.x) * invD.x;\n vec4 ty0 = (nodeMin.y + childMinY * nodeExtent.y - o.y) * invD.y;\n vec4 ty1 = (nodeMin.y + childMaxY * nodeExtent.y - o.y) * invD.y;\n vec4 tz0 = (nodeMin.z + childMinZ * nodeExtent.z - o.z) * invD.z;\n vec4 tz1 = (nodeMin.z + childMaxZ * nodeExtent.z - o.z) * invD.z;\n vec4 tNear = max(max((min(tx0, tx1) - vec4(padding.x)), (min(ty0, ty1) - vec4(padding.y))), (min(tz0, tz1) - vec4(padding.z)));\n vec4 tFar = min(min((max(tx0, tx1) + vec4(padding.x)), (max(ty0, ty1) + vec4(padding.y))), (max(tz0, tz1) + vec4(padding.z)));\n vec4 distance = mix(tFar, tNear, greaterThan(tNear, vec4(0.0)));\n distance = mix(distance, vec4(-1.0), greaterThan(tNear, min(tFar, vec4(tMax))));\n float childDistances[4] = float[4](distance.x, distance.y, distance.z, distance.w);\n\n // Push far-to-near so that the closest child is popped first.\n for (int order = 0; order < 4; order++) {\n float farthestDistance = -1.0;\n int farthestChild = -1;\n for (int child = 0; child < 4; child++) {\n if (childInfos[child] != 0u && childDistances[child] > farthestDistance) {\n farthestDistance = childDistances[child];\n farthestChild = child;\n }\n }\n if (farthestChild >= 0 && farthestDistance > 0.0) {\n uint childInfo = childInfos[farthestChild];\n int childAddress = int(childInfo);\n if ((childInfo & 0x80000000u) == 0u) {\n childAddress += blasInsOffset;\n }\n stack[++top] = ivec2(childAddress, insIdx);\n childDistances[farthestChild] = -1.0;\n }\n }\n }\n }\n else {\n int i = node.x + bufferOff;\n vec4 r1 = fetchBVH(i);\n int pointerToRightOrNumPrims = floatBitsToInt(r1.w);\n\n if (pointerToRightOrNumPrims >= 0) {\n int left = node.x + 2;\n int right = pointerToRightOrNumPrims + blasInsOffset;\n\n float d0 = intersectBox(o, d, fetchBVH(left + bufferOff).xyz, fetchBVH(left + 1 + bufferOff).xyz, tMax);\n float d1 = intersectBox(o, d, fetchBVH(right + bufferOff).xyz, fetchBVH(right + 1 + bufferOff).xyz, tMax);\n if (d0 > d1) {\n if (d0 > 0.0) {\n stack[++top] = ivec2(left, insIdx);\n }\n if (d1 > 0.0) {\n stack[++top] = ivec2(right, insIdx);\n }\n }\n else {\n if (d1 > 0.0) {\n stack[++top] = ivec2(right, insIdx);\n }\n if (d0 > 0.0) {\n stack[++top] = ivec2(left, insIdx);\n }\n }\n }\n else {\n if (insIdx < 0) {\n for (int k = 0; k < -pointerToRightOrNumPrims; k++) {\n vec4 r3 = fetchBVH(i + 2 + k);\n stack[++top] = ivec2(floatBitsToInt(r3.y), floatBitsToInt(r3.x));\n }\n }\n else {\n primitiveCount = -pointerToRightOrNumPrims;\n primitiveOffset = i + 2;\n }\n }\n }\n if (primitiveCount > 0) {\n ${intersectTriangles}\n }\n }\n`;\n}\n\nexport default createShaderMixin({\n uniforms: {\n positionBuffer: Shader.uniform('sampler2D'),\n normalBuffer: Shader.uniform('sampler2D'),\n tangentBuffer: Shader.uniform('sampler2D'),\n uvBuffer: Shader.uniform('sampler2D'),\n uv1Buffer: Shader.uniform('sampler2D'),\n colorBuffer: Shader.uniform('sampler2D'),\n materialIndexBuffer: Shader.uniform('sampler2D'),\n\n bvhBuffer: Shader.uniform('sampler2D'),\n instancesBuffer: Shader.uniform('sampler2D'),\n\n bvhColumns: Shader.uniform('int', 1),\n vertexColumns: Shader.uniform('int', 1),\n\n instancesColumns: Shader.uniform('int', 1),\n\n blasOffset: Shader.uniform('int'),\n\n isEmptyScene: Shader.uniform('bool')\n },\n main: glsl`\n\nstruct Triangle {\n vec3 p0;\n vec3 p1;\n vec3 p2;\n};\n#ifdef NUM_LIGHTS\nfloat intersectSphere(float rad, vec3 pos, Ray r) {\n vec3 op = pos - r.o;\n float eps = 0.001;\n float b = dot(op, r.d);\n float det = b * b - dot(op, op) + rad * rad;\n if (det < 0.0)\n return INF;\n\n det = sqrt(det);\n float t1 = b - det;\n if (t1 > eps)\n return t1;\n\n float t2 = b + det;\n if (t2 > eps)\n return t2;\n\n return INF;\n}\n\nfloat intersectRect(vec3 pos, vec3 u, vec3 v, vec3 n, Ray r) {\n float dt = dot(r.d, n);\n float t = (dot(n, pos) - dot(n, r.o)) / dt;\n\n if (t > EPS) {\n vec3 p = r.o + r.d * t;\n vec3 vi = p - pos;\n float a1 = dot(u / dot(u, u), vi);\n if (a1 >= 0.0 && a1 <= 1.0) {\n float a2 = dot(v / dot(v, v), vi);\n if (a2 >= 0.0 && a2 <= 1.0) {\n return t;\n }\n }\n }\n\n return INF;\n}\n\nfloat intersectLight(Ray r, Light light, out LightSampleRec lightSample) {\n // Directional lights are delta sources without finite geometry. Start at\n // INF so they cannot accidentally terminate a BSDF continuation as a\n // geometric light hit; rect and sphere branches overwrite this value.\n float d = INF;\n vec3 u = light.u;\n vec3 v = light.v;\n int type = int(light.type);\n\n#ifdef HAS_RECT_LIGHTS\n if (type == RECT_LIGHT) {\n // Adjust the position from center to start corner\n vec3 pos = light.position-u*0.5-v*0.5;\n vec3 normal = normalize(cross(u, v));\n if (dot(normal, r.d) > 0.) // Hide backfacing quad light\n return INF;\n\n d = intersectRect(pos, u, v, normal, r);\n if (d < 0.) {\n d = INF;\n }\n else {\n float cosTheta = dot(-r.d, normal);\n float spread = light.spread;\n if (cosTheta >= cos(spread * PI / 2.0)) {\n lightSample.pdf = (d * d) / (light.area * cosTheta);\n lightSample.emission = light.emission;\n }\n }\n }\n#endif\n#ifdef HAS_SPHERE_LIGHTS\n if (type == SPHERE_LIGHT) {\n if (light.radius <= 0.0) {\n vec3 toPoint = light.position - r.o;\n float projection = dot(toPoint, r.d);\n vec3 perpendicular = toPoint - projection * r.d;\n if (projection <= EPS || dot(perpendicular, perpendicular) > 1e-8) return INF;\n d = projection;\n lightSample.direction = r.d;\n lightSample.normal = -r.d;\n lightSample.dist = d;\n lightSample.pdf = 1.0;\n lightSample.isDelta = true;\n } else {\n d = intersectSphere(light.radius, light.position, r);\n lightSample.isDelta = false;\n }\n if (d < 0.) {\n d = INF;\n }\n else {\n float centerDistance = distance(r.o, light.position);\n float cosThetaMax = centerDistance <= light.radius\n ? -1.0\n : sqrt(max(0.0, 1.0 - light.radius * light.radius / (centerDistance * centerDistance)));\n if (light.radius > 0.0) {\n lightSample.pdf = centerDistance <= light.radius\n ? INV4PI\n : 1.0 / max(TWOPI * (1.0 - cosThetaMax), 1e-8);\n }\n lightSample.emission = light.radius <= 0.0\n ? light.emission / max(d * d, 1e-8)\n : light.emission;\n }\n }\n#endif\n#ifdef HAS_SPOT_LIGHTS\n if (type == SPOT_LIGHT) {\n if (light.radius <= 0.0) {\n vec3 toPoint = light.position - r.o;\n float projection = dot(toPoint, r.d);\n vec3 perpendicular = toPoint - projection * r.d;\n if (projection <= EPS || dot(perpendicular, perpendicular) > 1e-8) return INF;\n d = projection;\n lightSample.direction = r.d;\n lightSample.normal = -r.d;\n lightSample.dist = d;\n lightSample.pdf = 1.0;\n lightSample.isDelta = true;\n } else {\n d = intersectSphere(light.radius, light.position, r);\n lightSample.isDelta = false;\n }\n if (d != INF) {\n vec3 hitPt = r.o + d * r.d;\n float multiplier = evaluateSpotEmissionMultiplier(light, normalize(r.o - hitPt));\n if (multiplier <= 0.0) return INF;\n float centerDistance = distance(r.o, light.position);\n float cosThetaMax = centerDistance <= light.radius\n ? -1.0\n : sqrt(max(0.0, 1.0 - light.radius * light.radius / (centerDistance * centerDistance)));\n if (light.radius > 0.0) {\n lightSample.pdf = centerDistance <= light.radius\n ? INV4PI\n : 1.0 / max(TWOPI * (1.0 - cosThetaMax), 1e-8);\n }\n lightSample.emission = light.emission * multiplier * (light.radius <= 0.0\n ? 1.0 / max(d * d, 1e-8)\n : 1.0);\n }\n }\n#endif\n\n return d;\n}\n#endif\n\nint randomPickMaterial(ivec4 m0, ivec4 m1, ivec4 m2, vec3 bary) {\n vec2 m00 = unpackMaterialIndex(m0.x);\n vec2 m01 = unpackMaterialIndex(m0.y);\n vec2 m02 = unpackMaterialIndex(m0.z);\n vec2 m10 = unpackMaterialIndex(m1.x);\n vec2 m11 = unpackMaterialIndex(m1.y);\n vec2 m12 = unpackMaterialIndex(m1.z);\n vec2 m20 = unpackMaterialIndex(m2.x);\n vec2 m21 = unpackMaterialIndex(m2.y);\n vec2 m22 = unpackMaterialIndex(m2.z);\n float w00 = m00.y * bary.x;\n float w01 = m01.y * bary.x;\n float w02 = m02.y * bary.x;\n float w10 = m10.y * bary.y;\n float w11 = m11.y * bary.y;\n float w12 = m12.y * bary.y;\n float w20 = m20.y * bary.z;\n float w21 = m21.y * bary.z;\n float w22 = m22.y * bary.z;\n\n float wTotal = w00 + w01 + w02 + w10 + w11 + w12 + w20 + w21 + w22;\n // Accumulate weights\n w00 = w00 / wTotal;\n w01 = w01 / wTotal + w00;\n w02 = w02 / wTotal + w01;\n w10 = w10 / wTotal + w02;\n w11 = w11 / wTotal + w10;\n w12 = w12 / wTotal + w11;\n w20 = w20 / wTotal + w12;\n w21 = w21 / wTotal + w20;\n w22 = w22 / wTotal + w21;\n\n float rand = hashedSample(0u);\n float mIdx;\n if (rand < w00) {\n mIdx = m00.x;\n } else if (rand < w01) {\n mIdx = m01.x;\n } else if (rand < w02) {\n mIdx = m02.x;\n } else if (rand < w10) {\n mIdx = m10.x;\n } else if (rand < w11) {\n mIdx = m11.x;\n } else if (rand < w12) {\n mIdx = m12.x;\n } else if (rand < w20) {\n mIdx = m20.x;\n } else if (rand < w21) {\n mIdx = m21.x;\n } else {\n mIdx = m22.x;\n }\n return int(mIdx);\n}\n\n\nvec4 fetchIns(int i);\n\n\nmat2x3 anisotropy_axes(vec3 normal, vec3 dp1, vec3 dp2, vec2 duv1, vec2 duv2, vec4 inputTangent) {\n float determinant = duv1.x * duv2.y - duv1.y * duv2.x;\n vec3 t = inputTangent.xyz;\n vec3 b = vec3(0.0);\n // Internal W=2 marks an authored XYZ tangent whose sign is derived from UVs.\n float handedness = abs(inputTangent.w) > 1.5 ? 0.0 : inputTangent.w;\n if (abs(determinant) > 1e-12) {\n vec3 dt = (dp1 * duv2.y - dp2 * duv1.y) / determinant;\n vec3 db = (dp2 * duv1.x - dp1 * duv2.x) / determinant;\n if (dot(t, t) < 1e-8) { t = dt; }\n if (handedness == 0.0) { handedness = (dot(cross(normal, dt), db) < 0.0 ? -1.0 : 1.0); }\n }\n t -= normal * dot(normal, t);\n if (dot(t, t) < 1e-8) {\n float signZ = (normal.z >= 0.0 ? 1.0 : -1.0);\n float factor = -1.0 / (signZ + normal.z);\n float xy = normal.x * normal.y * factor;\n t = vec3(1.0 + signZ * normal.x * normal.x * factor, signZ * xy, -signZ * normal.x);\n b = vec3(xy, signZ + normal.y * normal.y * factor, -normal.y);\n }\n else { t = normalize(t); b = normalize(cross(normal, t)); }\n return mat2x3(t, b * (handedness < 0.0 ? -1.0 : 1.0));\n}\n\nvoid fetchAttributes(inout SurfaceInteraction si, Triangle tri, vec3 barycentric, ivec3 index) {\n si.hit = true;\n si.position = barycentric.x * tri.p0 + barycentric.y * tri.p1 + barycentric.z * tri.p2;\n ivec2 i0 = unpackTexel(index.x, vertexColumns);\n ivec2 i1 = unpackTexel(index.y, vertexColumns);\n ivec2 i2 = unpackTexel(index.z, vertexColumns);\n si.vertexColor = vec3(1.0);\n#ifdef USE_VERTEX_COLOR\n si.vertexColor = barycentric.x * texelFetch(colorBuffer, i0, 0).rgb\n + barycentric.y * texelFetch(colorBuffer, i1, 0).rgb\n + barycentric.z * texelFetch(colorBuffer, i2, 0).rgb;\n#endif\n\n vec3 n0 = texelFetch(normalBuffer, i0, 0).xyz;\n vec3 n1 = texelFetch(normalBuffer, i1, 0).xyz;\n vec3 n2 = texelFetch(normalBuffer, i2, 0).xyz;\n vec3 normal = normalize(barycentric.x * n0 + barycentric.y * n1 + barycentric.z * n2);\n si.vertexNormal = normal;\n\n // Approximate the smooth limit surface at this triangle hit. Direct-light\n // rays use this geometric offset only near the shadow terminator, avoiding\n // false self-shadowing by adjacent facets without moving ordinary hits.\n // Architecture follows Cycles' shadow_ray_smooth_surface_offset.\n vec3 geometricNormal = normalize(cross(tri.p1 - tri.p0, tri.p2 - tri.p0));\n vec3 smoothNormal = barycentric.x * n0 + barycentric.y * n1 + barycentric.z * n2;\n float a = dot(n2 - n0, tri.p0 - tri.p2);\n float b = dot(n2 - n1, tri.p1 - tri.p2);\n float c = dot(n1 - n0, tri.p1 - tri.p0);\n float h = a * barycentric.x * (barycentric.x - 1.0)\n + (a + b + c) * barycentric.x * barycentric.y\n + b * barycentric.y * (barycentric.y - 1.0);\n if (dot(smoothNormal, geometricNormal) > 0.0) {\n float h0 = max(max(dot(tri.p1 - tri.p0, n0), dot(tri.p2 - tri.p0, n0)), 0.0);\n float h1 = max(max(dot(tri.p0 - tri.p1, n1), dot(tri.p2 - tri.p1, n1)), 0.0);\n float h2 = max(max(dot(tri.p0 - tri.p2, n2), dot(tri.p1 - tri.p2, n2)), 0.0);\n h0 = max(dot(tri.p0 - si.position, n0) + h0, 0.0);\n h1 = max(dot(tri.p1 - si.position, n1) + h1, 0.0);\n h2 = max(dot(tri.p2 - si.position, n2) + h2, 0.0);\n h = max(min(min(h0, h1), h2), h * 0.5);\n }\n else {\n float h0 = max(max(dot(tri.p0 - tri.p1, n0), dot(tri.p0 - tri.p2, n0)), 0.0);\n float h1 = max(max(dot(tri.p1 - tri.p0, n1), dot(tri.p1 - tri.p2, n1)), 0.0);\n float h2 = max(max(dot(tri.p2 - tri.p0, n2), dot(tri.p2 - tri.p1, n2)), 0.0);\n h0 = max(dot(si.position - tri.p0, n0) + h0, 0.0);\n h1 = max(dot(si.position - tri.p1, n1) + h1, 0.0);\n h2 = max(dot(si.position - tri.p2, n2) + h2, 0.0);\n h = min(-min(min(h0, h1), h2), h * 0.5);\n }\n si.shadowOffset = smoothNormal * h;\n\n // TODO currently isampler doesn't work\n ivec4 m0 = ivec4(texelFetch(materialIndexBuffer, i0, 0));\n ivec4 m1 = ivec4(texelFetch(materialIndexBuffer, i1, 0));\n ivec4 m2 = ivec4(texelFetch(materialIndexBuffer, i2, 0));\n \n if (m0.x > 0 || m1.x > 0 || m2.x > 0) {\n si.materialIndex = randomPickMaterial(m0, m1, m2, barycentric);\n }\n \n vec2 uv0 = texelFetch(uvBuffer, i0, 0).xy;\n vec2 uv1 = texelFetch(uvBuffer, i1, 0).xy;\n vec2 uv2 = texelFetch(uvBuffer, i2, 0).xy;\n #if defined(NUM_DIFFUSE_MAPS) || defined(NUM_NORMAL_MAPS) || defined(NUM_METALLIC_ROUGHNESS_MAPS) || defined(NUM_ROUGHNESS_MAPS) || defined(NUM_METALNESS_MAPS) || defined(NUM_EMISSIVE_MAPS) || defined(NUM_THIN_FILM_MAPS) || defined(NUM_ANISOTROPY_MAPS) || defined(NUM_COAT_NORMAL_MAPS)\n vec2 uv = barycentric.x * uv0 + barycentric.y * uv1 + barycentric.z * uv2;\n vec2 secondaryUv0 = texelFetch(uv1Buffer, i0, 0).xy;\n vec2 secondaryUv1 = texelFetch(uv1Buffer, i1, 0).xy;\n vec2 secondaryUv2 = texelFetch(uv1Buffer, i2, 0).xy;\n vec2 secondaryUv = barycentric.x * secondaryUv0\n + barycentric.y * secondaryUv1\n + barycentric.z * secondaryUv2;\n\n vec4 uvInfo = fetchMatData(si.materialIndex * MAT_DATA_SIZE + MAT_ROW_UV);\n int uvProjection = int(uvInfo.r);\n if (uvProjection > 0) {\n uv = projectUV(uvProjection, si.position, normal, uvInfo.zw);\n secondaryUv = uv;\n } else {\n uv *= uvInfo.zw;\n secondaryUv *= uvInfo.zw;\n }\n #else\n vec2 uv = vec2(0.0);\n vec2 secondaryUv = vec2(0.0);\n #endif\n\n#ifdef USE_COAT_NORMAL_FRAMES\n si.coatNormal = normal;\n#ifdef NUM_COAT_NORMAL_MAPS\n float coatNormalIndex = fetchMatData(si.materialIndex * MAT_DATA_SIZE + MAT_ROW_LAYER_MAPS).z;\n#endif\n#endif\n vec4 directionMaterial = fetchMatData(si.materialIndex * MAT_DATA_SIZE + MAT_ROW_DIRECTION_NORMAL);\n float coatRotation = fetchMatData(si.materialIndex * MAT_DATA_SIZE + MAT_ROW_FILM_TEXTURE).x;\n if (directionMaterial.x >= 0.0 || directionMaterial.y != 0.0 || coatRotation != 0.0\n#ifdef USE_COAT_NORMAL_FRAMES\n || fetchMatData(si.materialIndex * MAT_DATA_SIZE + MAT_ROW_COAT).w > 0.0\n#endif\n ) {\n vec3 dp1 = tri.p0 - tri.p2;\n vec3 dp2 = tri.p1 - tri.p2;\n vec4 inputTangent = vec4(0.0);\n#ifdef USE_TANGENT\n inputTangent = barycentric.x * texelFetch(tangentBuffer, i0, 0)\n + barycentric.y * texelFetch(tangentBuffer, i1, 0)\n + barycentric.z * texelFetch(tangentBuffer, i2, 0);\n#endif\n mat2x3 geometryAxes = anisotropy_axes(normal, dp1, dp2, uv0-uv2, uv1-uv2, inputTangent);\n si.geometryTangent = geometryAxes[0];\n si.geometryBitangent = geometryAxes[1];\n mat2x3 directionAxes = geometryAxes;\n#ifdef NUM_ANISOTROPY_MAPS\n if (directionMaterial.x >= 0.0 && mapUvChannel[int(directionMaterial.x)] > 0.5) {\n vec2 a = texelFetch(uv1Buffer, i0, 0).xy;\n vec2 b = texelFetch(uv1Buffer, i1, 0).xy;\n vec2 c = texelFetch(uv1Buffer, i2, 0).xy;\n directionAxes = anisotropy_axes(normal, dp1, dp2, a-c, b-c, (inputTangent.w == 0.0 ? vec4(0.0) : inputTangent));\n }\n#endif\n si.directionTangent = directionAxes[0];\n si.directionBitangent = directionAxes[1];\n#if defined(USE_COAT_NORMAL_FRAMES) && defined(NUM_COAT_NORMAL_MAPS)\n if (coatNormalIndex >= 0.0) {\n int coatIndex = int(coatNormalIndex);\n mat2x3 coatAxes = geometryAxes;\n if (mapUvChannel[coatIndex] > 0.5) {\n vec2 a = texelFetch(uv1Buffer, i0, 0).xy;\n vec2 b = texelFetch(uv1Buffer, i1, 0).xy;\n vec2 c = texelFetch(uv1Buffer, i2, 0).xy;\n coatAxes = anisotropy_axes(normal, dp1, dp2, a-c, b-c,\n inputTangent.w == 0.0 ? vec4(0.0) : inputTangent);\n }\n vec3 decoded = sampleMap(coatIndex, getMapUvSet(coatIndex, uv, secondaryUv)).rgb * 2.0 - 1.0;\n vec3 mapped = coatAxes[0] * decoded.x * directionMaterial.z\n + coatAxes[1] * decoded.y * directionMaterial.w + normal * decoded.z;\n if (dot(mapped, mapped) > 1e-12) { si.coatNormal = normalize(mapped); }\n }\n#endif\n }\n\n si.uv = uv;\n si.uv1 = secondaryUv;\n\n #ifdef USE_TANGENT\n vec3 t0 = texelFetch(tangentBuffer, i0, 0).xyz;\n vec3 t1 = texelFetch(tangentBuffer, i1, 0).xyz;\n vec3 t2 = texelFetch(tangentBuffer, i2, 0).xyz;\n si.tangent = normalize(barycentric.x * t0 + barycentric.y * t1 + barycentric.z * t2);\n #endif\n #if defined(NUM_NORMAL_MAPS)\n vec3 dp1 = tri.p0 - tri.p2;\n vec3 dp2 = tri.p1 - tri.p2;\n vec2 duv1 = uv0 - uv2;\n vec2 duv2 = uv1 - uv2;\n si.normal = getMatNormal(\n si.materialIndex, uv, normal, dp1, dp2, duv1, duv2\n );\n #else\n si.normal = normal;\n #endif\n}\n\nstruct TriangleIntersect {\n float t;\n vec3 barycentric;\n};\n\nvec4 fetchBVH(int i) {\n return fetchData(bvhBuffer, i, bvhColumns);\n}\n\nvec4 fetchIns(int i) {\n return fetchData(instancesBuffer, i, instancesColumns);\n}\n\n// GLSL ES 3.00 has no bitCount intrinsic. Count the CWBVH mask bits using\n// integer operations supported by WebGL 2.\nuint countBvhBits(uint value) {\n value -= (value >> 1u) & 0x55555555u;\n value = (value & 0x33333333u) + ((value >> 2u) & 0x33333333u);\n value = (value + (value >> 4u)) & 0x0f0f0f0fu;\n return (value * 0x01010101u) >> 24u;\n}\n\nvec4 unpackBvh4Bounds(uint packedBounds) {\n return vec4(\n float(packedBounds & 0xffu),\n float((packedBounds >> 8u) & 0xffu),\n float((packedBounds >> 16u) & 0xffu),\n float(packedBounds >> 24u)\n ) / 255.0;\n}\n\n// Triangle-ray intersection\n// Faster than the classic Möller–Trumbore intersection algorithm\n// http://www.pbr-book.org/3ed-2018/Shapes/Triangle_Meshes.html#TriangleIntersection\nTriangleIntersect intersectTriangle(vec3 o, vec3 d, Triangle tri, int maxDim, vec3 shear, float tMax) {\n TriangleIntersect ti;\n\n // translate vertices based on ray origin\n vec3 p0t = tri.p0 - o;\n vec3 p1t = tri.p1 - o;\n vec3 p2t = tri.p2 - o;\n\n // permute components of triangle vertices\n if (maxDim == 0) {\n p0t = p0t.yzx;\n p1t = p1t.yzx;\n p2t = p2t.yzx;\n } else if (maxDim == 1) {\n p0t = p0t.zxy;\n p1t = p1t.zxy;\n p2t = p2t.zxy;\n }\n\n // apply shear transformation to translated vertex positions\n p0t.xy += shear.xy * p0t.z;\n p1t.xy += shear.xy * p1t.z;\n p2t.xy += shear.xy * p2t.z;\n\n // compute edge function coefficients\n vec3 e = vec3(\n p1t.x * p2t.y - p1t.y * p2t.x,\n p2t.x * p0t.y - p2t.y * p0t.x,\n p0t.x * p1t.y - p0t.y * p1t.x\n );\n\n#ifdef DEPTH_QUERY_MODE\n vec3 edgeScale = abs(vec3(p1t.x * p2t.y, p2t.x * p0t.y, p0t.x * p1t.y))\n + abs(vec3(p1t.y * p2t.x, p2t.y * p0t.x, p0t.y * p1t.x));\n e = mix(e, vec3(0.0), lessThanEqual(abs(e), edgeScale * 1e-6));\n#endif\n\n // check if intersection is inside triangle\n if (any(lessThan(e, vec3(0))) && any(greaterThan(e, vec3(0)))) {\n return ti;\n }\n\n float det = e.x + e.y + e.z;\n#ifdef DEPTH_QUERY_MODE\n if (det == 0.0) return ti;\n#endif\n\n p0t.z *= shear.z;\n p1t.z *= shear.z;\n p2t.z *= shear.z;\n float tScaled = (e.x * p0t.z + e.y * p1t.z + e.z * p2t.z);\n\n // check if closer intersection already exists\n if (abs(tScaled) > abs(tMax * det)) {\n return ti;\n }\n\n float invDet = 1. / det;\n ti.t = tScaled * invDet;\n ti.barycentric = e * invDet;\n\n return ti;\n}\n\nstruct Box {\n vec3 min;\n vec3 max;\n};\n\n// Conservative slabs only for the unjittered depth query shader.\nvec3 traversalInverse(vec3 d) {\n#ifdef DEPTH_QUERY_MODE\n vec3 denominator = mix(d, mix(vec3(1e-20), vec3(-1e-20), lessThan(d, vec3(0.0))), lessThan(abs(d), vec3(1e-20)));\n return 1.0 / denominator;\n#else\n return 1.0 / d;\n#endif\n}\n\nvec3 depthSlabPadding(vec3 invD, vec3 o) {\n#ifdef DEPTH_QUERY_MODE\n return abs(invD) * max(vec3(1.0), abs(o)) * 1e-6;\n#else\n return vec3(0.0);\n#endif\n}\n\n// Branchless ray/box intersection\n// https://tavianator.com/fast-branchless-raybounding-box-intersections/\nfloat intersectBox(vec3 o, vec3 d, vec3 bmin, vec3 bmax, float tMax) {\n#ifdef DEPTH_QUERY_MODE\n vec3 invD = traversalInverse(d);\n vec3 tBot = (bmin - o) * invD;\n vec3 tTop = (bmax - o) * invD;\n vec3 padding = depthSlabPadding(invD, o);\n#else\n vec3 tBot = (bmin - o) / d;\n vec3 tTop = (bmax - o) / d;\n vec3 padding = vec3(0.0);\n#endif\n vec3 tNear = min(tBot, tTop) - padding;\n vec3 tFar = max(tBot, tTop) + padding;\n float t0 = max(tNear.x, max(tNear.y, tNear.z));\n float t1 = min(tFar.x, min(tFar.y, tFar.z));\n\n return (t0 > t1 || t0 > tMax) ? -1.0 : (t0 > 0.0 ? t0 : t1);\n}\n\nint maxDimension(vec3 v) {\n return v.x > v.y ? (v.x > v.z ? 0 : 2) : (v.y > v.z ? 1 : 2);\n}\n\nvec3 getShear(vec3 d, int maxDim) {\n // Permute space so that the z dimension is the one where the absolute value of the ray's direction is largest.\n // Then create a shear transformation that aligns ray direction with the +z axis\n if (maxDim == 0) {\n return vec3(-d.y, -d.z, 1.0) / d.x;\n } else if (maxDim == 1) {\n return vec3(-d.z, -d.x, 1.0) / d.y;\n } else {\n return vec3(-d.x, -d.y, 1.0) / d.z;\n }\n}\n\n// Traverse BVH, find closest triangle intersection, and return surface information\nvoid intersectScene(in Ray ray, inout SurfaceInteraction si, bool doubleSided) {\n si.hit = false;\n\n if (isEmptyScene) {\n return;\n }\n\n int maxDim = maxDimension(abs(ray.d));\n vec3 shear = getShear(ray.d, maxDim);\n int hitInsIdx;\n\n${getBVHTraverseCode(glsl`\n si.triangleIndex = triangleIndex;\n hitTri = tri;\n hitFaceNormal = faceNormal;\n hitBarycentric = hit.barycentric;\n hitIndex = index;\n isHit = true;\n hitInsIdx = insIdx;\n`)}\n\n if (isHit) {\n si.instanceIndex = hitInsIdx;\n si.materialIndex = int(unpackMaterialIndex(floatBitsToInt(fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE}).z)).x);\n // fetchAttributes before transform to world space.\n fetchAttributes(si, hitTri, hitBarycentric, hitIndex);\n mat4 l2w = mat4(\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 5),\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 6),\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 7),\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 8)\n );\n mat4 w2l = mat4(\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 1),\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 2),\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 3),\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 4)\n );\n mat3 normalTransform = transpose(mat3(w2l));\n si.faceNormal = normalize(normalTransform * hitFaceNormal);\n si.vertexNormal = normalize(normalTransform * si.vertexNormal);\n si.normal = normalize(normalTransform * si.normal);\n#ifdef USE_COAT_NORMAL_FRAMES\n si.coatNormal = normalize(normalTransform * si.coatNormal);\n#endif\n#ifdef USE_TANGENT\n if (dot(si.tangent, si.tangent) > 0.0) {\n si.tangent = normalize(mat3(l2w) * si.tangent);\n }\n#endif\n si.shadowOffset = (l2w * vec4(si.shadowOffset, 0.0)).xyz;\n si.directionTangent = mat3(l2w) * si.directionTangent;\n si.directionBitangent = mat3(l2w) * si.directionBitangent;\n si.geometryTangent = mat3(l2w) * si.geometryTangent;\n si.geometryBitangent = mat3(l2w) * si.geometryBitangent;\n si.position = (l2w * vec4(si.position, 1.0)).xyz;\n }\n}\n\n// Local intersection helper for shader bevel.\nvoid intersectSceneBevel(in Ray ray, inout SurfaceInteraction si, int instanceIndex, float maxDistance) {\n si.hit = false;\n\n if (isEmptyScene) {\n return;\n }\n\n bool doubleSided = true;\n int maxDim = maxDimension(abs(ray.d));\n vec3 shear = getShear(ray.d, maxDim);\n int hitInsIdx;\n\n${getBVHTraverseCode(\n glsl`\n si.triangleIndex = triangleIndex;\n hitTri = tri;\n hitFaceNormal = faceNormal;\n hitBarycentric = hit.barycentric;\n hitIndex = index;\n isHit = true;\n hitInsIdx = insIdx;\n`,\n glsl`\n if (insIdx != instanceIndex) {\n continue;\n }\n`,\n 'maxDistance'\n)}\n\n if (isHit) {\n si.instanceIndex = hitInsIdx;\n si.materialIndex = int(unpackMaterialIndex(floatBitsToInt(fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE}).z)).x);\n fetchAttributes(si, hitTri, hitBarycentric, hitIndex);\n mat4 l2w = mat4(\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 5),\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 6),\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 7),\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 8)\n );\n mat4 w2l = mat4(\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 1),\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 2),\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 3),\n fetchIns(hitInsIdx * ${INSTANCE_VEC4_STRIDE} + 4)\n );\n mat3 normalTransform = transpose(mat3(w2l));\n si.faceNormal = normalize(normalTransform * hitFaceNormal);\n si.vertexNormal = normalize(normalTransform * si.vertexNormal);\n si.normal = normalize(normalTransform * si.normal);\n#ifdef USE_COAT_NORMAL_FRAMES\n si.coatNormal = normalize(normalTransform * si.coatNormal);\n#endif\n#ifdef USE_TANGENT\n if (dot(si.tangent, si.tangent) > 0.0) {\n si.tangent = normalize(mat3(l2w) * si.tangent);\n }\n#endif\n si.shadowOffset = (l2w * vec4(si.shadowOffset, 0.0)).xyz;\n si.directionTangent = mat3(l2w) * si.directionTangent;\n si.directionBitangent = mat3(l2w) * si.directionBitangent;\n si.geometryTangent = mat3(l2w) * si.geometryTangent;\n si.geometryBitangent = mat3(l2w) * si.geometryBitangent;\n si.position = (l2w * vec4(si.position, 1.0)).xyz;\n }\n}\n\nbool intersectSceneShadow(in Ray ray, float lightDistance, int currTriangleIndex) {\n\n if (isEmptyScene) {\n return false;\n }\n\n bool doubleSided = true;\n int maxDim = maxDimension(abs(ray.d));\n vec3 shear = getShear(ray.d, maxDim);\n\n${getBVHTraverseCode(\n glsl`\n // Traversal is clipped to lightDistance. Affine instance transforms keep\n // the ray parameter unchanged because local directions are not normalized.\n bool isOccluded = true;\n vec3 barycentric = hit.barycentric;\n vec3 position = barycentric.x * tri.p0 + barycentric.y * tri.p1 + barycentric.z * tri.p2;\n\n if (isOccluded) {\n#ifdef HAS_ALPHA\n int materialIndex = int(unpackMaterialIndex(floatBitsToInt(fetchIns(insIdx * ${INSTANCE_VEC4_STRIDE}).z)).x);\n ivec2 i0 = unpackTexel(index.x, vertexColumns);\n ivec2 i1 = unpackTexel(index.y, vertexColumns);\n ivec2 i2 = unpackTexel(index.z, vertexColumns);\n ivec4 m0 = ivec4(texelFetch(materialIndexBuffer, i0, 0));\n ivec4 m1 = ivec4(texelFetch(materialIndexBuffer, i1, 0));\n ivec4 m2 = ivec4(texelFetch(materialIndexBuffer, i2, 0));\n \n if (m0.x > 0 || m1.x > 0 || m2.x > 0) {\n materialIndex = randomPickMaterial(m0, m1, m2, barycentric);\n }\n \n int ii = materialIndex * MAT_DATA_SIZE;\n int mapAlphaUsage = int(fetchMatData(ii + MAT_ROW_MISC).a);\n float alpha = fetchMatData(ii + MAT_ROW_ALPHA_FILM).r;\n #ifdef NUM_DIFFUSE_MAPS\n if (mapAlphaUsage == ${mapAlphaUsageConstants.alpha + ''}) {\n int diffuseMapIndex = int(fetchMatData(ii + MAT_ROW_METALLIC_MAPS).z);\n if (diffuseMapIndex >= 0) {\n vec2 uv0 = texelFetch(uvBuffer, i0, 0).xy;\n vec2 uv1 = texelFetch(uvBuffer, i1, 0).xy;\n vec2 uv2 = texelFetch(uvBuffer, i2, 0).xy;\n vec2 uv = barycentric.x * uv0 + barycentric.y * uv1 + barycentric.z * uv2;\n vec4 uvInfo = fetchMatData(ii + MAT_ROW_UV);\n int uvProjection = int(uvInfo.r);\n if (uvProjection > 0) {\n uv = projectUV(uvProjection, position, hitFaceNormal, uvInfo.zw);\n } else {\n uv *= uvInfo.zw;\n }\n \n alpha *= sampleMap(diffuseMapIndex, uv).a;\n }\n }\n #endif\n #ifdef NUM_ALPHA_MAPS\n int alphaMapIndex = int(fetchMatData(ii + MAT_ROW_STANDALONE_MAPS).z);\n if (alphaMapIndex >= 0) {\n vec2 uv0 = texelFetch(uvBuffer, i0, 0).xy;\n vec2 uv1 = texelFetch(uvBuffer, i1, 0).xy;\n vec2 uv2 = texelFetch(uvBuffer, i2, 0).xy;\n vec2 uv = barycentric.x * uv0 + barycentric.y * uv1 + barycentric.z * uv2;\n vec4 uvInfo = fetchMatData(ii + MAT_ROW_UV);\n int uvProjection = int(uvInfo.r);\n if (uvProjection > 0) {\n uv = projectUV(uvProjection, position, hitFaceNormal, uvInfo.zw);\n } else {\n uv *= uvInfo.zw;\n }\n alpha *= sampleMap(alphaMapIndex, uv).g;\n }\n #endif\n float alphaCutoff = fetchMatData(ii + MAT_ROW_STANDALONE_MAPS).a;\n alpha = resolveMaterialAlpha(\n alpha, int(fetchMatData(ii + MAT_ROW_ALPHA).w), alphaCutoff\n );\n if (hashedSample(0u) > alpha) {\n // Continue after a transparent hit without traversing beyond the light.\n tMax = min(max(lightDistance, 0.0), RAY_MAX_DISTANCE);\n isOccluded = false;\n }\n#endif\n }\n if (isOccluded) {\n return true;\n }\n`,\n glsl`\n if (triangleIndex == currTriangleIndex) {\n continue;\n }\n`,\n 'min(max(lightDistance, 0.0), RAY_MAX_DISTANCE)'\n)}\n return false;\n}\n`\n});\n"]}
|
|
@@ -34,7 +34,7 @@ return vec2(pow((a*a + u*u) * 0.5, 0.25), clamp(1.0 - u/a, 0.0, 1.0));
|
|
|
34
34
|
|
|
35
35
|
#endif
|
|
36
36
|
|
|
37
|
-
vec3
|
|
37
|
+
vec3 oJ(vec3 c) {
|
|
38
38
|
return mix(pow(c * 0.9478672986 + 0.0521327014, vec3(2.4)), c * 0.0773993808, vec3(lessThan(c, vec3(0.04045))));
|
|
39
39
|
}
|
|
40
40
|
vec4 fetchMatData(int i) {
|
|
@@ -63,7 +63,7 @@ return vec2(float(val >> 8), float(val & 0xff) / 255.0);
|
|
|
63
63
|
#ifdef NUM_MAPS
|
|
64
64
|
vec3 decodeMapColor(int index, vec3 c) {
|
|
65
65
|
if (mapColorLinear[index] > 0.5) { return c; }
|
|
66
|
-
return
|
|
66
|
+
return oJ(c);
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
vec2 kJ(int index) {
|
|
@@ -78,7 +78,7 @@ vec2 jP(int index) {
|
|
|
78
78
|
return vec2(fract(mapInfo[index].z), mapInfo[index].w);
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
float
|
|
81
|
+
float fT(float value, float mode) {
|
|
82
82
|
if (mode < 0.5) return fract(value);
|
|
83
83
|
if (mode < 1.5) return clamp(value, 0.0, 1.0);
|
|
84
84
|
return 1.0 - abs(1.0 - (value - 2.0 * floor(value * 0.5)));
|
|
@@ -87,7 +87,7 @@ return 1.0 - abs(1.0 - (value - 2.0 * floor(value * 0.5)));
|
|
|
87
87
|
vec2 kv(int index, vec2 uv) {
|
|
88
88
|
vec4 transform = mapTransform[index];
|
|
89
89
|
vec2 value = uv * transform.xy + transform.zw;
|
|
90
|
-
return vec2(
|
|
90
|
+
return vec2(fT(value.x, mapWrap[index].x), fT(value.y, mapWrap[index].y));
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
vec2 sq(int index, vec2 uv) {
|
|
@@ -95,12 +95,12 @@ vec2 qH = kv(index, uv);
|
|
|
95
95
|
vec2 jk = 1.0 / vec2(textureSize(map, 0).xy);
|
|
96
96
|
vec2 dZ = jP(index);
|
|
97
97
|
vec2 kn = kJ(index) + jk;
|
|
98
|
-
vec2
|
|
99
|
-
vec2
|
|
98
|
+
vec2 oM = dZ - 0.5 * jk;
|
|
99
|
+
vec2 lD = dZ + kn - jk;
|
|
100
100
|
return clamp(
|
|
101
|
-
|
|
101
|
+
oM + qH * kn,
|
|
102
102
|
dZ,
|
|
103
|
-
|
|
103
|
+
lD
|
|
104
104
|
);
|
|
105
105
|
}
|
|
106
106
|
|
|
@@ -158,49 +158,49 @@ vec3 vertexColor
|
|
|
158
158
|
) {
|
|
159
159
|
int i = materialIndex * MAT_DATA_SIZE;
|
|
160
160
|
vec4 oV = fetchMatData(i + MAT_ROW_COLOR);
|
|
161
|
-
vec4
|
|
162
|
-
vec4
|
|
161
|
+
vec4 fo = fetchMatData(i + MAT_ROW_ROUGHNESS_NORMAL);
|
|
162
|
+
vec4 gp = fetchMatData(i + MAT_ROW_METALLIC_MAPS);
|
|
163
163
|
vec4 hI = fetchMatData(i + MAT_ROW_EMISSION);
|
|
164
|
-
vec4
|
|
164
|
+
vec4 gZ = fetchMatData(i + MAT_ROW_IOR_TRANSMISSION);
|
|
165
165
|
vec4 misc = fetchMatData(i + MAT_ROW_MISC);
|
|
166
|
-
vec4
|
|
167
|
-
vec4
|
|
166
|
+
vec4 nB = fetchMatData(i + MAT_ROW_ALPHA_FILM);
|
|
167
|
+
vec4 lR = fetchMatData(i + MAT_ROW_BASE_CONTROLS);
|
|
168
168
|
vec4 oZ = fetchMatData(i + MAT_ROW_SPECULAR_FUZZ);
|
|
169
169
|
vec4 subsurface = fetchMatData(i + MAT_ROW_SUBSURFACE);
|
|
170
170
|
vec4 wq = fetchMatData(i + MAT_ROW_FUZZ);
|
|
171
171
|
vec4 vL = fetchMatData(i + MAT_ROW_COAT);
|
|
172
|
-
vec4
|
|
173
|
-
vec4
|
|
174
|
-
vec4
|
|
172
|
+
vec4 lS = fetchMatData(i + MAT_ROW_COAT_CONTROLS);
|
|
173
|
+
vec4 mX = fetchMatData(i + MAT_ROW_FILM_DISPERSION);
|
|
174
|
+
vec4 nA = fetchMatData(i + MAT_ROW_STANDALONE_MAPS);
|
|
175
175
|
int mapAlphaUsage = int(misc.a);
|
|
176
176
|
|
|
177
177
|
mat.baseColor = oV.rgb;
|
|
178
178
|
mat.materialType = int(oV.w);
|
|
179
|
-
mat.specularRoughness =
|
|
180
|
-
mat.baseMetalness =
|
|
181
|
-
mat.specularAnisotropy = clamp(
|
|
179
|
+
mat.specularRoughness = fo.x;
|
|
180
|
+
mat.baseMetalness = gp.x;
|
|
181
|
+
mat.specularAnisotropy = clamp(gp.y, 0.0, 1.0);
|
|
182
182
|
mat.emission = hI.rgb;
|
|
183
183
|
mat.inside = false;
|
|
184
|
-
mat.specularIor =
|
|
185
|
-
mat.transmissionWeight =
|
|
184
|
+
mat.specularIor = gZ.x;
|
|
185
|
+
mat.transmissionWeight = gZ.y;
|
|
186
186
|
#ifdef HAS_MEDIUM
|
|
187
|
-
mat.mediumType = int(
|
|
187
|
+
mat.mediumType = int(gZ.z);
|
|
188
188
|
#else
|
|
189
189
|
mat.mediumType = 0;
|
|
190
190
|
#endif
|
|
191
|
-
mat.thinWalled =
|
|
191
|
+
mat.thinWalled = gZ.w > 0.5;
|
|
192
192
|
mat.bevel = misc.r;
|
|
193
193
|
mat.transmissionDispersionScale = misc.g;
|
|
194
|
-
mat.dispersionAbbeNumber = max(
|
|
194
|
+
mat.dispersionAbbeNumber = max(mX.y, 1e-6);
|
|
195
195
|
mat.thinFilmThickness = misc.b;
|
|
196
|
-
mat.thinFilmIor =
|
|
197
|
-
mat.thinFilmWeight = clamp(
|
|
196
|
+
mat.thinFilmIor = nB.b;
|
|
197
|
+
mat.thinFilmWeight = clamp(mX.x, 0.0, 1.0);
|
|
198
198
|
|
|
199
199
|
mat.thinFilmThickness *= 1000.0;
|
|
200
|
-
mat.baseWeight = clamp(
|
|
201
|
-
mat.diffuseRoughness = clamp(
|
|
202
|
-
mat.specularWeight = max(0.0,
|
|
203
|
-
mat.subsurfaceWeight = clamp(
|
|
200
|
+
mat.baseWeight = clamp(lR.x, 0.0, 1.0);
|
|
201
|
+
mat.diffuseRoughness = clamp(lR.y, 0.0, 1.0);
|
|
202
|
+
mat.specularWeight = max(0.0, lR.z);
|
|
203
|
+
mat.subsurfaceWeight = clamp(lR.w, 0.0, 1.0);
|
|
204
204
|
mat.specularColor = max(oZ.rgb, vec3(0.0));
|
|
205
205
|
mat.fuzzWeight = clamp(oZ.w, 0.0, 1.0);
|
|
206
206
|
mat.subsurfaceColor = max(subsurface.rgb, vec3(0.0));
|
|
@@ -209,10 +209,10 @@ mat.fuzzColor = max(wq.rgb, vec3(0.0));
|
|
|
209
209
|
mat.fuzzRoughness = clamp(wq.w, 0.0, 1.0);
|
|
210
210
|
mat.coatColor = max(vL.rgb, vec3(0.0));
|
|
211
211
|
mat.coatWeight = clamp(vL.w, 0.0, 1.0);
|
|
212
|
-
mat.coatRoughness = clamp(
|
|
213
|
-
mat.coatAnisotropy = clamp(
|
|
214
|
-
mat.coatIor = max(
|
|
215
|
-
mat.coatDarkening = clamp(
|
|
212
|
+
mat.coatRoughness = clamp(lS.x, ROUGHNESS_MIN, 1.0);
|
|
213
|
+
mat.coatAnisotropy = clamp(lS.y, 0.0, 1.0);
|
|
214
|
+
mat.coatIor = max(lS.z, 1.0);
|
|
215
|
+
mat.coatDarkening = clamp(lS.w, 0.0, 1.0);
|
|
216
216
|
|
|
217
217
|
#ifdef HAS_MEDIUM
|
|
218
218
|
mat.mediumExtinction = max(fetchMatData(i + MAT_ROW_MEDIUM_EXTINCTION).rgb, vec3(0.0));
|
|
@@ -223,27 +223,27 @@ mat.mediumType = max(max(mat.mediumExtinction.x, mat.mediumExtinction.y), mat.me
|
|
|
223
223
|
|
|
224
224
|
|
|
225
225
|
#ifdef HAS_ALPHA
|
|
226
|
-
mat.alpha =
|
|
226
|
+
mat.alpha = nB.r;
|
|
227
227
|
#else
|
|
228
228
|
mat.alpha = 1.0;
|
|
229
229
|
#endif
|
|
230
230
|
|
|
231
231
|
#ifdef NUM_DIFFUSE_MAPS
|
|
232
|
-
int diffuseMapIndex = int(
|
|
232
|
+
int diffuseMapIndex = int(gp.z);
|
|
233
233
|
if (diffuseMapIndex >= 0) {
|
|
234
|
-
vec4
|
|
234
|
+
vec4 oa = sampleMap(
|
|
235
235
|
diffuseMapIndex, getMapUvSet(diffuseMapIndex, uv, uv1)
|
|
236
236
|
);
|
|
237
237
|
|
|
238
238
|
|
|
239
|
-
mat.baseColor *= decodeMapColor(diffuseMapIndex,
|
|
239
|
+
mat.baseColor *= decodeMapColor(diffuseMapIndex, oa.rgb);
|
|
240
240
|
|
|
241
241
|
|
|
242
242
|
if (mapAlphaUsage == ${mapAlphaUsageConstants.transmission + ''}) {
|
|
243
|
-
mat.transmissionWeight = 1.0 -
|
|
243
|
+
mat.transmissionWeight = 1.0 - oa.a;
|
|
244
244
|
}
|
|
245
245
|
else if (mapAlphaUsage == ${mapAlphaUsageConstants.alpha + ''}) {
|
|
246
|
-
mat.alpha *=
|
|
246
|
+
mat.alpha *= oa.a;
|
|
247
247
|
}
|
|
248
248
|
}
|
|
249
249
|
#endif
|
|
@@ -251,20 +251,20 @@ mat.alpha *= ob.a;
|
|
|
251
251
|
${simpleTextureSample('alphaMap', 'glsl')}
|
|
252
252
|
|
|
253
253
|
mat.alpha = lr(
|
|
254
|
-
mat.alpha, int(fetchMatData(i + MAT_ROW_ALPHA).w),
|
|
254
|
+
mat.alpha, int(fetchMatData(i + MAT_ROW_ALPHA).w), nA.w
|
|
255
255
|
);
|
|
256
256
|
|
|
257
257
|
${simpleTextureSample('transmissionMap', 'glsl')}
|
|
258
258
|
|
|
259
259
|
#ifdef NUM_METALLIC_ROUGHNESS_MAPS
|
|
260
|
-
int metallicRoughnessMapIndex = int(
|
|
260
|
+
int metallicRoughnessMapIndex = int(gp.w);
|
|
261
261
|
if (metallicRoughnessMapIndex >= 0) {
|
|
262
|
-
vec4
|
|
262
|
+
vec4 fO = sampleMap(
|
|
263
263
|
metallicRoughnessMapIndex,
|
|
264
264
|
getMapUvSet(metallicRoughnessMapIndex, uv, uv1)
|
|
265
265
|
);
|
|
266
|
-
mat.baseMetalness *=
|
|
267
|
-
mat.specularRoughness *=
|
|
266
|
+
mat.baseMetalness *= fO.b;
|
|
267
|
+
mat.specularRoughness *= fO.g;
|
|
268
268
|
}
|
|
269
269
|
#endif
|
|
270
270
|
|
|
@@ -295,8 +295,9 @@ mat.emission *= mix(vec3(1.0), sqrt(max(vL.rgb, vec3(0.0))), qw);
|
|
|
295
295
|
${thinFilmTextureSample('glsl')}
|
|
296
296
|
|
|
297
297
|
${simpleTextureSample('coatWeightMap', 'glsl')}
|
|
298
|
-
#ifdef
|
|
299
|
-
|
|
298
|
+
#ifdef USE_COAT_NORMAL_FRAMES
|
|
299
|
+
|
|
300
|
+
mat.coatNormalEnabled = mat.coatWeight > 0.0 && (fo.y >= 0.0 || fetchMatData(i + MAT_ROW_LAYER_MAPS).z >= 0.0);
|
|
300
301
|
#endif
|
|
301
302
|
|
|
302
303
|
${simpleTextureSample('coatRoughnessMap', 'glsl')}
|
|
@@ -356,18 +357,18 @@ mat.alphaY = specularAlpha.y;
|
|
|
356
357
|
}
|
|
357
358
|
|
|
358
359
|
#if defined(NUM_NORMAL_MAPS)
|
|
359
|
-
vec3
|
|
360
|
+
vec3 ol(int materialIndex, vec2 uv, vec3 normal, vec3 dp1, vec3 dp2, vec2 oc, vec2 od) {
|
|
360
361
|
int i = materialIndex * MAT_DATA_SIZE;
|
|
361
|
-
vec4
|
|
362
|
-
int normalMapIndex = int(
|
|
362
|
+
vec4 fo = fetchMatData(i + MAT_ROW_ROUGHNESS_NORMAL);
|
|
363
|
+
int normalMapIndex = int(fo.y);
|
|
363
364
|
if (normalMapIndex >= 0) {
|
|
364
365
|
|
|
365
366
|
|
|
366
367
|
|
|
367
368
|
vec3 qW = cross(dp2, normal);
|
|
368
369
|
vec3 qV = cross(normal, dp1);
|
|
369
|
-
vec3 dpdu = qW *
|
|
370
|
-
vec3 sn = qW *
|
|
370
|
+
vec3 dpdu = qW * oc.x + qV * od.x;
|
|
371
|
+
vec3 sn = qW * oc.y + qV * od.y;
|
|
371
372
|
float dT = max(dot(dpdu, dpdu), dot(sn, sn));
|
|
372
373
|
if (dT > 1e-12) {
|
|
373
374
|
float sz = inversesqrt(dT);
|
|
@@ -378,15 +379,15 @@ else {
|
|
|
378
379
|
|
|
379
380
|
|
|
380
381
|
|
|
381
|
-
vec3
|
|
382
|
+
vec3 ns = abs(normal.z) < 0.999
|
|
382
383
|
? vec3(0.0, 0.0, 1.0)
|
|
383
384
|
: vec3(0.0, 1.0, 0.0);
|
|
384
|
-
dpdu = normalize(cross(
|
|
385
|
+
dpdu = normalize(cross(ns, normal));
|
|
385
386
|
sn = cross(normal, dpdu);
|
|
386
387
|
}
|
|
387
388
|
|
|
388
389
|
vec3 n = 2.0 * sampleMap(normalMapIndex, uv).rgb - 1.0;
|
|
389
|
-
n.xy *=
|
|
390
|
+
n.xy *= fo.zw;
|
|
390
391
|
n = normalize(n);
|
|
391
392
|
|
|
392
393
|
mat3 tbn = mat3(dpdu, sn, normal);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"materialBuffer.glsl.js","sourceRoot":"","sources":["../../../../src/renderer/glsl/chunks/materialBuffer.glsl.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AAC3F,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,0BAA0B,EAAE,MAAM,mCAAmC,CAAC;AAC/E,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AACpE,qDAAqD;AACrD,sDAAsD;AACtD,kFAAkF;AAElF,OAAO,EAAE,iBAAiB,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAElE,eAAe,iBAAiB,CAAC;IAC/B,QAAQ,EAAE;QACR,cAAc,EAAE,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC;QAC3C,qBAAqB,EAAE,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;QAC/C,OAAO,EAAE,MAAM,CAAC,YAAY,CAAC,MAAM,EAAE,UAAU,CAAC;QAChD,YAAY,EAAE,MAAM,CAAC,YAAY,CAAC,MAAM,EAAE,UAAU,CAAC;QACrD,YAAY,EAAE,MAAM,CAAC,YAAY,CAAC,OAAO,EAAE,UAAU,CAAC;QACtD,cAAc,EAAE,MAAM,CAAC,YAAY,CAAC,OAAO,EAAE,UAAU,CAAC;QACxD,OAAO,EAAE,MAAM,CAAC,YAAY,CAAC,MAAM,EAAE,UAAU,CAAC;QAChD,GAAG,EAAE,MAAM,CAAC,OAAO,CAAC,gBAAgB,CAAC;KACtC;IACD,OAAO,EAAE;QACP,aAAa,EAAE,CAAC;KACjB;IACD,IAAI,EAAE,IAAI,CAAA;EACV,gBAAgB,EAAE;;;;;;;;;;;;;;;;;;;;;mBAqBC,0BAA0B,CAAC,MAAM;;;mBAGjC,0BAA0B,CAAC,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4BAmGhB,oBAAoB,GAAG,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;uBA8FhC,sBAAsB,CAAC,YAAY,GAAG,EAAE;;;4BAGnC,sBAAsB,CAAC,KAAK,GAAG,EAAE;;;;;;EAM/D,mBAAmB,CAAC,UAAU,EAAE,MAAM,CAAC;;;;;;EAMvC,mBAAmB,CAAC,iBAAiB,EAAE,MAAM,CAAC;;;;;;;;;;;;;;EAc9C,mBAAmB,CAAC,cAAc,EAAE,MAAM,CAAC;;EAE3C,mBAAmB,CAAC,cAAc,EAAE,MAAM,CAAC;;;;;;;;;;;;;;;;;;;;;;EAsB3C,qBAAqB,CAAC,MAAM,CAAC;;EAE7B,mBAAmB,CAAC,eAAe,EAAE,MAAM,CAAC;;;;;EAK5C,mBAAmB,CAAC,kBAAkB,EAAE,MAAM,CAAC;;EAE/C,mBAAmB,CAAC,cAAc,EAAE,MAAM,CAAC;;EAE3C,mBAAmB,CAAC,kBAAkB,EAAE,MAAM,CAAC;;EAE/C,mBAAmB,CAAC,mBAAmB,EAAE,MAAM,CAAC;;EAEhD,mBAAmB,CAAC,kBAAkB,EAAE,MAAM,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2FlD;CACA,CAAC,CAAC","sourcesContent":["import { simpleTextureSample, thinFilmTextureSample } from '../../materialTextureSlots.js';\nimport { materialRowsGlsl } from '../../materialLayout.js';\nimport { materialAlphaModeConstants } from '../../../scene/OpenPBRMaterial.js';\nimport { INSTANCE_VEC4_STRIDE } from '../../bvhAccel.js';\nimport { mapAlphaUsageConstants } from '../../materialConstants.js';\n// Portions adapted from ray-tracing-renderer@0.10.15\n// (https://github.com/hoverinc/ray-tracing-renderer).\n// Copyright (c) 2019 HOVER, MIT license; see LICENSES/RayTracingRenderer-MIT.txt.\n\nimport { createShaderMixin, glsl, Shader } from '../../shader.js';\n\nexport default createShaderMixin({\n uniforms: {\n materialBuffer: Shader.uniform('sampler2D'),\n materialBufferColumns: Shader.uniform('int', 1),\n mapInfo: Shader.arrayUniform('vec4', 'NUM_MAPS'),\n mapTransform: Shader.arrayUniform('vec4', 'NUM_MAPS'),\n mapUvChannel: Shader.arrayUniform('float', 'NUM_MAPS'),\n mapColorLinear: Shader.arrayUniform('float', 'NUM_MAPS'),\n mapWrap: Shader.arrayUniform('vec2', 'NUM_MAPS'),\n map: Shader.uniform('sampler2DArray')\n },\n defines: {\n NUM_MATERIALS: 1\n },\n main: glsl`\n${materialRowsGlsl()}\n#ifdef HAS_GLTF_ANISOTROPY\nvec2 gltf_anisotropy_to_openpbr(float roughness, float strength) {\n if (strength == 0.0) { return vec2(roughness, 0.0); }\n float u = roughness * roughness;\n float a = u + (1.0 - u) * strength * strength;\n if (a == 0.0) { return vec2(0.0); }\n return vec2(pow((a*a + u*u) * 0.5, 0.25), clamp(1.0 - u/a, 0.0, 1.0));\n}\n\n#endif\n\nvec3 sRGBToLinear(vec3 c) {\n return mix(pow(c * 0.9478672986 + 0.0521327014, vec3(2.4)), c * 0.0773993808, vec3(lessThan(c, vec3(0.04045))));\n}\nvec4 fetchMatData(int i) {\n return fetchData(materialBuffer, i, materialBufferColumns);\n}\n\n// Shared by surface evaluation and shadow occlusion, after texture factors.\nfloat resolveMaterialAlpha(float alpha, int alphaMode, float alphaCutoff) {\n if (alphaMode == ${materialAlphaModeConstants.opaque}) {\n return 1.0;\n }\n if (alphaMode == ${materialAlphaModeConstants.mask}) {\n return alpha >= alphaCutoff ? 1.0 : 0.0;\n }\n return alpha;\n}\n\nfloat getMatType(int idx) {\n return fetchMatData(idx * MAT_DATA_SIZE + MAT_ROW_COLOR).w;\n}\n\nvec2 unpackMaterialIndex(int val) {\n return vec2(float(val >> 8), float(val & 0xff) / 255.0);\n}\n\n#ifdef NUM_MAPS\nvec3 decodeMapColor(int index, vec3 c) {\n if (mapColorLinear[index] > 0.5) { return c; }\n return sRGBToLinear(c);\n}\n\nvec2 getMapSize(int index) {\n return mapInfo[index].xy;\n}\n\nfloat getMapLayer(int index) {\n return floor(mapInfo[index].z);\n}\n\nvec2 getMapOffset(int index) {\n return vec2(fract(mapInfo[index].z), mapInfo[index].w);\n}\n\nfloat wrapMapCoordinate(float value, float mode) {\n if (mode < 0.5) return fract(value);\n if (mode < 1.5) return clamp(value, 0.0, 1.0);\n return 1.0 - abs(1.0 - (value - 2.0 * floor(value * 0.5)));\n}\n\nvec2 wrappedMapUv(int index, vec2 uv) {\n vec4 transform = mapTransform[index];\n vec2 value = uv * transform.xy + transform.zw;\n return vec2(wrapMapCoordinate(value.x, mapWrap[index].x), wrapMapCoordinate(value.y, mapWrap[index].y));\n}\n\nvec2 getMapUV(int index, vec2 uv) {\n vec2 repeatedUv = wrappedMapUv(index, uv);\n vec2 atlasTexel = 1.0 / vec2(textureSize(map, 0).xy);\n vec2 firstTexelCenter = getMapOffset(index);\n vec2 sourceExtent = getMapSize(index) + atlasTexel;\n vec2 sourceOrigin = firstTexelCenter - 0.5 * atlasTexel;\n vec2 lastTexelCenter = firstTexelCenter + sourceExtent - atlasTexel;\n return clamp(\n sourceOrigin + repeatedUv * sourceExtent,\n firstTexelCenter,\n lastTexelCenter\n );\n}\n\nvec2 getMapUvSet(int index, vec2 uv, vec2 uv1) {\n return mapUvChannel[index] > 0.5 ? uv1 : uv;\n}\n\nvec4 loadMapTexel(int index, ivec2 coordinate, ivec2 size, ivec2 origin, vec2 modes) {\n ivec2 p = clamp(coordinate, ivec2(0), size - 1);\n if (modes.x < 0.5) p.x = ((coordinate.x % size.x) + size.x) % size.x;\n if (modes.y < 0.5) p.y = ((coordinate.y % size.y) + size.y) % size.y;\n return texelFetch(map, ivec3(origin + p, int(getMapLayer(index))), 0);\n}\n\nvec4 sampleMap(int index, vec2 uv) {\n ivec2 size = ivec2(round(getMapSize(index) * vec2(textureSize(map, 0).xy) + 1.0));\n vec2 texel = wrappedMapUv(index, uv) * vec2(size) - 0.5;\n vec2 modes = mapWrap[index];\n if ((size.x > 1 && modes.x < 0.5 && (texel.x < 0.0 || texel.x > float(size.x - 1))) ||\n (size.y > 1 && modes.y < 0.5 && (texel.y < 0.0 || texel.y > float(size.y - 1)))) {\n ivec2 base = ivec2(floor(texel));\n vec2 weight = fract(texel);\n ivec2 origin = ivec2(round(getMapOffset(index) * vec2(textureSize(map, 0).xy) - 0.5));\n return mix(\n mix(loadMapTexel(index, base, size, origin, modes), loadMapTexel(index, base + ivec2(1, 0), size, origin, modes), weight.x),\n mix(loadMapTexel(index, base + ivec2(0, 1), size, origin, modes), loadMapTexel(index, base + ivec2(1, 1), size, origin, modes), weight.x), weight.y);\n }\n return texture(map, vec3(getMapUV(index, uv), getMapLayer(index)));\n}\n#endif\n\n// OpenPBR 1.1.1: RMS-preserving GGX anisotropy, shared by base and coat.\nvec2 openpbr_roughness_to_alpha(float roughness, float anisotropy) {\n float alpha = roughness * roughness;\n float alphaMin = ROUGHNESS_MIN * ROUGHNESS_MIN;\n if (anisotropy == 0.0) { return vec2(max(alphaMin, alpha)); }\n float complement = 1.0 - anisotropy;\n float alphaT = alpha * sqrt(2.0 / (1.0 + complement * complement));\n return max(vec2(alphaMin), vec2(alphaT, complement * alphaT));\n}\n\nvec4 fetchIns(int i);\nvec3 getInstanceColor(int instanceIndex) {\n if (instanceIndex < 0) return vec3(1.0);\n return fetchIns(instanceIndex * ${INSTANCE_VEC4_STRIDE + ''} + 9).rgb;\n}\n\nvoid getMaterial(\n inout OpenPbrResolvedMaterial mat,\n int materialIndex,\n vec2 uv,\n vec2 uv1,\n int instanceIndex,\n vec3 vertexColor\n) {\n int i = materialIndex * MAT_DATA_SIZE;\n vec4 colorAndMatType = fetchMatData(i + MAT_ROW_COLOR);\n vec4 roughnessAndNormal = fetchMatData(i + MAT_ROW_ROUGHNESS_NORMAL);\n vec4 metallicAndMapIndex = fetchMatData(i + MAT_ROW_METALLIC_MAPS);\n vec4 emissionAndEmissiveMapIndex = fetchMatData(i + MAT_ROW_EMISSION);\n vec4 iorAndTransmission = fetchMatData(i + MAT_ROW_IOR_TRANSMISSION);\n vec4 misc = fetchMatData(i + MAT_ROW_MISC);\n vec4 alphaAndThinFilm = fetchMatData(i + MAT_ROW_ALPHA_FILM);\n vec4 baseControls = fetchMatData(i + MAT_ROW_BASE_CONTROLS);\n vec4 specularAndFuzz = fetchMatData(i + MAT_ROW_SPECULAR_FUZZ);\n vec4 subsurface = fetchMatData(i + MAT_ROW_SUBSURFACE);\n vec4 fuzz = fetchMatData(i + MAT_ROW_FUZZ);\n vec4 coat = fetchMatData(i + MAT_ROW_COAT);\n vec4 coatControls = fetchMatData(i + MAT_ROW_COAT_CONTROLS);\n vec4 filmAndDispersion = fetchMatData(i + MAT_ROW_FILM_DISPERSION);\n vec4 standaloneMetalRoughMaps = fetchMatData(i + MAT_ROW_STANDALONE_MAPS);\n int mapAlphaUsage = int(misc.a);\n\n mat.baseColor = colorAndMatType.rgb;\n mat.materialType = int(colorAndMatType.w);\n mat.specularRoughness = roughnessAndNormal.x;\n mat.baseMetalness = metallicAndMapIndex.x;\n mat.specularAnisotropy = clamp(metallicAndMapIndex.y, 0.0, 1.0);\n mat.emission = emissionAndEmissiveMapIndex.rgb;\n mat.inside = false;\n mat.specularIor = iorAndTransmission.x;\n mat.transmissionWeight = iorAndTransmission.y;\n#ifdef HAS_MEDIUM\n mat.mediumType = int(iorAndTransmission.z);\n#else\n mat.mediumType = 0;\n#endif\n mat.thinWalled = iorAndTransmission.w > 0.5;\n mat.bevel = misc.r;\n mat.transmissionDispersionScale = misc.g;\n mat.dispersionAbbeNumber = max(filmAndDispersion.y, 1e-6);\n mat.thinFilmThickness = misc.b;\n mat.thinFilmIor = alphaAndThinFilm.b;\n mat.thinFilmWeight = clamp(filmAndDispersion.x, 0.0, 1.0);\n // OpenPBR specifies micrometres; the interference routine uses nanometres.\n mat.thinFilmThickness *= 1000.0;\n mat.baseWeight = clamp(baseControls.x, 0.0, 1.0);\n mat.diffuseRoughness = clamp(baseControls.y, 0.0, 1.0);\n mat.specularWeight = max(0.0, baseControls.z);\n mat.subsurfaceWeight = clamp(baseControls.w, 0.0, 1.0);\n mat.specularColor = max(specularAndFuzz.rgb, vec3(0.0));\n mat.fuzzWeight = clamp(specularAndFuzz.w, 0.0, 1.0);\n mat.subsurfaceColor = max(subsurface.rgb, vec3(0.0));\n mat.transmissionColor = max(fetchMatData(i + MAT_ROW_TRANSMISSION_COLOR).rgb, vec3(0.0));\n mat.fuzzColor = max(fuzz.rgb, vec3(0.0));\n mat.fuzzRoughness = clamp(fuzz.w, 0.0, 1.0);\n mat.coatColor = max(coat.rgb, vec3(0.0));\n mat.coatWeight = clamp(coat.w, 0.0, 1.0);\n mat.coatRoughness = clamp(coatControls.x, ROUGHNESS_MIN, 1.0);\n mat.coatAnisotropy = clamp(coatControls.y, 0.0, 1.0);\n mat.coatIor = max(coatControls.z, 1.0);\n mat.coatDarkening = clamp(coatControls.w, 0.0, 1.0);\n\n#ifdef HAS_MEDIUM\n mat.mediumExtinction = max(fetchMatData(i + MAT_ROW_MEDIUM_EXTINCTION).rgb, vec3(0.0));\n mat.mediumAnisotropy = clamp(fetchMatData(i + MAT_ROW_MEDIUM_EXTINCTION).a, -1.0, 1.0);\n mat.mediumScattering = max(fetchMatData(i + MAT_ROW_MEDIUM_SCATTERING).rgb, vec3(0.0));\n mat.mediumType = max(max(mat.mediumExtinction.x, mat.mediumExtinction.y), mat.mediumExtinction.z) > 0.0 ? 1 : 0;\n#endif\n\n\n#ifdef HAS_ALPHA\n mat.alpha = alphaAndThinFilm.r;\n#else\n mat.alpha = 1.0;\n#endif\n\n #ifdef NUM_DIFFUSE_MAPS\n int diffuseMapIndex = int(metallicAndMapIndex.z);\n if (diffuseMapIndex >= 0) {\n vec4 diffuse = sampleMap(\n diffuseMapIndex, getMapUvSet(diffuseMapIndex, uv, uv1)\n );\n // Material factors from THREE / glTF are already linear. Only color\n // textures need decoding from their stored sRGB values.\n mat.baseColor *= decodeMapColor(diffuseMapIndex, diffuse.rgb);\n\n // Map alpha to transmission\n if (mapAlphaUsage == ${mapAlphaUsageConstants.transmission + ''}) {\n mat.transmissionWeight = 1.0 - diffuse.a;\n }\n else if (mapAlphaUsage == ${mapAlphaUsageConstants.alpha + ''}) {\n mat.alpha *= diffuse.a;\n }\n }\n #endif\n\n ${simpleTextureSample('alphaMap', 'glsl')}\n\n mat.alpha = resolveMaterialAlpha(\n mat.alpha, int(fetchMatData(i + MAT_ROW_ALPHA).w), standaloneMetalRoughMaps.w\n );\n\n ${simpleTextureSample('transmissionMap', 'glsl')}\n\n #ifdef NUM_METALLIC_ROUGHNESS_MAPS\n int metallicRoughnessMapIndex = int(metallicAndMapIndex.w);\n if (metallicRoughnessMapIndex >= 0) {\n vec4 metallicRoughness = sampleMap(\n metallicRoughnessMapIndex,\n getMapUvSet(metallicRoughnessMapIndex, uv, uv1)\n );\n mat.baseMetalness *= metallicRoughness.b;\n mat.specularRoughness *= metallicRoughness.g;\n }\n #endif\n\n ${simpleTextureSample('roughnessMap', 'glsl')}\n\n ${simpleTextureSample('metalnessMap', 'glsl')}\n\n #ifdef NUM_EMISSIVE_MAPS\n int emissiveMapIndex = int(emissionAndEmissiveMapIndex.w);\n if (emissiveMapIndex >= 0) {\n vec3 emissive = sampleMap(\n emissiveMapIndex, getMapUvSet(emissiveMapIndex, uv, uv1)\n ).rgb;\n mat.emission *= decodeMapColor(emissiveMapIndex, emissive);\n }\n #endif\n\n #ifdef NUM_COAT_WEIGHT_MAPS\n int emissionCoatMapIndex = int(fetchMatData(i + MAT_ROW_LAYER_MAPS).x);\n if (emissionCoatMapIndex >= 0 && mat.materialType == 0) {\n float emissionCoatWeight = clamp(coat.w, 0.0, 1.0) * sampleMap(\n emissionCoatMapIndex, getMapUvSet(emissionCoatMapIndex, uv, uv1)\n ).r;\n mat.emission *= mix(vec3(1.0), sqrt(max(coat.rgb, vec3(0.0))), emissionCoatWeight);\n }\n #endif\n\n ${thinFilmTextureSample('glsl')}\n\n ${simpleTextureSample('coatWeightMap', 'glsl')}\n#ifdef NUM_COAT_NORMAL_MAPS\n mat.coatNormalEnabled = fetchMatData(i + MAT_ROW_LAYER_MAPS).z >= 0.0 && mat.coatWeight > 0.0;\n#endif\n\n ${simpleTextureSample('coatRoughnessMap', 'glsl')}\n\n ${simpleTextureSample('fuzzColorMap', 'glsl')}\n\n ${simpleTextureSample('fuzzRoughnessMap', 'glsl')}\n\n ${simpleTextureSample('specularWeightMap', 'glsl')}\n\n ${simpleTextureSample('specularColorMap', 'glsl')}\n\n vec4 directionInfo = fetchMatData(i + MAT_ROW_DIRECTION_NORMAL);\n mat.anisotropyDirection = vec2(1.0, 0.0);\n mat.anisotropyDirectionEnabled = directionInfo.y != 0.0;\n mat.coatAnisotropyRotation = fetchMatData(i + MAT_ROW_FILM_TEXTURE).x;\n#ifdef NUM_ANISOTROPY_MAPS\n if (directionInfo.x >= 0.0) {\n int index = int(directionInfo.x);\n vec4 texel = sampleMap(index, getMapUvSet(index, uv, uv1));\n mat.specularAnisotropy *= texel.b;\n vec2 direction = texel.rg * 2.0 - 1.0;\n if (dot(direction, direction) > 1e-4) mat.anisotropyDirection = normalize(direction);\n mat.anisotropyDirectionEnabled = true;\n }\n#endif\n#ifdef HAS_GLTF_ANISOTROPY\n if (fetchMatData(i + MAT_ROW_FILM_TEXTURE).y > 0.5 && mat.specularAnisotropy > 0.0) {\n vec2 native = gltf_anisotropy_to_openpbr(clamp(mat.specularRoughness, 0.0, 1.0), mat.specularAnisotropy);\n mat.specularRoughness = native.x;\n mat.specularAnisotropy = native.y;\n }\n#endif\n if (mat.anisotropyDirectionEnabled) {\n float theta = directionInfo.y;\n vec2 d = mat.anisotropyDirection;\n mat.anisotropyDirection = vec2(cos(theta)*d.x - sin(theta)*d.y, sin(theta)*d.x + cos(theta)*d.y);\n }\n\n // OpenPBR 1.1.1: coat refraction broadens all base microfacet lobes.\n // Keep the zero-coat / smooth-coat paths byte-identical.\n if (mat.coatWeight > 0.0 && mat.coatRoughness > 0.0) {\n float baseRoughness2 = mat.specularRoughness * mat.specularRoughness;\n float coatRoughness2 = mat.coatRoughness * mat.coatRoughness;\n mat.specularRoughness = mix(mat.specularRoughness,\n pow(min(1.0, baseRoughness2 * baseRoughness2 + 2.0 * coatRoughness2 * coatRoughness2), 0.25), mat.coatWeight);\n }\n vec3 color = getInstanceColor(instanceIndex) * vertexColor;\n mat.baseColor *= color;\n if (mat.materialType == UNLIT) mat.emission *= color;\n\n mat.specularRoughness = clamp(mat.specularRoughness, ROUGHNESS_MIN, 1.0);\n mat.baseMetalness = clamp(mat.baseMetalness, 0.0, 1.0);\n vec2 specularAlpha = openpbr_roughness_to_alpha(mat.specularRoughness, mat.specularAnisotropy);\n mat.alphaX = specularAlpha.x;\n mat.alphaY = specularAlpha.y;\n}\n\n#if defined(NUM_NORMAL_MAPS)\nvec3 getMatNormal(int materialIndex, vec2 uv, vec3 normal, vec3 dp1, vec3 dp2, vec2 duv1, vec2 duv2) {\n int i = materialIndex * MAT_DATA_SIZE;\n vec4 roughnessAndNormal = fetchMatData(i + MAT_ROW_ROUGHNESS_NORMAL);\n int normalMapIndex = int(roughnessAndNormal.y);\n if (normalMapIndex >= 0) {\n // http://www.thetenthplanet.de/archives/1180\n // TODO use tangent attribute\n // Compute co-tangent and co-bitangent vectors\n vec3 dp2perp = cross(dp2, normal);\n vec3 dp1perp = cross(normal, dp1);\n vec3 dpdu = dp2perp * duv1.x + dp1perp * duv2.x;\n vec3 dpdv = dp2perp * duv1.y + dp1perp * duv2.y;\n float gradientLengthSquared = max(dot(dpdu, dpdu), dot(dpdv, dpdv));\n if (gradientLengthSquared > 1e-12) {\n float invmax = inversesqrt(gradientLengthSquared);\n dpdu *= invmax;\n dpdv *= invmax;\n }\n else {\n // Imported meshes can legitimately omit UVs and use generated or\n // triplanar projection. Their zero UV derivatives must not turn the TBN\n // basis (and then every lighting result) into NaN.\n vec3 referenceAxis = abs(normal.z) < 0.999\n ? vec3(0.0, 0.0, 1.0)\n : vec3(0.0, 1.0, 0.0);\n dpdu = normalize(cross(referenceAxis, normal));\n dpdv = cross(normal, dpdu);\n }\n\n vec3 n = 2.0 * sampleMap(normalMapIndex, uv).rgb - 1.0;\n n.xy *= roughnessAndNormal.zw;\n n = normalize(n);\n\n mat3 tbn = mat3(dpdu, dpdv, normal);\n\n return normalize(tbn * n);\n } else {\n return normal;\n }\n}\n#endif\n\n`\n});\n"]}
|
|
1
|
+
{"version":3,"file":"materialBuffer.glsl.js","sourceRoot":"","sources":["../../../../src/renderer/glsl/chunks/materialBuffer.glsl.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AAC3F,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,0BAA0B,EAAE,MAAM,mCAAmC,CAAC;AAC/E,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AACpE,qDAAqD;AACrD,sDAAsD;AACtD,kFAAkF;AAElF,OAAO,EAAE,iBAAiB,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAElE,eAAe,iBAAiB,CAAC;IAC/B,QAAQ,EAAE;QACR,cAAc,EAAE,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC;QAC3C,qBAAqB,EAAE,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;QAC/C,OAAO,EAAE,MAAM,CAAC,YAAY,CAAC,MAAM,EAAE,UAAU,CAAC;QAChD,YAAY,EAAE,MAAM,CAAC,YAAY,CAAC,MAAM,EAAE,UAAU,CAAC;QACrD,YAAY,EAAE,MAAM,CAAC,YAAY,CAAC,OAAO,EAAE,UAAU,CAAC;QACtD,cAAc,EAAE,MAAM,CAAC,YAAY,CAAC,OAAO,EAAE,UAAU,CAAC;QACxD,OAAO,EAAE,MAAM,CAAC,YAAY,CAAC,MAAM,EAAE,UAAU,CAAC;QAChD,GAAG,EAAE,MAAM,CAAC,OAAO,CAAC,gBAAgB,CAAC;KACtC;IACD,OAAO,EAAE;QACP,aAAa,EAAE,CAAC;KACjB;IACD,IAAI,EAAE,IAAI,CAAA;EACV,gBAAgB,EAAE;;;;;;;;;;;;;;;;;;;;;mBAqBC,0BAA0B,CAAC,MAAM;;;mBAGjC,0BAA0B,CAAC,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4BAmGhB,oBAAoB,GAAG,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;uBA8FhC,sBAAsB,CAAC,YAAY,GAAG,EAAE;;;4BAGnC,sBAAsB,CAAC,KAAK,GAAG,EAAE;;;;;;EAM/D,mBAAmB,CAAC,UAAU,EAAE,MAAM,CAAC;;;;;;EAMvC,mBAAmB,CAAC,iBAAiB,EAAE,MAAM,CAAC;;;;;;;;;;;;;;EAc9C,mBAAmB,CAAC,cAAc,EAAE,MAAM,CAAC;;EAE3C,mBAAmB,CAAC,cAAc,EAAE,MAAM,CAAC;;;;;;;;;;;;;;;;;;;;;;EAsB3C,qBAAqB,CAAC,MAAM,CAAC;;EAE7B,mBAAmB,CAAC,eAAe,EAAE,MAAM,CAAC;;;;;;EAM5C,mBAAmB,CAAC,kBAAkB,EAAE,MAAM,CAAC;;EAE/C,mBAAmB,CAAC,cAAc,EAAE,MAAM,CAAC;;EAE3C,mBAAmB,CAAC,kBAAkB,EAAE,MAAM,CAAC;;EAE/C,mBAAmB,CAAC,mBAAmB,EAAE,MAAM,CAAC;;EAEhD,mBAAmB,CAAC,kBAAkB,EAAE,MAAM,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2FlD;CACA,CAAC,CAAC","sourcesContent":["import { simpleTextureSample, thinFilmTextureSample } from '../../materialTextureSlots.js';\nimport { materialRowsGlsl } from '../../materialLayout.js';\nimport { materialAlphaModeConstants } from '../../../scene/OpenPBRMaterial.js';\nimport { INSTANCE_VEC4_STRIDE } from '../../bvhAccel.js';\nimport { mapAlphaUsageConstants } from '../../materialConstants.js';\n// Portions adapted from ray-tracing-renderer@0.10.15\n// (https://github.com/hoverinc/ray-tracing-renderer).\n// Copyright (c) 2019 HOVER, MIT license; see LICENSES/RayTracingRenderer-MIT.txt.\n\nimport { createShaderMixin, glsl, Shader } from '../../shader.js';\n\nexport default createShaderMixin({\n uniforms: {\n materialBuffer: Shader.uniform('sampler2D'),\n materialBufferColumns: Shader.uniform('int', 1),\n mapInfo: Shader.arrayUniform('vec4', 'NUM_MAPS'),\n mapTransform: Shader.arrayUniform('vec4', 'NUM_MAPS'),\n mapUvChannel: Shader.arrayUniform('float', 'NUM_MAPS'),\n mapColorLinear: Shader.arrayUniform('float', 'NUM_MAPS'),\n mapWrap: Shader.arrayUniform('vec2', 'NUM_MAPS'),\n map: Shader.uniform('sampler2DArray')\n },\n defines: {\n NUM_MATERIALS: 1\n },\n main: glsl`\n${materialRowsGlsl()}\n#ifdef HAS_GLTF_ANISOTROPY\nvec2 gltf_anisotropy_to_openpbr(float roughness, float strength) {\n if (strength == 0.0) { return vec2(roughness, 0.0); }\n float u = roughness * roughness;\n float a = u + (1.0 - u) * strength * strength;\n if (a == 0.0) { return vec2(0.0); }\n return vec2(pow((a*a + u*u) * 0.5, 0.25), clamp(1.0 - u/a, 0.0, 1.0));\n}\n\n#endif\n\nvec3 sRGBToLinear(vec3 c) {\n return mix(pow(c * 0.9478672986 + 0.0521327014, vec3(2.4)), c * 0.0773993808, vec3(lessThan(c, vec3(0.04045))));\n}\nvec4 fetchMatData(int i) {\n return fetchData(materialBuffer, i, materialBufferColumns);\n}\n\n// Shared by surface evaluation and shadow occlusion, after texture factors.\nfloat resolveMaterialAlpha(float alpha, int alphaMode, float alphaCutoff) {\n if (alphaMode == ${materialAlphaModeConstants.opaque}) {\n return 1.0;\n }\n if (alphaMode == ${materialAlphaModeConstants.mask}) {\n return alpha >= alphaCutoff ? 1.0 : 0.0;\n }\n return alpha;\n}\n\nfloat getMatType(int idx) {\n return fetchMatData(idx * MAT_DATA_SIZE + MAT_ROW_COLOR).w;\n}\n\nvec2 unpackMaterialIndex(int val) {\n return vec2(float(val >> 8), float(val & 0xff) / 255.0);\n}\n\n#ifdef NUM_MAPS\nvec3 decodeMapColor(int index, vec3 c) {\n if (mapColorLinear[index] > 0.5) { return c; }\n return sRGBToLinear(c);\n}\n\nvec2 getMapSize(int index) {\n return mapInfo[index].xy;\n}\n\nfloat getMapLayer(int index) {\n return floor(mapInfo[index].z);\n}\n\nvec2 getMapOffset(int index) {\n return vec2(fract(mapInfo[index].z), mapInfo[index].w);\n}\n\nfloat wrapMapCoordinate(float value, float mode) {\n if (mode < 0.5) return fract(value);\n if (mode < 1.5) return clamp(value, 0.0, 1.0);\n return 1.0 - abs(1.0 - (value - 2.0 * floor(value * 0.5)));\n}\n\nvec2 wrappedMapUv(int index, vec2 uv) {\n vec4 transform = mapTransform[index];\n vec2 value = uv * transform.xy + transform.zw;\n return vec2(wrapMapCoordinate(value.x, mapWrap[index].x), wrapMapCoordinate(value.y, mapWrap[index].y));\n}\n\nvec2 getMapUV(int index, vec2 uv) {\n vec2 repeatedUv = wrappedMapUv(index, uv);\n vec2 atlasTexel = 1.0 / vec2(textureSize(map, 0).xy);\n vec2 firstTexelCenter = getMapOffset(index);\n vec2 sourceExtent = getMapSize(index) + atlasTexel;\n vec2 sourceOrigin = firstTexelCenter - 0.5 * atlasTexel;\n vec2 lastTexelCenter = firstTexelCenter + sourceExtent - atlasTexel;\n return clamp(\n sourceOrigin + repeatedUv * sourceExtent,\n firstTexelCenter,\n lastTexelCenter\n );\n}\n\nvec2 getMapUvSet(int index, vec2 uv, vec2 uv1) {\n return mapUvChannel[index] > 0.5 ? uv1 : uv;\n}\n\nvec4 loadMapTexel(int index, ivec2 coordinate, ivec2 size, ivec2 origin, vec2 modes) {\n ivec2 p = clamp(coordinate, ivec2(0), size - 1);\n if (modes.x < 0.5) p.x = ((coordinate.x % size.x) + size.x) % size.x;\n if (modes.y < 0.5) p.y = ((coordinate.y % size.y) + size.y) % size.y;\n return texelFetch(map, ivec3(origin + p, int(getMapLayer(index))), 0);\n}\n\nvec4 sampleMap(int index, vec2 uv) {\n ivec2 size = ivec2(round(getMapSize(index) * vec2(textureSize(map, 0).xy) + 1.0));\n vec2 texel = wrappedMapUv(index, uv) * vec2(size) - 0.5;\n vec2 modes = mapWrap[index];\n if ((size.x > 1 && modes.x < 0.5 && (texel.x < 0.0 || texel.x > float(size.x - 1))) ||\n (size.y > 1 && modes.y < 0.5 && (texel.y < 0.0 || texel.y > float(size.y - 1)))) {\n ivec2 base = ivec2(floor(texel));\n vec2 weight = fract(texel);\n ivec2 origin = ivec2(round(getMapOffset(index) * vec2(textureSize(map, 0).xy) - 0.5));\n return mix(\n mix(loadMapTexel(index, base, size, origin, modes), loadMapTexel(index, base + ivec2(1, 0), size, origin, modes), weight.x),\n mix(loadMapTexel(index, base + ivec2(0, 1), size, origin, modes), loadMapTexel(index, base + ivec2(1, 1), size, origin, modes), weight.x), weight.y);\n }\n return texture(map, vec3(getMapUV(index, uv), getMapLayer(index)));\n}\n#endif\n\n// OpenPBR 1.1.1: RMS-preserving GGX anisotropy, shared by base and coat.\nvec2 openpbr_roughness_to_alpha(float roughness, float anisotropy) {\n float alpha = roughness * roughness;\n float alphaMin = ROUGHNESS_MIN * ROUGHNESS_MIN;\n if (anisotropy == 0.0) { return vec2(max(alphaMin, alpha)); }\n float complement = 1.0 - anisotropy;\n float alphaT = alpha * sqrt(2.0 / (1.0 + complement * complement));\n return max(vec2(alphaMin), vec2(alphaT, complement * alphaT));\n}\n\nvec4 fetchIns(int i);\nvec3 getInstanceColor(int instanceIndex) {\n if (instanceIndex < 0) return vec3(1.0);\n return fetchIns(instanceIndex * ${INSTANCE_VEC4_STRIDE + ''} + 9).rgb;\n}\n\nvoid getMaterial(\n inout OpenPbrResolvedMaterial mat,\n int materialIndex,\n vec2 uv,\n vec2 uv1,\n int instanceIndex,\n vec3 vertexColor\n) {\n int i = materialIndex * MAT_DATA_SIZE;\n vec4 colorAndMatType = fetchMatData(i + MAT_ROW_COLOR);\n vec4 roughnessAndNormal = fetchMatData(i + MAT_ROW_ROUGHNESS_NORMAL);\n vec4 metallicAndMapIndex = fetchMatData(i + MAT_ROW_METALLIC_MAPS);\n vec4 emissionAndEmissiveMapIndex = fetchMatData(i + MAT_ROW_EMISSION);\n vec4 iorAndTransmission = fetchMatData(i + MAT_ROW_IOR_TRANSMISSION);\n vec4 misc = fetchMatData(i + MAT_ROW_MISC);\n vec4 alphaAndThinFilm = fetchMatData(i + MAT_ROW_ALPHA_FILM);\n vec4 baseControls = fetchMatData(i + MAT_ROW_BASE_CONTROLS);\n vec4 specularAndFuzz = fetchMatData(i + MAT_ROW_SPECULAR_FUZZ);\n vec4 subsurface = fetchMatData(i + MAT_ROW_SUBSURFACE);\n vec4 fuzz = fetchMatData(i + MAT_ROW_FUZZ);\n vec4 coat = fetchMatData(i + MAT_ROW_COAT);\n vec4 coatControls = fetchMatData(i + MAT_ROW_COAT_CONTROLS);\n vec4 filmAndDispersion = fetchMatData(i + MAT_ROW_FILM_DISPERSION);\n vec4 standaloneMetalRoughMaps = fetchMatData(i + MAT_ROW_STANDALONE_MAPS);\n int mapAlphaUsage = int(misc.a);\n\n mat.baseColor = colorAndMatType.rgb;\n mat.materialType = int(colorAndMatType.w);\n mat.specularRoughness = roughnessAndNormal.x;\n mat.baseMetalness = metallicAndMapIndex.x;\n mat.specularAnisotropy = clamp(metallicAndMapIndex.y, 0.0, 1.0);\n mat.emission = emissionAndEmissiveMapIndex.rgb;\n mat.inside = false;\n mat.specularIor = iorAndTransmission.x;\n mat.transmissionWeight = iorAndTransmission.y;\n#ifdef HAS_MEDIUM\n mat.mediumType = int(iorAndTransmission.z);\n#else\n mat.mediumType = 0;\n#endif\n mat.thinWalled = iorAndTransmission.w > 0.5;\n mat.bevel = misc.r;\n mat.transmissionDispersionScale = misc.g;\n mat.dispersionAbbeNumber = max(filmAndDispersion.y, 1e-6);\n mat.thinFilmThickness = misc.b;\n mat.thinFilmIor = alphaAndThinFilm.b;\n mat.thinFilmWeight = clamp(filmAndDispersion.x, 0.0, 1.0);\n // OpenPBR specifies micrometres; the interference routine uses nanometres.\n mat.thinFilmThickness *= 1000.0;\n mat.baseWeight = clamp(baseControls.x, 0.0, 1.0);\n mat.diffuseRoughness = clamp(baseControls.y, 0.0, 1.0);\n mat.specularWeight = max(0.0, baseControls.z);\n mat.subsurfaceWeight = clamp(baseControls.w, 0.0, 1.0);\n mat.specularColor = max(specularAndFuzz.rgb, vec3(0.0));\n mat.fuzzWeight = clamp(specularAndFuzz.w, 0.0, 1.0);\n mat.subsurfaceColor = max(subsurface.rgb, vec3(0.0));\n mat.transmissionColor = max(fetchMatData(i + MAT_ROW_TRANSMISSION_COLOR).rgb, vec3(0.0));\n mat.fuzzColor = max(fuzz.rgb, vec3(0.0));\n mat.fuzzRoughness = clamp(fuzz.w, 0.0, 1.0);\n mat.coatColor = max(coat.rgb, vec3(0.0));\n mat.coatWeight = clamp(coat.w, 0.0, 1.0);\n mat.coatRoughness = clamp(coatControls.x, ROUGHNESS_MIN, 1.0);\n mat.coatAnisotropy = clamp(coatControls.y, 0.0, 1.0);\n mat.coatIor = max(coatControls.z, 1.0);\n mat.coatDarkening = clamp(coatControls.w, 0.0, 1.0);\n\n#ifdef HAS_MEDIUM\n mat.mediumExtinction = max(fetchMatData(i + MAT_ROW_MEDIUM_EXTINCTION).rgb, vec3(0.0));\n mat.mediumAnisotropy = clamp(fetchMatData(i + MAT_ROW_MEDIUM_EXTINCTION).a, -1.0, 1.0);\n mat.mediumScattering = max(fetchMatData(i + MAT_ROW_MEDIUM_SCATTERING).rgb, vec3(0.0));\n mat.mediumType = max(max(mat.mediumExtinction.x, mat.mediumExtinction.y), mat.mediumExtinction.z) > 0.0 ? 1 : 0;\n#endif\n\n\n#ifdef HAS_ALPHA\n mat.alpha = alphaAndThinFilm.r;\n#else\n mat.alpha = 1.0;\n#endif\n\n #ifdef NUM_DIFFUSE_MAPS\n int diffuseMapIndex = int(metallicAndMapIndex.z);\n if (diffuseMapIndex >= 0) {\n vec4 diffuse = sampleMap(\n diffuseMapIndex, getMapUvSet(diffuseMapIndex, uv, uv1)\n );\n // Material factors from THREE / glTF are already linear. Only color\n // textures need decoding from their stored sRGB values.\n mat.baseColor *= decodeMapColor(diffuseMapIndex, diffuse.rgb);\n\n // Map alpha to transmission\n if (mapAlphaUsage == ${mapAlphaUsageConstants.transmission + ''}) {\n mat.transmissionWeight = 1.0 - diffuse.a;\n }\n else if (mapAlphaUsage == ${mapAlphaUsageConstants.alpha + ''}) {\n mat.alpha *= diffuse.a;\n }\n }\n #endif\n\n ${simpleTextureSample('alphaMap', 'glsl')}\n\n mat.alpha = resolveMaterialAlpha(\n mat.alpha, int(fetchMatData(i + MAT_ROW_ALPHA).w), standaloneMetalRoughMaps.w\n );\n\n ${simpleTextureSample('transmissionMap', 'glsl')}\n\n #ifdef NUM_METALLIC_ROUGHNESS_MAPS\n int metallicRoughnessMapIndex = int(metallicAndMapIndex.w);\n if (metallicRoughnessMapIndex >= 0) {\n vec4 metallicRoughness = sampleMap(\n metallicRoughnessMapIndex,\n getMapUvSet(metallicRoughnessMapIndex, uv, uv1)\n );\n mat.baseMetalness *= metallicRoughness.b;\n mat.specularRoughness *= metallicRoughness.g;\n }\n #endif\n\n ${simpleTextureSample('roughnessMap', 'glsl')}\n\n ${simpleTextureSample('metalnessMap', 'glsl')}\n\n #ifdef NUM_EMISSIVE_MAPS\n int emissiveMapIndex = int(emissionAndEmissiveMapIndex.w);\n if (emissiveMapIndex >= 0) {\n vec3 emissive = sampleMap(\n emissiveMapIndex, getMapUvSet(emissiveMapIndex, uv, uv1)\n ).rgb;\n mat.emission *= decodeMapColor(emissiveMapIndex, emissive);\n }\n #endif\n\n #ifdef NUM_COAT_WEIGHT_MAPS\n int emissionCoatMapIndex = int(fetchMatData(i + MAT_ROW_LAYER_MAPS).x);\n if (emissionCoatMapIndex >= 0 && mat.materialType == 0) {\n float emissionCoatWeight = clamp(coat.w, 0.0, 1.0) * sampleMap(\n emissionCoatMapIndex, getMapUvSet(emissionCoatMapIndex, uv, uv1)\n ).r;\n mat.emission *= mix(vec3(1.0), sqrt(max(coat.rgb, vec3(0.0))), emissionCoatWeight);\n }\n #endif\n\n ${thinFilmTextureSample('glsl')}\n\n ${simpleTextureSample('coatWeightMap', 'glsl')}\n#ifdef USE_COAT_NORMAL_FRAMES\n // Independent coat shading also applies when the coat has no normal map.\n mat.coatNormalEnabled = mat.coatWeight > 0.0 && (roughnessAndNormal.y >= 0.0 || fetchMatData(i + MAT_ROW_LAYER_MAPS).z >= 0.0);\n#endif\n\n ${simpleTextureSample('coatRoughnessMap', 'glsl')}\n\n ${simpleTextureSample('fuzzColorMap', 'glsl')}\n\n ${simpleTextureSample('fuzzRoughnessMap', 'glsl')}\n\n ${simpleTextureSample('specularWeightMap', 'glsl')}\n\n ${simpleTextureSample('specularColorMap', 'glsl')}\n\n vec4 directionInfo = fetchMatData(i + MAT_ROW_DIRECTION_NORMAL);\n mat.anisotropyDirection = vec2(1.0, 0.0);\n mat.anisotropyDirectionEnabled = directionInfo.y != 0.0;\n mat.coatAnisotropyRotation = fetchMatData(i + MAT_ROW_FILM_TEXTURE).x;\n#ifdef NUM_ANISOTROPY_MAPS\n if (directionInfo.x >= 0.0) {\n int index = int(directionInfo.x);\n vec4 texel = sampleMap(index, getMapUvSet(index, uv, uv1));\n mat.specularAnisotropy *= texel.b;\n vec2 direction = texel.rg * 2.0 - 1.0;\n if (dot(direction, direction) > 1e-4) mat.anisotropyDirection = normalize(direction);\n mat.anisotropyDirectionEnabled = true;\n }\n#endif\n#ifdef HAS_GLTF_ANISOTROPY\n if (fetchMatData(i + MAT_ROW_FILM_TEXTURE).y > 0.5 && mat.specularAnisotropy > 0.0) {\n vec2 native = gltf_anisotropy_to_openpbr(clamp(mat.specularRoughness, 0.0, 1.0), mat.specularAnisotropy);\n mat.specularRoughness = native.x;\n mat.specularAnisotropy = native.y;\n }\n#endif\n if (mat.anisotropyDirectionEnabled) {\n float theta = directionInfo.y;\n vec2 d = mat.anisotropyDirection;\n mat.anisotropyDirection = vec2(cos(theta)*d.x - sin(theta)*d.y, sin(theta)*d.x + cos(theta)*d.y);\n }\n\n // OpenPBR 1.1.1: coat refraction broadens all base microfacet lobes.\n // Keep the zero-coat / smooth-coat paths byte-identical.\n if (mat.coatWeight > 0.0 && mat.coatRoughness > 0.0) {\n float baseRoughness2 = mat.specularRoughness * mat.specularRoughness;\n float coatRoughness2 = mat.coatRoughness * mat.coatRoughness;\n mat.specularRoughness = mix(mat.specularRoughness,\n pow(min(1.0, baseRoughness2 * baseRoughness2 + 2.0 * coatRoughness2 * coatRoughness2), 0.25), mat.coatWeight);\n }\n vec3 color = getInstanceColor(instanceIndex) * vertexColor;\n mat.baseColor *= color;\n if (mat.materialType == UNLIT) mat.emission *= color;\n\n mat.specularRoughness = clamp(mat.specularRoughness, ROUGHNESS_MIN, 1.0);\n mat.baseMetalness = clamp(mat.baseMetalness, 0.0, 1.0);\n vec2 specularAlpha = openpbr_roughness_to_alpha(mat.specularRoughness, mat.specularAnisotropy);\n mat.alphaX = specularAlpha.x;\n mat.alphaY = specularAlpha.y;\n}\n\n#if defined(NUM_NORMAL_MAPS)\nvec3 getMatNormal(int materialIndex, vec2 uv, vec3 normal, vec3 dp1, vec3 dp2, vec2 duv1, vec2 duv2) {\n int i = materialIndex * MAT_DATA_SIZE;\n vec4 roughnessAndNormal = fetchMatData(i + MAT_ROW_ROUGHNESS_NORMAL);\n int normalMapIndex = int(roughnessAndNormal.y);\n if (normalMapIndex >= 0) {\n // http://www.thetenthplanet.de/archives/1180\n // TODO use tangent attribute\n // Compute co-tangent and co-bitangent vectors\n vec3 dp2perp = cross(dp2, normal);\n vec3 dp1perp = cross(normal, dp1);\n vec3 dpdu = dp2perp * duv1.x + dp1perp * duv2.x;\n vec3 dpdv = dp2perp * duv1.y + dp1perp * duv2.y;\n float gradientLengthSquared = max(dot(dpdu, dpdu), dot(dpdv, dpdv));\n if (gradientLengthSquared > 1e-12) {\n float invmax = inversesqrt(gradientLengthSquared);\n dpdu *= invmax;\n dpdv *= invmax;\n }\n else {\n // Imported meshes can legitimately omit UVs and use generated or\n // triplanar projection. Their zero UV derivatives must not turn the TBN\n // basis (and then every lighting result) into NaN.\n vec3 referenceAxis = abs(normal.z) < 0.999\n ? vec3(0.0, 0.0, 1.0)\n : vec3(0.0, 1.0, 0.0);\n dpdu = normalize(cross(referenceAxis, normal));\n dpdv = cross(normal, dpdu);\n }\n\n vec3 n = 2.0 * sampleMap(normalMapIndex, uv).rgb - 1.0;\n n.xy *= roughnessAndNormal.zw;\n n = normalize(n);\n\n mat3 tbn = mat3(dpdu, dpdv, normal);\n\n return normalize(tbn * n);\n } else {\n return normal;\n }\n}\n#endif\n\n`\n});\n"]}
|