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の3Dでインスタンス描画(前回の続き)

Posted at

はじめに

 インスタンシングで、イッツダンシング!いぇいいぇい。

 冗談はさておき、p5の枠組みでインスタンシングをやります。次の記事でVAOをやってたので参考にしようと思いました。

それで、例によって前回の続きです。

まあシェーダーは本家の物を使うべきなんですが、どうせ見栄えのする作品を作るのが目的ではない、ただのお遊びなので、これで問題ないです。それではさっそくいきます。

コード全文

p5.jsのバージョンは2.0.5です。

p5 3D instance

// 3D.
// インスタンシング。

const vs =
`#version 300 es
layout (location = 0) in vec3 aPosition;
layout (location = 1) in vec3 aNormal;
layout (location = 2) in vec3 aOffsetPosition;
uniform mat3 uModel;
uniform float uScale;
uniform vec3 uEye;
uniform vec3 uViewX;
uniform vec3 uViewY;
uniform vec3 uViewZ;
uniform vec4 uProj; // fov,aspect,near,far
out vec3 vNormal;
void main(){
  vNormal = aNormal;
  vec3 p = aPosition * uModel;
	p *= uScale;
	p += aOffsetPosition;
  p -= uEye;
  vec3 q = vec3(dot(p, uViewX), dot(p, uViewY), dot(p, uViewZ));
  float fov = uProj.x;
  float aspect = uProj.y;
  float near = uProj.z;
  float far = uProj.w;
  float factor = 1.0/tan(fov/2.0);
  gl_Position = vec4(
    q.x * aspect * factor,
    q.y * factor,
    ((near+far)/(near-far)) * q.z + (2.0*near*far)/(near-far),
    -q.z
  );
}
`;

const fs =
`#version 300 es
precision highp float;
in vec3 vNormal;
out vec4 fragColor;
void main(){
  fragColor = vec4(0.5+0.5*vNormal, 1.0);
}
`;

function setup(){
  createCanvas(400,400,WEBGL);

  const geom = new p5.Geometry();
  const cv = (x,y,z) => createVector(x,y,z);
  //geom.vertices.push(createVector(-50,-50,0), createVector(50,-50,0), createVector(-50,50,0), createVector(50,50,0));
  //geom.vertexNormals.push(createVector(0,0,1),createVector(0,0,1),createVector(0,0,1),createVector(0,0,1));
  //geom.faces.push([0,1,2],[2,1,3]);
  geom.vertices.push(
    cv(-1,-1,1), cv(1,-1,1), cv(-1,1,1), cv(1,1,1),
    cv(-1,-1,-1), cv(1,-1,-1), cv(-1,1,-1), cv(1,1,-1)
  );
  geom.vertexNormals = geom.vertices.map((v) => v.copy().normalize());
  geom.faces.push(
    [0,1,2], [2,1,3], [0,4,5], [0,5,1], [1,5,7], [1,7,3],
    [2,6,4], [2,4,0], [6,2,3], [6,3,7], [4,6,7], [4,7,5]
  );

  const eye = createVector(160,200,240);
  const center = createVector(0,0,0);
  const near = 20*sqrt(3);
  const far = 2000*sqrt(3);
  const aspect = width/height;
  const fov = PI/3;

  const v0 = center.copy().sub(eye).normalize(); // 視線方向単位ベクトル
  const up = createVector(0,1,0); // 暫定上方向ベクトル
  const viewX = v0.cross(up).normalize();
  const viewZ = v0.copy().mult(-1).normalize();
  const viewY = viewZ.cross(viewX).normalize();

  const sh = createShader(vs, fs);
  shader(sh);

  const gl = drawingContext;
  gl.enable(gl.CULL_FACE);

	const data = new Float32Array(729*3);

	let offset = 0;
	for(let z=-4; z<=4; z++){
		for(let y=-4; y<=4; y++){
			for(let x=-4; x<=4; x++){
				data[offset++] = x * 30;
				data[offset++] = y * 30;
				data[offset++] = z * 30;
			}
		}
	}
	
	const buf = gl.createBuffer();
	gl.bindBuffer(gl.ARRAY_BUFFER, buf);
	gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
	gl.bindBuffer(gl.ARRAY_BUFFER, null);
	
	const vao = gl.createVertexArray();
	gl.bindVertexArray(vao);
	model(geom);
	gl.bindBuffer(gl.ARRAY_BUFFER, buf);
	gl.vertexAttribPointer(2, 3, gl.FLOAT, false, 0, 0);
	gl.bindBuffer(gl.ARRAY_BUFFER, null);
	gl.enableVertexAttribArray(2);
	gl.vertexAttribDivisor(2, 1);
	gl.bindVertexArray(null);

  const loopFunction = () => {
    const t = frameCount*TAU/120;
    background(0);
    const axis = createVector(1,1,1).normalize();
    sh.setUniform("uModel", [
      cos(t) + (1-cos(t))*axis.x*axis.x, (1-cos(t))*axis.x*axis.y - sin(t)*axis.z, (1-cos(t))*axis.z*axis.x +sin(t)*axis.y,
      (1-cos(t))*axis.x*axis.y + sin(t)*axis.z, cos(t) + (1-cos(t))*axis.y*axis.y, (1-cos(t))*axis.y*axis.z - sin(t)*axis.x,
      (1-cos(t))*axis.z*axis.x - sin(t)*axis.y, (1-cos(t))*axis.y*axis.z + sin(t)*axis.x, cos(t) + (1-cos(t))*axis.z*axis.z
    ]);
		sh.setUniform("uScale", 5);
    sh.setUniform("uEye", [eye.x, eye.y, eye.z]);
    sh.setUniform("uViewX", [viewX.x, viewX.y, viewX.z]);
    sh.setUniform("uViewY", [viewY.x, viewY.y, viewY.z]);
    sh.setUniform("uViewZ", [viewZ.x, viewZ.y, viewZ.z]);
    sh.setUniform("uProj", [fov, aspect, near, far]);

	gl.bindVertexArray(vao);
	//model(geom);
		//gl.drawElements(gl.TRIANGLES, 36, gl.UNSIGNED_SHORT, 0);
		gl.drawElementsInstanced(gl.TRIANGLES, 36, gl.UNSIGNED_SHORT, 0, 729);
	gl.bindVertexArray(null);
  }

  draw = loopFunction;
}

 見てすぐわかると思いますが、インデントの変なところが追加部分です。分かりやすくていいですね。

wdqsqwdw3333e.png

 729個のcubeです。おそらくこのくらいならドローコール連打でもそこまで遅くないはずです。興味のある人はベンチマーク取ってみてください。自分は忙しいのでやりません。

シェーダー部分

 オフセットを作ります。vec3で、シェーダーにインスタンス変数を追加しておきます。

layout (location = 2) in vec3 aOffsetPosition;
...
uniform float uScale;
...
    p *= uScale;
	p += aOffsetPosition;

ついでにスケールもいじれるようにしました。調整がめんどくさい...あとカメラ、目線の位置をいじってあります。この2番スロットをインスタンスに使わせてもらいます。実は2番はp5が勝手に予約してしまう番号なんですが、今回p5の描画はしないのでここはフリーですし、どうせVAOで切り分けるので、問題ないですね。

データ用意

 ここにデータをぶち込む準備をします。特にランダム性を持たせる必要は無いので格子状です。

	const data = new Float32Array(729*3);

	let offset = 0;
	for(let z=-4; z<=4; z++){
		for(let y=-4; y<=4; y++){
			for(let x=-4; x<=4; x++){
				data[offset++] = x * 30;
				data[offset++] = y * 30;
				data[offset++] = z * 30;
			}
		}
	}

いつものようにバッファリングします。STATIC_DRAWでいいですね。固定です。

	const buf = gl.createBuffer();
	gl.bindBuffer(gl.ARRAY_BUFFER, buf);
	gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
	gl.bindBuffer(gl.ARRAY_BUFFER, null);

VAOの用意

 ここからが問題なんですが、実はmodel関数の仕様は全く詳しくないです。わかっていることは、現在の仕様では描画が終了してもIBOのバインドはどうやら解除されていないようです(完全上書き)。そういうわけなので、VAOで挟めば記録されます。このデータとアトリビュートさえあればドローコール一本でジオメトリは復元できます。そういうわけで堂々とVAOサンドイッチでmodelを挟みます。ついでにアトポン、enable,divisor,OK.準備完了です。
 いけ!

	const vao = gl.createVertexArray();
	gl.bindVertexArray(vao);
	model(geom);
	gl.bindBuffer(gl.ARRAY_BUFFER, buf);
	gl.vertexAttribPointer(2, 3, gl.FLOAT, false, 0, 0);
	gl.bindBuffer(gl.ARRAY_BUFFER, null);
	gl.enableVertexAttribArray(2);
	gl.vertexAttribDivisor(2, 1);
	gl.bindVertexArray(null);

ドローコール

 いけ!

	gl.bindVertexArray(vao);
	//model(geom);
		//gl.drawElements(gl.TRIANGLES, 36, gl.UNSIGNED_SHORT, 0);
		gl.drawElementsInstanced(gl.TRIANGLES, 36, gl.UNSIGNED_SHORT, 0, 729);
	gl.bindVertexArray(null);

おわりに

 インスタンシングはお手軽に大量の複製コピーを作れるので楽しいですねぇ。

 ここまでお読みいただいてありがとうございました。

 これ使うとインスタンシングできるらしい;

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?