xxxxxxxxxx
// 2023-04-08
// 久しぶりに見てみたけど。
// だめね
// まずp5wgexで書き直した方がいいのと
// 見づらいので
// ノイズはシンプレックスよりp5の方が軽いと思う
// あと
// ていうか固定ランダム目的で使うならノイズは不要でしょう
// それとさ
// いや別にいいんだけども
// millis()/1000かなんか
// この手のアニメーションは重くなることが想定される場合はmillis()/1000とか使って
// かくかくにならないよいうに対策しないと駄目よ。
// 以上です。
// 2023-07-27
// 1.7.0です
// webgl1で運用してます
// p5wgexに書き換えるのは大変そうです
// おそらくshader部分は同じでOKのはずですが...どうなるか
// 旧レンダーノードは基本framebufferしか使えないので...
// それ以上の拡張が見込めないということで開発中止になったわけです
// p5wgexはもうちょっと進化できる可能性があるので残っています
// どうせThreeにはかなわないけど...
// マニュアルが必要ね
// p5wgexで書き直しました。
// attributeは使う分だけuse()の際に全部あれしてるんですよね。
// 使う分だけまとめて上書きする感じではないかと。
// スマホの方見てきました
// 20fps切ってますね
// あちゃー
// まあでも時間ベースなのでごまかせてますよ。
// 時間ベースは正義!!
// ...
// frameCountベースの時あきらかに重かったのが時間ベースにしたことで改善しました
// うれしい
// accellは使ってないので消しました
// global減ったでしょう
// textureの不安も消えてますね。
// スマホ:256で300x300
// デスクトップ:512で600x600
// できる?
// clearColorってfbごとなんですね
// 初めて知ったわ
// やっぱちゃんとfbは戻さないと...
// Pixiの裏技でスマホと挙動変えました
// んでおそらくちらつきの原因はやっぱ距離関数で描画したせいです
// ポリゴンか2Dで描いてくださいということだそうです
// 仕方ないね
// 三角形増やすなら128x128にしないときついって
// OK
// とりあえず完成しました
// 梯子を外します
// bgShaderは不要なので排除します
// スマホの方30fpsに戻っちゃった
// まあいいか
// 60fpsに戻りましたよ
// 2023-09-17
// IRとか使って書き換え
// timer新しくしたのでgetDeltaをgetElapsedにしました
// それによりforkされた方がまた動かなくなりました
// 仕方ないね
// だってgetDelta()なのに経過時間(Elapsed)を取得するのはやっぱおかしいじゃないですか
// 2023-09-29
// pfc修正
// あとcopyPainterが死んだので応急処置
// まあ難しくないし一瞬で終わるんですけど
// めんどうですよね
// そのうち何とかします
// カスタマイズもできるようにしますね
// 2023-10-02
// pfcとrenderFoxBoardについて更新
// pfcは今後canvasで初期化しDOM要素とします。レンダリングしません。
// renderFoxBoardを使うとuniformを指定するだけの簡単な板ポリ描画についてはもう面倒な処理が不要となります、しかもblendも指定し放題!
// 問題は、pfcを使ってるすべてのコードが(以下略)
// ただこれなら2Dでも使えるんですよね。それはとても便利なのです。
// 2023-10-03
// foxIAがcanvasで更新されるようになったよ。
// swapEachFBOで書き換え
// doubleFBO廃止されるので
// あっさりできたわね
// やっぱこの概念要らんわ
// クラス化の障害になってるのよ
// 2023-10-21 swapFBOに改名
// 2024-08-27
// 1.10.0に更新
// びくともしねぇな
// まあありがたいよねこっちに影響しちゃうと色々と不便だから
/*
ダブルフレームバッファでパーティクル管理
末尾から使っていく
10個なら10個、100個なら100個
毎フレーム
アクティブなものを頭に寄せる
やりかたは2分割していって数を足していって
バーテックスシェーダで描画位置を決めればOK
死んでるパーティクルは邪魔にならなければどこでも(末尾でOK)
生きてるパーティクルは計算された位置にとばす
これでGPUでもパーティクル管理できる(はず)
まあ点描画前提だけど...
*/
// --------------------------------------------------------------- //
// global.
const ex = p5wgex;
let _node;
const _timer = new ex.Timer();
let IR;
let pfc;
// pixiがこういうのやってた
/** 端末ごとにパフォーマンスを調整するための変数です。 */
const nav = window.navigator;
let performanceLevel;
if (nav.platform !== undefined) {
// platformが定義されている場合
switch (nav.platform) {
case "Win32": // Windowsだったら
case "MacIntel": // OS Xだったら
performanceLevel = "high";
break;
case "iPhone": // iPhoneだったら
default:
// その他の端末も
performanceLevel = "low";
}
} else if (nav.userAgentData !== undefined) {
// userAgentDataが利用可能な場合
if (nav.userAgentData.mobile) {
performanceLevel = "low";
} else {
performanceLevel = "high";
}
} else {
// いずれでもなければ安全のためlowにする
performanceLevel = "low";
}
// PC:512x512, mobile:128x128
const TEX_SIZE = (performanceLevel === "high" ? 512 : 128);
// PC:600x600, mobile:300x300
const CANVAS_SIZE = (performanceLevel === "high" ? 600 : 300);
// --------------------------------------------------------------- //
// simplex noise shader.
const _SimplexNoise =
`
// 各種定数
const vec2 r_vec_20 = vec2(127.1, 311.7);
const vec2 r_vec_21 = vec2(269.5, 183.3);
const vec2 u_10 = vec2(1.0, 0.0);
const vec2 u_01 = vec2(0.0, 1.0);
const vec2 u_11 = vec2(1.0, 1.0);
const vec3 r_vec_30 = vec3(127.1, 311.7, 251.9);
const vec3 r_vec_31 = vec3(269.5, 183.3, 314.3);
const vec3 r_vec_32 = vec3(419.2, 371.9, 218.4);
const vec3 u_100 = vec3(1.0, 0.0, 0.0);
const vec3 u_010 = vec3(0.0, 1.0, 0.0);
const vec3 u_001 = vec3(0.0, 0.0, 1.0);
const vec3 u_110 = vec3(1.0, 1.0, 0.0);
const vec3 u_101 = vec3(1.0, 0.0, 1.0);
const vec3 u_011 = vec3(0.0, 1.0, 1.0);
const vec3 u_111 = vec3(1.0, 1.0, 1.0);
const float r_coeff = 43758.5453123;
// 2Dランダムベクトル(-1.0~1.0)
vec2 random2(vec2 st){
vec2 v;
v.x = sin(dot(st, r_vec_20)) * r_coeff;
v.y = sin(dot(st, r_vec_21)) * r_coeff;
return -1.0 + 2.0 * fract(v);
}
// 3Dランダムベクトル(-1.0~1.0)
vec3 random3(vec3 st){
vec3 v;
v.x = sin(dot(st, r_vec_30)) * r_coeff;
v.y = sin(dot(st, r_vec_31)) * r_coeff;
v.z = sin(dot(st, r_vec_32)) * r_coeff;
return -1.0 + 2.0 * fract(v); // -1.0~1.0に正規化
}
// シンプレックスノイズ(-1.0~1.0)
float snoise3(vec3 st){
vec3 p = st + (st.x + st.y + st.z) / 3.0;
vec3 f = fract(p);
vec3 i = floor(p);
vec3 g0, g1, g2, g3;
vec4 wt;
g0 = i;
g3 = i + u_111;
if(f.x >= f.y && f.x >= f.z){
g1 = i + u_100;
g2 = i + (f.y >= f.z ? u_110 : u_101);
wt = (f.y >= f.z ? vec4(1.0 - f.x, f.x - f.y, f.y - f.z, f.z) : vec4(1.0 - f.x, f.x - f.z, f.z - f.y, f.y));
}else if(f.y >= f.x && f.y >= f.z){
g1 = i + u_010;
g2 = i + (f.x >= f.z ? u_110 : u_011);
wt = (f.x >= f.z ? vec4(1.0 - f.y, f.y - f.x, f.x - f.z, f.z) : vec4(1.0 - f.y, f.y - f.z, f.z - f.x, f.x));
}else{
g1 = i + u_001;
g2 = i + (f.x >= f.y ? u_101 : u_011);
wt = (f.x >= f.y ? vec4(1.0 - f.z, f.z - f.x, f.x - f.y, f.y) : vec4(1.0 - f.z, f.z - f.y, f.y - f.x, f.x));
}
float value = 0.0;
wt = wt * wt * wt * (wt * (wt * 6.0 - 15.0) + 10.0);
value += wt.x * dot(p - g0, random3(g0));
value += wt.y * dot(p - g1, random3(g1));
value += wt.z * dot(p - g2, random3(g2));
value += wt.w * dot(p - g3, random3(g3));
return value;
}
`;
// --------------------------------------------------------------- //
// distFunction2D shader.
// caution:PI,TAUを使うので用意しておいてください。
const distFunction2D =
`
// ベクトル取得関数
vec2 fromAngle(float t){ return vec2(cos(t), sin(t)); }
// 正二面体群
// 基本領域の中心が0にくるように調整してある
vec2 dihedral_center(vec2 p, float n){
float k = PI * 0.5 / n;
vec2 e1 = vec2(sin(k), cos(k));
vec2 e2 = vec2(sin(k), -cos(k));
for(float i = 0.0; i < 99.0; i += 1.0){
if(i == n){ break; }
p -= 2.0 * min(dot(p, e1), 0.0) * e1;
p -= 2.0 * min(dot(p, e2), 0.0) * e2;
}
return p;
}
// 基本領域のはじっこに0がくるやつ(0~pi/n)
vec2 dihedral_bound(vec2 p, float n){
float k = PI / n;
vec2 e1 = vec2(0.0, 1.0);
vec2 e2 = vec2(sin(k), -cos(k));
for(float i = 0.0; i < 99.0; i += 1.0){
if(i == n){ break; }
p -= 2.0 * min(dot(p, e1), 0.0) * e1;
p -= 2.0 * min(dot(p, e2), 0.0) * e2;
}
return p;
}
// 線分。cからeと逆の方向に長さhで幅r.
float segment(vec2 p, vec2 c, vec2 e, float r, float h, float t){
p = (p - c) * mat2(cos(t), -sin(t), sin(t), cos(t));
return length(p - max(-h, min(0.0, dot(p, e))) * e) - r;
}
// 三角形
float triangle(vec2 p, vec2 c, float r, float t){
p -= c;
p *= mat2(cos(t), -sin(t), sin(t), cos(t));
vec2 e1 = vec2(0.0, 1.0);
vec2 e2 = vec2(0.5 * sqrt(3.0), -0.5);
for(int i = 0; i < 3; i++){
p -= 2.0 * min(dot(p, e1), 0.0) * e1;
p -= 2.0 * min(dot(p, e2), 0.0) * e2;
}
return p.x - r;
}
// 丸角長方形.
// 基本はc中心qが横幅縦幅でrだけ膨らませる感じ
// できました!むずいねぇ・・いわゆるあれ、roundRectですね。
float roundRect(vec2 p, vec2 c, vec2 q, float r, float t){
p = (p - c) * mat2(cos(t), -sin(t), sin(t), cos(t));
p = abs(p);
float d = max(p.x - q.x * 0.5, p.y - q.y * 0.5) - r;
float check = min(p.x - q.x * 0.5, p.y - q.y * 0.5);
if(check < 0.0){ return d; }
return length(p - q * 0.5) - r;
}
// 月型
float moon(vec2 p, vec2 c, float r, float t){
p = (p - c) * mat2(cos(t), -sin(t), sin(t), cos(t));
return max(length(p) - r, r * 0.7 - length(p - vec2(r * 0.55, 0.0)));
}
// 星型
float star(vec2 p, vec2 c, float r, float t){
p -= c;
p *= mat2(cos(t), -sin(t), sin(t), cos(t));
p = dihedral_bound(p, 5.0);
vec2 e = fromAngle(0.4 * PI);
return dot(p - vec2(r, 0.0), e);
}
// 弧.
// 中心cでnは半円弧の半分にしたやつの先っちょの単位ベクトル
// raが大きい半径でrbがふくらまし。
// 形としてはx軸正方向から二つの方向に回っていく感じね
// nは中心から先っちょへの単位ベクトルね。
float arc(vec2 p, vec2 c, vec2 n, float ra, float rb, float t){
p -= c;
p *= mat2(cos(t), -sin(t), sin(t), cos(t));
p.y = abs(p.y);
if(p.x * n.y - p.y * n.x > 0.0){ return abs(length(p) - ra) - rb; }
return length(p - n * ra) - rb;
}
`;
const distFunctionForObstacles =
`
// 距離関数
float dist(vec2 p){
float d = 99999.0;
for(float i = 0.0; i < 6.0; i += 1.0){
float angle = TAU * i / 6.0;
float d1 = arc(p, 0.75*vec2(cos(angle), sin(angle)), vec2(-0.8, 0.6), 0.15, 0.015, angle + uTime * TAU / 8.0);
d = min(d, d1);
}
for(float i = 0.5; i < 3.5; i += 1.0){
float angle = TAU * i / 3.0;
float d2 = arc(p, 0.3*vec2(cos(angle), sin(angle)), vec2(-0.8, 0.6), 0.15, 0.015, angle - uTime * TAU / 8.0);
d = min(d, d2);
}
float d3 = triangle(p, vec2(0.0, -0.85), 0.05, uTime * TAU / 6.0);
float d4 = triangle(p, vec2(0.0, 0.85), 0.05, -uTime * TAU / 6.0);
d = min(d, min(d3, d4));
return d;
}
`;
// --------------------------------------------------------------- //
// main shader.
// dataShader. 最初の位置と速度を設定するところ。
const dataVert =
`
attribute vec2 aPosition; // unique attribute.
void main(){
gl_Position = vec4(aPosition, 0.0, 1.0);
}
`;
const dataFrag=
`
precision highp float;
uniform float uTexSize;
uniform vec3 uSeeds;
` +
_SimplexNoise +
`
void main(){
vec2 p = gl_FragCoord.xy / uTexSize; // 0.0~1.0に正規化
// 初期位置と初期速度を設定
vec2 pos = (p - 0.5) * 2.0; // 位置は-1~1,-1~1で。
float nx = snoise3(vec3(uSeeds.x, p.x, p.y));
float ny = snoise3(vec3(uSeeds.y, p.x, p.y));
nx = fract((nx + 1.0) * 8.0) * 2.0 - 1.0;
ny = fract((ny + 1.0) * 8.0) * 2.0 - 1.0;
gl_FragColor = vec4(nx, ny + 2.0, 0.01 * snoise3(vec3(p.x, p.y, uSeeds.z)), 0.0); // 初期速度は0で。
}
`;
// moveShader. 位置と速度の更新をオフスクリーンレンダリングで更新する。
const moveVert =
`
attribute vec2 aPosition;
void main(){
gl_Position = vec4(aPosition, 0.0, 1.0);
}
`;
const moveFrag =
`
precision highp float;
uniform sampler2D uTex;
uniform float uTexSize;
uniform float uTime;
uniform float uSeed;
const vec2 deltaX = vec2(0.000001, 0.0);
const vec2 deltaY = vec2(0.0, 0.000001);
` +
_SimplexNoise +
`
// 定数
const float TAU = 6.28318;
const float PI = 3.14159;
` +
distFunction2D +
distFunctionForObstacles +
`
// 反射処理
bool applyReflect(in vec2 p, inout vec2 v){
if(dist(p + v) < 0.0){
vec2 n = normalize(vec2(dist(p + deltaX) - dist(p), dist(p + deltaY) - dist(p)));
v = reflect(v, n) * 0.9;
return true;
}
return false;
}
// メインコード
void main(){
vec2 p = gl_FragCoord.xy / uTexSize; // ピクセル位置そのまま
float delay = 1.0 - p.y; // 0.0~2.0のどれか
// 上の方ほど早く落ち始める感じで
vec4 t = texture2D(uTex, p);
vec2 pos = t.xy;
vec2 velocity = t.zw;
// 更新処理
velocity.y -= 0.002;
if(length(velocity) > 0.025){ velocity *= 0.9; }
// 反射
applyReflect(pos, velocity);
// 位置更新
pos += velocity;
if(pos.x < -1.0){ pos.x += 2.0; }
if(pos.x > 1.0){ pos.x -= 2.0; }
if(pos.y < -1.0){
pos.x = fract(2.0 + 2.0 * snoise3(vec3(p.x, p.y, uTime + uSeed))) * 2.0 - 1.0;
pos.y += 2.0;
velocity.x = 0.01 * snoise3(vec3(uTime - uSeed, p.x, p.y));
velocity.y = 0.0;
}
// ディレイで上の方から落ちるように修正
if(delay > uTime * 0.5){ gl_FragColor = t; }
else{gl_FragColor = vec4(pos, velocity); }
}
`;
// pointShader. 位置情報に基づいて点の描画を行う。
const pointVert =
`
attribute float aIndex;
uniform sampler2D uTex;
uniform vec2 uResolution; // 解像度
uniform float uTexSize; // テクスチャフェッチ用
varying vec2 vIndex;
void main() {
// uTexSize * uTexSize個の点を配置
// 0.5を足しているのはきちんとマス目にアクセスするためです
float x = (mod(aIndex, uTexSize) + 0.5) / uTexSize;
float y = (floor(aIndex / uTexSize) + 0.5) / uTexSize;
vec4 t = texture2D(uTex, vec2(x, y));
vec2 p = t.xy;
vIndex = vec2(x, y);
p *= vec2(min(uResolution.x, uResolution.y)) / uResolution;
gl_Position = vec4(p, 0.0, 1.0);
gl_PointSize = 0.1; // 動いてるときだけ大きく
}
`;
const pointFrag =
`
precision highp float;
varying vec2 vIndex;
uniform float uTime;
const vec3 khaki = vec3(0.94, 0.90, 0.55);
const vec3 bronze = vec3(0.8, 0.4, 0.3);
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(){
gl_FragColor = vec4((khaki+bronze)*0.5, 1.0);
// gl_FragColor = vec4(getRGB(0.5+0.1*vIndex.x * vIndex.y, (vIndex.x + vIndex.y) * 0.5, 1.0), 1.0);
}
`;
// --------------------------------------------------------------- //
// setup.
function setup(){
// _glはp5のwebgl, glはwebglのレンダリングコンテキスト。
createCanvas(CANVAS_SIZE, CANVAS_SIZE, WEBGL);
pixelDensity(1);
const gl = this._renderer.GL;
_node = new ex.RenderNode(gl);
// timer.
_timer.initialize('slot0');
// getElapsedで経過時間を取得する
// まず必要なシェーダ、Painterを用意する
// 次に使うattributeというかFigureを用意する
// 次にフレームバッファ
// テクスチャ
// 必要ならクリアカラーの設定
// 事前処理があれば実行する
// メインループへ
// Painter. bgは廃止しました。
// データ格納用
_node.registPainter('data', dataVert, dataFrag);
// 移動計算用。
_node.registPainter('move', moveVert, moveFrag);
// 点描画用。displayの方が名前としては適切かも?
_node.registPainter('point', pointVert, pointFrag);
// Figure.
// インデックス配列と長方形だけ。長方形は2Dでいいと思う。
// 点描画用のインデックスを格納する配列
const indices = [];
// 0~TEX_SIZE*TEX_SIZE-1のindexを放り込む
for(let i = 0; i < TEX_SIZE * TEX_SIZE; i++){
indices.push(i);
}
// 板ポリの頂点用。これは位置設定、背景、位置更新のすべてで使う
const positions = [-1, -1, 1, -1, -1, 1, 1, 1];
_node.registFigure('board', [{name:"aPosition", size:2, data:positions}]);
_node.registFigure('indices', [{name:"aIndex", size:1, data:indices}]);
// moveにする
// どうやらmoveで使ってるみたいですので
_node.registFBO("moveWrite", {w:TEX_SIZE, h:TEX_SIZE, color:{info:{type:"float"}}});
_node.registFBO("moveRead", {w:TEX_SIZE, h:TEX_SIZE, color:{info:{type:"float"}}});
// 位置と速度の初期設定
initialize();
pfc = new ex.PerformanceChecker(this.canvas);
//pfc.initialize();
// 障害物用
_node.registTexture("obstacles", {src:(function(){
const gr = createGraphics(width, height);
gr.noFill();
return gr;
})()});
// IRのクリックイベントで初期化する流れ. IRは便利ですね。
IR = new foxIA.Inspector(this.canvas, {click:true});
//IR.initialize(this.canvas, {click:true});
IR.add("click", () => {initialize()});
const sh = new ex.PlaneShader(_node).initialize();
sh.addUniform("sampler2D", "uTex", "fs");
sh.addCode(`color = texture(uTex,uv);`,"preProcess","fs").registPainter("display");
}
// --------------------------------------------------------------- //
// main loop.
function draw(){
// ここで位置と速度を更新
moveRendering();
// 背景となる障害物を描画
_node.bindFBO(null).clear();
drawObstacles(_timer.getElapsed('slot0'));
// パーティクルを描画
_node.use('point', 'indices')
.setFBOtexture2D('uTex', 'moveRead')
.setUniform('uTexSize', TEX_SIZE)
.setUniform('uResolution', [width, height])
.drawArrays('points')
.unbind();
// パフォーマンス更新
pfc.update();
_node.flush();
}
// --------------------------------------------------------------- //
// offscreen rendering.
// オフスクリーンレンダリングで初期の位置と速度を設定
function initialize(){
// フレームバッファをbind
// ビューポート設定はオートで実行されます
_node.bindFBO("moveWrite");
_node.clearFBO();
// attributeどうしてるのか疑問だったんですけど
// おそらくですけど
// attributeの揃えを取得してまとめてenableしてるっぽいですね
// そういうことみたいです
// drawArrays('points')でデータ入力した方がいいと思うんだけど
// そこら辺の改良は後でいくらでもできるわね
_node.use('data', 'board')
.setUniform('uTexSize', TEX_SIZE)
.setUniform('uSeeds', [Math.random(), Math.random(), Math.random()])
.drawArrays('triangle_strip')
.swapFBO('moveWrite', 'moveRead')
.unbind();
// swapしないとreadにデータを送る。セットされるのは常にwriteの方なのです。
}
// オフスクリーンレンダリングで位置と速度を更新
function moveRendering(){
// ここでもswapしますね
_node.bindFBO('moveWrite');
_node.clearFBO();
_node.use('move', 'board')
.setFBOtexture2D('uTex', 'moveRead') // doubleの場合はreadがセットされる仕組み
.setUniform('uTexSize', TEX_SIZE)
.setUniform('uTime', _timer.getElapsed('slot0'))
.setUniform('uSeed', Math.random())
.drawArrays('triangle_strip')
.swapFBO('moveWrite', 'moveRead')
.unbind();
}
// ------------------------------------------------------------- //
// draw obstacles.
// timeは秒数です。shaderの方と同期させる。
// strokeWeightは全体の割合で決める
// -1~1で-1~1だから単純に1を足して2で割ってCANVAS_SIZEを掛ければいい
// yだけ上下逆にするのを忘れないで(調整すればいい)
// arcで描けばいい
// 直径です
// 0.015*2の2に対する割合をCANVAS_SIZEと比べて...つまり(0.03/2) = (D/CANVAS_SIZE)
function drawObstacles(time){
const gr = _node.getTextureSource("obstacles");
gr.background(0);
gr.noFill();
gr.stroke("green");
gr.strokeWeight(CANVAS_SIZE * 0.03 / 2);
const halfAngle = Math.atan2(0.6, -0.8);
const diam = CANVAS_SIZE * 0.3 / 2;
// 外側の円弧
for (let i=0; i<6; i+=1) {
const angle = TAU*i/6;
const angleOffset = time * TAU/8;
const x = (0.75 * Math.cos(angle) + 1) * 0.5 * CANVAS_SIZE;
const y = (-0.75 * Math.sin(angle) + 1) * 0.5 * CANVAS_SIZE;
gr.arc(x, y, diam, diam, angle + angleOffset - halfAngle, angle + angleOffset + halfAngle);
}
// 内側の円弧
for (let i=0.5; i<3.5; i+=1) {
const angle = TAU*i/3;
const angleOffset = -time * TAU/8;
const x = (0.3 * Math.cos(angle) + 1) * 0.5 * CANVAS_SIZE;
const y = (-0.3 * Math.sin(angle) + 1) * 0.5 * CANVAS_SIZE;
gr.arc(x, y, diam, diam, angle + angleOffset - halfAngle, angle + angleOffset + halfAngle);
}
// 2つの三角形
const triangleSize = CANVAS_SIZE*0.1/2;
gr.noStroke();
gr.fill("lightgreen");
gr.translate(gr.width*0.5, gr.height*0.5 + 0.85 * gr.height * 0.5);
gr.rotate(time*TAU/6);
gr.triangle(triangleSize*0.5, -triangleSize*0.5*Math.sqrt(3), triangleSize*0.5, triangleSize*0.5*Math.sqrt(3), -triangleSize, 0);
gr.resetMatrix();
gr.translate(gr.width*0.5, gr.height*0.5 - 0.85 * gr.height * 0.5);
gr.rotate(-time*TAU/6);
gr.triangle(triangleSize*0.5, -triangleSize*0.5*Math.sqrt(3), triangleSize*0.5, triangleSize*0.5*Math.sqrt(3), -triangleSize, 0);
gr.resetMatrix();
_node.updateTexture("obstacles");
_node.renderFoxBoard("display", {uniforms:{
'tex/uTex':"obstacles"
}});
//ex.copyPainter(_node, {src:{name:"obstacles"}});
//_node.use("display","foxBoard").setTexture("uTex","obstacles").drawArrays("triangle_strip").unbind();
}
/*
没案
// bgShaderはスマホの方でちらつきが発生するのでやめました
// 2Dで描くことにしました
// --------------------------------------------------------------- //
// color constants shader.
const _ColorConstants =
// 色
`
const vec3 black = vec3(0.2);
const vec3 red = vec3(0.95, 0.3, 0.35);
const vec3 orange = vec3(0.98, 0.49, 0.13);
const vec3 yellow = vec3(0.95, 0.98, 0.2);
const vec3 green = vec3(0.3, 0.9, 0.4);
const vec3 lightgreen = vec3(0.7, 0.9, 0.1);
const vec3 purple = vec3(0.6, 0.3, 0.98);
const vec3 blue = vec3(0.2, 0.25, 0.98);
const vec3 skyblue = vec3(0.1, 0.65, 0.9);
const vec3 white = vec3(1.0);
const vec3 aquamarine = vec3(0.47, 0.98, 0.78);
const vec3 turquoise = vec3(0.25, 0.88, 0.81);
const vec3 coral = vec3(1.0, 0.5, 0.31);
const vec3 limegreen = vec3(0.19, 0.87, 0.19);
const vec3 khaki = vec3(0.94, 0.90, 0.55);
const vec3 navy = vec3(0.0, 0.0, 0.5);
const vec3 silver = vec3(0.5);
const vec3 gold = vec3(0.85, 0.67, 0.14);
const vec3 bronze = vec3(0.8, 0.4, 0.3);
`;
// bgShader. 背景を描画する。
const bgVert =
`
attribute vec3 aPosition;
void main(){
gl_Position = vec4(aPosition, 1.0);
}
`;
const bgFrag =
`
precision highp float;
uniform vec2 uResolution;
uniform float uTime;
// 定数
const float TAU = 6.28318;
const float PI = 3.14159;
` +
_ColorConstants +
distFunction2D +
commonDistFunction2D +
`
void main(){
vec2 p = gl_FragCoord.xy / uResolution.xy;
p = p * 2.0 - 1.0;
vec3 col = vec3(0.0);
//if(dist(p) < 0.0){ col = vec3(1.0); }
float d = dist(p); // 負の方で値を抑えたいけどどうしようね
col = vec3(exp(-d*32.0)) * silver;
gl_FragColor = vec4(col, 1.0);
}
`;
// caution:uTime(経過秒数)を使うので準備しておいてください。
const commonDistFunction2D_0 =
`
// 距離関数
float dist(vec2 p){
float d = 99999.0;
d = min(d, roundRect(p, vec2(-0.6, 0.0), vec2(0.3, 0.3), 0.0, PI * 0.25));
d = min(d, roundRect(p, vec2(0.6, 0.0), vec2(0.3, 0.3), 0.0, PI * 0.25));
d = min(d, roundRect(p, vec2(0.0, 0.6), vec2(0.3, 0.3), 0.0, PI * 0.25));
d = min(d, roundRect(p, vec2(0.4, -0.6), vec2(0.3, 0.3), 0.0, uTime * 0.3 + PI * 0.25));
d = min(d, roundRect(p, vec2(-0.4, -0.6), vec2(0.3, 0.3), 0.0, -uTime * 0.3 + PI * 0.25));
d = min(d, moon(p, vec2(-0.7, 0.5), 0.25, PI * 0.5 + uTime * TAU / 6.0));
d = min(d, moon(p, vec2(0.7, 0.5), 0.25, PI * 0.5 - uTime * TAU / 6.0));
d = min(d, arc(p, vec2(0.0, 0.0), normalize(vec2(-0.4, 0.5)), 0.2, 0.02, uTime * TAU / 8.0));
return d;
}
`;
*/