0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

p5.jsのbaseMaterialShaderでクォータニオンの演算を使う

0
Last updated at Posted at 2026-09-01

はじめに

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);
}}

おわりに

 最近はぽこあポケモンにハマっています。のれんを腕で押さなくていいのはとても楽しいです。
 ここまでお読みいただいてありがとうございました。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?