我正在尝试通过具有实例化几何体的原始WebGL the following three.js example重新创建;不过,经过测试,看起来我可能只需要像示例中那样绘制多个网格,但是我想我想先在这里进行仔细检查。
现在的问题本质上是-是否可以为要渲染的几何的每个实例传递每个顶点的数据?
希望这是有道理的,但如果没有-
基本上,现在发生的事情是给定当前数据,类似于
[s1,t1,s2,t2,s3,t3,s4,t4,s1,t1,s2,t2,s3,t3,s4,t4] // enough uvs for 2 instances, each s and t value makes up 1 uv coord for 1 vertex
而不是像这样的块中读取和分配数据
// instance 1 // instance 2
[[s1,t1,s2,t2,s3,t3,s4,t4], [s1,t1,s2,t2,s3,t3,s4,t4]]
// Each s and t forms 1 uv coord for 1 vertex
似乎是这样读取数据
[s1,t1,s2,t2,s3,t3,s4,t4,s1,t1,s2,t2,s3,t3,s4,t4]
// Each s and t pair is assigned to every vertex of each instance
是否有可能像第二个步骤那样使事情正常进行?如果不是这样,那很好,但是我认为应该问。
答案 0 :(得分:0)
这取决于您的意思
是否可以为要渲染的几何的每个实例传递每个顶点的数据?
如果通过属性来表示,那么答案是否定的。您可以获取每个顶点的数据或每个实例的数据,但不能获取每个实例的每个顶点的数据。
但是您还会获得2个额外的输入,gl_VertexID
和gl_InstanceID
(在WebGL2中),或者您可以添加自己的输入,用于计算UV或用于查找纹理中的数据以有效地达到您想要的结果。
例如,假设您有20x10的多维数据集。你可以做类似的事情
attribute vec4 position; // 36 cube positions
attribute vec2 uv; // 36 cube UVs
attribute float instanceId; // 1 per instance
uniform float cubesAcross; // set to 20
uniform float cubesDown; // set to 10
varying v_uv;
void main() {
float cubeX = mod(instanceId, cubesAcross);
float cubeY = floor(instanceId / cubesAcross);
v_vu = (vec2(cubeX, cubeY) + uv) / vec2(cubesAcross, cubesDown);
gl_Position = ...whatever you were doing for position before...
}
示例:
"use strict";
function main() {
const m4 = twgl.m4;
const gl = document.querySelector("canvas").getContext("webgl");
const ext = gl.getExtension('ANGLE_instanced_arrays');
if (!ext) {
return alert('need ANGLE_instanced_arrays');
}
twgl.addExtensionsToContext(gl);
const vs = `
attribute vec4 position;
attribute vec2 texcoord;
attribute mat4 matrix;
attribute float instanceId;
uniform float cubesAcross; // set to 20
uniform float cubesDown; // set to 10
varying vec2 v_texcoord;
void main() {
gl_Position = matrix * position;
float cubeX = mod(instanceId, cubesAcross);
float cubeY = floor(instanceId / cubesAcross);
v_texcoord = (vec2(cubeX, cubeY) + texcoord) / vec2(cubesAcross, cubesDown);
}
`;
const fs = `
precision mediump float;
varying vec2 v_texcoord;
void main() {
gl_FragColor = vec4(v_texcoord, 0, 1);
}
`;
// compile shaders, link, look up locations
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);
const cubesAcross = 20;
const cubesDown = 10;
const numCubes = cubesAcross * cubesDown;
// matrix per instance
const matrixData = new Float32Array(16 * numCubes);
const matrices = [];
const instanceIds = new Float32Array(numCubes);
for (let i = 0; i < numCubes; ++i) {
instanceIds[i] = i;
// make a typedarray view for each matrix
matrices.push(matrixData.subarray(i * 16, (i + 1) * 16));
}
const arrays = {
position: [
1, 1, -1,
1, 1, 1,
1, -1, 1,
1, -1, -1,
-1, 1, 1,
-1, 1, -1,
-1, -1, -1,
-1, -1, 1,
-1, 1, 1,
1, 1, 1,
1, 1, -1,
-1, 1, -1,
-1, -1, -1,
1, -1, -1,
1, -1, 1,
-1, -1, 1,
1, 1, 1,
-1, 1, 1,
-1, -1, 1,
1, -1, 1,
-1, 1, -1,
1, 1, -1,
1, -1, -1,
-1, -1, -1,
],
texcoord: [
1, 0,
0, 0,
0, 1,
1, 1,
1, 0,
0, 0,
0, 1,
1, 1,
0, 1,
1, 1,
1, 0,
0, 0,
0, 1,
1, 1,
1, 0,
0, 0,
1, 1,
0, 1,
0, 0,
1, 0,
1, 0,
0, 0,
0, 1,
1, 1,
],
indices: [
0, 1, 2, 0, 2, 3,
4, 5, 6, 4, 6, 7,
8, 9, 10, 8, 10, 11,
12, 13, 14, 12, 14, 15,
16, 17, 18, 16, 18, 19,
20, 21, 22, 20, 22, 23,
],
instanceId: { numComponents: 1, data: instanceIds, divisor: 1 },
matrix: { numComponents: 16, data: matrices, divisor: 1 },
};
// create buffers, upload data (gl.bufferData)
const bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays);
function render(time) {
time *= 0.001;
twgl.resizeCanvasToDisplaySize(gl.canvas);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.enable(gl.DEPTH_TEST);
gl.enable(gl.CULL_FACE);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
const fov = 30 * Math.PI / 180;
const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
const zNear = 0.5;
const zFar = 100;
const projection = m4.perspective(fov, aspect, zNear, zFar);
const eye = [1, 24, 76];
const target = [18, 10, 0];
const up = [0, 1, 0];
const camera = m4.lookAt(eye, target, up);
const view = m4.inverse(camera);
const viewProjection = m4.multiply(projection, view);
// update the instance for each matrix
const spacing = 2.5 + Math.sin(time) * .5;
let i = 0;
for (let y = 0; y < cubesDown; ++y) {
for (let x = 0; x < cubesAcross; ++x) {
const matrix = matrices[i++];
m4.translate(viewProjection, [
x * spacing,
y * spacing,
0,
], matrix);
}
}
gl.useProgram(programInfo.program);
// calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer, ext.vertexAttribDivisorANGLE
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
twgl.setUniforms(programInfo, {
cubesAcross,
cubesDown,
});
// upload instance matrices to buffer
gl.bindBuffer(gl.ARRAY_BUFFER, bufferInfo.attribs.matrix.buffer);
gl.bufferData(gl.ARRAY_BUFFER, matrixData, gl.DYNAMIC_DRAW);
ext.drawElementsInstancedANGLE(
gl.TRIANGLES, bufferInfo.numElements, gl.UNSIGNED_SHORT, 0, numCubes);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main();
body {
margin: 0;
}
canvas {
width: 100vw;
height: 100vh;
display: block;
}
<canvas></canvas>
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
如果您实际上希望每个实例的每个顶点数据唯一,则将其放入纹理中并传递一个vertexId。然后同时使用vertexId和instanceId来计算纹理坐标
attribute float instanceId;
attribute float vertexId;
uniform sampler2D dataTexture;
uniform vec2 dataTextureSize;
varying vec2 v_texcoord;
void main() {
// each row is for an instance, each texel
// per vertex. Of course if you want more data
// per vertex then multiply vertexId by the number of
// vec4s of data you need. If you need more instances
// then compute a more complex offset based off instanceId
vec2 uv = (vec2(vertexId, instanceId) + .5) / dataTextureSize;
vec4 data = texture2D(dataTexture, uv);
v_texcoord = data.xy;
"use strict";
function main() {
const m4 = twgl.m4;
const gl = document.querySelector("canvas").getContext("webgl");
const ext1 = gl.getExtension('OES_texture_float');
if (!ext1) {
return alert('need OES_texture_float');
}
const ext = gl.getExtension('ANGLE_instanced_arrays');
if (!ext) {
return alert('need ANGLE_instanced_arrays');
}
twgl.addExtensionsToContext(gl);
const vs = `
attribute vec4 position;
attribute mat4 matrix;
attribute float instanceId;
attribute float vertexId;
uniform sampler2D dataTexture;
uniform vec2 dataTextureSize;
varying vec2 v_texcoord;
void main() {
gl_Position = matrix * position;
// each row is for an instance, each texel
// per vertex. Of course if you want more data
// per vertex then multiply vertexId by the number of
// vec4s of data you need. If you need more instances
// then compute a more complex offset based off instanceId
vec2 uv = (vec2(vertexId, instanceId) + .5) / dataTextureSize;
vec4 data = texture2D(dataTexture, uv);
v_texcoord = data.xy;
}
`;
const fs = `
precision mediump float;
varying vec2 v_texcoord;
void main() {
gl_FragColor = vec4(v_texcoord, 0, 1);
}
`;
// compile shaders, link, look up locations
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);
const cubesAcross = 20;
const cubesDown = 10;
const numCubes = cubesAcross * cubesDown;
// matrix per instance
const matrixData = new Float32Array(16 * numCubes);
const matrices = [];
const instanceIds = new Float32Array(numCubes);
for (let i = 0; i < numCubes; ++i) {
instanceIds[i] = i;
// make a typedarray view for each matrix
matrices.push(matrixData.subarray(i * 16, (i + 1) * 16));
}
const arrays = {
position: [
1, 1, -1,
1, 1, 1,
1, -1, 1,
1, -1, -1,
-1, 1, 1,
-1, 1, -1,
-1, -1, -1,
-1, -1, 1,
-1, 1, 1,
1, 1, 1,
1, 1, -1,
-1, 1, -1,
-1, -1, -1,
1, -1, -1,
1, -1, 1,
-1, -1, 1,
1, 1, 1,
-1, 1, 1,
-1, -1, 1,
1, -1, 1,
-1, 1, -1,
1, 1, -1,
1, -1, -1,
-1, -1, -1,
],
vertexId: {
numComponents: 1,
data: [
0, 1, 2, 3,
4, 5, 6, 7,
8, 9, 10, 11,
12, 13, 14, 15,
16, 17, 18, 19,
20, 21, 21, 23,
],
},
indices: [
0, 1, 2, 0, 2, 3,
4, 5, 6, 4, 6, 7,
8, 9, 10, 8, 10, 11,
12, 13, 14, 12, 14, 15,
16, 17, 18, 16, 18, 19,
20, 21, 22, 20, 22, 23,
],
instanceId: { numComponents: 1, data: instanceIds, divisor: 1 },
matrix: { numComponents: 16, data: matrices, divisor: 1 },
};
// create buffers, upload data (gl.bufferData)
const bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays);
// put UV data in texture
const uvs = [];
for (let y = 0; y < cubesDown; ++y) {
const v0 = (y ) / cubesDown;
const v1 = (y + 1) / cubesDown;
for (let x = 0; x < cubesAcross; ++x) {
const u0 = (x ) / cubesAcross;
const u1 = (x + 1) / cubesAcross;
uvs.push(u0, v0, 0, 0, u1, v0, 0, 0, u0, v1, 0, 0, u1, v1, 0, 0);
uvs.push(u0, v0, 0, 0, u1, v0, 0, 0, u0, v1, 0, 0, u1, v1, 0, 0);
uvs.push(u0, v0, 0, 0, u1, v0, 0, 0, u0, v1, 0, 0, u1, v1, 0, 0);
uvs.push(u0, v0, 0, 0, u1, v0, 0, 0, u0, v1, 0, 0, u1, v1, 0, 0);
uvs.push(u0, v0, 0, 0, u1, v0, 0, 0, u0, v1, 0, 0, u1, v1, 0, 0);
uvs.push(u0, v0, 0, 0, u1, v0, 0, 0, u0, v1, 0, 0, u1, v1, 0, 0);
}
}
const texWidth = 24; // width = 24 vertices * 1 texel per
const texHeight = numCubes; // height = numInstances
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texImage2D(
gl.TEXTURE_2D,
0, // level
gl.RGBA,
texWidth,
texHeight,
0, // border
gl.RGBA,
gl.FLOAT,
new Float32Array(uvs));
function render(time) {
time *= 0.001;
twgl.resizeCanvasToDisplaySize(gl.canvas);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.enable(gl.DEPTH_TEST);
gl.enable(gl.CULL_FACE);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
const fov = 30 * Math.PI / 180;
const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
const zNear = 0.5;
const zFar = 100;
const projection = m4.perspective(fov, aspect, zNear, zFar);
const eye = [1, 24, 76];
const target = [18, 10, 0];
const up = [0, 1, 0];
const camera = m4.lookAt(eye, target, up);
const view = m4.inverse(camera);
const viewProjection = m4.multiply(projection, view);
// update the instance for each matrix
const spacing = 2.5 + Math.sin(time) * .5;
let i = 0;
for (let y = 0; y < cubesDown; ++y) {
for (let x = 0; x < cubesAcross; ++x) {
const matrix = matrices[i++];
m4.translate(viewProjection, [
x * spacing,
y * spacing,
0,
], matrix);
}
}
gl.useProgram(programInfo.program);
// calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer, ext.vertexAttribDivisorANGLE
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
twgl.setUniforms(programInfo, {
dataTexture: tex,
dataTextureSize: [texWidth, texHeight],
});
// upload instance matrices to buffer
gl.bindBuffer(gl.ARRAY_BUFFER, bufferInfo.attribs.matrix.buffer);
gl.bufferData(gl.ARRAY_BUFFER, matrixData, gl.DYNAMIC_DRAW);
ext.drawElementsInstancedANGLE(
gl.TRIANGLES, bufferInfo.numElements, gl.UNSIGNED_SHORT, 0, numCubes);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main();
body {
margin: 0;
}
canvas {
width: 100vw;
height: 100vh;
display: block;
}
<canvas></canvas>
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>