网络英语;实例化渲染——设置除数
WebGL; Instanced rendering - setting up divisors
我正在尝试使用实例化渲染在 webgl 中绘制很多立方体 (ANGLE_instanced_arrays
)。
但是我似乎无法理解如何设置除数。我有以下缓冲区;
36 个顶点(6 个面由 2 个三角形组成,每个三角形使用 3 个顶点)。
每个立方体 6 种颜色(每个面 1 种)。
每个立方体 1 个翻译。
重用每个立方体的顶点;我已经将它的除数设置为 0。
对于颜色,我将除数设置为 2(即两个三角形使用相同的颜色 - 一张脸)。
对于平移,我将除数设置为 12(即 6 个面的相同平移 * 每个面 2 个三角形)。
为了渲染我打电话
ext_angle.drawArraysInstancedANGLE(gl.TRIANGLES, 0, 36, num_cubes);
但这似乎并没有渲染我的立方体。
使用平移除数 1 可以,但颜色太差了,立方体是单一纯色。
我认为这是因为我的实例现在是完整的立方体,但是如果我限制 count
(即每个实例的顶点),我似乎无法完全通过顶点缓冲区,实际上,我只是为每个立方体渲染一个三角形。
我将如何渲染大量这样的立方体;有不同颜色的面孔?
实例化是这样的:
你最终会打电话给
ext.drawArraysInstancedANGLE(mode, first, numVertices, numInstances);
假设您正在绘制立方体的实例。一个立方体有 36 个顶点(每个面 6 个顶点 * 6 个面)。所以
numVertices = 36
假设你想画 100 个立方体,所以
numInstances = 100
假设您有一个像这样的顶点着色器
假设您有以下着色器
attribute vec4 position;
uniform mat4 matrix;
void main() {
gl_Position = matrix * position;
}
如果你什么都没做,只是打电话
var mode = gl.TRIANGLES;
var first = 0;
var numVertices = 36
var numInstances = 100
ext.drawArraysInstancedANGLE(mode, first, numVertices, numInstances);
它只会在同一个地方绘制同一个立方体 100 次
接下来你想给每个立方体一个不同的翻译,所以你更新你的着色器到这个
attribute vec4 position;
attribute vec3 translation;
uniform mat4 matrix;
void main() {
gl_Position = matrix * (position + vec4(translation, 0));
}
您现在创建一个缓冲区并为每个多维数据集放置一个翻译,然后像正常一样设置属性
gl.vertexAttribPointer(translationLocation, 3, gl.FLOAT, false, 0, 0)
但是你还设置了一个除数
ext.vertexAttribDivisorANGLE(translationLocation, 1);
1 表示'每个实例只前进到翻译缓冲区中的下一个值一次'
现在您希望每个立方体的每个面都有不同的颜色,并且您只希望数据中的每个面有一种颜色(您不想重复颜色)。 没有这样的设置因为你的numVertices = 36
你只能选择推进每个顶点(除数= 0)或每36个顶点的倍数一次(即numVertices) .
所以你说,如果实例面而不是立方体呢?那么现在你遇到了相反的问题。每张脸涂上一种颜色。 numVertices = 6
、numInstances = 600
(100 个立方体 * 每个立方体 6 个面)。您将颜色的除数设置为 1 以将每个面的颜色提升一次。您可以将平移除数设置为 6,以便每 6 个面(每 6 个实例)仅推进一次平移。但是现在你不再有一个立方体,你只有一个面。换句话说,你要画 600 张面朝同一个方向的面孔,每 6 张面孔平移到同一个位置。
要恢复立方体,您必须添加一些东西以在 6 个方向上定位面部实例。
好的,你用 6 个方向填充缓冲区。那行不通的。您不能将除数设置为任何将使用这 6 个方向的任何东西,每个面只前进一次,然后在下一个立方体的 6 个面后重置。只有 1 个除数设置。将其设置为 6 以重复每个面或 36 以重复每个立方体,但您希望每个面前进 和 每个立方体重置。不存在这样的选项。
您可以使用 6 个绘制调用来绘制它,每个面方向一个。换句话说,你要画所有的左面,然后是所有的右面,所有的顶面,等等...
为此,我们只制作 1 个面,每个立方体 1 个平移,每个立方体每个面 1 种颜色。我们将翻译和颜色的除数设置为 1。
然后我们画6次,每个面一个方向。每次绘制之间的区别是我们传递了面部的方向,我们更改了颜色属性的属性偏移量并将其步幅设置为 6 * 4 浮点数 (6 * 4 * 4)。
var vs = `
attribute vec4 position;
attribute vec3 translation;
attribute vec4 color;
uniform mat4 viewProjectionMatrix;
uniform mat4 localMatrix;
varying vec4 v_color;
void main() {
vec4 localPosition = localMatrix * position + vec4(translation, 0);
gl_Position = viewProjectionMatrix * localPosition;
v_color = color;
}
`;
var fs = `
precision mediump float;
varying vec4 v_color;
void main() {
gl_FragColor = v_color;
}
`;
var m4 = twgl.m4;
var gl = document.querySelector("canvas").getContext("webgl");
var ext = gl.getExtension("ANGLE_instanced_arrays");
if (!ext) {
alert("need ANGLE_instanced_arrays");
}
var program = twgl.createProgramFromSources(gl, [vs, fs]);
var positionLocation = gl.getAttribLocation(program, "position");
var translationLocation = gl.getAttribLocation(program, "translation");
var colorLocation = gl.getAttribLocation(program, "color");
var localMatrixLocation = gl.getUniformLocation(program, "localMatrix");
var viewProjectionMatrixLocation = gl.getUniformLocation(
program,
"viewProjectionMatrix");
function r(min, max) {
if (max === undefined) {
max = min;
min = 0;
}
return Math.random() * (max - min) + min;
}
function rp() {
return r(-20, 20);
}
// make translations and colors, colors are separated by face
var numCubes = 1000;
var colors = [];
var translations = [];
for (var cube = 0; cube < numCubes; ++cube) {
translations.push(rp(), rp(), rp());
// pick a random color;
var color = [r(1), r(1), r(1), 1];
// now pick 4 similar colors for the faces of the cube
// that way we can tell if the colors are correctly assigned
// to each cube's faces.
var channel = r(3) | 0; // pick a channel 0 - 2 to randomly modify
for (var face = 0; face < 6; ++face) {
color[channel] = r(.7, 1);
colors.push.apply(colors, color);
}
}
var buffers = twgl.createBuffersFromArrays(gl, {
position: [ // one face
-1, -1, -1,
-1, 1, -1,
1, -1, -1,
1, -1, -1,
-1, 1, -1,
1, 1, -1,
],
color: colors,
translation: translations,
});
var faceMatrices = [
m4.identity(),
m4.rotationX(Math.PI / 2),
m4.rotationX(Math.PI / -2),
m4.rotationY(Math.PI / 2),
m4.rotationY(Math.PI / -2),
m4.rotationY(Math.PI),
];
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.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.bindBuffer(gl.ARRAY_BUFFER, buffers.position);
gl.enableVertexAttribArray(positionLocation);
gl.vertexAttribPointer(positionLocation, 3, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, buffers.translation);
gl.enableVertexAttribArray(translationLocation);
gl.vertexAttribPointer(translationLocation, 3, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, buffers.color);
gl.enableVertexAttribArray(colorLocation);
ext.vertexAttribDivisorANGLE(positionLocation, 0);
ext.vertexAttribDivisorANGLE(translationLocation, 1);
ext.vertexAttribDivisorANGLE(colorLocation, 1);
gl.useProgram(program);
var fov = 60;
var aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
var projection = m4.perspective(fov * Math.PI / 180, aspect, 0.5, 100);
var radius = 30;
var eye = [
Math.cos(time) * radius,
Math.sin(time * 0.3) * radius,
Math.sin(time) * radius,
];
var target = [0, 0, 0];
var up = [0, 1, 0];
var camera = m4.lookAt(eye, target, up);
var view = m4.inverse(camera);
var viewProjection = m4.multiply(projection, view);
gl.uniformMatrix4fv(viewProjectionMatrixLocation, false, viewProjection);
// 6 faces * 4 floats per color * 4 bytes per float
var stride = 6 * 4 * 4;
var numVertices = 6;
faceMatrices.forEach(function(faceMatrix, ndx) {
var offset = ndx * 4 * 4; // 4 floats per color * 4 floats
gl.vertexAttribPointer(
colorLocation, 4, gl.FLOAT, false, stride, offset);
gl.uniformMatrix4fv(localMatrixLocation, false, faceMatrix);
ext.drawArraysInstancedANGLE(gl.TRIANGLES, 0, numVertices, numCubes);
});
requestAnimationFrame(render);
}
requestAnimationFrame(render);
body { margin: 0; }
canvas { width: 100vw; height: 100vh; display: block; }
<script src="https://twgljs.org/dist/2.x/twgl-full.min.js"></script>
<canvas></canvas>
我正在尝试使用实例化渲染在 webgl 中绘制很多立方体 (ANGLE_instanced_arrays
)。
但是我似乎无法理解如何设置除数。我有以下缓冲区;
36 个顶点(6 个面由 2 个三角形组成,每个三角形使用 3 个顶点)。 每个立方体 6 种颜色(每个面 1 种)。 每个立方体 1 个翻译。
重用每个立方体的顶点;我已经将它的除数设置为 0。 对于颜色,我将除数设置为 2(即两个三角形使用相同的颜色 - 一张脸)。 对于平移,我将除数设置为 12(即 6 个面的相同平移 * 每个面 2 个三角形)。
为了渲染我打电话
ext_angle.drawArraysInstancedANGLE(gl.TRIANGLES, 0, 36, num_cubes);
但这似乎并没有渲染我的立方体。
使用平移除数 1 可以,但颜色太差了,立方体是单一纯色。
我认为这是因为我的实例现在是完整的立方体,但是如果我限制 count
(即每个实例的顶点),我似乎无法完全通过顶点缓冲区,实际上,我只是为每个立方体渲染一个三角形。
我将如何渲染大量这样的立方体;有不同颜色的面孔?
实例化是这样的:
你最终会打电话给
ext.drawArraysInstancedANGLE(mode, first, numVertices, numInstances);
假设您正在绘制立方体的实例。一个立方体有 36 个顶点(每个面 6 个顶点 * 6 个面)。所以
numVertices = 36
假设你想画 100 个立方体,所以
numInstances = 100
假设您有一个像这样的顶点着色器
假设您有以下着色器
attribute vec4 position;
uniform mat4 matrix;
void main() {
gl_Position = matrix * position;
}
如果你什么都没做,只是打电话
var mode = gl.TRIANGLES;
var first = 0;
var numVertices = 36
var numInstances = 100
ext.drawArraysInstancedANGLE(mode, first, numVertices, numInstances);
它只会在同一个地方绘制同一个立方体 100 次
接下来你想给每个立方体一个不同的翻译,所以你更新你的着色器到这个
attribute vec4 position;
attribute vec3 translation;
uniform mat4 matrix;
void main() {
gl_Position = matrix * (position + vec4(translation, 0));
}
您现在创建一个缓冲区并为每个多维数据集放置一个翻译,然后像正常一样设置属性
gl.vertexAttribPointer(translationLocation, 3, gl.FLOAT, false, 0, 0)
但是你还设置了一个除数
ext.vertexAttribDivisorANGLE(translationLocation, 1);
1 表示'每个实例只前进到翻译缓冲区中的下一个值一次'
现在您希望每个立方体的每个面都有不同的颜色,并且您只希望数据中的每个面有一种颜色(您不想重复颜色)。 没有这样的设置因为你的numVertices = 36
你只能选择推进每个顶点(除数= 0)或每36个顶点的倍数一次(即numVertices) .
所以你说,如果实例面而不是立方体呢?那么现在你遇到了相反的问题。每张脸涂上一种颜色。 numVertices = 6
、numInstances = 600
(100 个立方体 * 每个立方体 6 个面)。您将颜色的除数设置为 1 以将每个面的颜色提升一次。您可以将平移除数设置为 6,以便每 6 个面(每 6 个实例)仅推进一次平移。但是现在你不再有一个立方体,你只有一个面。换句话说,你要画 600 张面朝同一个方向的面孔,每 6 张面孔平移到同一个位置。
要恢复立方体,您必须添加一些东西以在 6 个方向上定位面部实例。
好的,你用 6 个方向填充缓冲区。那行不通的。您不能将除数设置为任何将使用这 6 个方向的任何东西,每个面只前进一次,然后在下一个立方体的 6 个面后重置。只有 1 个除数设置。将其设置为 6 以重复每个面或 36 以重复每个立方体,但您希望每个面前进 和 每个立方体重置。不存在这样的选项。
您可以使用 6 个绘制调用来绘制它,每个面方向一个。换句话说,你要画所有的左面,然后是所有的右面,所有的顶面,等等...
为此,我们只制作 1 个面,每个立方体 1 个平移,每个立方体每个面 1 种颜色。我们将翻译和颜色的除数设置为 1。
然后我们画6次,每个面一个方向。每次绘制之间的区别是我们传递了面部的方向,我们更改了颜色属性的属性偏移量并将其步幅设置为 6 * 4 浮点数 (6 * 4 * 4)。
var vs = `
attribute vec4 position;
attribute vec3 translation;
attribute vec4 color;
uniform mat4 viewProjectionMatrix;
uniform mat4 localMatrix;
varying vec4 v_color;
void main() {
vec4 localPosition = localMatrix * position + vec4(translation, 0);
gl_Position = viewProjectionMatrix * localPosition;
v_color = color;
}
`;
var fs = `
precision mediump float;
varying vec4 v_color;
void main() {
gl_FragColor = v_color;
}
`;
var m4 = twgl.m4;
var gl = document.querySelector("canvas").getContext("webgl");
var ext = gl.getExtension("ANGLE_instanced_arrays");
if (!ext) {
alert("need ANGLE_instanced_arrays");
}
var program = twgl.createProgramFromSources(gl, [vs, fs]);
var positionLocation = gl.getAttribLocation(program, "position");
var translationLocation = gl.getAttribLocation(program, "translation");
var colorLocation = gl.getAttribLocation(program, "color");
var localMatrixLocation = gl.getUniformLocation(program, "localMatrix");
var viewProjectionMatrixLocation = gl.getUniformLocation(
program,
"viewProjectionMatrix");
function r(min, max) {
if (max === undefined) {
max = min;
min = 0;
}
return Math.random() * (max - min) + min;
}
function rp() {
return r(-20, 20);
}
// make translations and colors, colors are separated by face
var numCubes = 1000;
var colors = [];
var translations = [];
for (var cube = 0; cube < numCubes; ++cube) {
translations.push(rp(), rp(), rp());
// pick a random color;
var color = [r(1), r(1), r(1), 1];
// now pick 4 similar colors for the faces of the cube
// that way we can tell if the colors are correctly assigned
// to each cube's faces.
var channel = r(3) | 0; // pick a channel 0 - 2 to randomly modify
for (var face = 0; face < 6; ++face) {
color[channel] = r(.7, 1);
colors.push.apply(colors, color);
}
}
var buffers = twgl.createBuffersFromArrays(gl, {
position: [ // one face
-1, -1, -1,
-1, 1, -1,
1, -1, -1,
1, -1, -1,
-1, 1, -1,
1, 1, -1,
],
color: colors,
translation: translations,
});
var faceMatrices = [
m4.identity(),
m4.rotationX(Math.PI / 2),
m4.rotationX(Math.PI / -2),
m4.rotationY(Math.PI / 2),
m4.rotationY(Math.PI / -2),
m4.rotationY(Math.PI),
];
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.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.bindBuffer(gl.ARRAY_BUFFER, buffers.position);
gl.enableVertexAttribArray(positionLocation);
gl.vertexAttribPointer(positionLocation, 3, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, buffers.translation);
gl.enableVertexAttribArray(translationLocation);
gl.vertexAttribPointer(translationLocation, 3, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, buffers.color);
gl.enableVertexAttribArray(colorLocation);
ext.vertexAttribDivisorANGLE(positionLocation, 0);
ext.vertexAttribDivisorANGLE(translationLocation, 1);
ext.vertexAttribDivisorANGLE(colorLocation, 1);
gl.useProgram(program);
var fov = 60;
var aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
var projection = m4.perspective(fov * Math.PI / 180, aspect, 0.5, 100);
var radius = 30;
var eye = [
Math.cos(time) * radius,
Math.sin(time * 0.3) * radius,
Math.sin(time) * radius,
];
var target = [0, 0, 0];
var up = [0, 1, 0];
var camera = m4.lookAt(eye, target, up);
var view = m4.inverse(camera);
var viewProjection = m4.multiply(projection, view);
gl.uniformMatrix4fv(viewProjectionMatrixLocation, false, viewProjection);
// 6 faces * 4 floats per color * 4 bytes per float
var stride = 6 * 4 * 4;
var numVertices = 6;
faceMatrices.forEach(function(faceMatrix, ndx) {
var offset = ndx * 4 * 4; // 4 floats per color * 4 floats
gl.vertexAttribPointer(
colorLocation, 4, gl.FLOAT, false, stride, offset);
gl.uniformMatrix4fv(localMatrixLocation, false, faceMatrix);
ext.drawArraysInstancedANGLE(gl.TRIANGLES, 0, numVertices, numCubes);
});
requestAnimationFrame(render);
}
requestAnimationFrame(render);
body { margin: 0; }
canvas { width: 100vw; height: 100vh; display: block; }
<script src="https://twgljs.org/dist/2.x/twgl-full.min.js"></script>
<canvas></canvas>