Skip to content

Commit f342964

Browse files
committed
Add strands tutorial visual tests
1 parent a0bab6c commit f342964

6 files changed

Lines changed: 252 additions & 2 deletions

File tree

test/unit/visual/cases/webgl.js

Lines changed: 124 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1144,6 +1144,128 @@ visualSuite('WebGL', function() {
11441144
screenshot();
11451145
});
11461146
});
1147+
1148+
visualTest('Strands tutorial', function(p5, screenshot) {
1149+
// From Luke Plowden's Intro to Strands tutorial
1150+
// https://beta.p5js.org/tutorials/intro-to-p5-strands/
1151+
1152+
function starShaderCallback({ p5 }) {
1153+
const time = p5.uniformFloat(() => p5.millis());
1154+
const skyRadius = p5.uniformFloat(90);
1155+
1156+
function rand2(st) {
1157+
return p5.fract(p5.sin(p5.dot(st, [12.9898, 78.233])) * 43758.5453123);
1158+
}
1159+
1160+
function semiSphere() {
1161+
let id = p5.instanceID();
1162+
let theta = rand2([id, 0.1234]) * p5.TWO_PI + time / 100000;
1163+
let phi = rand2([id, 3.321]) * p5.PI + time / 50000;
1164+
1165+
let r = skyRadius;
1166+
r *= p5.sin(phi);
1167+
let x = r * p5.sin(phi) * p5.cos(theta);
1168+
let y = r * 1.5 * p5.cos(phi);
1169+
let z = r * p5.sin(phi) * p5.sin(theta);
1170+
return [x, y, z];
1171+
}
1172+
1173+
p5.getWorldInputs((inputs) => {
1174+
inputs.position += semiSphere();
1175+
return inputs;
1176+
});
1177+
1178+
p5.getObjectInputs((inputs) => {
1179+
let size = 1 + 0.5 * p5.sin(time * 0.002 + p5.instanceID());
1180+
inputs.position *= size;
1181+
return inputs;
1182+
});
1183+
}
1184+
1185+
function pixelateShaderCallback({ p5 }) {
1186+
const pixelCountX = p5.uniformFloat(() => 100);
1187+
1188+
p5.getColor((inputs, canvasContent) => {
1189+
const aspectRatio = inputs.canvasSize.x / inputs.canvasSize.y;
1190+
const pixelSize = [pixelCountX, pixelCountX / aspectRatio];
1191+
1192+
let coord = inputs.texCoord;
1193+
coord = p5.floor(coord * pixelSize) / pixelSize;
1194+
1195+
let col = p5.getTexture(canvasContent, coord);
1196+
return col//[coord, 0, 1];
1197+
});
1198+
}
1199+
1200+
function bloomShaderCallback({ p5, originalImage }) {
1201+
const preBlur = p5.uniformTexture(() => originalImage);
1202+
1203+
getColor((input, canvasContent) => {
1204+
const blurredCol = p5.getTexture(canvasContent, input.texCoord);
1205+
const originalCol = p5.getTexture(preBlur, input.texCoord);
1206+
1207+
const intensity = p5.max(originalCol, 0.1) * 12.2;
1208+
1209+
const bloom = originalCol + blurredCol * intensity;
1210+
return [bloom.rgb, 1];
1211+
});
1212+
}
1213+
1214+
p5.createCanvas(200, 200, p5.WEBGL);
1215+
const stars = p5.buildGeometry(() => p5.sphere(4, 4, 2))
1216+
const originalImage = p5.createFramebuffer();
1217+
1218+
function fresnelShaderCallback({ p5 }) {
1219+
const fresnelPower = p5.uniformFloat(2);
1220+
const fresnelBias = p5.uniformFloat(-0.1);
1221+
const fresnelScale = p5.uniformFloat(2);
1222+
1223+
p5.getCameraInputs((inputs) => {
1224+
let n = p5.normalize(inputs.normal);
1225+
let v = p5.normalize(-inputs.position);
1226+
let base = 1.0 - p5.dot(n, v);
1227+
let fresnel = fresnelScale * p5.pow(base, fresnelPower) + fresnelBias;
1228+
let col = p5.mix([0, 0, 0], [1, .5, .7], fresnel);
1229+
inputs.color = [col, 1];
1230+
return inputs;
1231+
});
1232+
}
1233+
1234+
const starShader = p5.baseMaterialShader().modify(starShaderCallback, { p5 });
1235+
const starStrokeShader = p5.baseStrokeShader().modify(starShaderCallback, { p5 })
1236+
const fresnelShader = p5.baseColorShader().modify(fresnelShaderCallback, { p5 });
1237+
const bloomShader = p5.baseFilterShader().modify(bloomShaderCallback, { p5, originalImage });
1238+
const pixelateShader = p5.baseFilterShader().modify(pixelateShaderCallback, { p5 });
1239+
1240+
originalImage.begin();
1241+
p5.background(0);
1242+
1243+
p5.push()
1244+
p5.strokeWeight(2)
1245+
p5.stroke(255,0,0)
1246+
p5.fill(255,100, 150)
1247+
p5.strokeShader(starStrokeShader)
1248+
p5.shader(starShader);
1249+
p5.model(stars, 100);
1250+
p5.pop()
1251+
1252+
p5.push()
1253+
p5.shader(fresnelShader)
1254+
p5.noStroke()
1255+
p5.sphere(30);
1256+
p5.filter(pixelateShader);
1257+
p5.pop()
1258+
1259+
originalImage.end();
1260+
1261+
p5.imageMode(p5.CENTER)
1262+
p5.image(originalImage, 0, 0)
1263+
1264+
p5.filter(p5.BLUR, 5)
1265+
p5.filter(bloomShader);
1266+
1267+
screenshot();
1268+
});
11471269
});
11481270

11491271
visualSuite('background()', function () {
@@ -1316,7 +1438,7 @@ visualSuite('WebGL', function() {
13161438
visualSuite('Tessellation', function() {
13171439
visualTest('Handles nearly identical consecutive vertices', function(p5, screenshot) {
13181440
p5.createCanvas(400, 400, p5.WEBGL);
1319-
1441+
13201442
const contours = [
13211443
[
13221444
[-3.8642425537109375, -6.120738636363637, 0],
@@ -1355,7 +1477,7 @@ visualSuite('WebGL', function() {
13551477
[-1.8045834628018462, 4.177556818181818, 0]
13561478
]
13571479
];
1358-
1480+
13591481
p5.background('red');
13601482
p5.push();
13611483
p5.stroke(0);

test/unit/visual/cases/webgpu.js

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,128 @@ visualSuite("WebGPU", function () {
130130
p5.model(model, 3);
131131
await screenshot();
132132
});
133+
134+
visualTest('Strands tutorial', async function(p5, screenshot) {
135+
// From Luke Plowden's Intro to Strands tutorial
136+
// https://beta.p5js.org/tutorials/intro-to-p5-strands/
137+
138+
function starShaderCallback({ p5 }) {
139+
const time = p5.uniformFloat(() => p5.millis());
140+
const skyRadius = p5.uniformFloat(90);
141+
142+
function rand2(st) {
143+
return p5.fract(p5.sin(p5.dot(st, [12.9898, 78.233])) * 43758.5453123);
144+
}
145+
146+
function semiSphere() {
147+
let id = p5.instanceID();
148+
let theta = rand2([id, 0.1234]) * p5.TWO_PI + time / 100000;
149+
let phi = rand2([id, 3.321]) * p5.PI + time / 50000;
150+
151+
let r = skyRadius;
152+
r *= p5.sin(phi);
153+
let x = r * p5.sin(phi) * p5.cos(theta);
154+
let y = r * 1.5 * p5.cos(phi);
155+
let z = r * p5.sin(phi) * p5.sin(theta);
156+
return [x, y, z];
157+
}
158+
159+
p5.getWorldInputs((inputs) => {
160+
inputs.position += semiSphere();
161+
return inputs;
162+
});
163+
164+
p5.getObjectInputs((inputs) => {
165+
let size = 1 + 0.5 * p5.sin(time * 0.002 + p5.instanceID());
166+
inputs.position *= size;
167+
return inputs;
168+
});
169+
}
170+
171+
function pixelateShaderCallback({ p5 }) {
172+
const pixelCountX = p5.uniformFloat(() => 100);
173+
174+
p5.getColor((inputs, canvasContent) => {
175+
const aspectRatio = inputs.canvasSize.x / inputs.canvasSize.y;
176+
const pixelSize = [pixelCountX, pixelCountX / aspectRatio];
177+
178+
let coord = inputs.texCoord;
179+
coord = p5.floor(coord * pixelSize) / pixelSize;
180+
181+
let col = p5.getTexture(canvasContent, coord);
182+
return col//[coord, 0, 1];
183+
});
184+
}
185+
186+
function bloomShaderCallback({ p5, originalImage }) {
187+
const preBlur = p5.uniformTexture(() => originalImage);
188+
189+
getColor((input, canvasContent) => {
190+
const blurredCol = p5.getTexture(canvasContent, input.texCoord);
191+
const originalCol = p5.getTexture(preBlur, input.texCoord);
192+
193+
const intensity = p5.max(originalCol, 0.1) * 12.2;
194+
195+
const bloom = originalCol + blurredCol * intensity;
196+
return [bloom.rgb, 1];
197+
});
198+
}
199+
200+
await p5.createCanvas(200, 200, p5.WEBGPU);
201+
const stars = p5.buildGeometry(() => p5.sphere(4, 4, 2))
202+
const originalImage = p5.createFramebuffer();
203+
204+
function fresnelShaderCallback({ p5 }) {
205+
const fresnelPower = p5.uniformFloat(2);
206+
const fresnelBias = p5.uniformFloat(-0.1);
207+
const fresnelScale = p5.uniformFloat(2);
208+
209+
p5.getCameraInputs((inputs) => {
210+
let n = p5.normalize(inputs.normal);
211+
let v = p5.normalize(-inputs.position);
212+
let base = 1.0 - p5.dot(n, v);
213+
let fresnel = fresnelScale * p5.pow(base, fresnelPower) + fresnelBias;
214+
let col = p5.mix([0, 0, 0], [1, .5, .7], fresnel);
215+
inputs.color = [col, 1];
216+
return inputs;
217+
});
218+
}
219+
220+
const starShader = p5.baseMaterialShader().modify(starShaderCallback, { p5 });
221+
const starStrokeShader = p5.baseStrokeShader().modify(starShaderCallback, { p5 })
222+
const fresnelShader = p5.baseColorShader().modify(fresnelShaderCallback, { p5 });
223+
const bloomShader = p5.baseFilterShader().modify(bloomShaderCallback, { p5, originalImage });
224+
const pixelateShader = p5.baseFilterShader().modify(pixelateShaderCallback, { p5 });
225+
226+
originalImage.begin();
227+
p5.background(0);
228+
229+
p5.push()
230+
p5.strokeWeight(2)
231+
p5.stroke(255,0,0)
232+
p5.fill(255,100, 150)
233+
p5.strokeShader(starStrokeShader)
234+
p5.shader(starShader);
235+
p5.model(stars, 100);
236+
p5.pop()
237+
238+
p5.push()
239+
p5.shader(fresnelShader)
240+
p5.noStroke()
241+
p5.sphere(30);
242+
p5.filter(pixelateShader);
243+
p5.pop()
244+
245+
originalImage.end();
246+
247+
p5.imageMode(p5.CENTER)
248+
p5.image(originalImage, 0, 0)
249+
250+
p5.filter(p5.BLUR, 5)
251+
p5.filter(bloomShader);
252+
253+
await screenshot();
254+
});
133255
});
134256

135257
visualSuite('filters', function() {
46 KB
Loading
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"numScreenshots": 1
3+
}
46.5 KB
Loading
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"numScreenshots": 1
3+
}

0 commit comments

Comments
 (0)