Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions build/t3d.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build/t3d.min.js

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions build/t3d.module.js

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion examples/files.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@
"material_vertexcolors",
"material_alphamask",
"material_clippingplanes",
"material_clearcoat"
"material_clearcoat",
"material_specularAA"
],
"camera": [
"camera_cameras",
Expand Down
231 changes: 231 additions & 0 deletions examples/material_specularAA.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
<!DOCTYPE html>
<html>

<head>
<meta charset="utf-8">
<title>t3d - specular AA</title>
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<link type="text/css" rel="stylesheet" href="main.css">

<body>
<div id="info">
<a href="" target="_blank">t3d</a> - specular AA
</div>

<script src="./libs/nanobar.js"></script>

<!-- Import maps polyfill -->
<!-- Remove this when import maps will be widely supported -->
<script async src="./libs/es-module-shims.js"></script>

<script type="importmap">
{
"imports": {
"t3d": "../build/t3d.module.js"
}
}
</script>

<script type="module">
import * as t3d from 't3d';
import { GLTFLoader } from './jsm/loaders/glTF/GLTFLoader.js';
import { TextureCubeLoader } from './jsm/loaders/TextureCubeLoader.js';
import { Texture2DLoader } from './jsm/loaders/Texture2DLoader.js';
import { OrbitControls } from './jsm/controls/OrbitControls.js';
import { SkyBox } from './jsm/objects/SkyBox.js';
import { ForwardRenderer } from './jsm/render/ForwardRenderer.js';
import { GUI } from './libs/lil-gui.esm.min.js';

let width = window.innerWidth || 2;
let height = window.innerHeight || 2;

const canvas = document.createElement('canvas');
canvas.width = width * window.devicePixelRatio;
canvas.height = height * window.devicePixelRatio;
canvas.style.width = width + "px";
canvas.style.height = height + "px";
document.body.appendChild(canvas);

const forwardRenderer = new ForwardRenderer(canvas);

const cube_texture = new TextureCubeLoader().load([
"resources/skybox/Bridge2/posx.jpg",
"resources/skybox/Bridge2/negx.jpg",
"resources/skybox/Bridge2/posy.jpg",
"resources/skybox/Bridge2/negy.jpg",
"resources/skybox/Bridge2/posz.jpg",
"resources/skybox/Bridge2/negz.jpg"
]);

const scene = new t3d.Scene();
// scene.environment = cube_texture;

// const sky_box = new SkyBox(cube_texture);
// sky_box.level = 0;

const nanobar = new Nanobar();
nanobar.el.style.background = "gray";

const loadingManager = new t3d.LoadingManager(function () {
nanobar.go(100);
nanobar.el.style.background = "transparent";
}, function (url, itemsLoaded, itemsTotal) {
if (itemsLoaded < itemsTotal) {
nanobar.go(itemsLoaded / itemsTotal * 100);
}
});

// const gemoetry = new t3d.PlaneGeometry();
const gemoetry = new t3d.SphereGeometry(.75, 64, 64);
const material = new t3d.PBRMaterial();
material.metalness = 0.7;
material.roughness = 0.3;
material.normalMap = new Texture2DLoader().load('./resources/MAT_Mech1.003_normal.png');
// material.normalMap = textureLoader.load('./resources/7898-normal.jpg');
// material.normalMap = textureLoader.load('./resources/NormalMap.png');
// material.normalScale.set(10, 10);
material.defines = {
SPECULAR_AA: false, // three、oasis
SPECULAR_AA1_0: false, // babylon geometricRoughnessFactor
SPECULAR_AA1_1: false, // babylon geometricAlphaGFactor
SPECULAR_AA2: false // Tokuyoshi and Kaplanyan 2021
};
material.diffuse.setHex(0x292929);
// material.side = t3d.DRAW_SIDE.DOUBLE;
const mesh = new t3d.Mesh(gemoetry, material);
scene.add(mesh);

// const loader = new GLTFLoader(loadingManager);
// loader.autoLogError = false;

// const file = "resources/models/gltf/dreadroamer/scene.gltf";
let materials = [];
// console.time('GLTFLoader');
// loader.load(file).then(function (result) {
// console.timeEnd('GLTFLoader');
// result.root.position.set(0, 0, 0);
// result.root.traverse(m => {
// if (m.material) {
// m.material.defines = {
// SPECULAR_AA: false, // three、oasis
// SPECULAR_AA1_0: false, // babylon geometricRoughnessFactor
// SPECULAR_AA1_1: false, // babylon geometricAlphaGFactor
// SPECULAR_AA2: false // Tokuyoshi and Kaplanyan 2021
// };
// m.material.uniforms.specularAntiAliasingVariance = 0.5915494;
// m.material.uniforms.specularAntiAliasingThreshold = 0.2;
// materials.push(m.material);
// }
// })
// scene.add(result.root);
// }).catch(e => console.error(e));

const ambientLight = new t3d.AmbientLight(0xffffff, 0.5);
scene.add(ambientLight);

const directionalLight = new t3d.DirectionalLight(0xffffff, 1);
directionalLight.position.set(10, 10, 0);
directionalLight.lookAt(new t3d.Vector3(), new t3d.Vector3(0, 1, 0));
scene.add(directionalLight);

const camera = new t3d.Camera();
camera.outputEncoding = t3d.TEXEL_ENCODING_TYPE.GAMMA;
camera.gammaFactor = 2;
camera.position.set(0, 0, 3);
camera.lookAt(new t3d.Vector3(0, 0, 0), new t3d.Vector3(0, 1, 0));
camera.setPerspective(45 / 180 * Math.PI, width / height, 0.1, 800);
// camera.add(sky_box);
scene.add(camera);
window.camera = camera;

const params = {
SPECULAR_AA: false,
SPECULAR_AA1_0: false, // babylon geometricRoughnessFactor
SPECULAR_AA1_1: false, // babylon geometricAlphaGFactor
SPECULAR_AA2: false, // Tokuyoshi and Kaplanyan 2021
specularAntiAliasingVariance: 0.5915494,
specularAntiAliasingThreshold: 0.2
}
const gui = new GUI();
gui.add(params, 'SPECULAR_AA').onChange((value) => {
materials.forEach(m => {
m.defines.SPECULAR_AA = value;
m.needsUpdate = true;
});
mesh.material.defines.SPECULAR_AA = value;
mesh.material.needsUpdate = true;
});
gui.add(params, 'SPECULAR_AA1_0').onChange((value) => {
materials.forEach(m => {
m.defines.SPECULAR_AA1_0 = value;
m.needsUpdate = true;
});
mesh.material.defines.SPECULAR_AA1_0 = value;
mesh.material.needsUpdate = true;
});
gui.add(params, 'SPECULAR_AA1_1').onChange((value) => {
materials.forEach(m => {
m.defines.SPECULAR_AA1_1 = value;
m.needsUpdate = true;
});
mesh.material.defines.SPECULAR_AA1_1 = value;
mesh.material.needsUpdate = true;
});
gui.add(params, 'SPECULAR_AA2').onChange((value) => {
materials.forEach(m => {
m.defines.SPECULAR_AA2 = value;
m.needsUpdate = true;
});
mesh.material.defines.SPECULAR_AA2 = value;
mesh.material.needsUpdate = true;

if (value) {
specularAntiAliasingVariance.enable();
specularAntiAliasingThreshold.enable();
} else {
specularAntiAliasingVariance.disable();
specularAntiAliasingThreshold.disable();
}
});
const specularAntiAliasingVariance = gui.add(params, 'specularAntiAliasingVariance', 0, 2, 0.001).onChange((value) => {
materials.forEach(m => {
m.uniforms.specularAntiAliasingVariance = value;
})
mesh.material.uniforms.specularAntiAliasingVariance = value;
});
const specularAntiAliasingThreshold = gui.add(params, 'specularAntiAliasingThreshold', 0, 1, 0.001).onChange((value) => {
materials.forEach(m => {
m.uniforms.specularAntiAliasingThreshold = value;
})
mesh.material.uniforms.specularAntiAliasingThreshold = value;
});

const controller = new OrbitControls(camera, canvas);
controller.target.set(0, 0, 0);
window.controller = controller;

function loop(count) {
requestAnimationFrame(loop);

controller.update();

forwardRenderer.render(scene, camera);
}
requestAnimationFrame(loop);

function onWindowResize() {
width = window.innerWidth || 2;
height = window.innerHeight || 2;

camera.setPerspective(45 / 180 * Math.PI, width / height, 1, 8000);

forwardRenderer.backRenderTarget.resize(width * window.devicePixelRatio, height * window.devicePixelRatio);

canvas.style.width = width + "px";
canvas.style.height = height + "px";
}
window.addEventListener("resize", onWindowResize, false);
</script>
</body>

</html>
Binary file added examples/resources/MAT_Mech1.003_normal.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
61 changes: 61 additions & 0 deletions src/shaders/shaderChunk/bsdfs.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -166,4 +166,65 @@ float GGXRoughnessToBlinnExponent( const in float ggxRoughness ) {

float BlinnExponentToGGXRoughness( const in float blinnExponent ) {
return sqrt( 2.0 / ( blinnExponent + 2.0 ) );
}

float getAARoughnessFactor(vec3 normal) {
// Kaplanyan 2016, "Stable specular highlights"
// Tokuyoshi 2017, "Error Reduction and Simplification for Shading Anti-Aliasing"
// Tokuyoshi and Kaplanyan 2019, "Improved Geometric Specular Antialiasing"

// This implementation is meant for deferred rendering in the original paper but
// we use it in forward rendering as well (as discussed in Tokuyoshi and Kaplanyan
// 2019). The main reason is that the forward version requires an expensive transform
// of the half vector by the tangent frame for every light.

vec3 dxy = max( abs(dFdx(normal)), abs(dFdy(normal)) );
return 0.04 + max( max(dxy.x, dxy.y), dxy.z );
}

float getAARoughnessFactor1(vec3 normalVector) {
vec3 nDfdx = dFdx(normalVector.xyz);
vec3 nDfdy = dFdy(normalVector.xyz);
float slopeSquare = max(dot(nDfdx, nDfdx), dot(nDfdy, nDfdy));

// Vive analytical lights roughness factor.
float geometricRoughnessFactor = pow(saturate(slopeSquare), 0.333);

// Adapt linear roughness (alphaG) to geometric curvature of the current pixel.
float geometricAlphaGFactor = sqrt(slopeSquare);
// BJS factor.
geometricAlphaGFactor *= 0.75;

#ifdef SPECULAR_AA1_0
return geometricRoughnessFactor;
#endif

#ifdef SPECULAR_AA1_1
return geometricAlphaGFactor;
#endif

return 0.;
}

uniform float specularAntiAliasingVariance;
uniform float specularAntiAliasingThreshold;
float getAARoughnessFactor2(float roughness, vec3 normal) {
// Kaplanyan 2016, "Stable specular highlights"
// Tokuyoshi 2017, "Error Reduction and Simplification for Shading Anti-Aliasing"
// Tokuyoshi and Kaplanyan 2019, "Improved Geometric Specular Antialiasing"
// Tokuyoshi and Kaplanyan 2021, "Stable Geometric Specular Antialiasing with Projected-Space NDF Filtering"

// This implementation is meant for deferred rendering in the original paper but
// we use it in forward rendering as well (as discussed in Tokuyoshi and Kaplanyan
// 2019). The main reason is that the forward version requires an expensive transform
// of the half vector by the tangent frame for every light.

// float specularAntiAliasingVariance = 0.5915494;
// float specularAntiAliasingThreshold = 0.2;
vec3 ddxN = dFdx(normal);
vec3 ddyN = dFdy(normal);
float variance = specularAntiAliasingVariance * (dot(ddxN, ddxN) + dot(ddyN, ddyN));
float kernelRoughness = min(variance, specularAntiAliasingThreshold);
float squareRoughness = saturate(roughness * roughness + kernelRoughness);
return sqrt(squareRoughness);
}
21 changes: 15 additions & 6 deletions src/shaders/shaderChunk/light_frag.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,21 @@
float roughness = max(roughnessFactor, 0.0525);
#endif

vec3 dxy = max(abs(dFdx(geometryNormal)), abs(dFdy(geometryNormal)));
float geometryRoughness = max(max(dxy.x, dxy.y), dxy.z);
roughness += geometryRoughness;
float aaroughnessFactor = 0.0;
#ifdef SPECULAR_AA
aaroughnessFactor = getAARoughnessFactor(N);
#endif
#ifdef SPECULAR_AA1_0
aaroughnessFactor = getAARoughnessFactor1(N);
#endif
#ifdef SPECULAR_AA1_1
aaroughnessFactor = getAARoughnessFactor1(N);
#endif
#ifdef SPECULAR_AA2
aaroughnessFactor = getAARoughnessFactor2(roughness, N);
#endif

roughness = min(roughness, 1.0);
roughness = min(max(roughness, aaroughnessFactor), 1.0);

#ifdef USE_CLEARCOAT
float clearcoat = u_Clearcoat;
Expand All @@ -33,8 +43,7 @@
#endif
clearcoat = saturate(clearcoat);
clearcoatRoughness = max(clearcoatRoughness, 0.0525);
clearcoatRoughness += geometryRoughness;
clearcoatRoughness = min(clearcoatRoughness, 1.0);
clearcoatRoughness = min(max(clearcoatRoughness, getAARoughnessFactor(geometryNormal)), 1.0);
#endif
#else
vec3 diffuseColor = outColor.xyz;
Expand Down