Skip to content

Commit

Permalink
feat: framework for lambertian importance sampler
Browse files Browse the repository at this point in the history
  • Loading branch information
bhouston committed Jul 29, 2020
1 parent 51477e5 commit 369485e
Show file tree
Hide file tree
Showing 10 changed files with 237 additions and 85 deletions.
24 changes: 5 additions & 19 deletions src/examples/pmrem/lambertian/fragment.glsl
Original file line number Diff line number Diff line change
@@ -1,27 +1,13 @@
precision highp float;

uniform mat4 viewToWorld;
uniform mat4 screenToView;
varying vec3 v_viewPosition;
varying vec3 v_viewNormal;

uniform sampler2D equirectangularMap;

varying vec4 v_homogeneousVertexPosition;

#pragma include <color/spaces/srgb>
#pragma include <cubemaps/equirectangular>
uniform samplerCube cubeMap;

void main() {

// step one, convert from screen space to ray.
vec3 viewPosition = ( viewToWorld * screenToView * v_homogeneousVertexPosition ).xyz;
vec3 viewDirection = normalize( viewPosition );

vec2 equirectangularUv = directionToLatLongUV( viewDirection );

vec3 outputColor = vec3(0.);
outputColor += sRGBToLinear( texture2D( equirectangularMap, equirectangularUv ).rgb );

gl_FragColor.rgb = linearTosRGB( outputColor );
gl_FragColor.a = 1.0;
vec3 reflectDir = reflect( normalize( v_viewPosition ),normalize( v_viewNormal ) );
gl_FragColor = textureCube( cubeMap, reflectDir );

}
69 changes: 51 additions & 18 deletions src/examples/pmrem/lambertian/index.ts
Original file line number Diff line number Diff line change
@@ -1,63 +1,96 @@
import { passGeometry } from "../../../lib/geometry/primitives/passGeometry";
import { icosahedronGeometry } from "../../../lib/geometry/primitives/polyhedronGeometry";
import { ShaderMaterial } from "../../../lib/materials/ShaderMaterial";
import { Euler } from "../../../lib/math/Euler";
import { Matrix4 } from "../../../lib/math/Matrix4";
import {
makeMatrix4Inverse,
makeMatrix4PerspectiveFov,
makeMatrix4RotationFromEuler,
makeMatrix4Translation,
} from "../../../lib/math/Matrix4.Functions";
import { Vector2 } from "../../../lib/math/Vector2";
import { Vector3 } from "../../../lib/math/Vector3";
import { makeBufferGeometryFromGeometry } from "../../../lib/renderers/webgl/buffers/BufferGeometry";
import { DepthTestFunc, DepthTestState } from "../../../lib/renderers/webgl/DepthTestState";
import { Attachment } from "../../../lib/renderers/webgl/framebuffers/Attachment";
import { Framebuffer } from "../../../lib/renderers/webgl/framebuffers/Framebuffer";
import { renderBufferGeometry } from "../../../lib/renderers/webgl/framebuffers/VirtualFramebuffer";
import { makeProgramFromShaderMaterial } from "../../../lib/renderers/webgl/programs/Program";
import { RenderingContext } from "../../../lib/renderers/webgl/RenderingContext";
import { makeTexImage2DFromTexture } from "../../../lib/renderers/webgl/textures/TexImage2D";
import {
makeTexImage2DFromCubeTexture,
makeTexImage2DFromEquirectangularTexture,
} from "../../../lib/renderers/webgl/textures/TexImage2D";
import { TextureFilter } from "../../../lib/renderers/webgl/textures/TextureFilter";
import { TextureWrap } from "../../../lib/renderers/webgl/textures/TextureWrap";
import { cubeFaceTargets, CubeMapTexture } from "../../../lib/textures/CubeTexture";
import { fetchImage } from "../../../lib/textures/loaders/Image";
import { Texture } from "../../../lib/textures/Texture";
import fragmentSource from "./fragment.glsl";
import { samplerMaterial } from "./sampler/SamplerMaterial";
import vertexSource from "./vertex.glsl";

async function init(): Promise<null> {
const geometry = passGeometry();
const passMaterial = new ShaderMaterial(vertexSource, fragmentSource);
const geometry = icosahedronGeometry(0.75, 2);
const material = new ShaderMaterial(vertexSource, fragmentSource);
const garageTexture = new Texture(await fetchImage("/assets/textures/cube/garage/latLong.jpg"));
garageTexture.wrapS = TextureWrap.Repeat;
garageTexture.wrapT = TextureWrap.ClampToEdge;
garageTexture.minFilter = TextureFilter.Linear;
const debugTexture = new Texture(await fetchImage("/assets/textures/cube/debug/latLong.png"));
debugTexture.wrapS = TextureWrap.Repeat;
debugTexture.wrapT = TextureWrap.ClampToEdge;
debugTexture.minFilter = TextureFilter.Linear;

const imageSize = new Vector2(1024, 1024);
const lambertianCubeTexture = new CubeMapTexture([imageSize, imageSize, imageSize, imageSize, imageSize, imageSize]);
lambertianCubeTexture.minFilter = TextureFilter.Linear;
lambertianCubeTexture.generateMipmaps = false;

const context = new RenderingContext(document.getElementById("framebuffer") as HTMLCanvasElement);
const canvasFramebuffer = context.canvasFramebuffer;
window.addEventListener("resize", () => canvasFramebuffer.resize());

const garageMap = makeTexImage2DFromTexture(context, garageTexture);
const debugMap = makeTexImage2DFromTexture(context, debugTexture);
const envCubeMap = makeTexImage2DFromEquirectangularTexture(context, garageTexture, new Vector2(1024, 1024));

const passProgram = makeProgramFromShaderMaterial(context, passMaterial);
const passUniforms = {
const samplerGeometry = passGeometry();
const samplerProgram = makeProgramFromShaderMaterial(context, samplerMaterial);
const samplerUniforms = {
color: new Vector3(1, 0, 0),
viewToWorld: new Matrix4(),
screenToView: makeMatrix4Inverse(makeMatrix4PerspectiveFov(45, 0.1, 4.0, 1.0, canvasFramebuffer.aspectRatio)),
latLongMap: garageMap,
envCubeMap: envCubeMap,
cubeFaceIndex: 0,
};

const samplerBufferGeometry = makeBufferGeometryFromGeometry(context, samplerGeometry);
const lambertianCubeMap = makeTexImage2DFromCubeTexture(context, lambertianCubeTexture);

const framebuffer = new Framebuffer(context);

cubeFaceTargets.forEach((target, index) => {
framebuffer.attach(Attachment.Color0, lambertianCubeMap, target, 0);
samplerUniforms.cubeFaceIndex = index;
samplerUniforms.color.x = index / 6.0;

renderBufferGeometry(framebuffer, samplerProgram, samplerUniforms, samplerBufferGeometry);
});

const program = makeProgramFromShaderMaterial(context, material);
const uniforms = {
localToWorld: new Matrix4(),
worldToView: makeMatrix4Translation(new Vector3(0, 0, -3.0)),
viewToScreen: makeMatrix4PerspectiveFov(25, 0.1, 4.0, 1.0, canvasFramebuffer.aspectRatio),
cubeMap: lambertianCubeMap,
};
const bufferGeometry = makeBufferGeometryFromGeometry(context, geometry);
const depthTestState = new DepthTestState(true, DepthTestFunc.Less);
function animate(): void {
requestAnimationFrame(animate);

const now = Date.now();

passUniforms.viewToWorld = makeMatrix4Inverse(
makeMatrix4RotationFromEuler(new Euler(Math.sin(now * 0.0003), now * 0.0004, 0)),
uniforms.localToWorld = makeMatrix4RotationFromEuler(
new Euler(now * 0.0001, now * 0.00033, now * 0.000077),
uniforms.localToWorld,
);
passUniforms.latLongMap = Math.floor(now / 5000) % 2 === 0 ? garageMap : debugMap;

renderBufferGeometry(canvasFramebuffer, passProgram, passUniforms, bufferGeometry, depthTestState);
renderBufferGeometry(canvasFramebuffer, program, uniforms, bufferGeometry, depthTestState);
}

animate();
Expand Down
5 changes: 5 additions & 0 deletions src/examples/pmrem/lambertian/sampler/SamplerMaterial.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { ShaderMaterial } from "../../../../lib/materials/ShaderMaterial";
import fragmentSource from "./fragment.glsl";
import vertexSource from "./vertex.glsl";

export const samplerMaterial = new ShaderMaterial(vertexSource, fragmentSource);
10 changes: 10 additions & 0 deletions src/examples/pmrem/lambertian/sampler/fragment.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
precision highp float;

uniform vec3 color;

void main() {

gl_FragColor.rgb = color;
gl_FragColor.a = 1.0;

}
7 changes: 7 additions & 0 deletions src/examples/pmrem/lambertian/sampler/vertex.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
attribute vec3 position;

void main() {

gl_Position = vec4( position, 1. );

}
17 changes: 10 additions & 7 deletions src/examples/pmrem/lambertian/vertex.glsl
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
attribute vec3 position;
attribute vec3 normal;

varying vec4 v_homogeneousVertexPosition;
uniform mat4 localToWorld;
uniform mat4 worldToView;
uniform mat4 viewToScreen;

void main() {
varying vec3 v_viewPosition;
varying vec3 v_viewNormal;

// homogeneous vertex position
gl_Position.xy = position.xy;
gl_Position.z = -1.; // position at near clipping plane. (set to 1. for far clipping plane.)
gl_Position.w = 1.; // nortmalized
void main() {

v_homogeneousVertexPosition = gl_Position;
v_viewNormal = normalize( ( worldToView * localToWorld * vec4( normalize( position ), 0. ) ).xyz );
v_viewPosition = ( worldToView * localToWorld * vec4( position, 1. ) ).xyz;
gl_Position = viewToScreen * vec4( v_viewPosition, 1. );

}
62 changes: 62 additions & 0 deletions src/lib/shaders/includes/brdfs/diffuse/lambertSampler.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#include <math/sampling/hammersley>
#include <normals/tangentSpace>

vec3 BRDF_Diffuse_Lambert_SampleDirection( vec2 sampleUv ) {
float phi = PI2 * sampleUv.x;
float cosTheta = 0.;
float sinTheta = 0.;

cosTheta = 1. - sampleUv.y;
sinTheta = sqrt( 1. - cosTheta*cosTheta );

return normalize( vec3( sinTheta * cos( phi ), sinTheta * sin( phi ), cosTheta ) );
}

float BRDF_Diffuse_Lambert_PDF(
const in vec3 normal,
const in vec3 lightDirection ) {

float dotNL = saturate( dot( normal, lightDirection ) );
return dotNL * RECIPROCAL_PI;
}

vec3 sampleIBL( vec3 direction, float lod );

vec3 BRDF_Diffuse_Lambert_Filter(vec3 N, float filterWidth ) {

vec4 color;
const float solidAngleTexel = 4. * PI / (6. * pow2( filterWidth );

for( uint i = 0; i < NUM_SAMPLES; i++ ) {

vec2 sampleUv = hammersley2( sampleIndex, NUM_SAMPLES );
vec3 tangentSpaceSampleDirection = BRDF_Diffuse_Lambert_SampleDirection( sampleUv );
vec3 surfaceSampleDirection = tangentToViewFromNormal( N ) * tangentSpaceSampleDirection;
vec3 H = surfaceSampleDirection;

// Note: reflect takes incident vector.
// Note: N = V
vec3 V = N;
vec3 L = normalize( reflect( -V, H ) );

float dotNL = dot( N, L );

if ( dotNL > 0. ) {
float lod = 0.;

// Mipmap Filtered Samples
// see https://github.com/derkreature/IBLBaker
// see https://developer.nvidia.com/gpugems/GPUGems3/gpugems3_ch20.html
float pdf = BRDF_Diffuse_Lambert_PDF( N, L );

float solidAngleSample = 1.0 / ( float( NUM_SAMPLES ) * pdf );

lod = 0.5 * log2( solidAngleSample / solidAngleTexel );
lod += LOD_BIAS;

color += vec4( sampleIBL( H, lod ), 1.0 );
}
}

return color.rgb / color.w;
}
Loading

0 comments on commit 369485e

Please sign in to comment.