Skip to content

Commit

Permalink
Merge pull request #9 from Shubidumdu/pbr
Browse files Browse the repository at this point in the history
Add new sketch "Pbr"
  • Loading branch information
Shubidumdu authored May 14, 2024
2 parents 98a4a37 + fd827c2 commit c3ddb83
Show file tree
Hide file tree
Showing 25 changed files with 510 additions and 123 deletions.
132 changes: 66 additions & 66 deletions .pnp.cjs

Large diffs are not rendered by default.

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
5 changes: 5 additions & 0 deletions ext.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,8 @@ declare module '*.mp3' {
const value: string;
export default value;
}

declare module '*.env' {
const value: string;
export default value;
}
4 changes: 4 additions & 0 deletions global.scss
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,7 @@ button {
background: #aaa;
}
}

#actionTabs img {
height: 0 !important;
}
14 changes: 7 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@
"vite": "^4.3.9"
},
"dependencies": {
"@babylonjs/core": "^6.46.1",
"@babylonjs/gui": "^6.46.1",
"@babylonjs/gui-editor": "^6.46.1",
"@babylonjs/inspector": "^6.46.1",
"@babylonjs/loaders": "^6.46.1",
"@babylonjs/materials": "^6.46.1",
"@babylonjs/serializers": "^6.46.1",
"@babylonjs/core": "^7.6.0",
"@babylonjs/gui": "^7.6.0",
"@babylonjs/gui-editor": "^7.6.0",
"@babylonjs/inspector": "^7.6.0",
"@babylonjs/loaders": "^7.6.0",
"@babylonjs/materials": "^7.6.0",
"@babylonjs/serializers": "^7.6.0",
"pixi.js": "^7.3.1",
"pure-rand": "^6.0.4",
"simplex-noise": "^4.0.1"
Expand Down
Binary file added pages/pbr/environment.env
Binary file not shown.
30 changes: 30 additions & 0 deletions pages/pbr/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PBR</title>
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
<link rel="icon" type="image/x-icon" href="/favicon.ico">
</head>
<body>
<canvas id="canvas"></canvas>
<div class="input-container">
<label><input type="range" min="0.01" max="1" step="0.01" value="0" id="metallic"></input>Metallic</label>
<label><input type="range" min="0.01" max="1" step="0.01" value="1" id="roughness">Roughness</input></label>
</div>
<footer>
Tricana Of Coimbra model by
<a href="https://sketchfab.com/3d-models/tricana-of-coimbra-dd63bb4a0bc14b54acbe14d94944a058"
target="_blank"
rel="noopener noreferrer"
>newtonribeiromachado</a>,
<a href="https://creativecommons.org/licenses/by/4.0/"
target="_blank"
rel="noopener noreferrer"
>CC BY 4.0 DEED</a>
</footer>
<script type="module" src="./main.ts"></script>
</body>
</html>
120 changes: 120 additions & 0 deletions pages/pbr/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import './style.scss';
import {
ArcRotateCamera,
CubeTexture,
Effect,
Engine,
PointLight,
Scene,
ShaderMaterial,
Vector3,
} from '@babylonjs/core';
import '@babylonjs/loaders/glTF';
import { importMeshes } from '../../utils/babylon';
import modelPath from './tricanaOfCoimbra.glb';
import environmentPath from './environment.env';
import { resizeCanvasToDisplaySize } from '../../utils/webgl';
import pbrVertexShader from './shaders/pbr.vertex.glsl?raw';
import pbrFragmentShader from './shaders/pbr.fragment.glsl?raw';

const metallicInput = document.getElementById('metallic') as HTMLInputElement;
const roughnessInput = document.getElementById('roughness') as HTMLInputElement;

const canvas = document.getElementById('canvas') as HTMLCanvasElement;
const engine = new Engine(canvas, true);
const scene = new Scene(engine);
const envTexture = CubeTexture.CreateFromPrefilteredData(
environmentPath,
scene,
);

scene.createDefaultSkybox(envTexture);

Effect.ShadersStore['pbrVertexShader'] = pbrVertexShader;
Effect.ShadersStore['pbrFragmentShader'] = pbrFragmentShader;

engine.displayLoadingUI();

const lights = [
new PointLight('light', new Vector3(4, 5, -1)),
new PointLight('light', new Vector3(5, 12, -1)),
new PointLight('light', new Vector3(-4, -6, 4)),
new PointLight('light', new Vector3(2, 8, -10)),
];

const lightColors = [
[0.6, 0.1, 0.1],
[0.1, 0.6, 0.9],
[1, 1, 1],
[1, 1, 1],
]
.flat()
.map((n) => n * 0);

const init = async () => {
const camera = new ArcRotateCamera(
'camera',
Math.PI / 2,
Math.PI / 2,
50,
Vector3.Zero(),
scene,
);
camera.minZ = 0;
camera.maxZ = 1000;
camera.target = Vector3.Zero();
camera.attachControl(canvas, true);
const [root, ...restMeshes] = await importMeshes(modelPath);
const customMaterial = makeShaderMaterial();

customMaterial.setArray3(
'sphericalHarmonics',
[
envTexture.sphericalPolynomial?.preScaledHarmonics?.l00,
envTexture.sphericalPolynomial?.preScaledHarmonics?.l1_1,
envTexture.sphericalPolynomial?.preScaledHarmonics?.l10,
envTexture.sphericalPolynomial?.preScaledHarmonics?.l11,
envTexture.sphericalPolynomial?.preScaledHarmonics?.l2_2,
envTexture.sphericalPolynomial?.preScaledHarmonics?.l2_1,
envTexture.sphericalPolynomial?.preScaledHarmonics?.l20,
envTexture.sphericalPolynomial?.preScaledHarmonics?.l21,
envTexture.sphericalPolynomial?.preScaledHarmonics?.l22,
].flatMap(({ x, y, z }: any) => [x, y, z]),
);

restMeshes.forEach((mesh) => {
mesh.material = customMaterial;
mesh.setParent(null);
});
root.dispose();

engine.hideLoadingUI();

customMaterial.setTexture('brdfLUT', scene.environmentBRDFTexture);
customMaterial.setTexture('environmentMap', envTexture);

engine.runRenderLoop(() => {
customMaterial.setArray3(
'lightPositions',
lights
.map((light) => [light.position.x, light.position.y, light.position.z])
.flat(),
);
customMaterial.setArray3('lightColors', lightColors);
customMaterial.setVector3('cameraPosition', camera.position);
customMaterial.setFloat('metallic', Number(metallicInput.value));
customMaterial.setFloat('roughness', Number(roughnessInput.value));
resizeCanvasToDisplaySize(canvas);
scene.render();
});
};

const makeShaderMaterial = () => {
const material = new ShaderMaterial('customPbrShader', scene, 'pbr', {
attributes: ['position', 'normal', 'color'],
uniforms: ['world', 'view', 'worldView', 'worldViewProjection'],
});
return material;
};

init();
142 changes: 142 additions & 0 deletions pages/pbr/shaders/pbr.fragment.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
#version 300 es
precision highp float;

uniform vec3 lightPositions[4];
uniform vec3 lightColors[4];
uniform mat4 world;
uniform vec3 cameraPosition;
uniform float metallic;
uniform float roughness;
uniform vec3 sphericalHarmonics[9];

uniform samplerCube environmentMap;
uniform sampler2D brdfLUT;

in vec3 vPosition;
in vec3 vNormal;
in vec3 vColor;

out vec4 fragColor;

vec3 lightColor = vec3(25.47, 21.31, 20.79) * 4.;

const float PI = 3.14159265359;

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;
return 1.;
}

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;
}

vec3 fresnelSchlickRoughness(float cosTheta, vec3 F0, float roughness) {
return F0 + (max(vec3(1.0 - roughness), F0) - F0) * pow(clamp(1.0 - cosTheta, 0.0, 1.0), 5.0);
}

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

vec3 irradianceSH(vec3 n) {
return
sphericalHarmonics[0]
+ sphericalHarmonics[1] * (n.y)
+ sphericalHarmonics[2] * (n.z)
+ sphericalHarmonics[3] * (n.x)
+ sphericalHarmonics[4] * (n.y * n.x)
+ sphericalHarmonics[5] * (n.y * n.z)
+ sphericalHarmonics[6] * (3.0 * n.z * n.z - 1.0)
+ sphericalHarmonics[7] * (n.z * n.x)
+ sphericalHarmonics[8] * (n.x * n.x - n.y * n.y);
}

void main(void){
vec3 albedo = pow(vColor, vec3(2.2));
vec3 N=vNormal;
vec3 V=normalize(cameraPosition - vPosition);
vec3 R = reflect(-V, N);

vec3 F0=vec3(0.04);
F0=mix(F0, albedo, metallic);

vec3 Lo = vec3(0.0);

for (int i = 0; i < 4; ++i) {
vec3 lightPosition = lightPositions[i];
vec3 L = normalize(lightPosition - vPosition);
vec3 H = normalize(V + L);
float distance = length(lightPosition - vPosition);
float attenuation = 1.0 / (distance * distance);
vec3 radiance = lightColors[i] * attenuation;

float NDF = DistributionGGX(N, H, roughness);
float G = GeometrySmith(N, V, L, roughness);
vec3 F = fresnelSchlick(max(dot(H, V), 0.0), F0);

vec3 numerator = NDF * G * F;
float denominator = 4.0 * max(dot(N, V), 0.0) * max(dot(N, L), 0.0) + 0.0001; // + 0.0001 to prevent divide by zero
vec3 specular = numerator / denominator;

vec3 kS = F;
vec3 kD = vec3(1.0) - kS;
kD *= 1.0 - metallic;

// scale light by NdotL
float NdotL = max(dot(N, L), 0.0);

// add to outgoing radiance Lo
Lo += (kD * albedo / PI + specular) * radiance * NdotL; // note that we already multiplied the BRDF by the Fresnel (kS) so we won't multiply by kS again
}

// ambient lighting (we now use IBL as the ambient term)
vec3 F = fresnelSchlickRoughness(max(dot(N, V), 0.0), F0, roughness);

vec3 kS = F;
vec3 kD = 1.0 - kS;
kD *= 1.0 - metallic;

vec3 irradiance = irradianceSH(N);
vec3 diffuse = irradiance * albedo;

const float MAX_REFLECTION_LOD = 4.0;
vec3 prefilteredColor = textureLod(environmentMap, R, roughness * MAX_REFLECTION_LOD).rgb;
vec2 brdf = texture(brdfLUT, vec2(max(dot(N, V), 0.0), roughness)).rg;
vec3 specular = prefilteredColor * (F * brdf.x + brdf.y);

vec3 ambient = (kD * diffuse + specular);

vec3 color = ambient + Lo;

// HDR tonemapping
color = color / (color + vec3(1.0));
// gamma correct
color = pow(color, vec3(1.0/2.2));

fragColor = vec4(color , 1.0);
}
21 changes: 21 additions & 0 deletions pages/pbr/shaders/pbr.vertex.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#version 300 es
precision highp float;

in vec3 position;
in vec3 normal;
in vec3 color;

uniform mat4 world;
uniform mat4 worldViewProjection;

out vec3 vPosition;
out vec3 vNormal;
out vec3 vColor;

void main(){
vec4 p=vec4(position,1.);
gl_Position=worldViewProjection*p;
vNormal=mat3(world)*normal;
vPosition=vec3(world*p);
vColor=color;
}
Loading

0 comments on commit c3ddb83

Please sign in to comment.