我试图让一个小例子一起使用d3-zoom为使用WebGL渲染的canvas元素提供简单的交互性。我想做的就是提供平移/缩放,使用4x4转换矩阵非常简单。
我遇到的问题是缩放(缩放)。如果您查看一些d3缩放示例,您会看到缩放焦点始终位于鼠标的位置。
如果直接使用缩放变换中的k
,tx
和ty
值,则平移有效,但缩放会偏移画布宽度和高度的一半,见
var width = 300,
height = 150;
var zoom = d3.zoom()
.on( 'zoom', zoomed );
var canvas = d3.select( 'body' )
.append( 'canvas' )
.attr( 'width', width )
.attr( 'height', height )
.call( zoom );
var gl = canvas.node().getContext( 'webgl' );
var shader = basic_shader(gl);
initialize_gl();
set_transform( 1, 0, 0 );
function zoomed () {
var t = d3.event.transform;
set_transform( t.k, t.x, t.y );
}
function initialize_gl () {
var sb = d3.color('steelblue');
gl.clearColor(sb.r / 255, sb.g / 255, sb.b / 255, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
var vertices = [
0.5, 0.5, 0.0, 1.0,
-0.5, 0.5, 0.0, 1.0,
0.5, -0.5, 0.0, 1.0,
-0.5, -0.5, 0.0, 1.0
];
var colors = [
1.0, 1.0, 1.0, 1.0, // white
1.0, 0.0, 0.0, 1.0, // red
0.0, 1.0, 0.0, 1.0, // green
0.0, 0.0, 1.0, 1.0 // blue
];
var vertex_buffer = gl.createBuffer();
var color_buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, color_buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);
gl.vertexAttribPointer(shader.color_attrib, 4, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
gl.vertexAttribPointer(shader.vertex_attrib, 4, gl.FLOAT, false, 0, 0);
}
function set_transform ( k, tx, ty ) {
var matrix = new Float32Array([
k, 0, 0, 0,
0, k, 0, 0,
0, 0, 1, 0,
2*tx/width, -2*ty/height, 0, 1
]);
gl.uniformMatrix4fv( shader.matrix_uniform, false, matrix );
gl.clear( gl.COLOR_BUFFER_BIT );
gl.drawArrays( gl.TRIANGLE_STRIP, 0, 4 );
}
function basic_vertex () {
return [
'attribute vec4 vertex_position;',
'attribute vec4 vertex_color;',
'varying lowp vec4 vert_color;',
'uniform mat4 matrix;',
'void main( void ) {',
' gl_Position = matrix * vertex_position;',
' vert_color = vertex_color;',
'}'
].join('\n');
}
function basic_fragment () {
return [
'varying lowp vec4 vert_color;',
'void main( void ) {',
' gl_FragColor = vert_color;',
'}'
].join('\n');
}
function basic_shader ( gl ) {
var program = gl_program( gl, basic_vertex(), basic_fragment() );
gl.useProgram( program );
program.vertex_attrib = gl.getAttribLocation( program, 'vertex_position' );
program.color_attrib = gl.getAttribLocation( program, 'vertex_color' );
program.matrix_uniform = gl.getUniformLocation( program, 'matrix' );
program.translate_uniform = gl.getUniformLocation( program, 'translate_matrix' );
program.scale_uniform = gl.getUniformLocation( program, 'scale_matrix' );
gl.enableVertexAttribArray( program.vertex_attrib );
gl.enableVertexAttribArray( program.color_attrib );
return program;
}
function gl_shader ( gl, type, code ) {
var shader = gl.createShader( type );
gl.shaderSource( shader, code );
gl.compileShader( shader );
return shader;
}
function gl_program ( gl, vertex_source, fragment_source ) {
var shader_program = gl.createProgram();
var vertex_shader = gl_shader( gl, gl.VERTEX_SHADER, vertex_source );
var fragment_shader = gl_shader( gl, gl.FRAGMENT_SHADER, fragment_source );
if ( shader_program && vertex_shader && fragment_shader ) {
gl.attachShader( shader_program, vertex_shader );
gl.attachShader( shader_program, fragment_shader );
gl.linkProgram( shader_program );
gl.deleteShader( vertex_shader );
gl.deleteShader( fragment_shader );
return shader_program;
}
}

<script src="https://d3js.org/d3.v4.min.js"></script>
&#13;
我的预感是,这与以下事实有关:在WebGL中,视口x和y坐标各自从-1到1,而d3-zoom使用canvas元素中的鼠标坐标,当标准化时可以在0到1的范围内。
如果将鼠标放在画布的左上角(画布坐标中的(0,0))并尝试缩放,则可以看到这种情况。它将缩放,就好像鼠标位于画布的中心(WebGL坐标中的(0,0))。
为了解决这个问题,你可以从x平移中减1(即坐标系宽度的一半[-1,1])并加1(即坐标系高度的一半[-1,1] ])到y翻译,如图所示
var width = 300,
height = 150;
var zoom = d3.zoom()
.on( 'zoom', zoomed );
var canvas = d3.select( 'body' )
.append( 'canvas' )
.attr( 'width', width )
.attr( 'height', height )
.call( zoom );
var gl = canvas.node().getContext( 'webgl' );
var shader = basic_shader(gl);
initialize_gl();
set_transform( 1, 0, 0 );
function zoomed () {
var t = d3.event.transform;
set_transform( t.k, t.x, t.y );
}
function initialize_gl () {
var sb = d3.color('steelblue');
gl.clearColor(sb.r / 255, sb.g / 255, sb.b / 255, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
var vertices = [
0.5, 0.5, 0.0, 1.0,
-0.5, 0.5, 0.0, 1.0,
0.5, -0.5, 0.0, 1.0,
-0.5, -0.5, 0.0, 1.0
];
var colors = [
1.0, 1.0, 1.0, 1.0, // white
1.0, 0.0, 0.0, 1.0, // red
0.0, 1.0, 0.0, 1.0, // green
0.0, 0.0, 1.0, 1.0 // blue
];
var vertex_buffer = gl.createBuffer();
var color_buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, color_buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);
gl.vertexAttribPointer(shader.color_attrib, 4, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
gl.vertexAttribPointer(shader.vertex_attrib, 4, gl.FLOAT, false, 0, 0);
}
function set_transform ( k, tx, ty ) {
var matrix = new Float32Array([
k, 0, 0, 0,
0, k, 0, 0,
0, 0, 1, 0,
2*tx/width-1.0, -2*ty/height+1.0, 0, 1
]);
gl.uniformMatrix4fv( shader.matrix_uniform, false, matrix );
gl.clear( gl.COLOR_BUFFER_BIT );
gl.drawArrays( gl.TRIANGLE_STRIP, 0, 4 );
}
function basic_vertex () {
return [
'attribute vec4 vertex_position;',
'attribute vec4 vertex_color;',
'varying lowp vec4 vert_color;',
'uniform mat4 matrix;',
'void main( void ) {',
' gl_Position = matrix * vertex_position;',
' vert_color = vertex_color;',
'}'
].join('\n');
}
function basic_fragment () {
return [
'varying lowp vec4 vert_color;',
'void main( void ) {',
' gl_FragColor = vert_color;',
'}'
].join('\n');
}
function basic_shader ( gl ) {
var program = gl_program( gl, basic_vertex(), basic_fragment() );
gl.useProgram( program );
program.vertex_attrib = gl.getAttribLocation( program, 'vertex_position' );
program.color_attrib = gl.getAttribLocation( program, 'vertex_color' );
program.matrix_uniform = gl.getUniformLocation( program, 'matrix' );
program.translate_uniform = gl.getUniformLocation( program, 'translate_matrix' );
program.scale_uniform = gl.getUniformLocation( program, 'scale_matrix' );
gl.enableVertexAttribArray( program.vertex_attrib );
gl.enableVertexAttribArray( program.color_attrib );
return program;
}
function gl_shader ( gl, type, code ) {
var shader = gl.createShader( type );
gl.shaderSource( shader, code );
gl.compileShader( shader );
return shader;
}
function gl_program ( gl, vertex_source, fragment_source ) {
var shader_program = gl.createProgram();
var vertex_shader = gl_shader( gl, gl.VERTEX_SHADER, vertex_source );
var fragment_shader = gl_shader( gl, gl.FRAGMENT_SHADER, fragment_source );
if ( shader_program && vertex_shader && fragment_shader ) {
gl.attachShader( shader_program, vertex_shader );
gl.attachShader( shader_program, fragment_shader );
gl.linkProgram( shader_program );
gl.deleteShader( vertex_shader );
gl.deleteShader( fragment_shader );
return shader_program;
}
}
&#13;
<script src="https://d3js.org/d3.v4.min.js"></script>
&#13;
然而,通过执行偏移,您的场景最初被翻译,这不是完全理想的。所以我的问题是,处理这个问题的最佳方法是什么?最好由d3方面还是WebGL方面处理?
答案 0 :(得分:3)
我只是移动了顶点以匹配矩阵
var vertices = [
.5, -.5, 0.0, 1.0,
1.5, -.5, 0.0, 1.0,
.5, -1.5, 0.0, 1.0,
1.5, -1.5, 0.0, 1.0
];
var width = 300,
height = 150;
var zoom = d3.zoom()
.on( 'zoom', zoomed );
var canvas = d3.select( 'body' )
.append( 'canvas' )
.attr( 'width', width )
.attr( 'height', height )
.call( zoom );
var gl = canvas.node().getContext( 'webgl' );
var shader = basic_shader(gl);
initialize_gl();
set_transform( 1, 0, 0 );
function zoomed () {
var t = d3.event.transform;
set_transform( t.k, t.x, t.y );
}
function initialize_gl () {
var sb = d3.color('steelblue');
gl.clearColor(sb.r / 255, sb.g / 255, sb.b / 255, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
var vertices = [
.5, -.5, 0.0, 1.0,
1.5, -.5, 0.0, 1.0,
.5, -1.5, 0.0, 1.0,
1.5, -1.5, 0.0, 1.0
];
var colors = [
1.0, 1.0, 1.0, 1.0, // white
1.0, 0.0, 0.0, 1.0, // red
0.0, 1.0, 0.0, 1.0, // green
0.0, 0.0, 1.0, 1.0 // blue
];
var vertex_buffer = gl.createBuffer();
var color_buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, color_buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);
gl.vertexAttribPointer(shader.color_attrib, 4, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
gl.vertexAttribPointer(shader.vertex_attrib, 4, gl.FLOAT, false, 0, 0);
}
function set_transform ( k, tx, ty ) {
var matrix = new Float32Array([
k, 0, 0, 0,
0, k, 0, 0,
0, 0, 1, 0,
2*tx/width-1.0, -2*ty/height+1.0, 0, 1
]);
gl.uniformMatrix4fv( shader.matrix_uniform, false, matrix );
gl.clear( gl.COLOR_BUFFER_BIT );
gl.drawArrays( gl.TRIANGLE_STRIP, 0, 4 );
}
function basic_vertex () {
return [
'attribute vec4 vertex_position;',
'attribute vec4 vertex_color;',
'varying lowp vec4 vert_color;',
'uniform mat4 matrix;',
'void main( void ) {',
' gl_Position = matrix * vertex_position;',
' vert_color = vertex_color;',
'}'
].join('\n');
}
function basic_fragment () {
return [
'varying lowp vec4 vert_color;',
'void main( void ) {',
' gl_FragColor = vert_color;',
'}'
].join('\n');
}
function basic_shader ( gl ) {
var program = gl_program( gl, basic_vertex(), basic_fragment() );
gl.useProgram( program );
program.vertex_attrib = gl.getAttribLocation( program, 'vertex_position' );
program.color_attrib = gl.getAttribLocation( program, 'vertex_color' );
program.matrix_uniform = gl.getUniformLocation( program, 'matrix' );
program.translate_uniform = gl.getUniformLocation( program, 'translate_matrix' );
program.scale_uniform = gl.getUniformLocation( program, 'scale_matrix' );
gl.enableVertexAttribArray( program.vertex_attrib );
gl.enableVertexAttribArray( program.color_attrib );
return program;
}
function gl_shader ( gl, type, code ) {
var shader = gl.createShader( type );
gl.shaderSource( shader, code );
gl.compileShader( shader );
return shader;
}
function gl_program ( gl, vertex_source, fragment_source ) {
var shader_program = gl.createProgram();
var vertex_shader = gl_shader( gl, gl.VERTEX_SHADER, vertex_source );
var fragment_shader = gl_shader( gl, gl.FRAGMENT_SHADER, fragment_source );
if ( shader_program && vertex_shader && fragment_shader ) {
gl.attachShader( shader_program, vertex_shader );
gl.attachShader( shader_program, fragment_shader );
gl.linkProgram( shader_program );
gl.deleteShader( vertex_shader );
gl.deleteShader( fragment_shader );
return shader_program;
}
}
&#13;
<script src="https://d3js.org/d3.v4.min.js"></script>
&#13;
但说实话,我可能会使用数学库并使用一些变换。我更容易理解代码。我不确定&#34;空间&#34; D3。我想虽然它只是传递了一个偏移和一个比例。在这种情况下
// change the space to be pixels with 0,0 in top left
var matrix = m4.ortho(0, gl.canvas.width, gl.canvas.height, 0, -1, 1);
// apply the d3 translate and zoom
matrix = m4.translate(matrix, [tx, ty, 0]);
matrix = m4.scale(matrix, [k, k, 1]);
// translate the unit quad to the center
matrix = m4.translate(matrix, [width / 2, height / 2, 0]);
// make the unit quad be half the size of the canvas
matrix = m4.scale(matrix, [width / 2, height / 2 , 1]);
var m4 = twgl.m4;
var width = 300,
height = 150;
var zoom = d3.zoom()
.on( 'zoom', zoomed );
var canvas = d3.select( 'body' )
.append( 'canvas' )
.attr( 'width', width )
.attr( 'height', height )
.call( zoom );
var gl = canvas.node().getContext( 'webgl' );
var shader = basic_shader(gl);
initialize_gl();
set_transform( 1, 0, 0 );
function zoomed () {
var t = d3.event.transform;
set_transform( t.k, t.x, t.y );
}
function initialize_gl () {
var sb = d3.color('steelblue');
gl.clearColor(sb.r / 255, sb.g / 255, sb.b / 255, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
var vertices = [
-.5, .5, 0.0, 1.0,
.5, .5, 0.0, 1.0,
-.5, -.5, 0.0, 1.0,
.5, -.5, 0.0, 1.0
];
var colors = [
1.0, 1.0, 1.0, 1.0, // white
1.0, 0.0, 0.0, 1.0, // red
0.0, 1.0, 0.0, 1.0, // green
0.0, 0.0, 1.0, 1.0 // blue
];
var vertex_buffer = gl.createBuffer();
var color_buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, color_buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);
gl.vertexAttribPointer(shader.color_attrib, 4, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
gl.vertexAttribPointer(shader.vertex_attrib, 4, gl.FLOAT, false, 0, 0);
}
function set_transform ( k, tx, ty ) {
// change the space to be pixels with 0,0 in top left
var matrix = m4.ortho(0, gl.canvas.width, gl.canvas.height, 0, -1, 1);
// apply the d3 translate and zoom
matrix = m4.translate(matrix, [tx, ty, 0]);
matrix = m4.scale(matrix, [k, k, 1]);
// translate the unit quad to the center
matrix = m4.translate(matrix, [width / 2, height / 2, 0]);
// make the unit quad be half the size of the canvas
matrix = m4.scale(matrix, [width / 2, height / 2 , 1]);
gl.uniformMatrix4fv( shader.matrix_uniform, false, matrix );
gl.clear( gl.COLOR_BUFFER_BIT );
gl.drawArrays( gl.TRIANGLE_STRIP, 0, 4 );
}
function basic_vertex () {
return [
'attribute vec4 vertex_position;',
'attribute vec4 vertex_color;',
'varying lowp vec4 vert_color;',
'uniform mat4 matrix;',
'void main( void ) {',
' gl_Position = matrix * vertex_position;',
' vert_color = vertex_color;',
'}'
].join('\n');
}
function basic_fragment () {
return [
'varying lowp vec4 vert_color;',
'void main( void ) {',
' gl_FragColor = vert_color;',
'}'
].join('\n');
}
function basic_shader ( gl ) {
var program = gl_program( gl, basic_vertex(), basic_fragment() );
gl.useProgram( program );
program.vertex_attrib = gl.getAttribLocation( program, 'vertex_position' );
program.color_attrib = gl.getAttribLocation( program, 'vertex_color' );
program.matrix_uniform = gl.getUniformLocation( program, 'matrix' );
program.translate_uniform = gl.getUniformLocation( program, 'translate_matrix' );
program.scale_uniform = gl.getUniformLocation( program, 'scale_matrix' );
gl.enableVertexAttribArray( program.vertex_attrib );
gl.enableVertexAttribArray( program.color_attrib );
return program;
}
function gl_shader ( gl, type, code ) {
var shader = gl.createShader( type );
gl.shaderSource( shader, code );
gl.compileShader( shader );
return shader;
}
function gl_program ( gl, vertex_source, fragment_source ) {
var shader_program = gl.createProgram();
var vertex_shader = gl_shader( gl, gl.VERTEX_SHADER, vertex_source );
var fragment_shader = gl_shader( gl, gl.FRAGMENT_SHADER, fragment_source );
if ( shader_program && vertex_shader && fragment_shader ) {
gl.attachShader( shader_program, vertex_shader );
gl.attachShader( shader_program, fragment_shader );
gl.linkProgram( shader_program );
gl.deleteShader( vertex_shader );
gl.deleteShader( fragment_shader );
return shader_program;
}
}
&#13;
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://twgljs.org/dist/3.x/twgl-full.min.js"></script>
&#13;