我正在尝试实现自己的基于WebGL的3D图形绘图应用程序。
正如我可能通过处理大量数据点,我希望我可以创建一个交互式网格,以帮助可视化数字的不同数量级与滚动支持(用于缩放)。
这是我的网格立方体的片段着色器。
precision highp float;
varying vec3 pos;
uniform float u_size;
uniform float u_scale;
float my_fmod(float inp_val, float inp_m) {
float m = inp_m;
float val = inp_val + 201.0 * m;
return abs(val - float(int(val/m)) * m);
}
float gridline(float nPos0, float nPos1, float fac, float very_small_number) {
if ( my_fmod(nPos0, 1./fac) < very_small_number || my_fmod(nPos1, 1./fac) < very_small_number) return 1.0;
return 0.0;
}
void main() {
float very_small_number = 0.015 / u_scale;
float size = u_size;
vec3 nPos = pos / u_scale;
vec3 color = vec3(0.,0.,0.5);
float sqrt5 = 2.236068;
gl_FragColor = vec4(0.,0.,0.,1.);
float n = 5.;
float sLine = 1.;
float t1 = 1.;
float t2 = 1.;
if (pos.x == -size || pos.x == size) {
if (gridline(nPos.y, nPos.z, n, very_small_number) > 0.5) gl_FragColor.xyz += color * t1;
if (gridline(nPos.y*n, nPos.z*n, n, very_small_number) > 0.5) gl_FragColor.xyz += color * t2;
} else if (pos.y == -size || pos.y == size) {
if (gridline(nPos.x, nPos.z, n, very_small_number) > 0.5) gl_FragColor.xyz += color * t1;
if (gridline(nPos.x*n, nPos.z*n, n, very_small_number) > 0.5) gl_FragColor.xyz += color * t2;
} else if (pos.z == -size || pos.z == size) {
if (gridline(nPos.x, nPos.y, n, very_small_number) > 0.5) gl_FragColor.xyz += color * t1;
if (gridline(nPos.x*n, nPos.y*n, n, very_small_number) > 0.5) gl_FragColor.xyz += color * t2;
}
}
如您所见,我画了两组网格。它们代表两种不同的数量级。此时,它可以完美地缩放,但我想添加一个允许在不同的数量级之间淡入淡出的特征,并且只显示一定比例的网格。
这是App的原始外观,
看起来不错。当我放大时,
网格看起来更大。但我希望我的着色器可以在小网格内绘制新的网格,而不是绘制不再可见的大网格。
当我缩小时,
显示的网格太多,会影响用户体验。
那么,我如何实现不同数量级之间的转换并在转换之间应用渐变?抱歉我的英语不好。任何帮助表示赞赏。祝你有愉快的一天。
编辑1:
以下是试验的完整代码。
https://github.com/Jonathan-D-Ip/WebGLPlottingApp/blob/master/Display.html
答案 0 :(得分:2)
如果是我,我不会使用片段着色器作为网格我会使用线条。我在一个尺寸上绘制一个网格,如果我接近过渡并需要一个新网格,我会在另一个尺度上绘制另一个网格。两个网格都有alpha设置,所以我可以淡出它们。
这是一个例子,使用鼠标滚轮或等效物进行缩放。也许您可以根据您的首选解决方案调整这些想法。重要的部分可能是这部分
const gridLevel = Math.log10(zoom * zoomAdjust);
const gridFract = euclideanModulo(gridLevel, 1);
const gridZoom = Math.pow(10, Math.floor(gridLevel));
const alpha1 = Math.max((1 - gridFract) * 1);
gl.enable(gl.BLEND);
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA)
drawGrid(viewProjection, gridZoom, [0, alpha1, 0, alpha1]);
const alpha2 = Math.max(gridFract * 10) - 1;
if (alpha2 > 0) {
drawGrid(viewProjection, gridZoom * 10, [0, alpha2, 0, alpha2],);
}
zoom
从0.0001变为10000并表示距目标的距离。该代码使用Math.log10
来查找获得该缩放级别所需的功率。换句话说,如果缩放为100,那么gridLevel
= 2.如果zoom
为1000,那么gridLevel
= 3.从中我们可以获得{{1}中10的幂之间的小数当我们在缩放级别之间移动时,它总是在0到1的范围内。
gridFract
告诉我们绘制其中一个网格的比例(我们只删除gridZoom
的小数部分)然后将10增加到该幂。 gridLevel
是下一个最大的网格尺寸。
gridZoom * 10
是网格的alpha。 alpha1
是第二个网格的alpha。
alpha2
&#13;
const m4 = twgl.m4;
const gl = document.querySelector("canvas").getContext("webgl", {alpha: false});
const zoomElem = document.querySelector("#zoom");
const zoomAdjust = 1; // change to adjust when things start/end. Try 5 or .5 for example
let zoom = 1;
const gridVS = `
attribute vec4 position;
uniform mat4 matrix;
void main() {
gl_Position = matrix * position;
}
`;
const gridFS = `
precision mediump float;
uniform vec4 color;
void main() {
gl_FragColor = color;
}
`;
const gridProgramInfo = twgl.createProgramInfo(gl, [gridVS, gridFS]);
const gridPlaneLines = [];
const numLines = 100;
for (let i = 0; i <= 100; ++i) {
gridPlaneLines.push(0, i, 100, i);
gridPlaneLines.push(i, 0, i, 100);
}
const gridPlaneBufferInfo = twgl.createBufferInfoFromArrays(gl, {
position: { numComponents: 2, data: gridPlaneLines },
});
function drawGrid(viewProjection, scale, color) {
gl.useProgram(gridProgramInfo.program);
twgl.setBuffersAndAttributes(gl, gridProgramInfo, gridPlaneBufferInfo);
const scaling = [scale, scale, scale];
// draw Z plane
{
let matrix = m4.scale(viewProjection, scaling);
matrix = m4.rotateY(matrix, Math.PI);
twgl.setUniforms(gridProgramInfo, {
matrix,
color,
});
twgl.drawBufferInfo(gl, gridPlaneBufferInfo, gl.LINES);
}
// draw X plane
{
let matrix = m4.scale(viewProjection, scaling);
matrix = m4.rotateY(matrix, Math.PI * .5);
twgl.setUniforms(gridProgramInfo, {
matrix,
color,
});
twgl.drawBufferInfo(gl, gridPlaneBufferInfo, gl.LINES);
}
// draw Y plane
{
let matrix = m4.scale(viewProjection, scaling);
matrix = m4.rotateY(matrix, Math.PI);
matrix = m4.rotateX(matrix, Math.PI * .5);
twgl.setUniforms(gridProgramInfo, {
matrix,
color,
});
twgl.drawBufferInfo(gl, gridPlaneBufferInfo, gl.LINES);
}
}
function render() {
twgl.resizeCanvasToDisplaySize(gl.canvas, window.devicePixelRatio);
gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
zoomElem.textContent = zoom.toFixed(5);
const fov = degToRad(60);
const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
const zNear = zoom / 100;
const zFar = zoom * 100;
const projection = m4.perspective(fov, aspect, zNear, zFar);
const eye = [zoom * -10, zoom * 5, zoom * -10];
const target = [0, 0, 0];
const up = [0, 1, 0];
const camera = m4.lookAt(eye, target, up);
const view = m4.inverse(camera);
const viewProjection = m4.multiply(projection, view);
const gridLevel = Math.log10(zoom * zoomAdjust);
const gridFract = euclideanModulo(gridLevel, 1);
const gridZoom = Math.pow(10, Math.floor(gridLevel));
const alpha1 = Math.max((1 - gridFract) * 1);
gl.enable(gl.BLEND);
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA)
drawGrid(viewProjection, gridZoom, [0, alpha1, 0, alpha1]);
const alpha2 = Math.max(gridFract * 10) - 1;
if (alpha2 > 0) {
drawGrid(viewProjection, gridZoom * 10, [0, alpha2, 0, alpha2],);
}
}
render();
function euclideanModulo(n, m) {
return ((n % m) + m) % m;
};
function degToRad(deg) {
return deg * Math.PI / 180;
}
window.addEventListener('wheel', (e) => {
e.preventDefault();
const amount = e.deltaY;
if (e.deltaY < 0) {
zoom *= 1 - clamp(e.deltaY / -500, 0, 1);
} else {
zoom *= 1 + clamp(e.deltaY / 500, 0, 1);
}
zoom = clamp(zoom, 0.0001, 10000);
render();
});
window.addEventListener('resize', render);
function clamp(v, min, max) {
return Math.max(min, Math.min(max, v));
}
&#13;
body { margin: 0; }
canvas { width: 100vw; height: 100vh; display: block; }
#ui { position: absolute; left: 1em; top: 1em; padding: 1em; color: white; }
&#13;