xxxxxxxxxx
/*
次のサイトを参考にしました
https://ayumu-nagamatsu.com/archives/431/
*/
// とりあえず仕方ないので、
// パラメータでよさげなのをlerpでつなぐか
// アイデアとしては
// どっかの配列にa,b,c,dが入ったオブジェクトをたくさん放り込んで、
// パターン的には60でそのまま保って60でlerpして、を繰り返す感じですかね。
// あと色もチェンジ。以上。
//let a = 2.0, b = -0.2, c = - 1.75, d = 1.0;
//let a = 1.5, b = -0.2, c = - 1.74, d = -0.9;
//let a = 1.0, b = 0.0, c = - 2.25, d = 0.2;
//let a = 1.0, b = 0.0, c = - 1.9, d = 0.4;
//let a = 0.7, b = 0.5, c = - 1.73, d = 0.25;
//let a = 0.304, b = 0.0, c = -1.7, d = 0.4;
//let a = 0.31, b = 0.47, c = -1.68, d = 0.66;
//let a = -0.51, b = 0.17, c = 2.26, d = -0.5;
//let a = -0.61, b = 0.02, c = 2.16, d = -0.52;
//let a = -0.9, b = -0.08, c = 2.2, d = -0.55;
//let a = -1.0, b = 0.05, c = 2.275, d = -0.5;
//let a = -1.0, b = 0.1, c = 1.52, d = -0.8;
//let a = -1.0, b = 0.1, c = 1.6, d = -0.8;
//let a = -1.0, b = 0.01, c = 1.5, d = -0.8;
//let a = -1.2, b = 0.01, c = 1.54, d = -0.83;
//let a = -1.6, b = -0.45, c = 1.54, d = -0.83;
//let a = -2.0, b = 0., c = 2.6, d = -0.5;
let myParam;
const SUSTAIN = 0;
const MODIFYING = 1;
let gl, _gl;
let _SIZE;
// このように長さ固定のFloat32Arrayを生成しておくこと
// 長さは固定する
const POINT_NUM = 24000;
const TERM = 4; // 4回で更新完了
let positions = new Float32Array(POINT_NUM * 3);
let myPointShader;
let pBuf; // pointSpriteのバッファにアクセスできないといけない
// じゃないと_drawPointsが使えないので
let count = 0;
// じゃあ最後にポイントシェーダいこうか
let vsPoint =
"precision mediump float;" +
// ↑これがないと両方でuCount使えなくてエラーになる
"attribute vec3 aPosition;" +
"uniform float uPointSize;" +
"uniform float uCount;" +
"const float TAU = 6.28318;" +
"const float PI = 3.14159;"+
"varying float vStrokeWeight;" +
"varying vec3 vPosition;" +
"varying vec4 vMaterialColor;" +
"varying float vHue;" +
"varying float vSat;" +
"uniform mat4 uModelViewMatrix;" +
"uniform mat4 uProjectionMatrix;" +
"void main() {" +
" vec3 p = aPosition;" +
// 設定
" vHue = floor(p.z) / 100.0;" +
" vSat = fract(p.z);" +
" p.z = 0.0;" +
" gl_Position = uProjectionMatrix * uModelViewMatrix * vec4(p, 1.0);" +
// サイズをいじる
" gl_PointSize = uPointSize;" +
" vStrokeWeight = uPointSize;" +
"}";
let fsPoint =
"precision mediump float;" +
"precision mediump int;" +
"uniform vec4 uMaterialColor;" +
"uniform float uCount;" +
"const float TAU = 6.28318;" +
"varying float vStrokeWeight;" +
"varying vec3 vPosition;" +
"varying float vHue;" +
"varying float vSat;" +
// getRGB,参上!
"vec3 getRGB(float h, float s, float b){" +
" vec3 c = vec3(h, s, b);" +
" vec3 rgb = clamp(abs(mod(c.x * 6.0 + vec3(0.0, 4.0, 2.0), 6.0) - 3.0) - 1.0, 0.0, 1.0);" +
" rgb = rgb * rgb * (3.0 - 2.0 * rgb);" +
" return c.z * mix(vec3(1.0), rgb, c.y);" +
"}" +
"void main(){" +
" float mask = 0.0;" +
// make a circular mask using the gl_PointCoord (goes from 0 - 1 on a point)
// might be able to get a nicer edge on big strokeweights with smoothstep but slightly less performant
" mask = step(0.98, length(gl_PointCoord * 2.0 - 1.0));" +
// if strokeWeight is 1 or less lets just draw a square
// this prevents weird artifacting from carving circles when our points are really small
// if strokeWeight is larger than 1, we just use it as is
" mask = mix(0.0, mask, clamp(floor(vStrokeWeight - 0.5),0.0,1.0));" +
// throw away the borders of the mask
// otherwise we get weird alpha blending issues
" if(mask > 0.98){" +
" discard;" +
" }" +
// 色をいじるパート
" gl_FragColor = vec4(getRGB(vHue, vSat, 1.0), 1.0);" +
"}";
let bgShader;
const vsBG =
"precision mediump float;" +
"attribute vec3 aPosition;" +
"void main(){" +
" gl_Position = vec4(aPosition, 0.5);" +
"}";
const fsBG =
"precision mediump float;" +
"uniform vec2 uResolution;" +
// hsb to rgb.
"vec3 getRGB(float h, float s, float b){" +
" vec3 c = vec3(h, s, b);" +
" vec3 rgb = clamp(abs(mod(c.x * 6.0 + vec3(0.0, 4.0, 2.0), 6.0) - 3.0) - 1.0, 0.0, 1.0);" +
" rgb = rgb * rgb * (3.0 - 2.0 * rgb);" +
" return c.z * mix(vec3(1.0), rgb, c.y);" +
"}" +
"void main(){" +
" vec2 p = gl_FragCoord.xy * 0.5 / uResolution.xy;" +
" gl_FragColor = vec4(getRGB(0.55, p.y, p.y), 1.0);" +
"}";
function setup(){
_gl = createCanvas(windowWidth, windowHeight, WEBGL);
_SIZE = min(windowWidth, windowHeight);
pixelDensity(1);
gl = _gl.GL;
myPointShader = createShader(vsPoint, fsPoint);
myPointShader.isPointShader = () => true; // これはpointShaderだよ!
_gl.userPointShader = myPointShader; // userPointShaderを変更
// シェーダーで直接背景を作る
// image関数を使わなければ問題ない!!
// 背景課題やね。いつまでもグラデーションってのもちょっとね。
bgShader = createShader(vsBG, fsBG);
myParam = new Param();
registParams();
}
function draw(){
let start = millis();
gl.disable(gl.DEPTH_TEST); // 初めにデプステストを消す
resetShader();
shader(bgShader); // 好きなシェーダーで背景を作る
noStroke();
bgShader.setUniform("uResolution", [width, height]);
plane(width, height);
gl.enable(gl.DEPTH_TEST); // あとでデプステストを復活させる
// 好きな画像をあれするにしてもimageで横着しないで
// シェーダーにテクスチャとしてぶちこんでいじったものを使えばよさそう
resetShader();
// 最後にポイント
shader(myPointShader);
stroke(0);
strokeWeight(_SIZE / 600);
myPointShader.setUniform("uCount", count);
myPoints(POINT_NUM);
count++;
myParam.update();
let end = millis();
//if(count % 30 == 0){ console.log((end-start)*60/1000); }
}
function registParams(){
myParam.regist(2.0, -0.2, -1.75, 1.0)
.regist(1.5, -0.2, -1.74, -0.9)
.regist(1.0, 0.0, -2.25, 0.2)
.regist(1.0, 0.0, -1.9, 0.4)
.regist(0.7, 0.5, -1.73, 0.25)
.regist(0.304, 0.0, -1.7, 0.4)
.regist(0.31, 0.47, -1.68, 0.66)
.regist(-0.51, 0.17, 2.26, -0.5)
.regist(-0.61, 0.02, 2.16, -0.52)
.regist(-0.9, -0.08, 2.2, -0.55)
.regist(-1.0, 0.05, 2.275, -0.5)
.regist(-1.0, 0.1, 1.52, -0.8)
.regist(-1.0, 0.1, 1.6, -0.8)
.regist(-1.0, 0.01, 1.5, -0.8)
.regist(-1.2, 0.01, 1.54, -0.83)
.regist(-1.6, -0.45, 1.54, -0.83)
.regist(-2.0, 0.1, 2.6, -0.5);
}
class Param{
constructor(x, y){
this.x = 0.1;
this.y = 0.1;
this.updateIndex = 0;
this.originX = 0.1;
this.originY = 0.1;
this.objs = [];
this.currentIndex = 0;
this.nextIndex = 0;
this.properFrameCount = 0;
this.state = SUSTAIN;
this.sustainFrameCount = 60;
this.modifyingFrameCount = 180;
}
dontHaveToUpdate(){
return this.properFrameCount > 4 && this.state === SUSTAIN;
}
getX(){
return this.x;
}
getY(){
return this.y;
}
set(x, y){
this.x = x;
this.y = y;
}
getIndex(){
return this.updateIndex;
}
increment(){
this.updateIndex++;
if(this.updateIndex === POINT_NUM){
this.updateIndex = 0;
this.reset();
}
}
reset(){
this.x = this.originX;
this.y = this.originY;
}
getParam(){
if(this.state === SUSTAIN){
return this.objs[this.currentIndex];
}
let prg = this.properFrameCount / this.modifyingFrameCount;
prg = ease(prg);
let p0 = this.objs[this.currentIndex];
let p1 = this.objs[this.nextIndex];
return {a:p0.a * (1 - prg) + p1.a * prg, b:p0.b * (1 - prg) + p1.b * prg, c:p0.c * (1 - prg) + p1.c * prg, d:p0.d * (1 - prg) + p1.d * prg};
}
regist(a, b, c, d){
this.objs.push({a:a, b:b, c:c, d:d});
return this;
}
update(){
this.properFrameCount++;
if(this.state === SUSTAIN && this.properFrameCount === this.sustainFrameCount){
this.properFrameCount = 0;
this.nextIndex = (this.currentIndex + 1) % this.objs.length;
this.state = MODIFYING;
}
if(this.state === MODIFYING && this.properFrameCount === this.modifyingFrameCount){
this.currentIndex = this.nextIndex;
this.properFrameCount = 0;
this.state = SUSTAIN;
}
}
}
function myPoints(num){
const gId = `myPoints|${num}`;
if(!_gl.geometryInHash(gId)){
const myPointsGeom = new p5.Geometry();
let v = createVector();
for(let i = 0; i < num; i++){
positions[i * 3] = 0;
positions[i * 3 + 1] = 0;
positions[i * 3 + 2] = 0;
myPointsGeom.vertices.push(v.set(0, 0, 0).copy());
}
pBuf = _gl.createBuffers(gId, myPointsGeom);
}
// これでいいんだ
myDrawPoints();
}
// _drawPointsを改造する
// とりあえずbindBufferとenableAttribが1回でいいのかどうか調べる
// いろいろやろうとしたらやっぱbuffer overflowくらった
// もう毎フレーム実行でいいのかもしれない
function myDrawPoints(){
_gl._setPointUniforms(myPointShader);
_gl._bindBuffer(_gl.immediateMode.buffers.point, gl.ARRAY_BUFFER, _gl._vToNArray(pBuf.model.vertices), Float32Array, gl.DYNAMIC_DRAW);
myPointShader.enableAttrib(myPointShader.attributes.aPosition, 3);
setPositions();
gl.bufferSubData(gl.ARRAY_BUFFER, 0, positions);
gl.drawArrays(gl.Points, 0, pBuf.model.vertices.length);
myPointShader.unbindShader();
}
function setPositions(){
if(myParam.dontHaveToUpdate()){ return; }
let pr = myParam.getParam();
let colId = getColId();
let a = pr.a;
let b = pr.b;
let c = pr.c;
let d = pr.d;
let tmp1, tmp2, tmp3, tmp4, tmp5, tmp6;
let index, x, y, u, v;
for(let i = 0; i < POINT_NUM / 4; i++){
x = myParam.getX();
y = myParam.getY();
tmp1 = x * x;
tmp2 = y * y;
tmp3 = tmp1 + tmp2;
tmp4 = tmp1 - tmp2;
tmp5 = tmp1 - 3 * tmp2;
tmp6 = a * tmp3 + b * x * tmp5 + c;
u = tmp6 * x + d * tmp4;
v = tmp6 * y - 2 * d * x * y;
myParam.set(u, v);
//if(i % 6 == 0){
index = myParam.getIndex();
setPos(index, u, v, colId);
myParam.increment();
//}
}
}
function setPos(index, u, v, colId){
positions[index * 3] = _SIZE * 0.33 * u;
positions[index * 3 + 1] = _SIZE * 0.33 * v;
positions[index * 3 + 2] = colId;
}
function getColId(){
let c = floor(myParam.currentIndex * 100 / myParam.objs.length);
return 0.99 + c;
}
function ease(x){ return x * x * (3 - 2 * x); }