Skip to content

Commit 4071212

Browse files
committed
graph parameter and switch overrides in materials added
1 parent 000212c commit 4071212

File tree

8 files changed

+518
-134
lines changed

8 files changed

+518
-134
lines changed

examples/graphics/shader-graph-chunks-builder.html

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -91,9 +91,9 @@
9191

9292
// create and hook up graph nodes
9393
var scrollNode = builder.addNode('add', [ timeParam, builder.addNode('uv0') ] );
94-
var texSampNodeA = builder.addTextureSample( texParamA, scrollNode, { precision: 'lowp' } );
94+
var texSampNodeA = builder.addTextureSample( texParamA, scrollNode, { precision: 'highp' } );
9595
var texSampNodeB = builder.addTextureSample( texParamB, scrollNode );
96-
var emisCalcNode = builder.addNode('mul', [ colConst, {node: texSampNodeA, swizzle: 'rgb'} ], { precision: 'lowp' } );
96+
var emisCalcNode = builder.addNode('mul', [ colConst, {node: texSampNodeA, swizzle: 'rgb'} ], { precision: 'highp' } );
9797

9898
// create a static switch to choose alpha source
9999
var switchValue = 1;
@@ -108,7 +108,7 @@
108108
var shaderGraphChunk = builder.getShaderGraphChunk();
109109

110110
//tmp - directly set private
111-
shaderGraphChunk._precision = 'lowp';
111+
// shaderGraphChunk._precision = 'lowp';
112112

113113
// replace model's materials with standard node materials
114114
var originalMaterials = [];
@@ -123,6 +123,7 @@
123123

124124
originalMaterials.push(meshInstance.material);
125125
shaderGraphMaterials.push(new pc.StandardNodeMaterial(meshInstance.material, shaderGraphChunk));
126+
// shaderGraphMaterials.push(new pc.StandardNodeMaterial(meshInstance.material));
126127
}
127128

128129
meshInstance.material = shaderGraphMaterials[index];
@@ -153,11 +154,11 @@
153154
shaderGraphMaterials.forEach(function (material) {
154155
if (flipSwitch)
155156
{
156-
material.setSwitch(switchName, switchValue);
157+
material.overrideGraphSwitch(switchName, switchValue);
157158
material.update();
158159
}
159-
material.setParameter('uTime', time);
160-
material.alphaTest = 1.0 - ((Math.sin(time * 3.14159) + 1.7) * 0.5);
160+
material.overrideGraphParameter('uTime', time);
161+
material.alphaTest = 1.0 - ((Math.sin(time * 3.14159) + 2.0) * 0.5);
161162
});
162163

163164
if (flipSwitch)
Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<title>PlayCanvas Shader Graph Chunks Builder</title>
5+
<meta charset="utf-8">
6+
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
7+
<link rel="icon" type="image/png" href="../playcanvas-favicon.png" />
8+
<script src="../../build/playcanvas.js"></script>
9+
<script src="../../build/playcanvas-extras.js"></script>
10+
<script src="../../build/playcanvas-shader-nodes.js"></script>
11+
<style>
12+
body {
13+
margin: 0;
14+
overflow-y: hidden;
15+
}
16+
</style>
17+
</head>
18+
19+
<body>
20+
<!-- The canvas element -->
21+
<canvas id="application-canvas"></canvas>
22+
23+
<!-- The script -->
24+
<script>
25+
var light, camera;
26+
var time = 0;
27+
28+
var entityA, entityB, entityC;
29+
var matA, matB, matC;
30+
31+
var canvas = document.getElementById("application-canvas");
32+
33+
// Create the application and start the update loop
34+
var app = new pc.Application(canvas);
35+
36+
// Set the canvas to fill the window and automatically change resolution to be the same as the canvas size
37+
app.setCanvasFillMode(pc.FILLMODE_FILL_WINDOW);
38+
app.setCanvasResolution(pc.RESOLUTION_AUTO);
39+
40+
window.addEventListener("resize", function () {
41+
app.resizeCanvas(canvas.width, canvas.height);
42+
});
43+
44+
var miniStats = new pcx.MiniStats(app);
45+
46+
// register core nodes
47+
app.shaderNodes.register(pcsg.shaderNodes);
48+
49+
// set ambient light
50+
app.scene.ambientLight = new pc.Color(0.2, 0.2, 0.2);
51+
52+
// Create an Entity with a camera component
53+
camera = new pc.Entity();
54+
camera.addComponent("camera", {
55+
clearColor: new pc.Color(0.4, 0.45, 0.5)
56+
});
57+
camera.translate(0, 7, 25);
58+
app.root.addChild(camera);
59+
60+
// Create an entity with a directional light component
61+
var light = new pc.Entity();
62+
light.addComponent("light", {
63+
type: "directional",
64+
color: new pc.Color(1, 1.0, 1.0)
65+
});
66+
app.root.addChild(light);
67+
light.setLocalEulerAngles(15, -20, 0);
68+
69+
// load in a model
70+
var url = "../assets/models/statue.glb";
71+
app.assets.loadFromUrl(url, "container", function (err, asset) {
72+
entityA = new pc.Entity();
73+
74+
var modelComponent = entityA.addComponent("model", {
75+
type: "asset",
76+
asset: asset.resource.model
77+
});
78+
var model = modelComponent.model;
79+
80+
app.root.addChild(entityA);
81+
82+
83+
entityB = entityA.clone();
84+
app.root.addChild(entityB);
85+
86+
entityC = entityA.clone();
87+
app.root.addChild(entityC);
88+
89+
entityA.translate(-6, 0, 0);
90+
entityB.translate( 0, 0, 0);
91+
entityC.translate( 6, 0, 0);
92+
93+
// load a texture asset
94+
app.assets.loadFromUrl("../assets/textures/logo.jpg", "texture", function (err, asset) {
95+
var logoMap = asset.resource;
96+
app.assets.loadFromUrl("../assets/textures/smiley.jpg", "texture", function (err, asset) {
97+
var smileyMap = asset.resource;
98+
app.assets.loadFromUrl("../assets/textures/particles-numbers.png", "texture", function (err, asset) {
99+
var numbersMap = asset.resource;
100+
101+
// build one graph
102+
// create a shader graph builder
103+
var builder = new pc.ShaderGraphBuilder(app);
104+
105+
// add input parameters
106+
var timeParam = builder.addParam('float', 'uTime', 0.0);
107+
var texParamA = builder.addParam('sampler2D', 'uTexMapA', logoMap);
108+
var texParamB = builder.addParam('sampler2D', 'uTexMapB', numbersMap);
109+
var tileParamA = builder.addParam('float', 'uTileA', 4.0 );
110+
var tileParamB = builder.addParam('float', 'uTileB', 1.0 );
111+
var colParam = builder.addParam('vec4', 'uCol', new pc.Vec4(0.5, 0.5, 1.0, 1.0) );
112+
var usefulConsts = builder.addConst('vec4', 'usefulConsts', new pc.Vec4(0.0, 0.5, 2.0, 1.0) );
113+
114+
// create and hook up graph nodes
115+
var uv0Node = builder.addNode('uv0');
116+
var wposNode = builder.addNode('worldPos');
117+
var wnormNode = builder.addNode('worldNorm');
118+
var albedoNode = builder.addNode('stdAlbedo');
119+
120+
var mulNodeA = builder.addNode('mul', [ timeParam, { node: usefulConsts, swizzle: 'gr' }, { node: usefulConsts, swizzle: 'g' } ] );
121+
var mulNodeB = builder.addNode('mul', [ timeParam, { node: usefulConsts, swizzle: 'rg' }, { node: usefulConsts, swizzle: 'g' } ] );
122+
var scrollNodeA = builder.addNode('add', [ mulNodeA, uv0Node ] );
123+
var scrollNodeB = builder.addNode('add', [ mulNodeB, uv0Node ] );
124+
var texSampNodeA = builder.addTextureSample( texParamA, builder.addNode('mul', [scrollNodeA, tileParamA]), { precision: 'lowp' } );
125+
var texSampNodeB = builder.addTextureSample( texParamB, builder.addNode('mul', [scrollNodeB, tileParamB]) );
126+
127+
// create a static switch to choose alpha source
128+
var switchValue = 0;
129+
var switchName = 'switchAlpha';
130+
var staticSwitch = builder.addStaticSwitch(switchName, [ { node: texSampNodeA, swizzle: 'rgbg' }, { node: texSampNodeB, swizzle: 'rgbg' } ], switchValue);
131+
132+
var calcNodeA = builder.addNode('mul', [{ node: usefulConsts, swizzle: 'g' }, { node: usefulConsts, swizzle: 'g' }, { node: staticSwitch, swizzle: 'g' }]);
133+
134+
var calcNodeS = builder.addNode('mul', [{ node: calcNodeA }, { node: colParam, swizzle: 'rgb' }, { node: albedoNode, swizzle: 'rgb' }]);
135+
var calcNodeIA = builder.addNode('sub', [{ node: usefulConsts, swizzle: 'a' }, { node: calcNodeA }]);
136+
var calcNodeD = builder.addNode('mul', [{ node: calcNodeIA }, { node: albedoNode, swizzle: 'rgb' }]);
137+
138+
// hook up outputs
139+
builder.addOutput( { node: staticSwitch, swizzle: 'a' } , 'float', 'sgGlossiness' );
140+
builder.addOutput( builder.addNode('mul', [{ node: usefulConsts, swizzle: 'ggg' }, { node: staticSwitch, swizzle: 'ggg' }]), 'vec3', 'sgSpecularity');
141+
builder.addOutput( builder.addNode('add', [{ node: calcNodeS, swizzle: 'rgb' }, { node: calcNodeD, swizzle: 'rgb' }]), 'vec3', 'sgAlbedo');
142+
143+
// return built shader graph chunk
144+
var shaderGraphChunk = builder.getShaderGraphChunk();
145+
146+
// assign to 3 materials - each assigned to a model
147+
matA = new pc.StandardNodeMaterial(model.meshInstances[0].material, shaderGraphChunk);
148+
matA.alphaTest = 1.0;
149+
matA.update();
150+
151+
matB = matA.clone();
152+
matC = matA.clone();
153+
154+
// replace model's materials with standard node materials
155+
for (var i=0; i<model.meshInstances.length; i++)
156+
{
157+
entityA.model.model.meshInstances[i].material = matA;
158+
entityB.model.model.meshInstances[i].material = matB;
159+
entityC.model.model.meshInstances[i].material = matC;
160+
}
161+
162+
var redCol=new pc.Vec4(1,0,0,1);
163+
var blueCol=new pc.Vec4(0,0,1,1);
164+
165+
// update
166+
app.on("update", function (dt) {
167+
prevTime = time;
168+
time += dt;
169+
170+
// graph parameters
171+
shaderGraphChunk.setParameter('uTime', time);
172+
173+
var t0=(time*2)%1.0;
174+
if (t0>=0.5) {
175+
shaderGraphChunk.setParameter('uCol', redCol);
176+
} else {
177+
shaderGraphChunk.setParameter('uCol', blueCol);
178+
}
179+
180+
var t4=time%4.0;
181+
if (t4>=2.0) {
182+
shaderGraphChunk.setSwitchValue(switchName, 0);
183+
} else {
184+
shaderGraphChunk.setSwitchValue(switchName, 1);
185+
}
186+
187+
shaderGraphChunk.update(app.graphicsDevice);
188+
189+
if (entityB && matB)
190+
{
191+
var tB=time%1.0;
192+
if (tB>=0.5)
193+
{
194+
matB.overrideGraphSwitch(switchName, 1);
195+
}
196+
else
197+
{
198+
matB.overrideGraphSwitch(switchName, 0);
199+
}
200+
matB.update();
201+
}
202+
203+
if (entityC && matC)
204+
{
205+
matC.overrideGraphParameter("uTime", time*-0.5 );
206+
matC.overrideGraphParameter("uTexMapA", numbersMap );
207+
matC.overrideGraphParameter("uTileA", 1.0 );
208+
matC.overrideGraphParameter("uTexMapB", smileyMap );
209+
matC.overrideGraphParameter("uTileB", 4.0 );
210+
}
211+
});
212+
213+
app.start();
214+
});
215+
});
216+
});
217+
});
218+
</script>
219+
</body>
220+
</html>

src/graphics/program-lib/programs/standard-node.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ standardnode.generateKey = function (options) {
1010
var key = this._generateKey(options);
1111

1212
if (options._shaderGraphChunk) {
13+
if (options._graphSwitchOverrides) options._shaderGraphChunk._switchOverrides = options._graphSwitchOverrides;
1314
key += options._shaderGraphChunk.getSwitchKey();
15+
if (options._graphSwitchOverrides) options._shaderGraphChunk._switchOverrides = null;
1416
}
1517

1618
return hashCode(key);
@@ -27,10 +29,13 @@ standardnode.createShaderDefinition = function (device, options) {
2729
graphCode.needsNormal = false;
2830

2931
if (options._shaderGraphChunk) {
32+
33+
if (options._graphSwitchOverrides) options._shaderGraphChunk._switchOverrides = options._graphSwitchOverrides;
34+
3035
rootShaderGraph = options._shaderGraphChunk;
3136

32-
rootDeclGLSL = rootShaderGraph.generateRootDeclCodeString();
33-
rootCallGLSL = rootShaderGraph.generateRootCallCodeString();
37+
rootDeclGLSL = rootShaderGraph.generateRootDeclCodeString(device);
38+
rootCallGLSL = rootShaderGraph.generateRootCallCodeString(device);
3439

3540
graphCode.vsDecl = '';
3641
if (rootShaderGraph.getIoPortByName('sgVertOff')) {
@@ -87,6 +92,8 @@ standardnode.createShaderDefinition = function (device, options) {
8792
if (rootShaderGraph.getIoPortByName('sgFragOut')) {
8893
graphCode.psEnd += 'gl_FragColor = sgFragOut;\n';
8994
}
95+
96+
if (options._graphSwitchOverrides) options._shaderGraphChunk._switchOverrides = null;
9097
}
9198

9299
return this._createShaderDefinition(device, options, graphCode);

src/graphics/program-library.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ ProgramLibrary.prototype.storeNewProgram = function (name, options) {
8484
var defaultMat = this._getDefaultStdMatOptions(options.pass);
8585

8686
for (var p in options) {
87-
if ((options.hasOwnProperty(p) && defaultMat[p] !== options[p] && p !== '_shaderGraphChunk') || p === "pass")
87+
if ((options.hasOwnProperty(p) && defaultMat[p] !== options[p] && p !== '_shaderGraphChunk' && p !== '_graphSwitchOverrides' && p !== '_graphParamOverrides') || p === "pass")
8888
opt[p] = options[p];
8989
}
9090
} else {

src/scene/forward-renderer.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ import { Material } from './materials/material.js';
5050
import { Mesh } from './mesh.js';
5151
import { MeshInstance } from './mesh-instance.js';
5252
import { VisibleInstanceList } from './layer.js';
53+
import { StandardMaterial } from './materials/standard-material.js';
5354

5455
// Global shadowmap resources
5556
var scaleShift = new Mat4().mul2(
@@ -1946,6 +1947,13 @@ Object.assign(ForwardRenderer.prototype, {
19461947
material.setParameters(device, drawCall.parameters);
19471948
}
19481949

1950+
// Unset material overrides back to shadergraph chunk defaults
1951+
if (i >= drawCallsCount - 1 || drawCalls[i + 1].material !== material) {
1952+
if (material.shaderGraphChunk) {
1953+
material.setParameters(device, undefined, true);
1954+
}
1955+
}
1956+
19491957
prevMaterial = material;
19501958
prevObjDefs = objDefs;
19511959
prevLightMask = lightMask;

0 commit comments

Comments
 (0)