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
11 changes: 11 additions & 0 deletions projects/ngx-three-demo/src/app/app-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { ViewsExampleComponent } from './views-example/views-example.component';
import { MultiRendererExampleComponent } from './multi-renderer-example/multi-renderer-example.component';
import { CSS3dRendererExampleComponent } from './css3d-renderer-example/css3d-renderer-example.component';
import { HtmlWithCSS3dExampleComponent } from './html-with-css3d-example/html-with-css3d-example.component';
import { FogExampleComponent } from './webgpu/fog-example.component';

export const EXAMPLE_ROUTES: (Route & {
data: {
Expand Down Expand Up @@ -284,6 +285,16 @@ export const EXAMPLE_ROUTES: (Route & {
],
},
},
{
path: 'webgpu-fog-example',
data: {
title: 'WebGPU Fog Example',
exampleComponent: FogExampleComponent,
codeUrls: [
'https://raw.githubusercontent.com/demike/ngx-three/main/projects/ngx-three-demo/src/app/webgpu/fog-example.component.ts',
],
},
},
];

EXAMPLE_ROUTES.forEach((route) => (route.component = ExamplePageComponent));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { ASSET_PATH } from '../assets';
@Component({
selector: 'app-loader-example',
templateUrl: './loader-example.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class LoaderExampleComponent {
public assetPath1 = `${ASSET_PATH}DamagedHelmet.glb`;
Expand Down
118 changes: 118 additions & 0 deletions projects/ngx-three-demo/src/app/webgpu/fog-example.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import { ChangeDetectionStrategy, Component, AfterViewInit, ViewChild } from '@angular/core';
import { NgxThreeModule, ThInstancedMesh } from 'ngx-three';
import { provideWebGPURenderer } from 'projects/ngx-three/src/lib/renderer/renderer-providers';
import * as THREE from 'three/webgpu';
import { float, positionView, positionWorld, triNoise3D, uniform, color, fog, normalWorld } from 'three/src/nodes/TSL';

@Component({
selector: 'app-fog-example',
template: ` <th-canvas #thecanvas>
<th-scene [objRef]="webGPUScene">
<th-perspectiveCamera #camera [args]="[45, aspect, near, far]" [position]="[30, 15, 30]">
<th-orbitControls
[autoRotate]="true"
[target]="[0, 2, 0]"
[minDistance]="7"
[maxDistance]="100"
[maxPolarAngle]="Math.PI / 2"
[autoRotateSpeed]="0.1"
/>
</th-perspectiveCamera>

<th-instancedMesh
#instancedMesh
[args]="[undefined, undefined, NUM_INSTANCES]"
[count]="NUM_INSTANCES"
[castShadow]="true"
[receiveShadow]="true"
>
<th-boxGeometry [args]="[1, 1, 1]" />
<th-meshPhongNodeMaterial [colorNode]="this.buildWindows" />
</th-instancedMesh>

<th-hemisphereLight [args]="[$any(skyColor).value, $any(groundColor).value, 0.5]" />

<th-mesh [scale]="[3, 3, 3]" [castShadow]="true" [receiveShadow]="true" [rotation]="[-Math.PI / 2, 0, 0]">
<th-planeGeometry [args]="[200, 200]" />
<th-meshPhongMaterial [color]="['0x999999']" />
</th-mesh>
</th-scene>
</th-canvas>`,
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
imports: [NgxThreeModule],
providers: [provideWebGPURenderer()],
})
export class FogExampleComponent implements AfterViewInit {
readonly webGPUScene = new THREE.Scene();
readonly Math = Math;
readonly NUM_INSTANCES = 4000;
readonly skyColor = color(0xf0f5f5);
readonly groundColor = color(0xd0dee7);
readonly aspect = window.innerWidth / window.innerHeight;
readonly near = 1;
readonly far = 600;
private fogNoiseDistance = positionView.z.negate().smoothstep(0, this.far - 300);
protected buildWindows = positionWorld.y
.mul(10)
.floor()
.mod(4)
.sign()
.mix(color(0x000066).add(this.fogNoiseDistance), color(0xffffff));
private backgroundNode = normalWorld.y.max(0).mix(this.groundColor, this.skyColor);
private fogNode: ReturnType<typeof fog>;

@ViewChild('instancedMesh', { static: true })
protected instancedMesh?: ThInstancedMesh;

constructor() {
this.fogNode = this.initFogNode();
this.webGPUScene.backgroundNode = this.backgroundNode;
this.webGPUScene.fogNode = this.fogNode;
}

ngAfterViewInit() {
this.initInstanceMatrix();
}

initInstanceMatrix() {
const dummy = new THREE.Object3D();
const center = new THREE.Vector3();
const buildMesh = this.instancedMesh!.objRef!;

for (let i = 0; i < buildMesh.count; i++) {
const scaleY = Math.random() * 7 + 0.5;

dummy.position.x = Math.random() * 600 - 300;
dummy.position.z = Math.random() * 600 - 300;

const distance = Math.max(dummy.position.distanceTo(center) * 0.012, 1);

dummy.position.y = 0.5 * scaleY * distance;

dummy.scale.x = dummy.scale.z = Math.random() * 3 + 0.5;
dummy.scale.y = scaleY * distance;

dummy.updateMatrix();

buildMesh.setMatrixAt(i, dummy.matrix);
}
// buildMesh.instanceMatrix.needsUpdate = true;
}

private initFogNode() {
const distance = this.fogNoiseDistance.mul(20).max(4);
const alpha = 0.98;
const groundFogArea = float(distance).sub(positionWorld.y).div(distance).pow(3).saturate().mul(alpha);

// an alternative way to create a TimerNode
const timer = uniform(0).onFrameUpdate((frame) => frame.time);

const fogNoiseA = triNoise3D(positionWorld.mul(0.005), 0.2, timer);
const fogNoiseB = triNoise3D(positionWorld.mul(0.01), 0.2, timer.mul(1.2));

const fogNoise = fogNoiseA.add(fogNoiseB).mul(this.groundColor);

return fog(this.fogNoiseDistance.oneMinus().mix(this.groundColor, fogNoise), groundFogArea);
}
}
3 changes: 2 additions & 1 deletion projects/ngx-three/src/lib/ThEngine.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { HOST_ELEMENT, ThView } from './ThView';
import { isObserved } from './util';
import { Observable, Subject, takeUntil } from 'rxjs';
import { RENDERER_PROVIDERS } from './renderer/renderer-providers';
import { WebGPURenderer } from 'three/webgpu';

export interface RenderState {
engine: ThEngineService;
Expand Down Expand Up @@ -132,7 +133,7 @@ export class ThEngineService implements OnDestroy {
}

for (const renderer of this.renderers) {
if (view.effectComposer && !(renderer instanceof WebGLRenderer)) {
if (view.effectComposer && !(renderer instanceof WebGLRenderer || renderer instanceof WebGPURenderer)) {
// effect composer needs a webgl renderer
continue;
}
Expand Down
153 changes: 153 additions & 0 deletions projects/ngx-three/src/lib/generated/ThInstancedPointsNodeMaterial.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
/* eslint-disable @typescript-eslint/ban-types */
/* eslint-disable @typescript-eslint/naming-convention */
/* eslint-disable no-underscore-dangle */
/* eslint-disable @angular-eslint/component-selector, @angular-eslint/component-class-suffix */
import {
ChangeDetectionStrategy,
Component,
Input,
Type,
forwardRef,
} from '@angular/core';
import Node from 'three/src/nodes/core/Node.js';
import { Texture } from 'three/src/textures/Texture.js';
import {
Color,
ColorRepresentation,
InstancedPointsNodeMaterial,
InstancedPointsNodeMaterialParameters,
} from 'three/webgpu';
import { applyValue } from '../util';
import { ThMaterial } from './ThMaterial';
import { ThNodeMaterial } from './ThNodeMaterial';

@Component({
selector: 'th-instancedPointsNodeMaterial',
template: '<ng-content/>',
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [
{
provide: ThMaterial,
useExisting: forwardRef(() => ThInstancedPointsNodeMaterial),
},
],
})
export class ThInstancedPointsNodeMaterial<
T extends InstancedPointsNodeMaterial = InstancedPointsNodeMaterial,
TARGS = /* params? */ InstancedPointsNodeMaterialParameters,
> extends ThNodeMaterial<T, TARGS> {
public getType(): Type<InstancedPointsNodeMaterial> {
return InstancedPointsNodeMaterial;
}

@Input()
public set useAlphaToCoverage(value: boolean) {
if (this._objRef) {
this._objRef.useAlphaToCoverage = value;
}
}

public get useAlphaToCoverage(): boolean | undefined {
return this._objRef?.useAlphaToCoverage;
}
@Input()
public set useColor(value: boolean | undefined) {
if (this._objRef) {
this._objRef.useColor = value;
}
}

public get useColor(): (boolean | undefined) | undefined {
return this._objRef?.useColor;
}
@Input()
public set pointWidth(value: number) {
if (this._objRef) {
this._objRef.pointWidth = value;
}
}

public get pointWidth(): number | undefined {
return this._objRef?.pointWidth;
}
@Input()
public set pointColorNode(value: Node | null) {
if (this._objRef) {
this._objRef.pointColorNode = value;
}
}

public get pointColorNode(): (Node | null) | undefined {
return this._objRef?.pointColorNode;
}
@Input()
public set pointWidthNode(value: Node | null) {
if (this._objRef) {
this._objRef.pointWidthNode = value;
}
}

public get pointWidthNode(): (Node | null) | undefined {
return this._objRef?.pointWidthNode;
}
public get isPointsMaterial(): true | undefined {
return this._objRef?.isPointsMaterial;
}
@Input()
public set color(
value:
| Color
| [
...args:
| [color: ColorRepresentation]
| [r: number, g: number, b: number],
],
) {
if (this._objRef) {
this._objRef.color = applyValue<Color>(this._objRef.color, value);
}
}
public get color(): Color | undefined {
return this._objRef?.color;
}
@Input()
public set map(value: Texture | null) {
if (this._objRef) {
this._objRef.map = value;
}
}

public get map(): (Texture | null) | undefined {
return this._objRef?.map;
}
@Input()
public set alphaMap(value: Texture | null) {
if (this._objRef) {
this._objRef.alphaMap = value;
}
}

public get alphaMap(): (Texture | null) | undefined {
return this._objRef?.alphaMap;
}
@Input()
public set size(value: number) {
if (this._objRef) {
this._objRef.size = value;
}
}

public get size(): number | undefined {
return this._objRef?.size;
}
@Input()
public set sizeAttenuation(value: boolean) {
if (this._objRef) {
this._objRef.sizeAttenuation = value;
}
}

public get sizeAttenuation(): boolean | undefined {
return this._objRef?.sizeAttenuation;
}
}
Loading
Loading