Skip to content

Commit e86ebc8

Browse files
Chau TranChau Tran
Chau Tran
authored and
Chau Tran
committed
feat(soba): migrate caustics
1 parent d418747 commit e86ebc8

File tree

7 files changed

+699
-0
lines changed

7 files changed

+699
-0
lines changed
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import { NgIf } from '@angular/common';
2+
import { Component, CUSTOM_ELEMENTS_SCHEMA, effect, Input } from '@angular/core';
3+
import { extend, injectNgtRef, NgtAnyRecord, NgtSignalStore } from 'angular-three';
4+
import * as THREE from 'three';
5+
import { LineBasicMaterial, LineSegments } from 'three';
6+
7+
extend({ LineSegments, LineBasicMaterial });
8+
9+
export interface NgtsEdgesState {
10+
threshold: number;
11+
color: THREE.ColorRepresentation;
12+
geometry: THREE.BufferGeometry;
13+
userData: NgtAnyRecord;
14+
}
15+
16+
@Component({
17+
selector: 'ngts-edges',
18+
standalone: true,
19+
template: `
20+
<ngt-line-segments [ref]="edgesRef" [raycast]="noop" ngtCompound>
21+
<ng-container *ngIf="withChildren; else noChildren">
22+
<ng-content />
23+
</ng-container>
24+
<ng-template #noChildren>
25+
<ngt-line-basic-material [color]="color" />
26+
</ng-template>
27+
</ngt-line-segments>
28+
`,
29+
imports: [NgIf],
30+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
31+
})
32+
export class NgtsEdges extends NgtSignalStore<NgtsEdgesState> {
33+
@Input() edgesRef = injectNgtRef<THREE.LineSegments>();
34+
35+
@Input() set threshold(threshold: number) {
36+
this.set({ threshold });
37+
}
38+
39+
@Input() set color(color: THREE.ColorRepresentation) {
40+
this.set({ color });
41+
}
42+
43+
@Input() set geometry(geometry: THREE.BufferGeometry) {
44+
this.set({ geometry });
45+
}
46+
47+
@Input() set userData(userData: NgtAnyRecord) {
48+
this.set({ userData });
49+
}
50+
51+
@Input() withChildren = false;
52+
53+
readonly noop = () => null;
54+
55+
constructor() {
56+
super({
57+
threshold: 15,
58+
color: 'black',
59+
userData: {},
60+
});
61+
this.#setupGeometry();
62+
}
63+
64+
#setupGeometry(): void {
65+
effect(() => {
66+
const edges = this.edgesRef.nativeElement;
67+
if (!edges) return;
68+
const parent = edges.parent as THREE.Mesh;
69+
if (parent) {
70+
const geom = this.get('geometry') || parent.geometry;
71+
const threshold = this.get('threshold');
72+
if (geom !== edges.userData['currentGeom'] || threshold !== edges.userData['currentThreshold']) {
73+
edges.userData['currentGeom'] = geom;
74+
edges.userData['currentThreshold'] = threshold;
75+
edges.geometry = new THREE.EdgesGeometry(geom, threshold);
76+
}
77+
}
78+
});
79+
}
80+
}

libs/soba/abstractions/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
export * from './billboard/billboard';
22
export * from './catmull-rom-line/catmull-rom-line';
33
export * from './cubic-bezier-line/cubic-bezier-line';
4+
export * from './edges/edges';
45
export * from './line/line';
56
export * from './quadratic-bezier-line/quadratic-bezier-line';
67
export * from './text-3d/text-3d';
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
import * as THREE from 'three';
2+
import { shaderMaterial } from '../shader-material/shader-material';
3+
4+
export const CausticsMaterial = shaderMaterial(
5+
{
6+
cameraMatrixWorld: new THREE.Matrix4(),
7+
cameraProjectionMatrixInv: new THREE.Matrix4(),
8+
normalTexture: null,
9+
depthTexture: null,
10+
lightDir: new THREE.Vector3(0, 1, 0),
11+
lightPlaneNormal: new THREE.Vector3(0, 1, 0),
12+
lightPlaneConstant: 0,
13+
near: 0.1,
14+
far: 100,
15+
modelMatrix: new THREE.Matrix4(),
16+
worldRadius: 1 / 40,
17+
ior: 1.1,
18+
bounces: 0,
19+
resolution: 1024,
20+
size: 10,
21+
intensity: 0.5,
22+
},
23+
/* glsl */ `varying vec2 vUv;
24+
void main() {
25+
vUv = uv;
26+
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
27+
}`,
28+
/* glsl */ `uniform mat4 cameraMatrixWorld;
29+
uniform mat4 cameraProjectionMatrixInv;
30+
uniform vec3 lightDir;
31+
uniform vec3 lightPlaneNormal;
32+
uniform float lightPlaneConstant;
33+
uniform float near;
34+
uniform float far;
35+
uniform float time;
36+
uniform float worldRadius;
37+
uniform float resolution;
38+
uniform float size;
39+
uniform float intensity;
40+
uniform float ior;
41+
precision highp isampler2D;
42+
precision highp usampler2D;
43+
uniform sampler2D normalTexture;
44+
uniform sampler2D depthTexture;
45+
uniform float bounces;
46+
varying vec2 vUv;
47+
vec3 WorldPosFromDepth(float depth, vec2 coord) {
48+
float z = depth * 2.0 - 1.0;
49+
vec4 clipSpacePosition = vec4(coord * 2.0 - 1.0, z, 1.0);
50+
vec4 viewSpacePosition = cameraProjectionMatrixInv * clipSpacePosition;
51+
// Perspective division
52+
viewSpacePosition /= viewSpacePosition.w;
53+
vec4 worldSpacePosition = cameraMatrixWorld * viewSpacePosition;
54+
return worldSpacePosition.xyz;
55+
}
56+
float sdPlane( vec3 p, vec3 n, float h ) {
57+
// n must be normalized
58+
return dot(p,n) + h;
59+
}
60+
float planeIntersect( vec3 ro, vec3 rd, vec4 p ) {
61+
return -(dot(ro,p.xyz)+p.w)/dot(rd,p.xyz);
62+
}
63+
vec3 totalInternalReflection(vec3 ro, vec3 rd, vec3 pos, vec3 normal, float ior, out vec3 rayOrigin, out vec3 rayDirection) {
64+
rayOrigin = ro;
65+
rayDirection = rd;
66+
rayDirection = refract(rayDirection, normal, 1.0 / ior);
67+
rayOrigin = pos + rayDirection * 0.1;
68+
return rayDirection;
69+
}
70+
void main() {
71+
// Each sample consists of random offset in the x and y direction
72+
float caustic = 0.0;
73+
float causticTexelSize = (1.0 / resolution) * size * 2.0;
74+
float texelsNeeded = worldRadius / causticTexelSize;
75+
float sampleRadius = texelsNeeded / resolution;
76+
float sum = 0.0;
77+
if (texture2D(depthTexture, vUv).x == 1.0) {
78+
gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
79+
return;
80+
}
81+
vec2 offset1 = vec2(-0.5, -0.5);//vec2(rand() - 0.5, rand() - 0.5);
82+
vec2 offset2 = vec2(-0.5, 0.5);//vec2(rand() - 0.5, rand() - 0.5);
83+
vec2 offset3 = vec2(0.5, 0.5);//vec2(rand() - 0.5, rand() - 0.5);
84+
vec2 offset4 = vec2(0.5, -0.5);//vec2(rand() - 0.5, rand() - 0.5);
85+
vec2 uv1 = vUv + offset1 * sampleRadius;
86+
vec2 uv2 = vUv + offset2 * sampleRadius;
87+
vec2 uv3 = vUv + offset3 * sampleRadius;
88+
vec2 uv4 = vUv + offset4 * sampleRadius;
89+
vec3 normal1 = texture2D(normalTexture, uv1, -10.0).rgb * 2.0 - 1.0;
90+
vec3 normal2 = texture2D(normalTexture, uv2, -10.0).rgb * 2.0 - 1.0;
91+
vec3 normal3 = texture2D(normalTexture, uv3, -10.0).rgb * 2.0 - 1.0;
92+
vec3 normal4 = texture2D(normalTexture, uv4, -10.0).rgb * 2.0 - 1.0;
93+
float depth1 = texture2D(depthTexture, uv1, -10.0).x;
94+
float depth2 = texture2D(depthTexture, uv2, -10.0).x;
95+
float depth3 = texture2D(depthTexture, uv3, -10.0).x;
96+
float depth4 = texture2D(depthTexture, uv4, -10.0).x;
97+
// Sanity check the depths
98+
if (depth1 == 1.0 || depth2 == 1.0 || depth3 == 1.0 || depth4 == 1.0) {
99+
gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
100+
return;
101+
}
102+
vec3 pos1 = WorldPosFromDepth(depth1, uv1);
103+
vec3 pos2 = WorldPosFromDepth(depth2, uv2);
104+
vec3 pos3 = WorldPosFromDepth(depth3, uv3);
105+
vec3 pos4 = WorldPosFromDepth(depth4, uv4);
106+
vec3 originPos1 = WorldPosFromDepth(0.0, uv1);
107+
vec3 originPos2 = WorldPosFromDepth(0.0, uv2);
108+
vec3 originPos3 = WorldPosFromDepth(0.0, uv3);
109+
vec3 originPos4 = WorldPosFromDepth(0.0, uv4);
110+
vec3 endPos1, endPos2, endPos3, endPos4;
111+
vec3 endDir1, endDir2, endDir3, endDir4;
112+
totalInternalReflection(originPos1, lightDir, pos1, normal1, ior, endPos1, endDir1);
113+
totalInternalReflection(originPos2, lightDir, pos2, normal2, ior, endPos2, endDir2);
114+
totalInternalReflection(originPos3, lightDir, pos3, normal3, ior, endPos3, endDir3);
115+
totalInternalReflection(originPos4, lightDir, pos4, normal4, ior, endPos4, endDir4);
116+
float lightPosArea = length(cross(originPos2 - originPos1, originPos3 - originPos1)) + length(cross(originPos3 - originPos1, originPos4 - originPos1));
117+
float t1 = planeIntersect(endPos1, endDir1, vec4(lightPlaneNormal, lightPlaneConstant));
118+
float t2 = planeIntersect(endPos2, endDir2, vec4(lightPlaneNormal, lightPlaneConstant));
119+
float t3 = planeIntersect(endPos3, endDir3, vec4(lightPlaneNormal, lightPlaneConstant));
120+
float t4 = planeIntersect(endPos4, endDir4, vec4(lightPlaneNormal, lightPlaneConstant));
121+
vec3 finalPos1 = endPos1 + endDir1 * t1;
122+
vec3 finalPos2 = endPos2 + endDir2 * t2;
123+
vec3 finalPos3 = endPos3 + endDir3 * t3;
124+
vec3 finalPos4 = endPos4 + endDir4 * t4;
125+
float finalArea = length(cross(finalPos2 - finalPos1, finalPos3 - finalPos1)) + length(cross(finalPos3 - finalPos1, finalPos4 - finalPos1));
126+
caustic += intensity * (lightPosArea / finalArea);
127+
// Calculate the area of the triangle in light spaces
128+
gl_FragColor = vec4(vec3(max(caustic, 0.0)), 1.0);
129+
}`
130+
);
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import * as THREE from 'three';
2+
import { shaderMaterial } from '../shader-material/shader-material';
3+
4+
export const CausticsProjectionMaterial = shaderMaterial(
5+
{
6+
causticsTexture: null,
7+
causticsTextureB: null,
8+
color: new THREE.Color(),
9+
lightProjMatrix: new THREE.Matrix4(),
10+
lightViewMatrix: new THREE.Matrix4(),
11+
},
12+
/* glsl */ `varying vec3 vWorldPosition;
13+
void main() {
14+
gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(position, 1.);
15+
vec4 worldPosition = modelMatrix * vec4(position, 1.);
16+
vWorldPosition = worldPosition.xyz;
17+
}`,
18+
/* glsl */ `varying vec3 vWorldPosition;
19+
uniform vec3 color;
20+
uniform sampler2D causticsTexture;
21+
uniform sampler2D causticsTextureB;
22+
uniform mat4 lightProjMatrix;
23+
uniform mat4 lightViewMatrix;
24+
void main() {
25+
// Apply caustics
26+
vec4 lightSpacePos = lightProjMatrix * lightViewMatrix * vec4(vWorldPosition, 1.0);
27+
lightSpacePos.xyz /= lightSpacePos.w;
28+
lightSpacePos.xyz = lightSpacePos.xyz * 0.5 + 0.5;
29+
vec3 front = texture2D(causticsTexture, lightSpacePos.xy).rgb;
30+
vec3 back = texture2D(causticsTextureB, lightSpacePos.xy).rgb;
31+
gl_FragColor = vec4((front + back) * color, 1.0);
32+
#include <tonemapping_fragment>
33+
#include <encodings_fragment>
34+
}`
35+
);

libs/soba/shaders/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
export * from './caustics-material/caustics-material';
2+
export * from './caustics-material/caustics-projection-material';
13
export * from './discard-material/discard-material';
24
export * from './soft-shadow-material/soft-shadow-material';
35
export * from './sparkles-material/sparkles-material';

0 commit comments

Comments
 (0)