Skip to content

Commit 87c581c

Browse files
authored
Nodes: ViewportNode (mrdoob#24934)
* NodeBuilder: Add .getFragCoord(), .isFlipY() * Add ViewportNode * ShaderNode: Add viewport* elements * Example: add texture screen projection * cleanup
1 parent a392c8d commit 87c581c

File tree

7 files changed

+155
-12
lines changed

7 files changed

+155
-12
lines changed

examples/jsm/nodes/Nodes.js

+3
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ import FrontFacingNode from './display/FrontFacingNode.js';
6161
import NormalMapNode from './display/NormalMapNode.js';
6262
import PosterizeNode from './display/PosterizeNode.js';
6363
import ToneMappingNode from './display/ToneMappingNode.js';
64+
import ViewportNode from './display/ViewportNode.js';
6465

6566
// math
6667
import MathNode from './math/MathNode.js';
@@ -187,6 +188,7 @@ const nodeLib = {
187188
NormalMapNode,
188189
PosterizeNode,
189190
ToneMappingNode,
191+
ViewportNode,
190192

191193
// math
192194
MathNode,
@@ -306,6 +308,7 @@ export {
306308
NormalMapNode,
307309
PosterizeNode,
308310
ToneMappingNode,
311+
ViewportNode,
309312

310313
// math
311314
MathNode,

examples/jsm/nodes/core/NodeBuilder.js

+12
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,18 @@ class NodeBuilder {
181181

182182
}
183183

184+
getFragCoord() {
185+
186+
console.warn( 'Abstract function.' );
187+
188+
}
189+
190+
isFlipY() {
191+
192+
return false;
193+
194+
}
195+
184196
getTexture( /* textureProperty, uvSnippet */ ) {
185197

186198
console.warn( 'Abstract function.' );
+106
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import Node from '../core/Node.js';
2+
import { uniform, div, vec2, invert } from '../shadernode/ShaderNodeBaseElements.js';
3+
import { Vector2 } from 'three';
4+
import { NodeUpdateType } from '../core/constants.js';
5+
6+
let resolution;
7+
8+
class ViewportNode extends Node {
9+
10+
static COORDINATE = 'coordinate';
11+
static RESOLUTION = 'resolution';
12+
static TOP_LEFT = 'topLeft';
13+
static BOTTOM_LEFT = 'bottomLeft';
14+
static TOP_RIGHT = 'topRight';
15+
static BOTTOM_RIGHT = 'bottomRight';
16+
17+
constructor( scope ) {
18+
19+
super();
20+
21+
this.scope = scope;
22+
23+
this.isScreenNode = true;
24+
25+
}
26+
27+
getNodeType() {
28+
29+
return this.scope === ViewportNode.COORDINATE ? 'vec4' : 'vec2';
30+
31+
}
32+
33+
getUpdateType() {
34+
35+
let updateType = NodeUpdateType.NONE;
36+
37+
if ( this.scope === ViewportNode.RESOLUTION ) {
38+
39+
updateType = NodeUpdateType.FRAME;
40+
41+
}
42+
43+
this.updateType = updateType;
44+
45+
return updateType;
46+
47+
}
48+
49+
update( { renderer } ) {
50+
51+
renderer.getSize( resolution );
52+
53+
}
54+
55+
construct( builder ) {
56+
57+
const scope = this.scope;
58+
59+
if ( scope === ViewportNode.COORDINATE ) return;
60+
61+
let output = null;
62+
63+
if ( scope === ViewportNode.RESOLUTION ) {
64+
65+
resolution ||= new Vector2();
66+
67+
output = uniform( resolution );
68+
69+
} else {
70+
71+
const coordinateNode = vec2( new ViewportNode( ViewportNode.COORDINATE ) );
72+
const resolutionNode = new ViewportNode( ViewportNode.RESOLUTION );
73+
74+
output = div( coordinateNode, resolutionNode );
75+
76+
let outX = output.x;
77+
let outY = output.y;
78+
79+
if ( /top/i.test( scope ) && builder.isFlipY() ) outY = invert( outY );
80+
else if ( /bottom/i.test( scope ) && builder.isFlipY() === false ) outY = invert( outY );
81+
82+
if ( /right/i.test( scope ) ) outX = invert( outX );
83+
84+
output = vec2( outX, outY );
85+
86+
}
87+
88+
return output;
89+
90+
}
91+
92+
generate( builder ) {
93+
94+
if ( this.scope === ViewportNode.COORDINATE ) {
95+
96+
return builder.getFragCoord();
97+
98+
}
99+
100+
return super.generate( builder );
101+
102+
}
103+
104+
}
105+
106+
export default ViewportNode;

examples/jsm/nodes/shadernode/ShaderNodeElements.js

+8
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import ColorSpaceNode from '../display/ColorSpaceNode.js';
1111
import NormalMapNode from '../display/NormalMapNode.js';
1212
import PosterizeNode from '../display/PosterizeNode.js';
1313
import ToneMappingNode from '../display/ToneMappingNode.js';
14+
import ViewportNode from '../display/ViewportNode.js';
1415

1516
// lighting
1617
import LightsNode from '../lighting/LightsNode.js';
@@ -92,6 +93,13 @@ export const toneMapping = ( mapping, exposure, color ) => nodeObject( new ToneM
9293

9394
export const posterize = nodeProxy( PosterizeNode );
9495

96+
export const viewportCoordinate = nodeImmutable( ViewportNode, ViewportNode.COORDINATE );
97+
export const viewportResolution = nodeImmutable( ViewportNode, ViewportNode.RESOLUTION );
98+
export const viewportTopLeft = nodeImmutable( ViewportNode, ViewportNode.TOP_LEFT );
99+
export const viewportBottomLeft = nodeImmutable( ViewportNode, ViewportNode.BOTTOM_LEFT );
100+
export const viewportTopRight = nodeImmutable( ViewportNode, ViewportNode.TOP_RIGHT );
101+
export const viewportBottomRight = nodeImmutable( ViewportNode, ViewportNode.BOTTOM_RIGHT );
102+
95103
// lighting
96104

97105
//export const lighting = nodeProxy( LightingNode ); // abstract

examples/jsm/renderers/webgl/nodes/WebGLNodeBuilder.js

+8-11
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { defaultShaderStages, NodeFrame, MathNode, GLSLNodeParser, NodeBuilder } from 'three/nodes';
22
import SlotNode from './SlotNode.js';
3-
import { PerspectiveCamera, ShaderChunk, ShaderLib, UniformsUtils, UniformsLib,
4-
LinearEncoding, RGBAFormat, UnsignedByteType, sRGBEncoding } from 'three';
3+
import { PerspectiveCamera, ShaderChunk, ShaderLib, UniformsUtils, UniformsLib } from 'three';
54

65
const nodeFrame = new NodeFrame();
76
nodeFrame.camera = new PerspectiveCamera();
@@ -572,23 +571,21 @@ class WebGLNodeBuilder extends NodeBuilder {
572571

573572
}
574573

575-
getTextureEncodingFromMap( map ) {
574+
getFrontFacing() {
576575

577-
const isWebGL2 = this.renderer.capabilities.isWebGL2;
576+
return 'gl_FrontFacing';
578577

579-
if ( isWebGL2 && map && map.isTexture && map.format === RGBAFormat && map.type === UnsignedByteType && map.encoding === sRGBEncoding ) {
578+
}
580579

581-
return LinearEncoding; // disable inline decode for sRGB textures in WebGL 2
580+
getFragCoord() {
582581

583-
}
584-
585-
return super.getTextureEncodingFromMap( map );
582+
return 'gl_FragCoord';
586583

587584
}
588585

589-
getFrontFacing() {
586+
isFlipY() {
590587

591-
return 'gl_FrontFacing';
588+
return true;
592589

593590
}
594591

examples/jsm/renderers/webgpu/nodes/WebGPUNodeBuilder.js

+12
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,18 @@ class WebGPUNodeBuilder extends NodeBuilder {
399399

400400
}
401401

402+
getFragCoord() {
403+
404+
return this.getBuiltin( 'position', 'fragCoord', 'vec4<f32>', 'fragment' );
405+
406+
}
407+
408+
isFlipY() {
409+
410+
return false;
411+
412+
}
413+
402414
getAttributes( shaderStage ) {
403415

404416
const snippets = [];

examples/webgpu_materials.html

+6-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434

3535
import { TeapotGeometry } from 'three/addons/geometries/TeapotGeometry.js';
3636

37-
import { ShaderNode, vec3, dot, triplanarTexture } from 'three/nodes';
37+
import { ShaderNode, vec3, dot, triplanarTexture, viewportBottomLeft } from 'three/nodes';
3838

3939
import Stats from 'three/addons/libs/stats.module.js';
4040

@@ -182,6 +182,11 @@
182182
material.colorNode = triplanarTexture( new Nodes.TextureNode( texture ) );
183183
materials.push( material );
184184

185+
// Screen Projection Texture
186+
material = new Nodes.MeshBasicNodeMaterial();
187+
material.colorNode = Nodes.texture( texture, viewportBottomLeft );
188+
materials.push( material );
189+
185190
//
186191
// Geometry
187192
//

0 commit comments

Comments
 (0)