|
@@ -11,121 +11,144 @@ class PbrShader extends hxsl.Shader {
|
|
|
@param var lightColor : Vec3;
|
|
|
|
|
|
@const var specularMode : Bool;
|
|
|
- @param var metalness : Float;
|
|
|
+ var metalness : Float;
|
|
|
+ var roughness : Float;
|
|
|
|
|
|
- @param var roughness : Float;
|
|
|
+ @param var defMetalness : Float;
|
|
|
+ @param var defRoughness : Float;
|
|
|
|
|
|
@param var envMap : SamplerCube;
|
|
|
@param var irrDiffuse : SamplerCube;
|
|
|
@param var irrSpecular : SamplerCube;
|
|
|
+ @param var irrSpecularLevels : Float;
|
|
|
+ @param var irrLut : Sampler2D;
|
|
|
+
|
|
|
+ @const var directLighting : Bool;
|
|
|
+ @const var indirectLighting : Bool;
|
|
|
+ @const var specularLighting : Bool;
|
|
|
|
|
|
var l : Vec3;
|
|
|
var n : Vec3;
|
|
|
var v : Vec3;
|
|
|
var h : Vec3;
|
|
|
|
|
|
- function calcG(v:Vec3) : Float {
|
|
|
-
|
|
|
- // UE4 uses k = (R + 1)² / 8 "disney hotness" for analytic light sources
|
|
|
-
|
|
|
- var k = (roughness * roughness) / 2;
|
|
|
- return n.dot(v) / (n.dot(v) * (1 - k) + k);
|
|
|
+ function __init__fragment() {
|
|
|
+ metalness = defMetalness;
|
|
|
+ roughness = defRoughness;
|
|
|
}
|
|
|
|
|
|
- function fragment() {
|
|
|
+ function calcG(v:Vec3) : Float {
|
|
|
+ var k = (roughness + 1).pow(2) / 8;// (roughness * roughness) / 2;
|
|
|
+ var NdV = n.dot(v);
|
|
|
+ return NdV / (NdV * (1 - k) + k);
|
|
|
+ }
|
|
|
|
|
|
- var color : Vec3;
|
|
|
+ function calcLight( lightPos : Vec3 ) : Vec3 {
|
|
|
|
|
|
+ var color = vec3(0.);
|
|
|
var diffuseColor = pixelColor.rgb;
|
|
|
- var lightDir = (lightPos - transformedPosition).normalize();
|
|
|
+ var specularColor = vec3(metalness);
|
|
|
var normal = transformedNormal.normalize();
|
|
|
var view = (camera.position - transformedPosition).normalize();
|
|
|
-
|
|
|
-
|
|
|
- var lambert = lightDir.dot(normal).max(0.);
|
|
|
+ var lightDir = (lightPos - transformedPosition).normalize();
|
|
|
|
|
|
if( specularMode ) {
|
|
|
|
|
|
+ var diffuse = lightDir.dot(normal).max(0.);
|
|
|
+
|
|
|
var specularPower = metalness * 40.;
|
|
|
var r = reflect(-lightDir, normal).normalize();
|
|
|
- var specValue = r.dot(view).max(0.).pow(specularPower);
|
|
|
- color = diffuseColor * (lambert + specValue) * lightColor;
|
|
|
+ var specular = r.dot(view).max(0.).pow(specularPower);
|
|
|
+ color = diffuseColor * (diffuse + specular) * lightColor;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
+ //if( metalness > 0.5 )
|
|
|
+ // specularColor = diffuseColor;
|
|
|
+
|
|
|
l = lightDir;
|
|
|
n = normal;
|
|
|
- v = (camera.position - transformedPosition).normalize();
|
|
|
+ v = view;
|
|
|
h = (l + v).normalize();
|
|
|
|
|
|
|
|
|
|
|
|
// diffuse BRDF
|
|
|
var direct : Vec3;
|
|
|
- var F = 0.;
|
|
|
|
|
|
- #if !flash
|
|
|
- if( n.dot(l) < 0 ) {
|
|
|
- direct = vec3(0.);
|
|
|
- } else
|
|
|
- #end
|
|
|
- {
|
|
|
|
|
|
- // ------------- DIRECT LIGHT -------------------------
|
|
|
+ // ------------- DIRECT LIGHT -------------------------
|
|
|
|
|
|
- var F0 = metalness;
|
|
|
- var diffuse = diffuseColor * n.dot(l) / PI;
|
|
|
+ var F0 = metalness;
|
|
|
+ var diffuse = diffuseColor * n.dot(l).saturate() / PI;
|
|
|
|
|
|
- // General Cook-Torrance formula for microfacet BRDF
|
|
|
- // f(l,v) = D(h).F(v,h).G(l,v,h) / 4(n.l)(n.v)
|
|
|
+ // General Cook-Torrance formula for microfacet BRDF
|
|
|
+ // f(l,v) = D(h).F(v,h).G(l,v,h) / 4(n.l)(n.v)
|
|
|
|
|
|
- // formulas below from 2013 Siggraph "Real Shading in UE4"
|
|
|
- var alpha = roughness.pow(2);
|
|
|
+ // formulas below from 2013 Siggraph "Real Shading in UE4"
|
|
|
+ var alpha = roughness.pow(2);
|
|
|
|
|
|
- // D = normal distribution fonction
|
|
|
- // GGX (Trowbridge-Reitz) "Disney"
|
|
|
- var D = alpha.pow(2) / (PI * ( n.dot(h).pow(2) * (alpha.pow(2) - 1.) + 1).pow(2));
|
|
|
+ // D = normal distribution fonction
|
|
|
+ // GGX (Trowbridge-Reitz) "Disney"
|
|
|
+ var D = alpha.pow(2) / (PI * ( n.dot(h).pow(2) * (alpha.pow(2) - 1.) + 1).pow(2));
|
|
|
|
|
|
- // F = fresnel term
|
|
|
+ // F = fresnel term
|
|
|
+ // Schlick approx
|
|
|
+ // pow 5 optimized with Spherical Gaussian
|
|
|
+ // var F = F0 + (1 - F0) * pow(1 - v.dot(h).min(1.), 5.);
|
|
|
+ var F = F0 + (1.01 - F0) * exp2( ( -5.55473 * v.dot(h) - 6.98316) * v.dot(h) );
|
|
|
|
|
|
- // Schlick approx
|
|
|
- // var F = F0 + (1 - F0) * pow(1 - v.dot(h), 5.);
|
|
|
- // pow 5 optimized with Spherical Gaussian
|
|
|
- F = F0 + (1 - F0) * (2.).pow( ( -5.55473 * v.dot(h) - 6.98316) * v.dot(h) );
|
|
|
+ // G = geometric attenuation
|
|
|
+ // Schlick (modified for UE4 with k=alpha/2)
|
|
|
+ var G = calcG(l) * calcG(v);
|
|
|
|
|
|
- // G = geometric attenuation
|
|
|
- // Schlick (modified for UE4 with k=alpha/2)
|
|
|
- var G = calcG(l) * calcG(v);
|
|
|
+ var specular = (D * F * G / (4 * n.dot(l) * n.dot(v))).max(0.);
|
|
|
|
|
|
- var specular = D * F * G / (4 * n.dot(l) * n.dot(v));
|
|
|
+ // custom falloff to prevent infinite when we have n.h ~= 0
|
|
|
+ if( n.dot(l) < 0 ) specular *= (1 + n.dot(l) * 6).saturate();
|
|
|
|
|
|
- direct = (diffuse + specular) * lightColor;
|
|
|
+ if( !specularLighting )
|
|
|
+ specular = 0.;
|
|
|
|
|
|
-
|
|
|
- }
|
|
|
+ direct = (diffuse + specular) * lightColor;
|
|
|
|
|
|
|
|
|
// ------------- INDIRECT LIGHT -------------------------
|
|
|
|
|
|
var diffuse = irrDiffuse.get(normal).rgb.pow(vec3(2.2)) * diffuseColor;
|
|
|
- var specular = vec3(0, 0, 0); // TODO - generate PMREM from CubeMap
|
|
|
+
|
|
|
+ var envSpec = textureCubeLod(irrSpecular, reflect(-v,n), roughness * irrSpecularLevels).rgb.pow(vec3(2.2));
|
|
|
+
|
|
|
+ var envBRDF = irrLut.get(vec2(roughness, n.dot(v)));
|
|
|
+ var specular = envSpec * (specularColor * envBRDF.x + envBRDF.y);
|
|
|
+
|
|
|
+ if( !specularLighting )
|
|
|
+ specular = vec3(0.);
|
|
|
+
|
|
|
var indirect = diffuse + specular;
|
|
|
|
|
|
- color = direct + indirect;
|
|
|
+ if( directLighting )
|
|
|
+ color += direct;
|
|
|
+ if( indirectLighting )
|
|
|
+ color += indirect;
|
|
|
+ }
|
|
|
+
|
|
|
+ return color;
|
|
|
+ }
|
|
|
+
|
|
|
|
|
|
+ function fragment() {
|
|
|
+ var color = calcLight(lightPos);
|
|
|
+
|
|
|
+ if( !specularMode ) {
|
|
|
// reinhard tonemapping
|
|
|
color *= exp(exposure);
|
|
|
color = color / (color + vec3(1.));
|
|
|
|
|
|
// gamma correct
|
|
|
color = color.pow(vec3(1 / 2.2));
|
|
|
-
|
|
|
}
|
|
|
|
|
|
- #if flash
|
|
|
- color *= float(n.dot(l) > 0.);
|
|
|
- #end
|
|
|
-
|
|
|
pixelColor.rgb = color;
|
|
|
}
|
|
|
|
|
@@ -135,23 +158,41 @@ class PbrShader extends hxsl.Shader {
|
|
|
super();
|
|
|
exposure = 0;
|
|
|
specularMode = false;
|
|
|
- lightPos.set(5, 15, 20);
|
|
|
- lightColor.set(1, 1, 1);
|
|
|
+ directLighting = true;
|
|
|
+ indirectLighting = true;
|
|
|
+ specularLighting = true;
|
|
|
+ lightPos.set(10, 30, 40);
|
|
|
+ lightColor.set(0.1, 0.1, 0.1);
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
-class Irradiance extends h3d.shader.ScreenShader {
|
|
|
+class AdjustShader extends hxsl.Shader {
|
|
|
|
|
|
static var SRC = {
|
|
|
+ @param var roughnessValue : Float;
|
|
|
+ @param var metalnessValue : Float;
|
|
|
+ var roughness : Float;
|
|
|
+ var metalness : Float;
|
|
|
+ function fragment() {
|
|
|
+ roughness = roughnessValue;
|
|
|
+ metalness = metalnessValue;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- @const var face : Int;
|
|
|
- @const var samplesBits : Int;
|
|
|
- @param var envMap : SamplerCube;
|
|
|
+ public function new(rough, metal) {
|
|
|
+ super();
|
|
|
+ roughnessValue = rough;
|
|
|
+ metalnessValue = metal;
|
|
|
+ }
|
|
|
|
|
|
- @const var isSpecular : Bool;
|
|
|
- @param var roughness : Float;
|
|
|
+}
|
|
|
+
|
|
|
+class IrradBase extends h3d.shader.ScreenShader {
|
|
|
|
|
|
+ static var SRC = {
|
|
|
+
|
|
|
+ @const var samplesBits : Int;
|
|
|
#if (js || flash)
|
|
|
@const var SamplesCount : Int;
|
|
|
@param var hammerTbl : Array<Vec4,SamplesCount>;
|
|
@@ -175,14 +216,56 @@ class Irradiance extends h3d.shader.ScreenShader {
|
|
|
#end
|
|
|
}
|
|
|
|
|
|
- function cosineWeightedSampling( p : Vec2 ) : Vec3 {
|
|
|
+ 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;
|
|
|
- return vec3( sq * cos(alpha), sq * sin(alpha), sqrt(p.x));
|
|
|
+ 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);
|
|
|
+ }
|
|
|
var n : Vec3;
|
|
|
switch( face ) {
|
|
|
case 0: n = vec3(1, d.y, -d.x);
|
|
@@ -198,29 +281,19 @@ class Irradiance extends h3d.shader.ScreenShader {
|
|
|
function fragment() {
|
|
|
var color = vec3(0.);
|
|
|
var n = getNormal();
|
|
|
- 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));
|
|
|
var totalWeight = 1e-10;
|
|
|
var numSamples = 1 << samplesBits;
|
|
|
for( i in 0...1 << samplesBits ) {
|
|
|
var p = hammersley(i, numSamples);
|
|
|
var l : Vec3;
|
|
|
if( isSpecular ) {
|
|
|
- // importance sampling GGX BRDF
|
|
|
- var a = roughness * roughness;
|
|
|
- var phi = 2 * PI * p.x;
|
|
|
- var cosT = sqrt((1 - p.y) / (1 + (a * a - 1) * p.y));
|
|
|
- var sinT = sqrt(1 - cosT * cosT);
|
|
|
- var ltan = vec3(sinT * cos(phi), sinT * sin(phi), cosT);
|
|
|
- var h = tanX * ltan.x + tanY * ltan.y + n * ltan.z;
|
|
|
+ var h = importanceSampleGGX(roughness, p, n);
|
|
|
var v = n; // approx
|
|
|
- l = -reflect(v, h);
|
|
|
+ l = reflect(-v, h).normalize();
|
|
|
} else {
|
|
|
- var ltan = cosineWeightedSampling(p);
|
|
|
- l = tanX * ltan.x + tanY * ltan.y + n * ltan.z;
|
|
|
+ l = cosineWeightedSampling(p, n);
|
|
|
}
|
|
|
- var amount = n.dot(l);
|
|
|
+ var amount = n.dot(l).saturate();
|
|
|
if( amount > 0 ) {
|
|
|
color += envMap.get(l).rgb.pow(vec3(2.2)) * amount;
|
|
|
totalWeight += amount;
|
|
@@ -233,11 +306,56 @@ class Irradiance extends h3d.shader.ScreenShader {
|
|
|
|
|
|
}
|
|
|
|
|
|
+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 Pbr extends hxd.App {
|
|
|
|
|
|
var fui : h2d.Flow;
|
|
|
- var cameraRot = Math.PI / 4;
|
|
|
+ var cameraRot = -Math.PI / 4;
|
|
|
var cameraDist = 5.5;
|
|
|
var font : h2d.Font;
|
|
|
var hue : Float;
|
|
@@ -245,9 +363,12 @@ class Pbr extends hxd.App {
|
|
|
var brightness : Float;
|
|
|
var color : h2d.Bitmap;
|
|
|
var sphere : h3d.scene.Mesh;
|
|
|
+ var grid : h3d.scene.Object;
|
|
|
|
|
|
var sampleBits : Int;
|
|
|
|
|
|
+ var shader : PbrShader;
|
|
|
+
|
|
|
var envMap : h3d.mat.Texture;
|
|
|
var irradDiffuse : h3d.mat.Texture;
|
|
|
var irradSpecular : h3d.mat.Texture;
|
|
@@ -258,15 +379,16 @@ class Pbr extends hxd.App {
|
|
|
engine.debug = true;
|
|
|
#end
|
|
|
|
|
|
- //displaySampling(256, new h3d.Vector(1, 0., 0.), 0.5);
|
|
|
+ //displaySampling(256, new h3d.Vector(0.5, 0.5, 0.5), 0.3);
|
|
|
|
|
|
font = hxd.res.DefaultFont.get();
|
|
|
|
|
|
+
|
|
|
+ shader = new PbrShader();
|
|
|
+
|
|
|
var sp = new h3d.prim.Sphere(1, 128, 128);
|
|
|
sp.addNormals();
|
|
|
sp.addUVs();
|
|
|
- sphere = new h3d.scene.Mesh(sp, s3d);
|
|
|
- var shader = sphere.material.mainPass.addShader(new PbrShader());
|
|
|
|
|
|
var bg = new h3d.scene.Mesh(sp, s3d);
|
|
|
bg.scale(10);
|
|
@@ -277,18 +399,6 @@ class Pbr extends hxd.App {
|
|
|
fui.verticalSpacing = 5;
|
|
|
fui.isVertical = true;
|
|
|
|
|
|
- var g = new h3d.scene.Graphics(s3d);
|
|
|
- g.lineStyle(1, 0xFF0000);
|
|
|
- g.moveTo(0, 0, 0);
|
|
|
- g.lineTo(2, 0, 0);
|
|
|
- g.lineStyle(1, 0x00FF00);
|
|
|
- g.moveTo(0, 0, 0);
|
|
|
- g.lineTo(0, 2, 0);
|
|
|
- g.lineStyle(1, 0x0000FF);
|
|
|
- g.moveTo(0, 0, 0);
|
|
|
- g.lineTo(0, 0, 2);
|
|
|
- g.lineStyle();
|
|
|
-
|
|
|
envMap = new h3d.mat.Texture(512, 512, [Cubic]);
|
|
|
inline function set(face:Int, res:hxd.res.Image) {
|
|
|
#if flash
|
|
@@ -317,16 +427,22 @@ class Pbr extends hxd.App {
|
|
|
set(5, hxd.Res.bottom);
|
|
|
|
|
|
|
|
|
- var size = 64;
|
|
|
+ var size, ssize;
|
|
|
#if (js || flash)
|
|
|
sampleBits = 5;
|
|
|
size = 16;
|
|
|
+ ssize = 16;
|
|
|
#else
|
|
|
+ size = 64;
|
|
|
+ ssize = 256;
|
|
|
sampleBits = 10;
|
|
|
#end
|
|
|
|
|
|
+ computeIrradLut();
|
|
|
+
|
|
|
irradDiffuse = new h3d.mat.Texture(size, size, [Cubic]);
|
|
|
- irradSpecular = new h3d.mat.Texture(size, size, [Cubic, MipMapped]);
|
|
|
+ irradSpecular = new h3d.mat.Texture(ssize, ssize, [Cubic, MipMapped]);
|
|
|
+ irradSpecular.mipMap = Linear;
|
|
|
computeIrradiance();
|
|
|
|
|
|
shader.envMap = envMap;
|
|
@@ -335,29 +451,70 @@ class Pbr extends hxd.App {
|
|
|
|
|
|
var cubeShader = bg.material.mainPass.addShader(new h3d.shader.CubeMap(envMap));
|
|
|
|
|
|
- shader.metalness = 0.2;
|
|
|
- shader.roughness = 0.3;
|
|
|
+ shader.defMetalness = 0.2;
|
|
|
+ shader.defRoughness = 0.5;
|
|
|
+ shader.exposure = 0.;
|
|
|
hue = 0.2;
|
|
|
saturation = 0.2;
|
|
|
- brightness = 0.2;
|
|
|
+ brightness = -1;
|
|
|
+
|
|
|
+
|
|
|
+ function addSphere(x,y) {
|
|
|
+ var sphere = new h3d.scene.Mesh(sp, s3d);
|
|
|
+ sphere.x = x;
|
|
|
+ sphere.y = y;
|
|
|
+ sphere.material.mainPass.addShader(shader);
|
|
|
+ return sphere;
|
|
|
+ }
|
|
|
+
|
|
|
+ sphere = addSphere(0, 0);
|
|
|
+
|
|
|
+ grid = new h3d.scene.Object(s3d);
|
|
|
+ var max = 5;
|
|
|
+ for( x in 0...max )
|
|
|
+ for( y in 0...max ) {
|
|
|
+ var s = addSphere(x - max * 0.5, y - max * 0.5);
|
|
|
+ grid.addChild(s);
|
|
|
+ s.scale(0.4);
|
|
|
+ s.material.mainPass.addShader(new AdjustShader( Math.pow(x / (max - 1), 1), Math.pow(y / (max - 1), 2)));
|
|
|
+ }
|
|
|
+ grid.visible = false;
|
|
|
|
|
|
addCheck("PBR", function() return !shader.specularMode, function(b) shader.specularMode = !b);
|
|
|
addSlider("Exposure", -3, 3, function() return shader.exposure, function(v) shader.exposure = v);
|
|
|
- addSlider("Metalness", 0, 1, function() return shader.metalness, function(v) shader.metalness = v);
|
|
|
- addSlider("Roughness", 0, 1, function() return shader.roughness, function(v) shader.roughness = v);
|
|
|
+ addSlider("Metalness", 0.02, 0.99, function() return shader.defMetalness, function(v) shader.defMetalness = v);
|
|
|
+ addSlider("Roughness", 0, 1, function() return shader.defRoughness, function(v) shader.defRoughness = v);
|
|
|
+
|
|
|
+ addCheck("Direct", function() return shader.directLighting, function(b) shader.directLighting = b);
|
|
|
+ addCheck("Indirect", function() return shader.indirectLighting, function(b) shader.indirectLighting = b);
|
|
|
+ addCheck("Specular", function() return shader.specularLighting, function(b) shader.specularLighting = b);
|
|
|
+ addCheck("Grid", function() return grid.visible, function(b) {
|
|
|
+ grid.visible = !grid.visible;
|
|
|
+ sphere.visible = !sphere.visible;
|
|
|
+ });
|
|
|
|
|
|
color = new h2d.Bitmap(h2d.Tile.fromColor(0xFFFFFF, 30, 30), fui);
|
|
|
fui.getProperties(color).paddingLeft = 50;
|
|
|
|
|
|
- addSlider("Hue", 0, Math.PI, function() return hue, function(v) hue = v);
|
|
|
+ addSlider("Hue", 0, Math.PI*2, function() return hue, function(v) hue = v);
|
|
|
addSlider("Saturation", 0, 1, function() return saturation, function(v) saturation = v);
|
|
|
- addSlider("Brightness", 0, 1, function() return brightness, function(v) brightness = v);
|
|
|
+ addSlider("Brightness", -1, 1, function() return brightness, function(v) brightness = v);
|
|
|
addChoice("Env", ["Default", "IDiff", "ISpec"], function(i) cubeShader.texture = [envMap, irradDiffuse, irradSpecular][i]);
|
|
|
|
|
|
s2d.addEventListener(onEvent);
|
|
|
}
|
|
|
|
|
|
+ function computeIrradLut() {
|
|
|
+ var irradLut = new h3d.mat.Texture(128, 128, #if js RGBA32F #else RGBA16F #end);
|
|
|
+ var screen = new h3d.pass.ScreenFx(new IrradianceLut());
|
|
|
+ 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;
|
|
@@ -380,10 +537,15 @@ class Pbr extends hxd.App {
|
|
|
while( irradSpecular.width > 1 << (mipLevels - 1) )
|
|
|
mipLevels++;
|
|
|
|
|
|
+ shader.irrSpecularLevels = mipLevels - 4;
|
|
|
+
|
|
|
for( i in 0...6 ) {
|
|
|
screen.shader.face = i;
|
|
|
for( j in 0...mipLevels ) {
|
|
|
- screen.shader.roughness = j / 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();
|
|
|
}
|
|
@@ -495,18 +657,22 @@ class Pbr extends hxd.App {
|
|
|
}
|
|
|
|
|
|
override function update(dt:Float) {
|
|
|
+
|
|
|
s3d.camera.pos.set(Math.cos(cameraRot) * cameraDist, Math.sin(cameraRot) * cameraDist, cameraDist * 2 / 3);
|
|
|
|
|
|
var color = new h3d.Vector(1, 0, 0);
|
|
|
var m = new h3d.Matrix();
|
|
|
m.identity();
|
|
|
- m.colorHue(hue);
|
|
|
m.colorSaturation(saturation);
|
|
|
+ m.colorHue(hue);
|
|
|
m.colorBrightness(brightness);
|
|
|
color.transform3x4(m);
|
|
|
+ color.setColor(color.toColor()); // saturate
|
|
|
|
|
|
this.color.color.load(color);
|
|
|
this.sphere.material.color.load(color);
|
|
|
+ for( s in grid )
|
|
|
+ s.toMesh().material.color.load(color);
|
|
|
|
|
|
}
|
|
|
|
|
@@ -551,21 +717,24 @@ class Pbr extends hxd.App {
|
|
|
|
|
|
var a = roughness * roughness;
|
|
|
var phi = 2 * Math.PI * h.x;
|
|
|
- var cosT = Math.sqrt((1 - h.y) / (1 + (a * a - 1) * h.y));
|
|
|
+ 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 hx = tanX.x * ltan.x + tanY.x * ltan.y + n.x * ltan.z;
|
|
|
- var hy = tanX.y * ltan.x + tanY.y * ltan.y + n.y * ltan.z;
|
|
|
- var hz = tanX.z * ltan.x + tanY.z * ltan.y + n.z * ltan.z;
|
|
|
+ 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(new h3d.Vector(hx, hy, hz));
|
|
|
- // -reflect(v,h)
|
|
|
- l.x = v.x - 2 * hDotV * h.x;
|
|
|
- l.y = v.y - 2 * hDotV * h.y;
|
|
|
- l.z = v.z - 2 * hDotV * h.z;
|
|
|
+ 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 {
|
|
|
|