Published
Edited
Dec 10, 2020
1 fork
2 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function DepthBuffer(regl, depth) {
this.color = regl.texture({ format: 'rgba', type: 'float', wrap: 'clamp' });
this.depth = depth || regl.renderbuffer({ format: 'depth stencil', radius: 1 });
this.destroyDepth = (depth === undefined);

this.framebuffer = regl.framebuffer({
color: this.color,
depthStencil: this.depth
});

this.dispose = function() {
this.framebuffer.destroy();
this.color.destroy();
if (this.destroyDepth) this.depth.destroy();
};

this.draw = regl({
framebuffer: this.framebuffer,
cull: {
enable: true,
face: 'back'
},
stencil: {
enable: true,
mask: 0xff,
func: {
cmp: 'always',
ref: regl.prop('material.type'),
mask: 0xff
},
op: {
fail: 'keep',
zfail: 'keep',
zpass: 'replace'
},
},
uniforms: {
model: regl.prop('mesh.transform'),
},
attributes: {
position: { buffer: regl.prop('mesh.positions') },
},
primitive: 'triangles',
elements: regl.prop('mesh.cells'),
vert:
`precision mediump float;

attribute vec3 position;

uniform mat4 projection, view, model;

varying float fragDepth;

void main()
{
vec4 p = model * vec4(position, 1.0);
vec4 q = projection * view * p;
fragDepth = q.w;
gl_Position = q;
}`,
frag:
`precision mediump float;

varying float fragDepth;

void main()
{
gl_FragColor = vec4(fragDepth);
}`
});
}
Insert cell
Insert cell
function GBuffer(regl, depth) {
this.color = regl.texture({ format: 'rgba', type: 'float', wrap: 'clamp' });
this.normal = regl.texture({ format: 'rgba', type: 'float', wrap: 'clamp' });
this.data = regl.texture({ format: 'rgba', type: 'float', wrap: 'clamp' });
this.depth = depth || regl.renderbuffer({ format: 'depth stencil', radius: 1 });
this.destroyDepth = (depth === undefined);

this.framebuffer = regl.framebuffer({
color: [ this.color, this.normal, this.data ],
depthStencil: this.depth
});

this.dispose = function() {
this.framebuffer.destroy();
this.color.destroy();
this.normal.destroy();
this.data.destroy();
if (this.destroyDepth) this.depthStencil.destroy();
};

this.draw = regl({
framebuffer: this.framebuffer,
cull: {
enable: true,
face: 'back'
},
stencil: {
enable: true,
mask: 0xff,
func: { cmp: 'always', ref: regl.prop('material.type'), mask: 0xff },
op: { fail: 'keep', zfail: 'keep', zpass: 'replace' }
},
depth: {
enable: true,
mask: false,
func: 'equal'
},
uniforms: {
model: regl.prop('mesh.transform'),
'material.color': regl.prop('material.color'),
'material.normal': regl.prop('material.normal'),
'material.roughness': regl.prop('material.roughness'),
'material.data': regl.prop('material.data')
},
attributes: {
position: { buffer: regl.prop('mesh.positions') },
normal: { buffer: regl.prop('mesh.normals') },
uv: { buffer: regl.prop('mesh.uv') },
tangent: { buffer: regl.prop('mesh.tangents') },
bitangent: { buffer: regl.prop('mesh.bitangents') },
},
primitive: 'triangles',
elements: regl.prop('mesh.cells'),
vert:
`precision mediump float;

attribute vec3 position;
attribute vec3 normal;
attribute vec3 tangent;
attribute vec3 bitangent;
attribute vec2 uv;

uniform mat4 projection, view, model;

varying vec3 fragPosition;
varying vec3 fragNormal;
varying vec3 fragTangent;
varying vec3 fragBitangent;
varying vec2 fragTexCoord;
varying float fragDepth;

void main()
{
mat3 normalMatrix = mat3(model);

vec4 p = model * vec4(position, 1.0);
vec4 q = projection * view * p;

fragPosition = p.xyz;
fragNormal = normalMatrix * normalize(normal);
fragTangent = normalMatrix * normalize(tangent);
fragBitangent = normalMatrix * normalize(bitangent);
fragTexCoord = uv;
fragDepth = q.w;

gl_Position = q;
}`,
frag:
`#extension GL_EXT_draw_buffers : require
precision mediump float;

struct Material
{
sampler2D color;
sampler2D normal;
sampler2D roughness;
sampler2D data;
};

vec3 computeNormal(vec3 N, vec3 T, vec3 B, vec2 uv, sampler2D normalMap)
{
mat3 TBN = mat3(T, B, N);
vec3 Nm = texture2D(normalMap, uv).rgb;
Nm = Nm * 2.0 - 1.0;
Nm = normalize(TBN * Nm);
return Nm;
}

varying vec3 fragPosition;
varying vec3 fragNormal;
varying vec3 fragTangent;
varying vec3 fragBitangent;
varying vec2 fragTexCoord;
varying float fragDepth;

uniform Material material;

void main()
{
vec4 color = pow(texture2D(material.color, fragTexCoord), vec4(2.2));
vec3 normal = computeNormal(fragNormal, fragTangent, fragBitangent, fragTexCoord, material.normal);
float roughness = texture2D(material.roughness, fragTexCoord).r;
vec4 data = texture2D(material.data, fragTexCoord);
gl_FragData[0] = color;
gl_FragData[1] = vec4(normal, roughness);
gl_FragData[2] = data;
}`,
});
}
Insert cell
Insert cell
function LightBuffer(regl, depth) {
this.diffuse = regl.texture({ format: 'rgba', type: 'float', wrap: 'clamp' });
this.specular = regl.texture({ format: 'rgba', type: 'float', wrap: 'clamp' });
this.depth = depth || regl.renderbuffer({ format: 'depth stencil', radius: 1 });
this.destroyDepth = (depth === undefined);

this.framebuffer = regl.framebuffer({
color: [ this.diffuse, this.specular ],
depthStencil: this.depth
});

this.dispose = function() {
this.framebuffer.destroy();
this.diffuse.destroy();
this.specular.destroy();
if (this.destroyDepth) this.depth.destroy();
};

const command = {
framebuffer: this.framebuffer,
cull: {
enable: true,
face: 'back'
},
depth: {
enable: true,
mask: false,
func: 'less'
},
attributes: {
xy: [[-1, -1], [1, 1], [-1, 1], [1, -1]]
},
uniforms: {
'gbuffer.color': regl.prop('gbuffer.color'),
'gbuffer.normal_r': regl.prop('gbuffer.normal'),
'gbuffer.data': regl.prop('gbuffer.data'),
depthbuffer: regl.prop('depthbuffer.color'),
screenToView: (context, props) => { return glm.mat4.invert([], context.projection); },
viewToWorld: (context, props) => { return glm.mat4.invert([], context.view); },
globalIllumination: regl.prop('environment.radiance')
},
primitive: 'triangles',
elements: [[0, 1, 2], [0, 3, 1]],
vert:
`precision mediump float;

attribute vec2 xy;

varying vec2 fragCoords;
varying vec2 fragUV;

void main()
{
fragCoords = xy;
fragUV = (xy + vec2(1.0)) / 2.0;
gl_Position = vec4(xy, 0.0, 1.0);
}`,
frag:
`#extension GL_EXT_draw_buffers : require
precision mediump float;

struct GBuffer
{
sampler2D color;
sampler2D normal_r;
sampler2D data;
};

struct PointLight
{
vec3 position;
vec3 color;
float intensity;
};

const float PI = 3.14159;

vec3 fresnelSchlick(float cosTheta, vec3 F0)
{
return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
}

float DistributionGGX(vec3 N, vec3 H, float roughness)
{
float a = roughness*roughness;
float a2 = a*a;
float NdotH = max(dot(N, H), 0.0);
float NdotH2 = NdotH*NdotH;
float num = a2;
float denom = (NdotH2 * (a2 - 1.0) + 1.0);
denom = PI * denom * denom;
return num / denom;
}

float GeometrySchlickGGX(float NdotV, float roughness)
{
float r = (roughness + 1.0);
float k = (r*r) / 8.0;

float num = NdotV;
float denom = NdotV * (1.0 - k) + k;
return num / denom;
}

float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness)
{
float NdotV = max(dot(N, V), 0.0);
float NdotL = max(dot(N, L), 0.0);
float ggx2 = GeometrySchlickGGX(NdotV, roughness);
float ggx1 = GeometrySchlickGGX(NdotL, roughness);
return ggx1 * ggx2;
}

float fRR(float roughness, vec3 L, vec3 V, vec3 N)
{
float NdotL = max(dot(N, L), 0.0);
float NdotV = max(dot(N, V), 0.0);
float HdotL = max(dot(normalize(L + V), L), 0.0);
float FL = max(pow(1.0 - NdotL, 5.0), 0.0);
float FV = max(pow(1.0 - NdotV, 5.0), 0.0);
float RR = 2.0 * roughness * HdotL * HdotL;
return RR * (FL + FV + FL * FV * (RR - 1.0));
}

vec3 fS(float roughness, vec3 L, vec3 V, vec3 N)
{
vec3 H = normalize(L + V);

vec3 F0 = vec3(0.04);
vec3 F = fresnelSchlick(max(dot(H, V), 0.0), F0);

float NDF = DistributionGGX(N, H, roughness);
float G = GeometrySmith(N, V, L, roughness);

vec3 numerator = NDF * G * F;
float denominator = 4.0 * max(dot(N, V), 0.0) * max(dot(N, L), 0.0);
vec3 specular = numerator / max(denominator, 0.001);
return specular;
}

varying vec2 fragCoords;
varying vec2 fragUV;

uniform GBuffer gbuffer;
uniform sampler2D depthbuffer;

uniform vec3 eye;
uniform mat4 projection, view, screenToView, viewToWorld;

const int numLights = 5;
uniform samplerCube globalIllumination;
uniform PointLight lights[numLights];
uniform samplerCube shadowmaps[numLights];

void main()
{

vec4 color = texture2D(gbuffer.color, fragUV);
vec4 normal_r = texture2D(gbuffer.normal_r, fragUV);
vec4 data = texture2D(gbuffer.data, fragUV);
float depth = texture2D(depthbuffer, fragUV).r;

// No pretexturing (for materials that have implicity scattering in their diffuse texture)
vec3 baseColor = color.a * vec3(1.0) / PI;

// Completely pre-texture (for materials with no scattering)
if (data.w == 0.0) baseColor = baseColor * color.rgb;

// Partially pre-texture. Color will be blended with surrounding pixels during SSS,
// bluring the diffuse texture a bit
if (data.w == 2.0) baseColor = baseColor * sqrt(color.rgb);

float roughness = normal_r.a;

vec3 fragViewPosition = (screenToView * (depth * vec4(fragCoords, 0.0, 1.0))).xyz;
vec3 fragWorldPosition = (viewToWorld * vec4(fragViewPosition, 1.0)).xyz;

vec3 V = normalize(eye - fragWorldPosition);
vec3 N = normal_r.rgb;
float NdotV = max(dot(N, V), 0.0);
float FV = max(pow(1.0 - NdotV, 5.0), 0.0);

vec3 diffuse = vec3(0.0);
vec3 retroreflect = vec3(0.0);
vec3 specular = vec3(0.0);
for (int i = 0; i < numLights; ++i)
{
PointLight light = lights[i];
vec3 L = normalize(light.position - fragWorldPosition);

float r = distance(light.position, fragWorldPosition);

vec3 up = vec3(0.0, 1.0, 0.0);
vec3 right = cross(up, -L);
up = cross(-L, right);

// Quick PCF Shadows from the point lights
float shadow = 0.0;
vec2 sampleDelta = PI / (2.0 * vec2(1024.0));
float bias = max(0.05 * (1.0 - dot(N, L)), 0.005);
for(int x = -1; x <= 1; ++x)
{
for(int y = -1; y <= 1; ++y)
{
float theta = float(x) * sampleDelta.x;
float phi = float(y) * sampleDelta.y;

// spherical to cartesian (in tangent space)
vec3 tangentSample = vec3(sin(theta) * cos(phi), sin(theta) * sin(phi), cos(theta));
// tangent space to world
vec3 sampleVec = tangentSample.x * right + tangentSample.y * up + tangentSample.z * (-L);

float pcfDepth = textureCube(shadowmaps[i], sampleVec).r;
shadow += r - bias < pcfDepth ? 1.0 : 0.0;
}
}
shadow /= 9.0;

vec3 radiance = light.intensity * pow(light.color, vec3(2.2)) / (4.0 * PI * r * r);
float NdotL = max(dot(N, L), 0.0);
float FL = max(pow(1.0 - NdotL, 5.0), 0.0);

diffuse += baseColor * (1.0 - 0.5 * FL) * (1.0 - 0.5 * FV) * radiance * shadow * NdotL;
retroreflect += baseColor * fRR(roughness, L, V, N) * radiance * shadow * NdotL;
specular += fS(roughness, L, V, N) * radiance * shadow * NdotL;
}

vec3 ambient = textureCube(globalIllumination, N).rgb;
diffuse += baseColor * (1.0 - 0.5 * FV) * ambient;

// These are more complicated to do with image-based lighting, so they are not implemented
// retroreflect += color.rgb / PI * fRR(roughness, N, V, N) * vec3(0.0);
// specular += fS(roughness, N, V, N) * vec3(0.0);

gl_FragData[0] = vec4(diffuse + retroreflect, 1.0);
gl_FragData[1] = vec4(specular, 1.0);
}`,
};

for (let i = 0; i < 5; i++) {
command.uniforms['lights[' + i + '].position'] = (context, props) => {
return props['lights'][i].position;
};
command.uniforms['lights[' + i + '].color'] = (context, props) => {
return hex2rgb(props['lights'][i].color);
};
command.uniforms['lights[' + i + '].intensity'] = (context, props) => {
return props['lights'][i].intensity;
};
command.uniforms['shadowmaps[' + i + ']'] = (context, props) => {
return props['lights'][i].shadowmap;
};
}

this.draw = regl(command);
}
Insert cell
Insert cell
function TranslucencyBuffer(regl, depth) {
this.color = regl.texture({ format: 'rgba', type: 'float', wrap: 'clamp' });
this.depth = depth || regl.renderbuffer({ format: 'depth stencil', radius: 1 });

this.framebuffer = regl.framebuffer({
color: this.color,
depthStencil: this.depth
});

const command = {
framebuffer: this.framebuffer,
cull: {
enable: true,
face: 'front'
},
uniforms: {
model: regl.prop('mesh.transform'),
'material.type': regl.prop('material.type'),
'material.color': regl.prop('material.color'),
'material.normal': regl.prop('material.normal'),
'material.roughness': regl.prop('material.roughness'),
'material.data': regl.prop('material.data'),
globalIllumination: regl.prop('environment.radiance'),
screenToView: (context, props) => { return glm.mat4.invert([], context.projection); },
viewToWorld: (context, props) => { return glm.mat4.invert([], context.view); }
},
attributes: {
position: { buffer: regl.prop('mesh.positions') },
normal: { buffer: regl.prop('mesh.normals') },
uv: { buffer: regl.prop('mesh.uv') },
tangent: { buffer: regl.prop('mesh.tangents') },
bitangent: { buffer: regl.prop('mesh.bitangents') },
},
primitive: 'triangles',
elements: regl.prop('mesh.cells'),
vert:
`precision mediump float;

attribute vec3 position;
attribute vec3 normal;
attribute vec3 tangent;
attribute vec3 bitangent;
attribute vec2 uv;

uniform mat4 projection, view, model, screenToView, viewToWorld;

varying vec3 fragPosition;
varying vec3 fragNormal;
varying vec3 fragTangent;
varying vec3 fragBitangent;
varying vec2 fragCoord;
varying vec2 fragTexCoord;
varying float fragDepth;

void main()
{
mat3 normalMatrix = mat3(model);//transpose(inverse(mat3(model)));

vec4 p = model * vec4(position, 1.0);
vec4 q = projection * view * p;

fragPosition = p.xyz;
fragNormal = normalMatrix * normalize(normal);
fragTangent = normalMatrix * normalize(tangent);
fragBitangent = normalMatrix * normalize(bitangent);
fragCoord = q.xy / q.w;
fragTexCoord = uv;
fragDepth = q.w;

gl_Position = q;
}`,
frag:
`precision mediump float;

struct Material
{
int type;
sampler2D color;
sampler2D normal;
sampler2D roughness;
sampler2D data;
};

struct PointLight
{
vec3 position;
vec3 color;
float intensity;
};

vec3 computeNormal(vec3 N, vec3 T, vec3 B, vec2 uv, sampler2D normalMap)
{
mat3 TBN = mat3(T, B, N);
vec3 Nm = texture2D(normalMap, uv).rgb;
Nm = Nm * 2.0 - 1.0;
Nm = normalize(TBN * Nm);
return Nm;
}

const float PI = 3.14159;

varying vec3 fragPosition;
varying vec3 fragNormal;
varying vec3 fragTangent;
varying vec3 fragBitangent;
varying vec2 fragTexCoord;
varying vec2 fragCoord;
varying float fragDepth;

uniform mat4 projection, view, model, screenToView, viewToWorld;
uniform Material material;

const int numLights = 5;
uniform vec3 eye;
uniform samplerCube globalIllumination;
uniform PointLight lights[numLights];
uniform samplerCube shadowmaps[numLights];

void main()
{
vec4 color = pow(texture2D(material.color, fragTexCoord), vec4(2.2));
vec3 normal = computeNormal(fragNormal, fragTangent, fragBitangent, fragTexCoord, material.normal);
float roughness = texture2D(material.roughness, fragTexCoord).r;
vec4 data = texture2D(material.data, fragTexCoord);

// No pretexturing (for materials that have implicity scattering in their diffuse texture)
vec3 baseColor = vec3(1.0) / PI;

// Completely pre-texture (for materials with no scattering)
if (data.w == 0.0) baseColor = baseColor * color.rgb;

// Partially pre-texture. Color will be blended with surrounding pixels during SSS,
// bluring the diffuse texture a bit
if (data.w == 2.0) baseColor = baseColor * sqrt(color.rgb);

vec3 V = normalize(eye - fragPosition);
vec3 N = normal;
float NdotV = max(dot(N, V), 0.0);
float FV = max(pow(1.0 - NdotV, 5.0), 0.0);

vec3 transmission = vec3(0.0);
for (int i = 0; i < numLights; ++i)
{
PointLight light = lights[i];
vec3 L = normalize(light.position - fragPosition);
float r = distance(light.position, fragPosition);

float closestDistance = textureCube(shadowmaps[i], -L).r;

float bias = max(0.05 * (1.0 - dot(N, L)), 0.005);
float shadow = r - bias < closestDistance ? 1.0 : 0.0;

vec3 radiance = light.intensity * pow(light.color, vec3(2.2)) / (4.0 * PI * r * r);
float NdotL = max(dot(N, L), 0.0);
float FL = max(pow(1.0 - NdotL, 5.0), 0.0);

transmission += baseColor * (1.0 - 0.5 * FL) * (1.0 - 0.5 * FV) * radiance * shadow * NdotL;
}

vec3 ambient = textureCube(globalIllumination, N).rgb;
transmission += baseColor * ambient;

gl_FragColor = vec4(transmission, fragDepth);
}`,
};

for (let i = 0; i < 5; i++) {
command.uniforms['lights[' + i + '].position'] = (context, props) => {
return props['lights'][i].position;
};
command.uniforms['lights[' + i + '].color'] = (context, props) => {
return hex2rgb(props['lights'][i].color);
};
command.uniforms['lights[' + i + '].intensity'] = (context, props) => {
return props['lights'][i].intensity;
};
command.uniforms['shadowmaps[' + i + ']'] = (context, props) => {
return props['lights'][i].shadowmap;
};
}

this.draw = regl(command);
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function SSSSSPass(regl) {
const renderable = {
depth: { enable: false },
stencil: {
enable: true,
mask: 0x00,
func: {
cmp: 'equal',
ref: 2,
mask: 0xff
},
op: {
fail: 'keep',
zfail: 'keep',
zpass: 'keep'
},
},
attributes: {
xy: [[-1, -1], [1, 1], [-1, 1], [1, -1]]
},
uniforms: {
'gbuffer.color': regl.prop('gbuffer.color'),
'gbuffer.normal_r': regl.prop('gbuffer.normal'),
'gbuffer.data': regl.prop('gbuffer.data'),
'lightbuffer.diffuse': regl.prop('lightbuffer.diffuse'),
'lightbuffer.specular': regl.prop('lightbuffer.specular'),
depthbuffer: regl.prop('depthbuffer.color'),
translucencybuffer: regl.prop('translucencybuffer.color'),
screenToView: (context, props) => { return glm.mat4.invert([], context.projection); },
viewToWorld: (context, props) => { return glm.mat4.invert([], context.view); },
width: (context, props) => props['gbuffer'].color.width,
height: (context, props) => props['gbuffer'].color.height,
samples: regl.prop('samples')
},
primitive: 'triangles',
elements: [[0, 1, 2], [0, 3, 1]],
vert:
`precision mediump float;

attribute vec2 xy;

varying vec2 fragCoords;
varying vec2 fragUV;

void main()
{
fragCoords = xy;
fragUV = (xy + vec2(1.0)) / 2.0;
gl_Position = vec4(xy, 0.0, 1.0);
}`,
frag:
`precision mediump float;

struct GBuffer
{
sampler2D color;
sampler2D normal_r;
sampler2D data;
};

struct LightBuffer
{
sampler2D diffuse;
sampler2D specular;
};

varying vec2 fragCoords;
varying vec2 fragUV;

uniform vec3 eye;
uniform float width, height;
uniform mat4 projection, view, screenToView, viewToWorld;

uniform GBuffer gbuffer;
uniform LightBuffer lightbuffer;
uniform sampler2D translucencybuffer;
uniform sampler2D depthbuffer;

const int sampleCount = ` + 512 + `;
uniform vec2 samples[sampleCount];

const float PI = 3.14159;

float random (vec2 st) {
return fract(sin(dot(st.xy, vec2(12.9898,78.233))) * 43758.5453123);
}

vec3 Rd(vec3 scatterDistance, float radius)
{
return (exp(-radius / scatterDistance) + exp(-radius / (scatterDistance * 3.0))) / (8.0 * PI * scatterDistance) / radius;
}

vec3 p(vec3 scatterDistance, float radius)
{
return (exp(-radius / scatterDistance) + exp(-radius / (scatterDistance * 3.0))) / (8.0 * PI * scatterDistance);
}

void main()
{
vec4 baseColorSample = texture2D(gbuffer.color, fragUV);
vec4 dataSample = texture2D(gbuffer.data, fragUV);
vec4 diffuseSample = texture2D(lightbuffer.diffuse, fragUV);
vec4 specularSample = texture2D(lightbuffer.specular, fragUV);

vec3 baseColor = baseColorSample.rgb;
vec3 diffuse = diffuseSample.rgb;
vec3 specular = specularSample.rgb;
vec3 scatterDistance = dataSample.rgb;

float depth = texture2D(depthbuffer, fragUV).r;
float depthBack = texture2D(translucencybuffer, fragUV).a;

float maxRadius = max(max(scatterDistance.r, max(scatterDistance.g, scatterDistance.b)), 0.00001);

vec3 fragViewPosition = (screenToView * (depth * vec4(fragCoords, 0.0, 1.0))).xyz;
vec3 fragWorldPosition = (viewToWorld * vec4(fragViewPosition, 1.0)).xyz;

float jitter = 2.0 * PI * random(mod(fragUV * vec2(width, height), 64.0));

vec3 sssDiffuse = vec3(0.0);
vec3 weight = vec3(0.0);
for (int i = 0; i < sampleCount; i++)
{
float r = samples[i].y * maxRadius;
float theta = samples[i].x + jitter;

vec2 sampleCoords = vec2(cos(theta) * r, sin(theta) * r);
sampleCoords = ((projection * vec4(fragViewPosition + vec3(sampleCoords, 0.0), 1.0)) / depth).xy;
vec2 sampleUV = (sampleCoords + 1.0) / 2.0;
// sample front faces
{
vec4 sample = texture2D(lightbuffer.diffuse, sampleUV);
float sampleDepth = texture2D(depthbuffer, sampleUV).r;

vec3 sampleViewPosition = (screenToView * (sampleDepth * vec4(sampleCoords, 0.0, 1.0))).xyz;
vec3 sampleWorldPosition = (viewToWorld * vec4(sampleViewPosition, 1.0)).xyz;

float radius = max(distance(sampleWorldPosition, fragWorldPosition), 0.00001);
vec3 diffusion = Rd(scatterDistance, radius);
vec3 pdf = p(vec3(maxRadius), r);

sssDiffuse += diffusion / pdf * sample.rgb;
weight += diffusion / pdf;
}

// sample back faces
{
vec4 sample = texture2D(translucencybuffer, sampleUV);
float sampleDepth = sample.a;

vec3 sampleViewPosition = (screenToView * (sampleDepth * vec4(sampleCoords, 0.0, 1.0))).xyz;
vec3 sampleWorldPosition = (viewToWorld * vec4(sampleViewPosition, 1.0)).xyz;

float radius = max(distance(sampleWorldPosition, fragWorldPosition), 0.00001);
vec3 diffusion = Rd(scatterDistance, radius);
vec3 pdf = p(vec3(maxRadius), r);

sssDiffuse += diffusion / pdf * sample.rgb;
weight += diffusion / pdf;
}
}

// Divide by sum of weights to renormalize
sssDiffuse = sssDiffuse / max(weight, 0.0001);

if (dataSample.a == 0.0) baseColor = vec3(1.0);
if (dataSample.a == 2.0) baseColor = sqrt(baseColor);

vec3 color = baseColor * sssDiffuse;

// revert to diffuse BRDF if scatter distance is 0
if (scatterDistance.r <= 0.0) color.r = baseColor.r * diffuse.r;
if (scatterDistance.g <= 0.0) color.g = baseColor.g * diffuse.g;
if (scatterDistance.b <= 0.0) color.b = baseColor.b * diffuse.b;

gl_FragColor = vec4(color + specular, 1.0);
}`
};
for (let i = 0; i < 512; i++)
renderable.uniforms['samples[' + i + ']'] = (context, props) => {
return props['samples'][i];
};
this.draw = regl(renderable);
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more