はじめに
let myShader;
async function setup() {
createCanvas(400, 400, WEBGL);smooth();
camera(0, 400, 0, 0, 0, 0, 0, 0, 1);
perspective(PI/3, 1, 40*sqrt(3), 4000*sqrt(3));
const geom = new p5.Geometry();
const v = (x,y,z) => {
geom.vertices.push(createVector(x,y,z));
}
const f = (a,b,c) => {
geom.faces.push([a,b,c]);
}
for(let k=0;k<96;k++){
const phi = TAU*k/96;
const cx = 60*cos(phi);
const cy = 60*sin(phi);
const n0 = createVector(cos(phi),sin(phi),0);
const n1 = createVector(0,0,1);
for(let m=0; m<96; m++){
const theta = TAU*m/96;
const x = cx + n0.x*20*cos(theta) + n1.x*20*sin(theta);
const y = cy + n0.y*20*cos(theta) + n1.y*20*sin(theta);
const z = n0.z*20*cos(theta) + n1.z*20*sin(theta);
//v(x,y,z);
const diffPos = 5+5*sin(phi*8);
const subX = cx + n0.x*(20+diffPos)*cos(theta) + n1.x*(20+diffPos)*sin(theta);
const subY = cy + n0.y*(20+diffPos)*cos(theta) + n1.y*(20+diffPos)*sin(theta);
const subZ = n0.z*(20+diffPos)*cos(theta) + n1.z*(20+diffPos)*sin(theta);
v(subX,subY,subZ);
//geom.vertexProperty("aSubPosition", [subX, subY, subZ]);
geom.vertexProperty("aNoiseValue", noise(x/60, y/60, z/60));
}
}
// 0,24,25, 0,25,1, ...
for(let k=0; k<96; k++){
for(let m=0; m<96; m++){
const ld = k*96 + m;
const lu = k*96 + (m<95 ? m+1 : 0);
const rd = (k<95 ? (k+1)*96 + m : m);
const ru = (k<95 ? (m<95 ? (k+1)*96+m+1 : (k+1)*96) : (m<95 ? m+1 : 0));
f(ld,rd,ru); f(ld,ru,lu);
}
}
geom.computeNormals();
myShader = baseMaterialShader().modify({
// Manually specifying a uniform and attribute
vertexDeclarations: `
in vec3 aSubPosition;
in float aNoiseValue;
out float vNoiseValue;
uniform float uTime;
vec4 multQ(in vec4 q1, in vec4 q2){
// 外でのq2, q1の順に掛け算する。そうしないと回転としての取り扱いで不整合が出るので。
float w = q2.w * q1.w - q2.x * q1.x - q2.y * q1.y - q2.z * q1.z;
float x = q2.w * q1.x + q2.x * q1.w + q2.y * q1.z - q2.z * q1.y;
float y = q2.w * q1.y + q2.y * q1.w + q2.z * q1.x - q2.x * q1.z;
float z = q2.w * q1.z + q2.z * q1.w + q2.x * q1.y - q2.y * q1.x;
return vec4(x, y, z, w);
}
vec4 conjQ(in vec4 q){
return vec4(-q.x, -q.y, -q.z, q.w);
}
vec4 getQuarternionFromAA(in vec3 axis, in float angle){
return vec4(sin(angle*0.5)*normalize(axis), cos(angle*0.5));
}
vec4 getQuarternionFromAA(in vec4 v){
return getQuarternionFromAA(v.xyz, v.w);
}
vec4 getQuarternionFromAA(in float x, in float y, in float z, in float angle){
return getQuarternionFromAA(vec3(x, y, z), angle);
}
vec4 powQ(in vec4 q, in float a, in float threshold){
float m = dot(q, q);
if(m < threshold){
return vec4(0.0);
}
if(q.w < 0.0){
q *= -1.0;
}
float n = sqrt(m);
float c = q.w/n;
float s = sqrt(m - q.w*q.w)/n;
float t = atan(s, c); // 0~PI/2
float multiplier = pow(n, a);
if(abs(t) < threshold){
q.w = (q.w/n)*multiplier;
q.x = (q.x/n)*multiplier;
q.y = (q.y/n)*multiplier;
q.z = (q.z/n)*multiplier;
return q;
}
vec3 axis = (q.xyz/n)/s;
float phi = a*t;
return multiplier * vec4(sin(phi)*axis, cos(phi));
}
vec4 powQ(in vec4 q, in float a){
return powQ(q, a, 1e-10);
}
vec4 slerpQ(in vec4 q1, in vec4 q2, in float r, in float threshold){
float m = dot(q1, q1);
if(m < threshold){
return vec4(0.0);
}
// multQが逆になっているので掛ける順序を逆にする
vec4 q = multQ(conjQ(q1), q2) * (1.0/m);
return multQ(q1, powQ(q, r, threshold));
}
vec4 slerpQ(in vec4 q1, in vec4 q2, in float r){
return slerpQ(q1, q2, r, 1e-10);
}
mat4 getRotationQ(in vec4 q){
return mat4(
2.0*q.w*q.w-1.0+2.0*q.x*q.x, 2.0*(q.x*q.y-q.z*q.w), 2.0*(q.x*q.z+q.y*q.w), 0.0,
2.0*(q.x*q.y+q.z*q.w), 2.0*q.w*q.w-1.0+2.0*q.y*q.y, 2.0*(q.y*q.z-q.x*q.w), 0.0,
2.0*(q.x*q.z-q.y*q.w), 2.0*(q.y*q.z+q.x*q.w), 2.0*q.w*q.w-1.0+2.0*q.z*q.z, 0.0,
0.0, 0.0, 0.0, 1.0
);
}
void applyRotationQ(inout vec3 v, inout vec3 n, in vec4 q){
mat4 tf = getRotationQ(q);
v = (vec4(v, 1.0) * tf).xyz;
// nはイントラ不要
n = (vec4(n, 0.0) * tf).xyz;
}
`,
fragmentDeclarations: 'in float vNoiseValue;',
'Vertex getWorldInputs': `(Vertex inputs) {
vec4 q0 = getQuarternionFromAA(vec3(1.0, 0.0, 0.0), 0.7);
vec4 q1 = getQuarternionFromAA(vec3(0.0, 1.0, 1.0), -1.3);
vec4 q2 = getQuarternionFromAA(vec3(1.0, 0.0, 0.0), uTime);
vec4 q = slerpQ(q0, q1, 0.5-0.5*cos(uTime*6.28318));
applyRotationQ(inputs.position, inputs.normal, q);
inputs.position.x += 90.0*sin(uTime*6.28318);
applyRotationQ(inputs.position, inputs.normal, q);
inputs.position.yz -= 90.0*sin(uTime*6.28318);
applyRotationQ(inputs.position, inputs.normal, q);
applyRotationQ(inputs.position, inputs.normal, q2);
return inputs;
}`,
'void afterVertex': `() { vNoiseValue = aNoiseValue; }`,
'vec4 getFinalColor': `(vec4 color, vec2 texCoord) { color.rgb *= vNoiseValue * 4.0; return color; }`
});
draw=()=>{
orbitControl();
background(255);
shader(myShader);
myShader.setUniform("uTime", millis()/1000);
lights();
noStroke();
fill(200, 99, 60);
model(geom);
}}
おわりに
最近はぽこあポケモンにハマっています。のれんを腕で押さなくていいのはとても楽しいです。
ここまでお読みいただいてありがとうございました。