[英]Using d3-zoom to interact with WebGL
我试图得到一个小例子,它使用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( tk, tx, ty ); } 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>
我的预感是,这与以下事实有关:在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( tk, tx, ty ); } 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; } }
<script src="https://d3js.org/d3.v4.min.js"></script>
但是,通过执行偏移,您的场景最初会被翻译,这并不完全理想。 所以我的问题是,处理这个问题的最佳方法是什么? 最好由d3端还是WebGL端处理?
我只是移动了你的顶点以匹配你的矩阵
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( tk, tx, ty ); } 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; } }
<script src="https://d3js.org/d3.v4.min.js"></script>
但说实话,我可能会使用数学库并使用一些变换。 我更容易理解代码。 我不确定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( tk, tx, ty ); } 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; } }
<script src="https://d3js.org/d3.v4.min.js"></script> <script src="https://twgljs.org/dist/3.x/twgl-full.min.js"></script>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.