three
Version:
JavaScript 3D library
85 lines (63 loc) • 3.17 kB
JavaScript
export default /* glsl */`
// Transmission code is based on glTF-Sampler-Viewer
// https://github.com/KhronosGroup/glTF-Sample-Viewer
uniform sampler2D transmissionMap;
uniform sampler2D thicknessMap;
uniform vec2 transmissionSamplerSize;
uniform sampler2D transmissionSamplerMap;
uniform mat4 modelMatrix;
uniform mat4 projectionMatrix;
varying vec4 vWorldPosition;
vec3 getVolumeTransmissionRay(vec3 n, vec3 v, float thickness, float ior, mat4 modelMatrix) {
// Direction of refracted light.
vec3 refractionVector = refract(-v, normalize(n), 1.0 / ior);
// Compute rotation-independant scaling of the model matrix.
vec3 modelScale;
modelScale.x = length(vec3(modelMatrix[0].xyz));
modelScale.y = length(vec3(modelMatrix[1].xyz));
modelScale.z = length(vec3(modelMatrix[2].xyz));
// The thickness is specified in local space.
return normalize(refractionVector) * thickness * modelScale;
}
float applyIorToRoughness(float roughness, float ior) {
// Scale roughness with IOR so that an IOR of 1.0 results in no microfacet refraction and
// an IOR of 1.5 results in the default amount of microfacet refraction.
return roughness * clamp(ior * 2.0 - 2.0, 0.0, 1.0);
}
vec3 getTransmissionSample(vec2 fragCoord, float roughness, float ior) {
float framebufferLod = log2(transmissionSamplerSize.x) * applyIorToRoughness(roughness, ior);
return texture2DLodEXT(transmissionSamplerMap, fragCoord.xy, framebufferLod).rgb;
}
vec3 applyVolumeAttenuation(vec3 radiance, float transmissionDistance, vec3 attenuationColor, float attenuationDistance) {
if (attenuationDistance == 0.0) {
// Attenuation distance is +∞ (which we indicate by zero), i.e. the transmitted color is not attenuated at all.
return radiance;
} else {
// Compute light attenuation using Beer's law.
vec3 attenuationCoefficient = -log(attenuationColor) / attenuationDistance;
vec3 transmittance = exp(-attenuationCoefficient * transmissionDistance); // Beer's law
return transmittance * radiance;
}
}
vec3 getIBLVolumeRefraction(vec3 n, vec3 v, float perceptualRoughness, vec3 baseColor, vec3 specularColor,
vec3 position, mat4 modelMatrix, mat4 viewMatrix, mat4 projMatrix, float ior, float thickness,
vec3 attenuationColor, float attenuationDistance) {
vec3 transmissionRay = getVolumeTransmissionRay(n, v, thickness, ior, modelMatrix);
vec3 refractedRayExit = position + transmissionRay;
// Project refracted vector on the framebuffer, while mapping to normalized device coordinates.
vec4 ndcPos = projMatrix * viewMatrix * vec4(refractedRayExit, 1.0);
vec2 refractionCoords = ndcPos.xy / ndcPos.w;
refractionCoords += 1.0;
refractionCoords /= 2.0;
// Sample framebuffer to get pixel the refracted ray hits.
vec3 transmittedLight = getTransmissionSample(refractionCoords, perceptualRoughness, ior);
vec3 attenuatedColor = applyVolumeAttenuation(transmittedLight, length(transmissionRay), attenuationColor, attenuationDistance);
return (1.0 - specularColor) * attenuatedColor * baseColor;
}
`;