|
@@ -261,169 +261,6 @@ class PbrMaterialShader extends hxsl.Shader {
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
-class IrradBase extends h3d.shader.ScreenShader {
|
|
|
|
-
|
|
|
|
- static var SRC = {
|
|
|
|
-
|
|
|
|
- @const var samplesBits : Int;
|
|
|
|
- @const var useTable : Bool;
|
|
|
|
- @const(1024) var SamplesCount : Int;
|
|
|
|
- @param var hammerTbl : Array<Vec4,SamplesCount>;
|
|
|
|
- function _reversebits( i : Int ) : Int {
|
|
|
|
- var r = (i << 16) | (i >>> 16);
|
|
|
|
- r = ((r & 0x00ff00ff) << 8) | ((r & 0xff00ff00) >>> 8);
|
|
|
|
- r = ((r & 0x0f0f0f0f) << 4) | ((r & 0xf0f0f0f0) >>> 4);
|
|
|
|
- r = ((r & 0x33333333) << 2) | ((r & 0xcccccccc) >>> 2);
|
|
|
|
- r = ((r & 0x55555555) << 1) | ((r & 0xaaaaaaaa) >>> 1);
|
|
|
|
- return r;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- function hammersley( i : Int, max : Int ) : Vec2 {
|
|
|
|
- if( useTable )
|
|
|
|
- return hammerTbl[i].xy;
|
|
|
|
- var ri = _reversebits(i) * 2.3283064365386963e-10;
|
|
|
|
- return vec2(float(i) / float(max), ri);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- function importanceSampleGGX( roughness : Float, p : Vec2, n : Vec3 ) : Vec3 {
|
|
|
|
- var a = roughness * roughness;
|
|
|
|
- var phi = 2 * PI * p.x;
|
|
|
|
- var cosT = sqrt((1 - p.y) / (1 + (a * a - 1) * p.y)).min(1.);
|
|
|
|
- var sinT = sqrt(1 - cosT * cosT);
|
|
|
|
- var ltan = vec3(sinT * cos(phi), sinT * sin(phi), cosT);
|
|
|
|
-
|
|
|
|
- var up = abs(n.z) < 0.999 ? vec3(0, 0, 1) : vec3(1, 0, 0);
|
|
|
|
- var tanX = normalize(cross(up, n));
|
|
|
|
- var tanY = normalize(cross(n, tanX));
|
|
|
|
- return tanX * ltan.x + tanY * ltan.y + n * ltan.z;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- };
|
|
|
|
-
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-class Irradiance extends IrradBase {
|
|
|
|
-
|
|
|
|
- static var SRC = {
|
|
|
|
-
|
|
|
|
- @const var face : Int;
|
|
|
|
- @param var envMap : SamplerCube;
|
|
|
|
-
|
|
|
|
- @const var isSpecular : Bool;
|
|
|
|
- @param var roughness : Float;
|
|
|
|
-
|
|
|
|
- @param var cubeSize : Float;
|
|
|
|
- @param var cubeScaleFactor : Float;
|
|
|
|
-
|
|
|
|
- function cosineWeightedSampling( p : Vec2, n : Vec3 ) : Vec3 {
|
|
|
|
- var sq = sqrt(1 - p.x);
|
|
|
|
- var alpha = 2 * PI * p.y;
|
|
|
|
- var ltan = vec3( sq * cos(alpha), sq * sin(alpha), sqrt(p.x));
|
|
|
|
-
|
|
|
|
- var up = abs(n.z) < 0.999 ? vec3(0, 0, 1) : vec3(1, 0, 0);
|
|
|
|
- var tanX = normalize(cross(up, n));
|
|
|
|
- var tanY = normalize(cross(n, tanX));
|
|
|
|
- return tanX * ltan.x + tanY * ltan.y + n * ltan.z;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- function getNormal() : Vec3 {
|
|
|
|
- var d = input.uv * 2. - 1.;
|
|
|
|
- if( isSpecular ) {
|
|
|
|
- // WRAP edge fixup
|
|
|
|
- // https://seblagarde.wordpress.com/2012/06/10/amd-cubemapgen-for-physically-based-rendering/
|
|
|
|
- d += cubeScaleFactor * (d * d * d);
|
|
|
|
- }
|
|
|
|
- #if hldx
|
|
|
|
- d.y *= -1;
|
|
|
|
- #end
|
|
|
|
- var n : Vec3;
|
|
|
|
- switch( face ) {
|
|
|
|
- case 0: n = vec3(1, d.y, -d.x);
|
|
|
|
- case 1: n = vec3(-1, d.y, d.x);
|
|
|
|
- case 2: n = vec3(d.x, 1, -d.y);
|
|
|
|
- case 3: n = vec3(d.x, -1, d.y);
|
|
|
|
- case 4: n = vec3(d.x, d.y, 1);
|
|
|
|
- default: n = vec3(-d.x, d.y,-1);
|
|
|
|
- }
|
|
|
|
- return n.normalize();
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- function fragment() {
|
|
|
|
- var color = vec3(0.);
|
|
|
|
- var n = getNormal();
|
|
|
|
- var totalWeight = 1e-10;
|
|
|
|
- var numSamples = 1 << samplesBits;
|
|
|
|
- for( i in 0...numSamples ) {
|
|
|
|
- var p = hammersley(i, numSamples);
|
|
|
|
- var l : Vec3;
|
|
|
|
- if( isSpecular ) {
|
|
|
|
- var h = importanceSampleGGX(roughness, p, n);
|
|
|
|
- var v = n; // approx
|
|
|
|
- l = reflect(-v, h).normalize();
|
|
|
|
- } else {
|
|
|
|
- l = cosineWeightedSampling(p, n);
|
|
|
|
- }
|
|
|
|
- var amount = n.dot(l).saturate();
|
|
|
|
- if( amount > 0 ) {
|
|
|
|
- color += envMap.get(l).rgb.pow(vec3(2.2)) * amount;
|
|
|
|
- totalWeight += amount;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- output.color = vec4((color / totalWeight).pow(vec3(1/2.2)), 1.);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-class IrradianceLut extends IrradBase {
|
|
|
|
-
|
|
|
|
- static var SRC = {
|
|
|
|
-
|
|
|
|
- function GGX( NdotV : Float, roughness : Float ) : Float {
|
|
|
|
- var k = (roughness * roughness) * 0.5; // only in IBL, use (r + 1)² / 8 for lighting
|
|
|
|
- return NdotV / (NdotV * (1.0 - k) + k);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- function G_Smith(roughness : Float, nDotV : Float, nDotL : Float) : Float {
|
|
|
|
- return GGX(nDotL, roughness) * GGX(nDotV, roughness);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- function fragment() {
|
|
|
|
- var roughness = input.uv.x;
|
|
|
|
- var NoV = input.uv.y;
|
|
|
|
- var v = vec3( sqrt( 1.0 - NoV * NoV ), 0., NoV );
|
|
|
|
- var n = vec3(0, 0, 1.);
|
|
|
|
- var numSamples = 1 << samplesBits;
|
|
|
|
- var a = 0., b = 0.;
|
|
|
|
- for( i in 0...numSamples ) {
|
|
|
|
- var xi = hammersley(i, numSamples);
|
|
|
|
- var h = importanceSampleGGX( roughness, xi, n );
|
|
|
|
- var l = reflect(-v, h);
|
|
|
|
- var NoL = saturate( dot(n, l) );
|
|
|
|
- var NoH = saturate( dot(n, h) );
|
|
|
|
- var VoH = saturate( dot(v, h) );
|
|
|
|
- if( NoL > 0 ) {
|
|
|
|
- var g = G_Smith( roughness, NoV, NoL );
|
|
|
|
- var gvis = g * VoH / (NoH * NoV);
|
|
|
|
- var fresnel = pow( 1 - VoH, 5. );
|
|
|
|
- a += (1 - fresnel) * gvis;
|
|
|
|
- b += fresnel * gvis;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- output.color = vec4(a / numSamples, b / numSamples, 0, 1);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- public function new() {
|
|
|
|
- super();
|
|
|
|
- samplesBits = 10;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
class CubeMap extends hxsl.Shader {
|
|
class CubeMap extends hxsl.Shader {
|
|
|
|
|
|
static var SRC = {
|
|
static var SRC = {
|
|
@@ -455,14 +292,11 @@ class Pbr extends hxd.App {
|
|
var sphere : h3d.scene.Mesh;
|
|
var sphere : h3d.scene.Mesh;
|
|
var grid : h3d.scene.Object;
|
|
var grid : h3d.scene.Object;
|
|
|
|
|
|
- var sampleBits : Int;
|
|
|
|
-
|
|
|
|
var shader : PbrShader;
|
|
var shader : PbrShader;
|
|
var material : PbrMaterialShader;
|
|
var material : PbrMaterialShader;
|
|
|
|
|
|
var envMap : h3d.mat.Texture;
|
|
var envMap : h3d.mat.Texture;
|
|
- var irradDiffuse : h3d.mat.Texture;
|
|
|
|
- var irradSpecular : h3d.mat.Texture;
|
|
|
|
|
|
+ var irrad : h3d.scene.pbr.Irradiance;
|
|
|
|
|
|
override function init() {
|
|
override function init() {
|
|
|
|
|
|
@@ -535,23 +369,14 @@ class Pbr extends hxd.App {
|
|
axis.material.blendMode = Alpha;
|
|
axis.material.blendMode = Alpha;
|
|
axis.visible = false;
|
|
axis.visible = false;
|
|
|
|
|
|
- var size, ssize;
|
|
|
|
- size = 64;
|
|
|
|
- ssize = 256;
|
|
|
|
- sampleBits = 10;
|
|
|
|
-
|
|
|
|
- computeIrradLut();
|
|
|
|
-
|
|
|
|
- irradDiffuse = new h3d.mat.Texture(size, size, [Cube, Target]);
|
|
|
|
- irradDiffuse.name = "irradDiffuse";
|
|
|
|
- irradSpecular = new h3d.mat.Texture(ssize, ssize, [Cube, Target, MipMapped, ManualMipMapGen]);
|
|
|
|
- irradSpecular.name = "irradSpecular";
|
|
|
|
- irradSpecular.mipMap = Linear;
|
|
|
|
- computeIrradiance();
|
|
|
|
|
|
+ irrad = new h3d.scene.pbr.Irradiance(envMap);
|
|
|
|
+ irrad.compute();
|
|
|
|
|
|
shader.envMap = envMap;
|
|
shader.envMap = envMap;
|
|
- shader.irrDiffuse = irradDiffuse;
|
|
|
|
- shader.irrSpecular = irradSpecular;
|
|
|
|
|
|
+ shader.irrDiffuse = irrad.diffuse;
|
|
|
|
+ shader.irrSpecular = irrad.specular;
|
|
|
|
+ shader.irrLut = irrad.lut;
|
|
|
|
+ shader.irrSpecularLevels = irrad.specLevels;
|
|
|
|
|
|
var cubeShader = bg.material.mainPass.addShader(new CubeMap(envMap));
|
|
var cubeShader = bg.material.mainPass.addShader(new CubeMap(envMap));
|
|
|
|
|
|
@@ -632,8 +457,8 @@ class Pbr extends hxd.App {
|
|
var prims : Array<h3d.prim.Primitive> = [sp, cube];
|
|
var prims : Array<h3d.prim.Primitive> = [sp, cube];
|
|
addChoice("Prim", ["Sphere","Cube"], function(i) sphere.primitive = prims[i], prims.indexOf(sphere.primitive));
|
|
addChoice("Prim", ["Sphere","Cube"], function(i) sphere.primitive = prims[i], prims.indexOf(sphere.primitive));
|
|
|
|
|
|
- addChoice("EnvMap", ["Default", "IDiff", "ISpec"], function(i) cubeShader.texture = [envMap, irradDiffuse, irradSpecular][i]);
|
|
|
|
- addSlider("EnvLod", 0, 10, function() return cubeShader.lod, function(v) cubeShader.lod = v);
|
|
|
|
|
|
+ addChoice("EnvMap", ["Default", "IDiff", "ISpec"], function(i) cubeShader.texture = [envMap, irrad.diffuse, irrad.specular][i]);
|
|
|
|
+ addSlider("EnvLod", 0, irrad.specLevels, function() return cubeShader.lod, function(v) cubeShader.lod = v);
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
@@ -658,52 +483,6 @@ class Pbr extends hxd.App {
|
|
sphere.material.mainPass.addShader(mat);
|
|
sphere.material.mainPass.addShader(mat);
|
|
}
|
|
}
|
|
|
|
|
|
- function computeIrradLut() {
|
|
|
|
- var irradLut = new h3d.mat.Texture(128, 128, [Target], RGBA16F);
|
|
|
|
- irradLut.name = "irradLut";
|
|
|
|
- var screen = new h3d.pass.ScreenFx(new IrradianceLut());
|
|
|
|
- screen.shader.samplesBits = sampleBits;
|
|
|
|
-
|
|
|
|
- engine.driver.setRenderTarget(irradLut);
|
|
|
|
- screen.render();
|
|
|
|
- engine.driver.setRenderTarget(null);
|
|
|
|
- shader.irrLut = irradLut;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- function computeIrradiance() {
|
|
|
|
-
|
|
|
|
- var screen = new h3d.pass.ScreenFx(new Irradiance());
|
|
|
|
- screen.shader.samplesBits = sampleBits;
|
|
|
|
- screen.shader.envMap = envMap;
|
|
|
|
-
|
|
|
|
- for( i in 0...6 ) {
|
|
|
|
- screen.shader.face = i;
|
|
|
|
- engine.driver.setRenderTarget(irradDiffuse, i);
|
|
|
|
- screen.render();
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- screen.shader.isSpecular = true;
|
|
|
|
-
|
|
|
|
- var mipLevels = 1;
|
|
|
|
- while( irradSpecular.width > 1 << (mipLevels - 1) )
|
|
|
|
- mipLevels++;
|
|
|
|
-
|
|
|
|
- shader.irrSpecularLevels = mipLevels - 5;
|
|
|
|
-
|
|
|
|
- for( i in 0...6 ) {
|
|
|
|
- screen.shader.face = i;
|
|
|
|
- for( j in 0...mipLevels ) {
|
|
|
|
- var size = irradSpecular.width >> j;
|
|
|
|
- screen.shader.cubeSize = size;
|
|
|
|
- screen.shader.cubeScaleFactor = size == 1 ? 0 : (size * size) / Math.pow(size - 1, 3);
|
|
|
|
- screen.shader.roughness = j / shader.irrSpecularLevels;
|
|
|
|
- engine.driver.setRenderTarget(irradSpecular, i, j);
|
|
|
|
- screen.render();
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- engine.driver.setRenderTarget(null);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
function addCheck( text, get : Void -> Bool, set : Bool -> Void ) {
|
|
function addCheck( text, get : Void -> Bool, set : Bool -> Void ) {
|
|
var i = new h2d.Interactive(110, font.lineHeight, fui);
|
|
var i = new h2d.Interactive(110, font.lineHeight, fui);
|
|
i.backgroundColor = 0xFF808080;
|
|
i.backgroundColor = 0xFF808080;
|
|
@@ -804,84 +583,6 @@ class Pbr extends hxd.App {
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
-
|
|
|
|
- // ----- DEBUG
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- function _reversebits( i : Int ) : Int {
|
|
|
|
- var r = (i << 16) | (i >>> 16);
|
|
|
|
- r = ((r & 0x00ff00ff) << 8) | ((r & 0xff00ff00) >>> 8);
|
|
|
|
- r = ((r & 0x0f0f0f0f) << 4) | ((r & 0xf0f0f0f0) >>> 4);
|
|
|
|
- r = ((r & 0x33333333) << 2) | ((r & 0xcccccccc) >>> 2);
|
|
|
|
- r = ((r & 0x55555555) << 1) | ((r & 0xaaaaaaaa) >>> 1);
|
|
|
|
- return r;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- function hammersley( i : Int, max : Int ) : h3d.Vector {
|
|
|
|
- var ri = _reversebits(i) * 2.3283064365386963e-10;
|
|
|
|
- return new h3d.Vector(i / max, ri);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- function cosineWeightedSampling( p : h3d.Vector ) : h3d.Vector {
|
|
|
|
- var sq = Math.sqrt(1 - p.x);
|
|
|
|
- var alpha = 2 * Math.PI * p.y;
|
|
|
|
- return new h3d.Vector( sq * Math.cos(alpha), sq * Math.sin(alpha), Math.sqrt(p.x));
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- function displaySampling( max : Int, n : h3d.Vector, ?roughness : Float ) {
|
|
|
|
- n.normalize();
|
|
|
|
- var up = n.z < 0.999 ? new h3d.Vector(0, 0, 1) : new h3d.Vector(1, 0, 0);
|
|
|
|
- var tanX = up.cross(n);
|
|
|
|
- tanX.normalize();
|
|
|
|
- var tanY = n.cross(tanX);
|
|
|
|
- tanY.normalize();
|
|
|
|
-
|
|
|
|
- var sp = new h3d.prim.Sphere(0.01, 4, 4);
|
|
|
|
- for( i in 0...max ) {
|
|
|
|
- var h = hammersley(i, max);
|
|
|
|
-
|
|
|
|
- var l = new h3d.Vector();
|
|
|
|
- if( roughness != null ) {
|
|
|
|
-
|
|
|
|
- var a = roughness * roughness;
|
|
|
|
- var phi = 2 * Math.PI * h.x;
|
|
|
|
- var cosT = Math.min(Math.sqrt((1 - h.y) / (1 + (a * a - 1) * h.y)), 1.);
|
|
|
|
- var sinT = Math.sqrt(1 - cosT * cosT);
|
|
|
|
- var ltan = new h3d.Vector(sinT * Math.cos(phi), sinT * Math.sin(phi), cosT);
|
|
|
|
-
|
|
|
|
- var h = new h3d.Vector(
|
|
|
|
- tanX.x * ltan.x + tanY.x * ltan.y + n.x * ltan.z,
|
|
|
|
- tanX.y * ltan.x + tanY.y * ltan.y + n.y * ltan.z,
|
|
|
|
- tanX.z * ltan.x + tanY.z * ltan.y + n.z * ltan.z
|
|
|
|
- );
|
|
|
|
-
|
|
|
|
- var v = n; // approx
|
|
|
|
-
|
|
|
|
- var hDotV = v.dot3(h);
|
|
|
|
- // reflect(-v,h)
|
|
|
|
-
|
|
|
|
- l.x = 2 * hDotV * h.x - v.x;
|
|
|
|
- l.y = 2 * hDotV * h.y - v.y;
|
|
|
|
- l.z = 2 * hDotV * h.z - v.z;
|
|
|
|
-
|
|
|
|
- } else {
|
|
|
|
-
|
|
|
|
- var c = cosineWeightedSampling(h);
|
|
|
|
- l.x = tanX.x * c.x + tanY.x * c.y + n.x * c.z;
|
|
|
|
- l.y = tanX.y * c.x + tanY.y * c.y + n.y * c.z;
|
|
|
|
- l.z = tanX.z * c.x + tanY.z * c.y + n.z * c.z;
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- l.normalize();
|
|
|
|
-
|
|
|
|
- var s = new h3d.scene.Mesh(sp, s3d);
|
|
|
|
- s.x = l.x;
|
|
|
|
- s.y = l.y;
|
|
|
|
- s.z = l.z;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
// ---------------------
|
|
// ---------------------
|
|
|
|
|
|
|
|
|